Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- lua: turtle logistical network
- local node_list = {}
- -- given a turtle with id X:
- -- node_list[X] = { x-coord, y-coord, z-coord }
- local msg_ids = {}
- local current_pos = vector.new( 0, 0, 0 )
- local current_facing = 0
- local known_position_accurate = false
- local facingTable = {
- [0] = "south", -- increase in z
- [1]= "west", -- decrease in x
- [2] = "north", -- decrease in z
- [3] = "east", -- increase in x
- ["south"] = 0,
- ["west"] = 1,
- ["north"] = 2,
- ["east"] = 3,
- } -- turning right increases facing (modulo 4)
- local modem = {}
- local modem_side = ""
- local query_channel = 515
- local transaction_channel = 516
- local gps_channel = 65534
- local header = "TLogistics"
- local transaction_header = "TLogistics_Local"
- local logistical_data_packet = { id=os.computerID() }
- if turtle ~= nil then
- logistical_data_packet.turtle = true
- logistical_data_packet.crafty = (turtle.craft ~= nil)
- logistical_data_packet.equipped = (turtle.dig ~= nil)
- logistical_data_packet.attack = (turtle.attack ~= nil)
- else
- logistical_data_packet.turtle = false
- logistical_data_packet.crafty = false
- logistical_data_packet.equipped = false
- logistical_data_packet.attack = false
- end
- local rendezvous_timing_margin = 5
- local rendezvous_waiting_time = 15
- local operation_max_headstart = 7
- -- the time it takes for a turtle to move n blocks forward, or turn n times approximately equals
- -- .4(n-1)
- -- for example, it takes 1.65 (0.05 + .4(4)) seconds to rotate five times.
- -- the time it takes for a turtle to dig n blocks approximately equals
- -- .5n
- -- for example, it takes 2 seconds to dig five blocks.
- -- there also seems to be a 0.05 second delay before starting any action.
- -- do note that timings were performed using os.clock().
- local function get_move_time( n ) return (.4 * (n-1)) end
- local function get_dig_time( n ) return (.5 * n) end
- local function euclidean_distance( vec1, vec2 ) return math.sqrt((( vec1.x - vec2.x )^2) + (( vec1.y - vec2.y )^2) + (( vec1.z - vec2.z )^2)) end
- local function mc_time_to_absolute( t )
- -- t = (os.day()*24)+os.time()
- local hours = math.floor(t)
- local dec = t - hours
- local tenths = math.floor(dec*10)
- local hundredths = math.floor(dec*100) - (tenths*10)
- local thousandths = math.floor(dec*1000) - ((tenths*100) + (hundredths*10))
- return (hours * 50) + (tenths * 5) + (hundredths * 0.5) + (thousandths * 0.05)
- end
- local function get_mc_time()
- return (os.day()*24) + os.time()
- end
- -- get time since world start (since the start of the world's day/night cycle) in seconds (similar to os.clock() but for the whole world)
- local function get_global_clock()
- return mc_time_to_absolute( get_mc_time() )
- end
- local function wait_until_time( t )
- local diff = t - get_global_clock()
- os.sleep(diff)
- end
- --[[
- local node_class = {
- id = 0,
- connected = {},
- }
- local node_connection = { to_id, distance }
- ]]
- local move_queue = {} -- list of rendezvous objects: { vec=position-vector, op=operation, item=item, count=count, id=unique-id, best_time=best_wait_time, worst_time=worst_wait_time }
- -- operations supported:
- -- "put": requestor places count items in another turtle
- -- "get": requestor gets count items from another turtle
- -- "wait": requestor waits count seconds (for a furnace or etc.)
- local move_queue_locked = false
- local move_queue_lock_queue = {}
- local function lock_queue()
- local id = math.random(2^30)
- if move_queue_locked then
- table.insert( move_queue_lock_queue, id )
- while true do
- local ev, i2 = os.pullEvent("logistics_move_queue_unlock");
- if i2 == id then
- return
- end
- end
- else
- move_queue_locked = true
- end
- end
- local function unlock_queue( id )
- if #move_queue_lock_queue > 0 then
- local next_id = table.remove( move_queue_lock_queue, 1 )
- os.raiseEvent("logistics_move_queue_unlock", next_id)
- else
- move_queue_locked = false
- end
- end
- local setting_rendezvous_times = false
- local function send_message( data, modem )
- new_id = math.random( 1, 2^30 )
- while msg_ids[new_id] do
- new_id = math.random( 1, 2^30 )
- end
- data.header = header
- data.msg_id = new_id
- data.timestamp = get_global_clock()
- data.sender = os.computerID()
- modem.transmit( query_channel, query_channel, data )
- end
- local position_file = "current.position"
- local function load_position()
- f = fs.open(position_file, 'r')
- local x = tonumber( f.readLine() )
- local y = tonumber( f.readLine() )
- local z = tonumber( f.readLine() )
- current_facing = tonumber( f.readLine() )
- current_pos = vector.new( x, y, z )
- f.close()
- end
- local function save_position()
- --print("saving position...")
- f = fs.open(position_file, 'w')
- f.writeLine(tostring(current_pos.x))
- f.writeLine(tostring(current_pos.y))
- f.writeLine(tostring(current_pos.z))
- f.writeLine(tostring(current_facing))
- f.close()
- end
- local position_lock = "position.lock"
- local function lock_position( type )
- f = fs.open(position_lock, 'w')
- f.write(type)
- f.close()
- end
- local native_turtle = { up = turtle.up, down = turtle.down, forward = turtle.forward, back = turtle.back, turnLeft = turtle.turnLeft, turnRight = turtle.turnRight }
- turtle.up = function()
- lock_position("up")
- local stat = false
- if native_turtle.up() then
- current_pos.y = current_pos.y + 1
- if modem.transmit then
- send_message( { receiver = -1, type="position", vec=current_pos }, modem )
- end
- save_position()
- stat = true
- end
- fs.delete(position_lock)
- return stat
- end
- turtle.down = function()
- lock_position("down")
- local stat = false
- if native_turtle.down() then
- current_pos.y = current_pos.y - 1
- if modem.transmit then
- send_message( { receiver = -1, type="position", vec=current_pos }, modem )
- end
- save_position()
- stat = true
- end
- fs.delete(position_lock)
- return stat
- end
- turtle.forward = function()
- lock_position("forward")
- if not native_turtle.forward() then
- fs.delete( position_lock )
- return false
- end
- if current_facing == 0 then
- current_pos.z = current_pos.z + 1
- elseif current_facing == 1 then
- current_pos.x = current_pos.x - 1
- elseif current_facing == 2 then
- current_pos.z = current_pos.z - 1
- elseif current_facing == 3 then
- current_pos.x = current_pos.x + 1
- end
- if modem.transmit then
- send_message( { receiver = -1, type="position", vec=current_pos }, modem )
- end
- save_position()
- fs.delete(position_lock)
- return true
- end
- turtle.back = function()
- lock_position("back")
- if not native_turtle.back() then
- fs.delete( position_lock )
- return false
- end
- if current_facing == 0 then
- current_pos.z = current_pos.z - 1
- elseif current_facing == 1 then
- current_pos.x = current_pos.x + 1
- elseif current_facing == 2 then
- current_pos.z = current_pos.z + 1
- elseif current_facing == 3 then
- current_pos.x = current_pos.x - 1
- end
- if modem.transmit then
- send_message( { receiver = -1, type="position", vec=current_pos }, modem )
- end
- save_position()
- fs.delete( position_lock )
- return true
- end
- turtle.turnLeft = function()
- lock_position("left")
- if not native_turtle.turnLeft() then
- return false
- end
- current_facing = (current_facing-1) % 4
- save_position()
- fs.delete( position_lock )
- return true
- end
- turtle.turnRight = function()
- lock_position("right")
- if not native_turtle.turnRight() then
- return false
- end
- current_facing = (current_facing+1) % 4
- save_position()
- fs.delete( position_lock )
- return true
- end
- local function move_up()
- while true do
- present, data = turtle.inspectUp()
- if present then
- if string.sub(data.name, 1, 13) ~= "ComputerCraft" then -- don't damage infrastructure
- turtle.digUp()
- break
- end
- else
- break
- end
- os.sleep(0)
- end
- turtle.up()
- end
- local function move_down()
- while true do
- present, data = turtle.inspectDown()
- if present then
- if string.sub(data.name, 1, 13) ~= "ComputerCraft" then
- turtle.digDown()
- break
- end
- else
- break
- end
- os.sleep(0)
- end
- turtle.down()
- end
- local function move_forward()
- while true do
- present, data = turtle.inspect()
- if present then
- if string.sub(data.name, 1, 13) ~= "ComputerCraft" then
- turtle.dig()
- break
- end
- else
- break
- end
- os.sleep(0)
- end
- turtle.forward()
- end
- local function move_in_direction( dir, n_moves )
- while current_facing ~= dir do
- turtle.turnRight()
- end
- for i=1,n_moves do
- move_forward()
- end
- end
- function goTo( vec )
- displacement = current_pos - vec
- --[[
- print( "displacement:" )
- print( "x: " .. tostring(displacement.x) )
- print( "y: " .. tostring(displacement.y) )
- print( "z: " .. tostring(displacement.z) )
- ]]
- if( displacement.y > 0 ) then
- -- move down
- for i=1,math.abs(displacement.y) do
- move_down()
- end
- elseif (displacement.y < 0) then
- -- move up
- for i=1,math.abs(displacement.y) do
- move_up()
- end
- end
- if( displacement.x > 0 ) then
- -- move west
- move_in_direction( facingTable.west, math.abs(displacement.x) )
- elseif (displacement.x < 0) then
- -- move east
- move_in_direction( facingTable.east, math.abs(displacement.x) )
- end
- if( displacement.z > 0 ) then
- -- move north
- move_in_direction( facingTable.north, math.abs(displacement.z) )
- elseif (displacement.z < 0) then
- -- move south
- move_in_direction( facingTable.south, math.abs(displacement.z) )
- end
- os.sleep(0.15) -- sleep for a bit to give us time to fully move into position
- end
- local function create_node_graph()
- local graph = {}
- for i,vec1 in pairs(node_list) do
- local distances = {}
- for i2,vec2 in pairs(node_list) do
- if ( i2 ~= node_id ) then
- local dist = euclidean_distance( vec1, vec2 )
- table.insert( distances, { i2, dist } )
- end
- end
- table.sort( distances, function(a,b) return (a[2] < b[2]) end )
- graph[i] = distances
- end
- local distances = {}
- for i,v in pairs(node_list) do
- local vec2 = vector.new( unpack(v) )
- local dist = euclidean_distance( current_pos, vec2 )
- table.insert( distances, { i, dist } )
- end
- table.sort( distances, function(a,b) return (a[2] < b[2]) end )
- graph[os.computerID()] = distances
- return graph
- end
- local function dijkstra( graph, from_node, to_node )
- -- graph is of the format returned by create_node_graph().
- -- i.e graph[id] is a sorted list of distances to all other nodes in the format { id, distance }
- local unvisited = {}
- local tent_distances = {}
- local previous = {}
- local current_node = from_node
- for i,v in pairs(graph) do
- tent_distances[i] = -1
- unvisited[i] = false
- end
- tent_distances[from_node] = 0
- while true do
- neighbors = graph[current_node]
- for i=1, #neighbors do
- if unvisited[ neighbors[i][1] ] then -- if the neighbor we're looking at is unvisited...
- local poss_dist = tent_distances[current_node] + neighbors[i][2] -- poss_dist = tentative_distance[current_node] + distance( current_node, current_neighbor )
- if poss_dist < tent_distances[ neighbors[i][1] ] then
- tent_distances[ neighbors[i][1] ] = poss_dist
- previous[ neighbors[i][1] ] = current_node
- end
- end
- end
- unvisited[ current_node ] = false
- if( current_node == to_node ) then
- break
- end
- found_pair = false
- next_node = 0
- next_dist = 0
- for i,v in pairs(graph) do
- if unvisited[i] then
- if found_pair then
- if tent_distances[i] < next_dist then
- next_node = i
- next_dist = tent_distances[i]
- end
- else
- next_node = i
- next_dist = tent_distances[i]
- found_pair = true
- end
- end
- end
- if not found_pair then
- break
- end
- end
- return tent_distances, previous
- end
- local function wait_for_message( modem )
- modem.open( query_channel )
- modem.open( gps_channel )
- while true do
- local msg = { os.pullEvent("modem_message") }
- --print("received message on channel " .. msg[3] .. " with replies on " .. msg[4] )
- if ( msg[3] == gps_channel ) and ( msg[5] == "PING" ) then
- if known_position_accurate then
- modem.transmit( msg[4], gps_channel, { current_pos.x, current_pos.y, current_pos.z, logistical = logistical_data_packet } )
- end
- elseif (msg[3] == query_channel) then
- local data = msg[5]
- if (type(data) == "table") and (data.header == header) then
- if not msg_ids[ data.msg_id ] then
- modem.transmit( query_channel, query_channel, msg[5] ) -- retransmit
- msg_ids[ data.msg_id ] = true
- --print("received message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) )
- if (data.receiver == os.computerID()) or (data.receiver == -1) then
- return msg
- end
- end
- end
- end
- end
- end
- local function send_transaction_message( data, modem )
- data.header = transaction_header
- data.timestamp = get_global_clock()
- data.sender = os.computerID()
- modem.transmit( transaction_channel, transaction_channel, data )
- end
- local function wait_for_transaction_message( modem, timeout )
- modem.open( transaction_channel )
- modem.open( query_channel )
- modem.open( gps_channel )
- if timeout > 0 then
- local t = os.startTimer( timeout )
- while true do
- local msg = { os.pullEvent() }
- if msg[1] == "timer" then
- if msg[2] == t then
- return
- end
- elseif msg[1] == "modem_message" then
- if (msg[3] == transaction_channel) then
- local data = msg[5]
- if (type(data) == "table") and (data.header == transaction_header) then
- print("received transaction message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) .. " type: " .. tostring(data.type) )
- if (data.receiver == os.computerID()) or (data.receiver == -1) then
- return msg
- end
- end
- end
- end
- end
- else
- while true do
- local msg = { os.pullEvent("modem_message") }
- if (msg[3] == transaction_channel) then
- local data = msg[5]
- if (type(data) == "table") and (data.header == transaction_header) then
- print("received transaction message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) .. " type: " .. tostring(data.type) )
- if (data.receiver == os.computerID()) or (data.receiver == -1) then
- return msg
- end
- end
- end
- end
- end
- end
- local function wait_for_unit( side, id, timeout )
- local start = os.clock()
- while true do
- if (peripheral.getType(side) == "turtle") or (peripheral.getType(side) == "computer") then
- -- peripheral.call(side, "getID") is not reliable for some reason
- --if peripheral.call(side, "getID") == id then
- return true
- --end
- end
- if (os.clock() - start) >= timeout then
- return false
- end
- os.sleep( 0.5 )
- end
- end
- local function do_transfer( direction, op )
- local fulfilled = {}
- for j=1, #op.item do
- fulfilled[j] = false
- end
- for i=1,16 do
- -- find slots
- d = turtle.getItemDetail(i)
- if d then
- print( "item in slot " .. tostring(i) .. ": " .. d.name)
- for j=1,#op.item do
- if not fulfilled[j] then
- if d.name == op.item[j] then
- turtle.select( i )
- if direction == "up" then
- turtle.dropUp( op.count[j] )
- elseif direction == "down" then
- turtle.dropDown( op.count[j] )
- else
- turtle.drop( op.count[j] )
- end
- if d.count >= op.count[j] then
- fulfilled[j] = true
- else
- op.count[j] = op.count[j] - d.count
- end
- d = turtle.getItemDetail(i)
- if not d then
- break
- end
- end
- end
- end
- end
- end
- end
- local function rendezvous_transaction( op, modem )
- modem.open(transaction_channel)
- if op.requestee == os.computerID() then
- send_transaction_message( { type="arrived", receiver = op.requestor }, modem )
- else
- send_transaction_message( { type="arrived", receiver = op.requestee }, modem )
- end
- print("requestor: " .. tostring(op.requestor))
- print("requestee: " .. tostring(op.requestee))
- print("we are: " .. tostring(os.computerID()))
- while true do
- local msg = wait_for_transaction_message( modem, rendezvous_waiting_time )
- if not msg then
- return
- end
- local data = msg[5]
- print("got transaction message, type=" .. data.type)
- if (data.type == "end_transaction") or (data.type == "transaction_error") then
- return
- end
- -- requestor is always 1 y-level higher than the requestee at the rendezvous point
- -- they should also have the same x/z coordinates
- -- though we need to check to see if this is true.
- if data.type == "arrived" then
- if op.requestor == os.computerID() then -- we are requestor
- if wait_for_unit( "bottom", op.requestee, rendezvous_waiting_time ) then
- if op.op == "put" then
- print("performing transaction...")
- -- we need to put the things in now
- do_transfer( "down", op )
- send_transaction_message( { type="end_transaction", receiver = op.requestee }, modem )
- return
- else
- print("waiting for requestee to perform transaction...")
- send_transaction_message( { type="transaction", receiver = op.requestee }, modem )
- end
- else print("error: requestee not in position -- nothing's there") send_transaction_message( { type="transaction_error", receiver = op.requestor }, modem ) return end -- if there isn't a turtle beneath us, then something went wrong; exit early
- else -- we are requestee
- if wait_for_unit( "top", op.requestor, rendezvous_waiting_time ) then
- if op.op == "get" then
- print("performing transaction...")
- do_transfer( "up", op )
- send_transaction_message( { type="end_transaction", receiver = op.requestor }, modem )
- return
- else
- print("waiting for requestor to perform transaction...")
- send_transaction_message( { type="transaction", receiver = op.requestor }, modem )
- end
- else print("error: requestor not in position -- nothing's there") send_transaction_message( { type="transaction_error", receiver = op.requestee }, modem ) return end
- end
- elseif data.type == "transaction" then
- print("are we requestor: " .. tostring(op.requestor == os.computerID()))
- if op.requestor == os.computerID() then -- we are requestor
- print("Waiting for requestee...")
- if wait_for_unit( "bottom", op.requestee, rendezvous_waiting_time ) then
- if op.op == "put" then
- print("performing transaction...")
- do_transfer( "down", op )
- end
- end -- if something goes wrong, then fall through
- send_transaction_message( { type="end_transaction", receiver = op.requestee }, modem )
- else -- we are requestee
- print("Waiting for requestor...")
- if wait_for_unit( "top", op.requestor, rendezvous_waiting_time ) then
- if op.op == "get" then
- print("performing transaction...")
- do_transfer( "up", op )
- end
- end -- if something goes wrong, then fall through
- send_transaction_message( { type="end_transaction", receiver = op.requestor }, modem )
- end
- return
- elseif data.type == "transaction_error" then
- return
- elseif data.type == "end_transaction" then
- return
- end
- end
- end
- function do_moves()
- local last_move_end = 0
- while true do
- if #move_queue == 0 then
- os.pullEvent("logistics_queue_push")
- end
- print("move queue length: " .. tostring(#move_queue))
- op = table.remove(move_queue, 1)
- if op.op ~= "empty" then
- goTo( op.vec )
- if op.op == "wait" then
- os.queueEvent("logistics_arrived", op.id)
- while true do
- local event, id = os.pullEvent("logistics_continue")
- if id == op.id then
- break
- end
- end
- elseif op.op == "get" or op.op == "put" then
- rendezvous_transaction( op, modem )
- elseif op.op == "static_get" then
- local u, ud = turtle.inspectUp()
- local d, dd = turtle.inspectDown()
- if u and ud.name == op.container then
- for i=1, #op.slot do
- turtle.select( op.slot[i] or j )
- if type(op.count[i]) == "number" then
- turtle.suckUp( op.count[i] )
- else
- turtle.suckUp()
- end
- end
- elseif d and dd.name == op.container then
- for i=1, #op.slot do
- turtle.select( op.slot[i] or j )
- if type(op.count[i]) == "number" then
- turtle.suckDown( op.count[i] )
- else
- turtle.suckDown()
- end
- end
- else
- for i=1, 4 do
- local f, fd = turtle.inspect()
- if f and fd.name == op.container then
- for j=1, #op.slot do
- turtle.select( op.slot[j] or j )
- if type(op.count[i]) == "number" then
- turtle.suck( op.count[i] )
- else
- turtle.suck()
- end
- end
- break
- end
- turtle.turnRight()
- end
- end
- elseif op.op == "static_put" then
- local u, ud = turtle.inspectUp()
- local d, dd = turtle.inspectDown()
- if u and ud.name == op.container then
- for i=1, #op.slot do
- turtle.select( op.slot[i] or j )
- if type(op.count[i]) == "number" then
- turtle.dropUp( op.count[i] )
- else
- turtle.dropUp()
- end
- end
- elseif d and dd.name == op.container then
- for i=1, #op.slot do
- turtle.select( op.slot[i] or j )
- if type(op.count[i]) == "number" then
- turtle.dropDown( op.count[i] )
- else
- turtle.dropDown()
- end
- end
- else
- for i=1, 4 do
- local f, fd = turtle.inspect()
- if f and fd.name == op.container then
- for j=1, #op.slot do
- turtle.select( op.slot[j] or j )
- if type(op.count[j]) == "number" then
- turtle.drop( op.count[j] )
- else
- turtle.drop()
- end
- end
- break
- end
- turtle.turnRight()
- end
- end
- end
- --[[
- if #move_queue == 1 then
- if move_queue[1].id == op.id then
- table.remove(move_queue, 1)
- end
- end
- ]]
- os.queueEvent("logistics_operation_complete", op.id)
- print("transaction complete: " .. tostring(#move_queue) .. " transactions remaining")
- if #move_queue == 0 then
- if _G.homeVec ~= nil then
- goTo( homeVec )
- end
- --[[else
- -- if we got things done pretty quickly, then just wait until the next agreed time - operation_max_headstart
- -- this is to avoid timing out while waiting for the other party (ex. with rendezvouses)
- if get_global_clock() < (op.time - operation_max_headstart) then
- print("very early -- sleeping till operation_max_headstart (" .. tonumber((op.time - operation_max_headstart) - get_global_clock()) .. " seconds)")
- wait_until_time(op.time - operation_max_headstart)
- end]]
- end
- else
- os.queueEvent("logistics_waiting", op.id)
- while true do
- local event, id = os.pullEvent("logistics_continue")
- if id == op.id then
- break
- end
- end
- end
- end
- end
- function handle_requests()
- modem.open( transaction_channel )
- modem.open( query_channel )
- modem.open( gps_channel )
- -- gps:
- -- clients send 1 string: "PING"
- -- servers send tables of coordinates: { [1]=x, y, z }
- while true do
- msg = wait_for_message( modem )
- local data = msg[5]
- local sender = data.sender
- local receiver = data.receiver
- local timestamp = data.timestamp
- if data.type == "rendezvous" then
- if setting_rendezvous_times then
- send_message( { sender = os.computerID(), receiver = sender, type="rendezvous_retry", op = data.op }, modem )
- else
- -- Rendezvous request: Ask to meet at a certain location (for item swapping, etc.)
- -- Format: { start=start-time, vec=rendezvous-position, op=operation, item=item, count=count }
- -- Response: affirmative, move start time, projected move end time (optimistic), projected move end time (worst-case)
- -- the optimistic end time assumes that all moves before this take their optimistic move times (no obstructions).
- -- the worst-case end time assumes that all moves before this take their worst-case move times (every position obstructed by a block).
- -- the worst case end time does not take other computers / infrastructure (modems, etc.) into account.
- local displacement = vector.new(0,0,0)
- local op_vec = vector.new( data.op.vec.x, data.op.vec.y, data.op.vec.z )
- if #move_queue == 0 then
- displacement = current_pos - op_vec
- else
- displacement = move_queue[#move_queue].vec - op_vec
- end
- -- calculate movement times
- local move_time = get_move_time( math.abs(displacement.x) ) + get_move_time( math.abs(displacement.y) ) + get_move_time( math.abs(displacement.z) )
- local dig_time = get_dig_time( math.abs(displacement.x) ) + get_dig_time( math.abs(displacement.y) ) + get_dig_time( math.abs(displacement.z) )
- local worst_case = move_time + dig_time
- local start = worst_case
- if #move_queue == 0 then
- worst_case = worst_case + get_global_clock() + rendezvous_timing_margin + rendezvous_waiting_time
- start = start + get_global_clock()
- else
- worst_case = worst_case + move_queue[#move_queue].time + rendezvous_timing_margin + rendezvous_waiting_time
- start = start + move_queue[#move_queue].time
- end
- local best_rendezvous_time = math.max( data.op.time, worst_case );
- local best_rendezvous_start = math.max( data.op.start, start );
- local op = { }
- op.vec=vector.new( data.op.vec.x, data.op.vec.y, data.op.vec.z )
- op.op=data.op.op
- op.item=data.op.item
- op.count=data.op.count
- op.time = best_rendezvous_time
- op.start = best_rendezvous_start
- op.id = data.op.id
- op.requestor = data.op.requestor
- op.requestee = os.computerID()
- op.init = false
- table.insert(move_queue, op)
- send_message( { receiver = data.sender, type="rendezvous_set", ["op"]=op }, modem )
- os.queueEvent("logistics_queue_push")
- end
- elseif (data.type == "rendezvous_set") or (data.type == "rendezvous_retry") then
- os.queueEvent("logistics_rendezvous_reply", msg)
- elseif data.type == "unit_query" then
- -- Query request: Get information on items / unit info
- -- Format: { type="unit_query" }
- -- Response: { type="unit_query_response", inv=<inv-data>, unit_info=<logistical-data-packet>, position, facing, n_tasks, etc. }
- local inv = {}
- for i=1, 16 do
- local s = turtle.getItemDetail(i)
- if s then
- inv[i] = s
- end
- end
- send_message( { receiver = data.sender, type="unit_query_response", inv=inv, unit_info=logistical_data_packet, position = current_pos, facing = current_facing, n_tasks = #move_queue }, modem )
- elseif data.type == "search_query" then
- -- Broadcast Query request: locate resources (other nodes, certain materials, etc.)
- -- Format: { type="search_query", resource={ <resource-type: item/block name ("minecraft:stone", "minecraft:sign"), unit type ("crafty", "chest", "control", "mining"), etc.> .. }, types={ <"item"/"unit"> .. } count = { counts .. } }
- -- * on counts: if count == 0, then number doesn't matter. (match any number)
- -- Response: If one of the terms is fulfilled:
- -- { type="search_query_response", resource={ <fulfilled terms> }, types={ <ditto> }, count={ <number_provided or number_searched> } }
- local fulfilled_resource = {}
- local fulfilled_type = {}
- local fulfilled_count = {}
- print("received search query.")
- for i=1, #data.resource do
- if data.types[i] == "item" then
- for j=1, 16 do
- local s = turtle.getItemDetail(j)
- if s then
- if s.name == data.resource[i] then
- if s.count >= data.count[i] then
- print("match at slot " .. tostring(j) .. " for request " .. tostring(i))
- table.insert( fulfilled_resource, data.resource[i] )
- table.insert( fulfilled_type, data.types[i] )
- table.insert( fulfilled_count, s.count )
- end
- end
- end
- end
- elseif data.types[i] == "unit" then
- if logistical_data_packet[data.resource[i]] then
- print("match for request " .. tostring(i))
- table.insert( fulfilled_resource, data.resource[i] )
- table.insert( fulfilled_type, data.types[i] )
- table.insert( fulfilled_count, 1 )
- end
- end
- end
- if #fulfilled_resource > 0 then
- print("sending back query matches...")
- send_message( { receiver = data.sender, type="search_query_response", resource=fulfilled_resource, types=fulfilled_type, count=fulfilled_count }, modem )
- end
- elseif data.type == "unit_query_response" then
- os.queueEvent("logistics_unit_query_response", msg)
- elseif data.type == "search_query_response" then
- os.queueEvent("logistics_search_query_response", msg)
- end
- end
- end
- local function get_op_times( vec, additional_time )
- local displacement = vector.new(0,0,0)
- if #move_queue == 0 then
- displacement = current_pos - vec
- else
- displacement = move_queue[#move_queue].vec - vec
- end
- -- calculate movement times
- local move_time = get_move_time( math.abs(displacement.x) ) + get_move_time( math.abs(displacement.y) ) + get_move_time( math.abs(displacement.z) )
- local dig_time = get_dig_time( math.abs(displacement.x) ) + get_dig_time( math.abs(displacement.y) ) + get_dig_time( math.abs(displacement.z) )
- local worst_case = move_time + dig_time
- if #move_queue == 0 then
- worst_case = worst_case + get_global_clock() + additional_time
- else
- worst_case = worst_case + move_queue[#move_queue].time + additional_time
- end
- local start = worst_case - additional_time
- return worst_case, start
- end
- function set_rendezvous( id, op_type, vec, item, count )
- op = { op=op_type, item=item, count=count, id = math.random(0, (2^30)), ["vec"] = vec, requestor = os.computerID(), requestee = id, init = true }
- op.time, op.start = get_op_times( vec, rendezvous_timing_margin + rendezvous_waiting_time )
- setting_rendezvous_times = true -- lock the queue
- send_message( { receiver = id, type = "rendezvous", op=op }, modem )
- local n_retries = 0
- local timer = os.startTimer(5)
- while true do
- local ev, d = os.pullEvent()
- if ev == "timer" then
- if d == timer then -- timeout, give up
- return
- end
- elseif ev == "logistics_rendezvous_reply" then
- local msg = d
- if( msg[5].op.id == op.id ) then
- if( msg[5].type == "rendezvous_set") then
- msg[5].op.init = true
- msg[5].op.vec.y = msg[5].op.vec.y+1
- table.insert(move_queue, msg[5].op)
- setting_rendezvous_times = false
- return msg[5].op
- elseif (msg[5].type == "rendezvous_retry") then
- if n_retries > 3 then -- target unit too busy, give up
- return
- end
- n_retries = n_retries+1
- os.sleep( math.random(1,5) )
- send_message( { receiver = id, type = "rendezvous", op=op }, modem )
- timer = os.startTimer(5)
- end
- end
- end
- end
- end
- function do_background_processes( m )
- if m ~= nil then
- modem = m
- end
- modem.open( transaction_channel )
- modem.open( query_channel )
- modem.open( gps_channel )
- parallel.waitForAll( handle_requests, do_moves )
- end
- local function round( a ) if( type(a) ~= "number" ) then return a else if( a < 0 ) then return math.ceil(a) else return math.floor(a) end end end
- function gps_bootstrap( facing )
- current_facing = facing
- local x, y, z = gps.locate( 5 )
- current_pos = vector.new( round(x), round(y), round(z) )
- known_position_accurate = true
- local modems = {}
- local found_wireless = false
- for _, v in pairs(rs.getSides()) do
- if peripheral.getType( v ) == "modem" then
- if not peripheral.call( v, "isWireless" ) then
- table.insert(modems, v)
- else
- modem = peripheral.wrap(v)
- modem_side = v
- found_wireless = true
- break
- end
- end
- end
- if not found_wireless then
- if #modems > 0 then
- modem_side = table.remove(modems, 1)
- modem = peripheral.wrap( modem_side )
- end
- end
- if modem.transmit ~= nil then
- modem.open( transaction_channel )
- modem.open( query_channel )
- modem.open( gps_channel )
- end
- end
- function bootstrap()
- if fs.exists(position_file) then
- load_position()
- if fs.exists(position_lock) then
- -- position is unreliable, let's see how:
- local f = fs.open(position_lock, 'r')
- local type = f.readAll()
- f.close()
- if type == "right" then
- -- for now, let's assume that we did do the turn
- current_facing = (current_facing + 1) % 4
- elseif type == "left" then
- current_facing = (current_facing - 1) % 4
- else
- gps_bootstrap( current_facing )
- save_position()
- fs.delete(position_lock)
- return
- end
- end
- else
- print("determining facing.")
- local x, y, z = gps.locate( 5 )
- pos1 = vector.new( round(x), round(y), round(z) )
- print(" initial position: " .. tostring(pos1.x) .. ', ' .. tostring(pos1.y) .. ', ' .. tostring(pos1.z) )
- local n_turns = 0
- while turtle.detect() do
- n_turns = n_turns + 1
- if n_turns >= 4 then
- print("all positions blocked, returning now...")
- return false
- end
- native_turtle.turnRight()
- end
- native_turtle.forward()
- local x, y, z = gps.locate( 5 )
- pos2 = vector.new( round(x), round(y), round(z) )
- print(" secondary position: " .. tostring(pos2.x) .. ', ' .. tostring(pos2.y) .. ', ' .. tostring(pos2.z) )
- displacement = pos2 - pos1
- print(" displacement: " .. tostring(displacement.x) .. ', ' .. tostring(displacement.y) .. ', ' .. tostring(displacement.z) )
- if displacement.x ~= 0 then
- if displacement.x > 0 then
- -- we moved east.
- current_facing = facingTable.east
- else
- current_facing = facingTable.west
- end
- elseif displacement.z ~= 0 then
- if displacement.z > 0 then
- -- we moved south.
- current_facing = facingTable.south
- else
- current_facing = facingTable.north
- end
- end
- gps_bootstrap( current_facing )
- save_position()
- return
- end
- known_position_accurate = true
- local modems = {}
- local found_wireless = false
- for _, v in pairs(rs.getSides()) do
- if peripheral.getType( v ) == "modem" then
- if not peripheral.call( v, "isWireless" ) then
- table.insert(modems, v)
- else
- modem = peripheral.wrap(v)
- modem_side = v
- found_wireless = true
- break
- end
- end
- end
- if not found_wireless then
- if #modems > 0 then
- modem_side = table.remove(modems, 1)
- modem = peripheral.wrap( modem_side )
- end
- end
- if modem.transmit ~= nil then
- modem.open( transaction_channel )
- modem.open( query_channel )
- modem.open( gps_channel )
- end
- end
- function set_current_position( x, y, z, f )
- current_facing = f
- current_pos = vector.new( x, y, z )
- known_position_accurate = true
- end
- function print_debug_info()
- print( "x:", current_pos.x )
- print( "y:", current_pos.y )
- print( "z:", current_pos.z )
- print( "f:", current_facing )
- print( "accurate: ", known_position_accurate )
- print( "modem initialized: ", (modem.transmit ~= nil))
- print( "modem side: ", modem_side)
- end
- function schedule_move_to( vec, time_spent )
- op = { op="wait", item=item, count=time_spent, id = math.random(0, (2^30)), ["vec"] = vec }
- op.time, op.start = get_op_times( vec, time_spent )
- table.insert(move_queue, op)
- return op.id
- end
- function wait_move_to( vec, time_spent )
- local id = schedule_move_to( vec, time_spent )
- while true do
- local ev, id2 = os.pullEvent("logistics_arrived")
- if id == id2 then
- return id
- end
- end
- end
- function schedule_wait( time_spent )
- op = { op="empty", count=time_spent, id = math.random(0, (2^30)) }
- if #move_queue == 0 then
- op.time = get_global_clock() + time_spent
- op.start = get_global_clock()
- else
- op.time = move_queue[#move_queue].time + time_spent
- op.start = move_queue[#move_queue].time
- end
- table.insert(move_queue, op)
- return op.id
- end
- function schedule_static_rendezvous( vec, op, container, slot, count )
- op = { op="static_" .. op, container=container, slot=slot, count=count, id = math.random(0, (2^30)), ["vec"] = vec }
- op.time, op.start = get_op_times( vec, rendezvous_timing_margin + rendezvous_waiting_time )
- table.insert(move_queue, op)
- return op.id
- end
- function get_current_pos()
- return vector.new( current_pos.x, current_pos.y, current_pos.z )
- end
- function continue_moves(id)
- os.queueEvent("logistics_continue", id)
- end
- function push_moves()
- os.queueEvent("logistics_queue_push")
- end
- function network_search( items, types, counts )
- send_message( { receiver = -1, type="search_query", resource=items, types=types, count = counts }, modem )
- local responses = {}
- local timer = os.startTimer(5)
- while true do
- local ev = { os.pullEvent() }
- if ev[1] == "timer" then
- break
- elseif ev[1] == "logistics_search_query_response" then
- local msg = ev[2]
- local data = msg[5]
- table.insert( responses, { id = data.sender, resource=data.resource, types=data.types, count=data.count })
- end
- end
- return responses
- end
- function unit_query( id )
- send_message( {receiver = id, type="unit_query"}, modem )
- local responses = {}
- local timer = os.startTimer(5)
- while true do
- local ev = { os.pullEvent() }
- if ev[1] == "timer" then
- break
- elseif ev[1] == "logistics_unit_query_response" then
- local msg = ev[2]
- local data = msg[5]
- table.insert( responses, { id = data.sender, inv=data.inv, unit_info=data.unit_info, position = data.position, facing = data.facing, n_tasks = data.n_tasks })
- end
- end
- return responses
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement