This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
KingofGamesYami's profile picture

Optimizing commands calls

Started by KingofGamesYami, 12 June 2015 - 10:35 PM
KingofGamesYami #1
Posted 13 June 2015 - 12:35 AM
Spoiler

--find block position

local _POSITION_ = { commands.getBlockPosition() }

local tArgs = { ... }

local platform = {
  vector.new( 0, 0, 0 ),
  vector.new( 1, 0, 0 ),
  vector.new( 0, 0, 1 ),
  vector.new( 1, 0, 1 ),
  vector.new( -1, 0, 0 ),
  vector.new( 0, 0, -1 ),
  vector.new( -1, 0, -1 ),
  vector.new( 1, 0, -1 ),
  vector.new( -1, 0, 1 ),
  vector.new( 2, 0, 0 ),
  vector.new( 0, 0, 2 ),
  vector.new( -2, 0, 0 ),
  vector.new( 0, 0, -2 ),
}

local function getPlayerPosition( p )
  local success, t = commands.exec( "/tp " .. p .. " ~ ~ ~" )
  if not success then
    return false
  end
  local x, y, z = t[1]:match( "to (%-?%d+%.?%d*),(%-?%d+%.?%d*),(%-?%d+%.?%d*)" )
  return math.floor( x ), math.floor( y ), math.floor( z )
end


local last = {}

local id = os.startTimer( 0.7 )

while true do
  local x, y, z = getPlayerPosition( "KingofGamesYami" )
  local pos = vector.new( x, y - 1, z )
  local current = {}
  local toExecute = {}
  local time = os.clock()
  for i, v in ipairs( platform ) do
    local b = pos + v
    local bstr = b:tostring()
    local name = commands.getBlockInfo( b.x, b.y, b.z ).name
    if name == "minecraft:air" then
      current[ bstr ] = true
      toExecute[ #toExecute + 1 ] = "setblock " .. b.x .. " " .. b.y .. " " .. b.z .. " minecraft:glass"
    elseif last[ bstr ] then
      current[ bstr ] = true
    end
  end
  for k, v in pairs( last ) do
    local b = vector.new( loadstring( "return " .. k )() )
    if not current[ k ] then
      toExecute[ #toExecute + 1 ] = "setblock " .. b.x .. " " .. b.y .. " " .. b.z .. " minecraft:air"
    end
  end
  for i, v in ipairs( toExecute ) do
    commands.execAsync( v )
  end
  last = current
  print( os.clock() - time )
  while true do
    local event, tid = os.pullEvent( "timer" )
    if id == tid then
      break
    end
  end
  id = os.startTimer( 0.7 )
end

This program is intended to generate a glass platform underneath the player, automatically adjusting it as needed. The problem is, the main body of the code takes 0.65 seconds to execute - far too long to be of use for a running player.

I'm wondering what I could possibly do to reduce this chunk of time, if anything.
Lyqyd #2
Posted 13 June 2015 - 01:13 AM
This might help a bit. You've already got vectors, don't just trash 'em!


for i, v in ipairs( platform ) do
    local b = pos + v
    local bstr = b:tostring()
    local name = commands.getBlockInfo( b.x, b.y, b.z ).name
    if name == "minecraft:air" then
	  current[ bstr ] = v
	  if not last[bstr] then
        toExecute[ #toExecute + 1 ] = "setblock " .. b.x .. " " .. b.y .. " " .. b.z .. " minecraft:glass"
      end
      last[bstr] = nil
    end
  end
  for k, v in pairs( last ) do
	  toExecute[ #toExecute + 1 ] = "setblock " .. v.x .. " " .. v.y .. " " .. v.z .. " minecraft:air"
    end
  end
KingofGamesYami #3
Posted 13 June 2015 - 03:25 AM
That might help a small amount, but I doubt the overall speed will be greatly improved - I want to get down to 0.25 at the least.

Related Question: I remember hearing something about scoreboards placing a block when an event occurs, allowing command computers to interact with players in ways they normally would not be able to. Would it be possible to use these:

stat.crouchOneCm
stat.diveOneCm
stat.fallOneCm
stat.sprintOneCm
stat.swimOneCm
stat.walkOneCm

to detect when a player's position has changed (short of teleportation), and place a block when that happens? I know next to nothing about the scoreboard, I've yet to figure out how to tell it to do something, like place a block. Most likely, I would have it place a block of redstone, which *should* trigger a redstone event, which the command computer can respond to.

This will eliminate some of the irritating effects of using /tp player ~ ~ ~, because I can call it only when the position has changed enough that I may have to adjust the platform.
Bomb Bloke #4
Posted 13 June 2015 - 03:29 AM
commands.getBlockInfo() requests data from the world, then yields until it gets it, which isn't terribly fast. Using the parallel API, you can set up multiple requests at once, and dramatically speed up the process.

I realise my scripts tend to end up a bit too verbose to provide simple examples of, well, anything really, but if you take a look around lines 293 - 316 in my WorldPorter script you can see an example of this. I generate one function which checks one block and increases a counter so that when it's next called it'll check another block. I then fill a table with copies of pointers to that one function, and unpack it into a parallel API call (line 356).

You would also, ideally, not bother calling commands.getBlockInfo() on blocks which you already know the identity of (those being the ones you've already turned to glass).
KingofGamesYami #5
Posted 13 June 2015 - 02:44 PM
So, something like this:


local requests = {}
local function doRequest()
  while #requests > 1 do
    local r = table.remove( requests, 1 )
    --#get information and act on it
  end
end
--#add things to the requests table as I need to
parallel.waitForAll( doRequest, doRequest, doRequest, doRequest, doRequest )
Bomb Bloke #6
Posted 13 June 2015 - 03:40 PM
That sort of thing, yes. It'll go slightly faster if you remove from the tail-end of the table, though - that way it doesn't need to move all the other indexes down.
KingofGamesYami #7
Posted 13 June 2015 - 09:10 PM
Welp, I've optimized it a lot now, incorporating Lyqyd & Bomb Bloke's suggestions, it works much faster now.

But, after creating the platform, the next run deletes it - regardless of weather or not I'm still there.

Spoiler

--find block position

local _POSITION_ = { commands.getBlockPosition() }

local tArgs = { ... }

local platform = {
  vector.new( 0, 0, 0 ),
  vector.new( 1, 0, 0 ),
  vector.new( 0, 0, 1 ),
  vector.new( 1, 0, 1 ),
  vector.new( -1, 0, 0 ),
  vector.new( 0, 0, -1 ),
  vector.new( -1, 0, -1 ),
  vector.new( 1, 0, -1 ),
  vector.new( -1, 0, 1 ),
  vector.new( 2, 0, 0 ),
  vector.new( 0, 0, 2 ),
  vector.new( -2, 0, 0 ),
  vector.new( 0, 0, -2 ),
}

local function getPlayerPosition( p )
  local success, t = commands.exec( "/tp " .. p .. " ~ ~ ~" )
  if not success then
    return false
  end
  local x, y, z = t[1]:match( "to (%-?%d+%.?%d*),(%-?%d+%.?%d*),(%-?%d+%.?%d*)" )
  return math.floor( x ), math.floor( y ), math.floor( z )
end



local id = os.startTimer( 0.7 )

local p = {}

local function fillP()
  for k, v in pairs( platform ) do
    p[ k ] = v
  end
end

local last = {}
local current = {}
local pos
local toExecute = {}

local function checkPlatform()
  while #p > 0 do
    local  v = table.remove( p, #p )
    local b = pos + v
    local bstr = b:tostring()
    local name = (last[bstr] and "glass") or commands.getBlockInfo( b.x, b.y, b.z ).name
    if name == "minecraft:air" then
      current[ bstr ] = b
      if not last[ bstr ] then
        toExecute[ #toExecute + 1 ] = "setblock " .. b.x .. " " .. b.y .. " " .. b.z .. " minecraft:glass"
      end
    elseif name == "glass" then
      current[ bstr ] = b
    end
  end
end

while true do
  local x, y, z = getPlayerPosition( "KingofGamesYami" )
  pos = vector.new( x, y - 1, z )
  local time = os.clock()
  fillP()

  parallel.waitForAll( checkPlatform, checkPlatform, checkPlatform, checkPlatform, checkPlatform )

  for k, v in pairs( last ) do
    if not current[ bstr ] then
      toExecute[ #toExecute + 1 ] = "setblock " .. v.x .. " " .. v.y .. " " .. v.z .. " minecraft:air"
    end
  end

  for k, v in pairs( toExecute ) do
    commands.execAsync( v )
  end

  last = current
  current = {}
  toExecute = {}
  print( os.clock() - time )
  while true do
    local event, tid = os.pullEvent( "timer" )
    if id == tid then
      break
    end
  end
  id = os.startTimer( 0.7 )
end

Update: It appears the program is doing the following:

1. Creates Platform
2. Deletes Platform on next iteration
3. Creates Glass Platform anywhere but where the platform was previously

I'm stumped with the logic here - help is greatly appreciated.
Edited on 13 June 2015 - 07:59 PM
Lyqyd #8
Posted 13 June 2015 - 11:05 PM
You missed the last[bstr] = nil when adding entries to the current table, and you don't need to check the current table for the entries when removing glass blocks (since any entries in the last table which match entries in the current table will have been removed already).

You were checking the entries incorrectly in your cleanup loop, by the way. Using bstr there instead of k won't work. Changing the code to match my original code will fix the issue, and may or may not be faster. Or you could just check the right key.