Simulation = {} function Simulation.new(self) local o = { definitions = {}, wires = {}, gates = {}, nwires = 0, ngates = 0, ninports = 0, noutports = 0, groupqueue = {}, groupfxqueue = {}, gatequeue = {}, initqueue = {}, inputqueue = {}, tickqueue = {}, inputqueue_nonempty = false, initqueue_nonempty = false, callbacks = nil, currenttick = 0 } setmetatable(o, self) self.__index = self return o end function Simulation.addtoworld(self, obj, x, y, z) if self[x] == nil then self[x] = {} end if self[x][y] == nil then self[x][y] = {} end if self[x][y][z] == nil then self[x][y][z] = {} end self[x][y][z][obj] = obj end function Simulation.getfromworld(self, x, y, z) if self[x] == nil or self[x][y] == nil or self[x][y][z] == nil then return {} else return self[x][y][z] end end function Simulation.getdefinitionbyref(self, objref) return self.definitions[objref] end function Simulation.getgatebyref(self, objref) return self.gates[objref] end function Simulation.getwirebyref(self, objref) return self.wires[objref] end function Simulation.addgatedefinition(self, definition) self.definitions[definition.objref] = definition end function Simulation.addwire(self, wire) self.wires[Wire.getobjref(wire)] = wire local bounds = Wire.getbounds(wire) for x = bounds[1]+1, bounds[4]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do Simulation.addtoworld(self, wire, x, bounds[2], z) Simulation.addtoworld(self, wire, x, bounds[5], z) end end for y = bounds[2]+1, bounds[5]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do Simulation.addtoworld(self, wire, bounds[1], y, z) Simulation.addtoworld(self, wire, bounds[4], y, z) end end for x = bounds[1]+1, bounds[4]-1, 2 do for y = bounds[2]+1, bounds[5]-1, 2 do Simulation.addtoworld(self, wire, x, y, bounds[3]) Simulation.addtoworld(self, wire, x, y, bounds[6]) end end self.nwires = self.nwires + 1 Simulation.connectwire(self, wire) end function Simulation.addgate(self, gate) self.gates[gate.objref] = gate for k, port in pairs(gate.ports) do local offset = Port.getconnectionposition(port) Simulation.addtoworld(self, port, offset[1], offset[2], offset[3]) Simulation.connectport(self, port) if Port.gettype(port) == PortTypes.input then self.ninports = self.ninports + 1 elseif Port.gettype(port) == PortTypes.output then self.noutports = self.noutports + 1 end end self.ngates = self.ngates + 1 Simulation.queuegateinit(self, gate) Simulation.queuegate(self, gate) end function Simulation.removewire(self, objref) local wire = self.wires[objref] if wire ~= nil then self.wires[objref] = nil local bounds = Wire.getbounds(wire) for x = bounds[1]+1, bounds[4]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do self[x][bounds[2]][z][wire] = nil self[x][bounds[5]][z][wire] = nil end end for y = bounds[2]+1, bounds[5]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do self[bounds[1]][y][z][wire] = nil self[bounds[4]][y][z][wire] = nil end end for x = bounds[1]+1, bounds[4]-1, 2 do for y = bounds[2]+1, bounds[5]-1, 2 do self[x][y][bounds[3]][wire] = nil self[x][y][bounds[6]][wire] = nil end end self.nwires = self.nwires - 1 Group.removewire(Wire.getgroup(wire), wire) end end function Simulation.removegate(self, objref) local gate = self.gates[objref] if gate ~= nil then for k, port in pairs(gate.ports) do local pos = Port.getconnectionposition(port) self[pos[1]][pos[2]][pos[3]][port] = nil Group.removeport(Port.getgroup(port), port) if Port.gettype(port) == PortTypes.input then self.ninports = self.ninports - 1 elseif Port.gettype(port) == PortTypes.output then self.noutports = self.noutports - 1 end end end Simulation.dequeuegate(self, gate) self.gates[objref] = nil self.ngates = self.ngates - 1 end local function is_wire(obj) return obj.layer~=nil end function Simulation.connectwireat(self, wire, x, y, z) local objs = Simulation.getfromworld(self, x, y, z) for k, obj in pairs(objs) do if obj ~= wire and obj.group ~= nil then if is_wire(obj) then -- wire if Wire.getlayer(obj) == Wire.getlayer(wire) then -- same layer Group.addwire(obj.group, wire) end else -- port Group.addwire(obj.group, wire) end end end end function Simulation.connectwire(self, wire) local bounds = Wire.getbounds(wire) for x = bounds[1]+1, bounds[4]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do Simulation.connectwireat(self, wire, x, bounds[2], z) Simulation.connectwireat(self, wire, x, bounds[5], z) end end for y = bounds[2]+1, bounds[5]-1, 2 do for z = bounds[3]+1, bounds[6]-1, 2 do Simulation.connectwireat(self, wire, bounds[1], y, z) Simulation.connectwireat(self, wire, bounds[4], y, z) end end for x = bounds[1]+1, bounds[4]-1, 2 do for y = bounds[2]+1, bounds[5]-1, 2 do Simulation.connectwireat(self, wire, x, y, bounds[3]) Simulation.connectwireat(self, wire, x, y, bounds[6]) end end if Wire.getgroup(wire)==nil then Group.addwire(Group.new(Group), wire) end end function Simulation.connectport(self, port) local connpos = Port.getconnectionposition(port) local objs = Simulation.getfromworld(self, connpos[1], connpos[2], connpos[3]) for k, obj in pairs(objs) do if obj ~= port and obj.group ~= nil then Group.addport(obj.group, port) end end if Port.getgroup(port) == nil then Group.addport(Group.new(Group), port) end end function Simulation.queuegate(self, gate) if not gate.in_queue then table.insert(self.gatequeue, gate) gate.in_queue = true end end function Simulation.queuegatelater(self, gate, delay) local tick = self.currenttick + delay if self.tickqueue[tick] == nil then self.tickqueue[tick] = {} end self.tickqueue[tick][gate] = gate end function Simulation.queuegateinput(self, gate, argv) self.inputqueue[gate] = self.inputqueue[gate] or {} table.insert(self.inputqueue[gate], argv) self.inputqueue_nonempty = true end function Simulation.queuegateinit(self, gate) self.initqueue[gate] = gate self.initqueue_nonempty = true end function Simulation.queuegroup(self, group) if not group.in_queue then table.insert(self.groupqueue, group) group.in_queue = true end end function Simulation.dequeuegroup(self, group) if group.in_queue then array_remove(self.groupqueue, group) end self.groupfxqueue[group] = nil end function Simulation.dequeuegate(self, gate) if gate.in_queue then array_remove(self.gatequeue, gate) end self.initqueue[gate] = nil self.inputqueue[gate] = nil for tick, tickq in pairs(self.tickqueue) do tickq[gate] = nil end end function Simulation.queuegroupfx(self, group) self.groupfxqueue[group] = group end function Simulation.queuecallback(self, gate, ...) self.callbacks = self.callbacks or {} self.callbacks[gate.objref] = {...} end function Simulation.tick(self) for k, group in ipairs(self.groupqueue) do Group.update(group) group.in_queue = false end self.groupqueue = {} if self.initqueue_nonempty then for k, gate in pairs(self.initqueue) do Gate.init(gate) end self.initqueue = {} self.initqueue_nonempty = false end if self.inputqueue_nonempty then for gate, inputs in pairs(self.inputqueue) do for inputidx, argv in ipairs(inputs) do Gate.input(gate, argv) end end self.inputqueue = {} self.inputqueue_nonempty = false end if self.tickqueue[self.currenttick] ~= nil then for i, gate in pairs(self.tickqueue[self.currenttick]) do Simulation.queuegate(self, gate) end self.tickqueue[self.currenttick] = nil end for k, gate in ipairs(self.gatequeue) do Gate.logic(gate) gate.in_queue = false end self.gatequeue = {} self.currenttick = self.currenttick + 1 end function Simulation.sendfxupdate(self) for k, group in pairs(self.groupfxqueue) do if group.state ~= group.fxstate then group.fxstate = group.state local data = bool_to_int[group.state] for i, wire in pairs(group.wires) do data = data .. "\t" .. Wire.getobjref(wire) end client:send("WU\t" .. data .. "\n") end end self.groupfxqueue = {} end function Simulation.sendcallbacks(self) if self.callbacks ~= nil then local data = "CB" for objref, args in pairs(self.callbacks) do local escargs = {} for argidx, argv in ipairs(args) do table.insert(escargs, expandescape(tostring(argv))) end data = data .. "\t" .. objref .. "\t"..(#escargs)..(#escargs>0 and ("\t"..table.concat(escargs, "\t")) or "") end client:send(data .. "\n") self.callbacks = nil end end