From b3176c4ccf4106d6d9c6aed3b94da44b63a91688 Mon Sep 17 00:00:00 2001 From: Eagle517 <15026800+Eagle517@users.noreply.github.com> Date: Thu, 18 Apr 2019 23:00:32 -0600 Subject: [PATCH] Initial commit --- StartBackend.bat | 4 + sim/gate.lua | 53 +++++++ sim/gatedef.lua | 87 ++++++++++++ sim/group.lua | 147 +++++++++++++++++++ sim/main.lua | 312 +++++++++++++++++++++++++++++++++++++++++ sim/port.lua | 67 +++++++++ sim/simulation.lua | 343 +++++++++++++++++++++++++++++++++++++++++++++ sim/utility.lua | 53 +++++++ sim/wire.lua | 27 ++++ 9 files changed, 1093 insertions(+) create mode 100644 StartBackend.bat create mode 100644 sim/gate.lua create mode 100644 sim/gatedef.lua create mode 100644 sim/group.lua create mode 100644 sim/main.lua create mode 100644 sim/port.lua create mode 100644 sim/simulation.lua create mode 100644 sim/utility.lua create mode 100644 sim/wire.lua diff --git a/StartBackend.bat b/StartBackend.bat new file mode 100644 index 0000000..0dd6b81 --- /dev/null +++ b/StartBackend.bat @@ -0,0 +1,4 @@ +@echo off +cd "sim" +luajit "main.lua" +pause \ No newline at end of file diff --git a/sim/gate.lua b/sim/gate.lua new file mode 100644 index 0000000..e6f9acf --- /dev/null +++ b/sim/gate.lua @@ -0,0 +1,53 @@ +Gate = {} + +function Gate:new(objref, definition) + local o = { + objref = objref, + definition = definition, + ports = {} + } + setmetatable(o, self) + self.__index = self + return o +end + +function Gate:addport(port) + self.ports[#self.ports+1] = port + port.gate = self +end + +function Gate:setportstate(index, state) + self.ports[index]:setstate(state) +end + +-- function Gate:cb(...) +-- local args = {...} +-- local str = tostring(#args) + +-- for i, v in ipairs(args) do +-- v = bool_to_int[v] or tostring(v) +-- str = str .. "\t" .. tostring(v) +-- end + +-- sim:queuecallback(self, str) +-- end + +function Gate:cb(str) + sim:queuecallback(self, str) +end + +function Gate:queue(delay) + sim:queuegatelater(self, delay) +end + +function Gate:testlogic(n) + local time = os.clock() + for i = 1, n do + self.definition.logic(self) + end + client:send("TEST\t" .. (os.clock()-time) .. "\n") +end + +function Gate:gettick() + return sim.currenttick +end diff --git a/sim/gatedef.lua b/sim/gatedef.lua new file mode 100644 index 0000000..1ce8618 --- /dev/null +++ b/sim/gatedef.lua @@ -0,0 +1,87 @@ + +require "utility" + +GateDefinition = { + ports = {}, + logic = function(gate) end, + input = function(gate, argv) end +} + +function GateDefinition:new(objref, name, description, init, logic, input, global, ports) + local o = { + objref = objref, + name = name, + description = description, + ports = ports or {} + } + + init = collapseescape(init) + logic = collapseescape(logic) + input = collapseescape(input) + global = collapseescape(global) + + local initfunc = loadstring(tostring(init)) + if initfunc~=nil then + o.init = initfunc() or function()end + else + print("Error loading init func for ".. (name or "")) + end + + local logicfunc = loadstring(tostring(logic)) + if logicfunc ~= nil then + o.logic = logicfunc() or function()end + else + print("Error loading logic function for " .. (name or "")) + print(logic) + end + + local inputfunc = loadstring(tostring(input)) + if inputfunc ~= nil then + o.input = inputfunc() or function()end + else + print("Error loading input function for " .. (name or "")) + end + + local globalfunc = loadstring(tostring(global)) + if globalfunc~=nil then + globalfunc() + else + print("Error loading global function for ".. (name or "")) + end + + setmetatable(o, self) + self.__index = self + return o +end + +function GateDefinition:constructgate(objref, position, rotation) + local gate = Gate:new(objref, self) + + for i = 1, #self.ports do + local port = self.ports[i] + local type = port.type + local pos = {port.position[1], port.position[2], port.position[3]} + local dir = port.direction + + if dir < 4 then + dir = (dir + rotation) % 4 + end + + local x = pos[1] + + if rotation == 1 then + pos[1] = pos[2] + pos[2] = -x + elseif rotation == 2 then + pos[1] = -pos[1] + pos[2] = -pos[2] + elseif rotation == 3 then + pos[1] = -pos[2] + pos[2] = x + end + + gate:addport(Port:new(type, dir, {position[1]+pos[1], position[2]+pos[2], position[3]+pos[3]}, port.causeupdate)) + end + + return gate +end diff --git a/sim/group.lua b/sim/group.lua new file mode 100644 index 0000000..88123ab --- /dev/null +++ b/sim/group.lua @@ -0,0 +1,147 @@ +Group = {} + +function Group:new() + local o = { + state = false, + fxstate = false, + updatetick = 0, + wires = {}, + out_ports = {}, + in_ports = {}, + + nwires = 0, + nout_ports = 0, + nin_ports = 0 + } + setmetatable(o, self) + self.__index = self + return o +end + +function Group:getsize() + return self.nwires + self.nout_ports + self.nin_ports +end + +function Group:addwire(wire) + if wire.group ~= self then + if wire.group ~= nil then + self:mergewith(wire.group) + else + self.wires[wire] = wire + self.nwires = self.nwires + 1 + + wire.group = self + wire:update() + sim:queuegroup(self) + end + end +end + +function Group:removewire(wire) + wire.group = nil + self.wires[wire] = nil + + for k, wire in pairs(self.wires) do + wire.group = nil + end + + for k, port in pairs(self.out_ports) do + port.group = nil + end + + for k, port in pairs(self.in_ports) do + port.group = nil + end + + for k, wire in pairs(self.wires) do + sim:connectwire(wire) + end + + for k, port in pairs(self.out_ports) do + sim:connectport(port) + end + + for k, port in pairs(self.in_ports) do + sim:connectport(port) + end + + self.wires = {} + self.out_ports = {} + self.in_ports = {} + + self.nwires = 0 + self.nout_ports = 0 + self.nin_ports = 0 +end + +function Group:addport(port) + port.group = self + + if port.type == PortTypes.output then + self.out_ports[port] = port + self.nout_ports = self.nout_ports + 1 + sim:queuegroup(self) + elseif port.type == PortTypes.input then + self.in_ports[port] = port + self.nin_ports = self.nin_ports + 1 + port:setinputstate(self.state) + end +end + +function Group:removeport(port) + if port.type == PortTypes.output then + self.out_ports[port] = nil + self.nout_ports = self.nout_ports - 1 + elseif port.type == PortTypes.input then + self.in_ports[port] = nil + self.nin_ports = self.nin_ports - 1 + end + + sim:queuegroup(self) +end + +function Group:mergewith(group) + if self:getsize() >= group:getsize() then + group:mergeinto(self) + return self + else + self:mergeinto(group) + return group + end +end + +function Group:mergeinto(group) + for k, wire in pairs(self.wires) do + wire.group = nil + group:addwire(wire) + end + + for k, port in pairs(self.out_ports) do + group:addport(port) + end + + for k, port in pairs(self.in_ports) do + group:addport(port) + end + + self.wires = {} + self.out_ports = {} + self.in_ports = {} + + self.nwires = 0 + self.nout_ports = 0 + self.nin_ports = 0 +end + +function Group:setstate(state) + if state ~= self.state then + self.state = state + self.updatetick = sim.currenttick + + for k, port in pairs(self.in_ports) do + port:setinputstate(state) + end + + sim:queuegroupfx(self) + end +end diff --git a/sim/main.lua b/sim/main.lua new file mode 100644 index 0000000..8a96c8e --- /dev/null +++ b/sim/main.lua @@ -0,0 +1,312 @@ +dofile("simulation.lua") +dofile("group.lua") +dofile("wire.lua") +dofile("gatedef.lua") +dofile("gate.lua") +dofile("port.lua") + +OPT_TICK_ENABLED = true +OPT_TICK_TIME = 0 +OPT_FX_UPDATES = true +OPT_FX_TIME = 0.03 + +bool_to_int = {[false] = 0, [true] = 1} + +local lastticktime = 0 +local ticks = 0 +local tickrate = 0 +local lastmeasuretime = 0 +local lastfxtime = 0 + +local avgticks = {} +local totalticks = 0 + +sim = Simulation:new() + +local units = { + "uTz", + "mTz", + "Tz", + "kTz", + "MTz", + "GTz", +} + +local function round(x) + return math.floor(x+0.5) +end + +local function unitize(v) + local unit = 1 + v = v*1000000 + + while v >= 1000 do + v = v/1000 + unit = unit+1 + end + + local s + if v >= 100 then + s = "" .. round(v/10)*10 + elseif v >= 10 then + s = "" .. round(v) + elseif v >= 1 then + s = "" .. round(v*10)/10 + if #s == 1 then s = s .. ".0" end + else + s = 0 + end + + return s .. " " .. units[unit] +end + +function vectotable(vec) + local tbl = {} + for comp in string.gmatch(vec, "([^%s]+)") do + tbl[#tbl+1] = tonumber(comp) + end + return tbl +end + +function tabletostring(table) + local str = tostring(table[1]) + for i = 2, #table do + str = str .. " " .. tostring(table[i]) + end + return str +end + +function toboolean(value) + local num = tonumber(value) + if num == 1 then + return true + else + return false + end +end + +local function acceptclient() + client = server:accept() + client:settimeout(0) + local ip, port = client:getsockname() + print("Connection from " .. ip .. ":" .. port) +end + +local socket = require("socket") +server = assert(socket.bind("*", 25000)) +client = nil + +local ip, port = server:getsockname() +print("Server listening on " .. ip .. ":" .. port) + +acceptclient() + +while 1 do + local line, err = client:receive() + + if not err then + local data = {} + local i = 1 + line = line:gsub(";;", "; ;") + for str in string.gmatch(line, "([^;]+)") do + data[i] = str + i = i + 1 + end + + local i = 1 + while i <= #data do + if data[i] == "W" then + local min = vectotable(data[i+3]) + local max = vectotable(data[i+4]) + local bounds = {min[1], min[2], min[3], max[1], max[2], max[3]} + + local wire = Wire:new(tonumber(data[i+1]), tonumber(data[i+2]), bounds) + sim:addwire(wire) + + i = i + 4 + elseif data[i] == "G" then + local objref = tonumber(data[i+1]) + local definition = sim:getdefinitionbyref(tonumber(data[i+2])) or GateDefinition + local position = vectotable(data[i+3]) + local rotation = tonumber(data[i+4]) + local gate = definition:constructgate(objref, position, rotation) + + sim:addgate(gate) + --print(gate.objref) + gate.definition.init(gate) + gate.definition.logic(gate) + + i = i + 4 + elseif data[i] == "RW" then + sim:removewire(tonumber(data[i+1])) + i = i + 1 + elseif data[i] == "RG" then + sim:removegate(tonumber(data[i+1])) + i = i + 1 + elseif data[i] == "GD" then + --print(table.concat(data, "\n", i, math.min(#data, i+100))) + local objref = tonumber(data[i+1]) + local name = data[i+2] + local desc = data[i+3] + local init = data[i+4] + local logic = data[i+5] + local input = data[i+6] + local global = data[i+7] + local numports = tonumber(data[i+8]) + local ports = {} + + for a = i+9, numports*5+i+8, 5 do + local port = { + type = tonumber(data[a]), + position = vectotable(data[a+1]), + direction = tonumber(data[a+2]), + causeupdate = toboolean(data[a+3]), + name = data[a+4], + } + ports[#ports+1] = port + + if not port.direction then print(line) end + end + + local definition = GateDefinition:new(objref, name, desc, init, logic, input, global, ports) + sim:addgatedefinition(definition) + + i = i + 8 + numports*5 + elseif data[i] == "SL" then + local wire = sim:getwirebyref(tonumber(data[i+1])) + if wire ~= nil then + wire:setlayer(tonumber(data[i+2])) + end + + i = i + 2 + elseif data[i] == "SP" then + local gate = sim:getgatebyref(tonumber(data[i+1])) + if gate ~= nil then + gate.ports[tonumber(data[i+2])]:setstate(toboolean(data[i+3])) + end + + i = i + 3 + elseif data[i] == "SG" then + local wire = sim:getwirebyref(tonumber(data[i+1])) + if wire ~= nil then + wire.group:setstate(toboolean(data[i+2])) + end + + i = i + 2 + elseif data[i] == "OPT" then + local option = data[i+1] + local value = tonumber(data[i+2]) + + if option == "TICK_ENABLED" then + OPT_TICK_ENABLED = toboolean(value) + elseif option == "TICK_TIME" then + if value < 0 or value > 999999 then + value = 0 + end + OPT_TICK_TIME = value + elseif option == "FX_UPDATES" then + OPT_FX_UPDATES = toboolean(value) + elseif option == "FX_TIME" then + if value < 0 or value > 999999 then + value = 0 + end + OPT_FX_TIME = value + end + + i = i + 2 + elseif data[i] == "GINFO" then + local userid = data[i+1] + local objref = tonumber(data[i+2]) + + local obj = sim:getwirebyref(objref) or sim:getgatebyref(objref) + + if obj ~= nil then + local info = "" + + if obj.logictype == 0 then + local numportsi = 0; for k, wire in pairs(obj.group.in_ports ) do numportsi = numportsi+1 end + local numportso = 0; for k, wire in pairs(obj.group.out_ports) do numportso = numportso+1 end + + info = "\\c5WIRE
" .. (obj.group.state and "\\c2ON" or "\\c0OFF") .. "\n" .. + "IN PORTS: " ..numportsi.."\n".. + "OUT PORTS: "..numportso + ; + else + info = "\\c5" .. obj.definition.name .. "
" + for i = 1, #obj.ports do + info = info .. (obj.ports[i].state and "\\c2" or "\\c0") .. obj.definition.ports[i].name .. (i ~= #obj.ports and " " or "") + end + end + + if info ~= "" then + client:send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n") + end + end + + i = i + 2 + elseif data[i] == "SINFO" then + client:send("SINFO\t" .. data[i+1] .. "\t" .. sim.nwires .. "\t" .. sim.ngates .. "\t" .. sim.ninports .. "\t" .. sim.noutports .. "\n") + i = i + 1 + elseif data[i] == "TICK" then + sim:tick() + ticks = ticks + 1 + elseif data[i] == "TEST" then + local gate = sim:getgatebyref(tonumber(data[i+1])) + gate:testlogic(tonumber(data[i+2])) + i = i + 2 + elseif data[i] == "IN" then + local gate = sim:getgatebyref(tonumber(data[i+1])) + local argc = tonumber(data[i+2]) + local argv = {} + for a = i+3, i+3+argc-1 do + argv[#argv+1] = collapseescape(data[a]) + end + sim:queuegateinput(gate, argv) + + i = i+2+argc + end + + i = i + 1 + end + elseif err == "closed" then + sim = Simulation:new() + acceptclient() + end + + local time = os.clock() + + if OPT_TICK_ENABLED then + if time - lastticktime >= OPT_TICK_TIME then + sim:tick() + ticks = ticks + 1 + lastticktime = time + + local timetonext = time+OPT_TICK_TIME-os.clock() + + local sleeptime = timetonext*0.9 + if sleeptime>0 then socket.sleep(sleeptime) end + end + else + socket.sleep(0.1) + end + + if time-lastfxtime >= OPT_FX_TIME then + sim:sendfxupdate() + sim:sendcallbacks() + lastfxtime = time + end + + if time-lastmeasuretime >= 0.1 then + if #avgticks >= 20 then + totalticks = totalticks - table.remove(avgticks, 1) + end + + table.insert(avgticks, ticks) + totalticks = totalticks + ticks + + ticks = 0 + + client:send("TPS\t" .. unitize((totalticks/#avgticks)/0.1) .. "\n") + lastmeasuretime = os.clock() + end +end diff --git a/sim/port.lua b/sim/port.lua new file mode 100644 index 0000000..9ee5662 --- /dev/null +++ b/sim/port.lua @@ -0,0 +1,67 @@ +PortTypes = { + output = 0, + input = 1 +} + +PortDirections = { + [0] = {-1, 0, 0}, + [1] = {0, 1, 0}, + [2] = {1, 0, 0}, + [3] = {0, -1, 0}, + [4] = {0, 0, 1}, + [5] = {0, 0, -1} +} + +Port = { + logictype = 1, +} + +function Port:new(type, direction, position, causeupdate) + local o = { + type = type, + direction = direction, + position = position, + causeupdate = causeupdate, + state = false, + gate = nil, + group = nil, + } + setmetatable(o, self) + self.__index = self + return o +end + +function Port:setstate(state) + if state ~= self.state then + self.state = state + sim:queuegroup(self.group) + end +end + +function Port:setinputstate(state) + if state ~= self.state then + self.state = state + if self.causeupdate then + sim:queuegate(self.gate) + end + end +end + +function Port:getconnectionposition() + local offset = PortDirections[self.direction] + return {self.position[1]+offset[1], self.position[2]+offset[2], self.position[3]+offset[3]} +end + +function Port:isrising() + if self.group == nil then + return false + end + return self.group.state and (self.group.updatetick == sim.currenttick) +end + +function Port:isfalling() + if self.group == nil then + return false + end + return self.group.state == false and (self.group.updatetick == sim.currenttick) +end diff --git a/sim/simulation.lua b/sim/simulation.lua new file mode 100644 index 0000000..3afadb2 --- /dev/null +++ b/sim/simulation.lua @@ -0,0 +1,343 @@ + +require "utility" + +Simulation = {} + +function Simulation:new() + local o = { + definitions = {}, + wires = {}, + gates = {}, + + nwires = 0, + ngates = 0, + ninports = 0, + noutports = 0, + + groupqueue = {}, + groupfxqueue = {}, + gatequeue = {}, + initqueue = {}, + inputqueue = {}, + tickqueue = {}, + + callbacks = {}, + + currenttick = 0 + } + setmetatable(o, self) + self.__index = self + return o +end + +function Simulation:addtoworld(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(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(objref) + return self.definitions[objref] +end + +function Simulation:getgatebyref(objref) + return self.gates[objref] +end + +function Simulation:getwirebyref(objref) + return self.wires[objref] +end + +function Simulation:addgatedefinition(definition) + self.definitions[definition.objref] = definition +end + +function Simulation:addwire(wire) + self.wires[wire.objref] = wire + + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + self:addtoworld(wire, x, wire.bounds[2], z) + self:addtoworld(wire, x, wire.bounds[5], z) + end + end + + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + self:addtoworld(wire, wire.bounds[1], y, z) + self:addtoworld(wire, wire.bounds[4], y, z) + end + end + + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + self:addtoworld(wire, x, y, wire.bounds[3]) + self:addtoworld(wire, x, y, wire.bounds[6]) + end + end + + self.nwires = self.nwires + 1 + self:connectwire(wire) +end + +function Simulation:addgate(gate) + self.gates[gate.objref] = gate + + for k, port in pairs(gate.ports) do + local offset = port:getconnectionposition() + self:addtoworld(port, offset[1], offset[2], offset[3]) + self:connectport(port) + + if port.type == PortTypes.input then + self.ninports = self.ninports + 1 + elseif port.type == PortTypes.output then + self.noutports = self.noutports + 1 + end + end + + self.ngates = self.ngates + 1 +end + +function Simulation:removewire(objref) + local wire = self.wires[objref] + if wire ~= nil then + self.wires[objref] = nil + + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + sim[x][wire.bounds[2]][z][wire] = nil + sim[x][wire.bounds[5]][z][wire] = nil + end + end + + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + sim[wire.bounds[1]][y][z][wire] = nil + sim[wire.bounds[4]][y][z][wire] = nil + end + end + + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + sim[x][y][wire.bounds[3]][wire] = nil + sim[x][y][wire.bounds[6]][wire] = nil + end + end + + self.nwires = self.nwires - 1 + wire.group:removewire(wire) + end +end + +function Simulation:removegate(objref) + local gate = self.gates[objref] + if gate ~= nil then + for k, port in pairs(gate.ports) do + local pos = port:getconnectionposition() + self[pos[1]][pos[2]][pos[3]][port] = nil + port.group:removeport(port) + + if port.type == PortTypes.input then + self.ninports = self.ninports - 1 + elseif port.type == PortTypes.output then + self.noutports = self.noutports - 1 + end + end + end + + self.gates[objref] = nil + self.ngates = self.ngates - 1 +end + +function Simulation:connectwireat(wire, x, y, z) + local objs = self:getfromworld(x, y, z) + for k, obj in pairs(objs) do + if obj ~= wire and obj.group ~= nil then + if obj.logictype == 0 and obj.layer == wire.layer then + if obj.layer == wire.layer then + obj.group:addwire(wire) + end + elseif obj.logictype == 1 then + obj.group:addwire(wire) + end + end + end +end + +function Simulation:connectwire(wire) + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + self:connectwireat(wire, x, wire.bounds[2], z) + self:connectwireat(wire, x, wire.bounds[5], z) + end + end + + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + for z = wire.bounds[3]+1, wire.bounds[6]-1, 2 do + self:connectwireat(wire, wire.bounds[1], y, z) + self:connectwireat(wire, wire.bounds[4], y, z) + end + end + + for x = wire.bounds[1]+1, wire.bounds[4]-1, 2 do + for y = wire.bounds[2]+1, wire.bounds[5]-1, 2 do + self:connectwireat(wire, x, y, wire.bounds[3]) + self:connectwireat(wire, x, y, wire.bounds[6]) + end + end + + if wire.group == nil then + Group:new():addwire(wire) + end +end + +function Simulation:connectport(port) + local connpos = port:getconnectionposition() + local objs = self:getfromworld(connpos[1], connpos[2], connpos[3]) + for k, obj in pairs(objs) do + if obj ~= port and obj.group ~= nil then + obj.group:addport(port) + end + end + + if port.group == nil then + Group:new():addport(port) + end +end + +function Simulation:queuegate(gate) + self.gatequeue[gate] = gate +end + +function Simulation:queuegatelater(gate, delay) + local tick = self.currenttick + delay + if self.tickqueue[tick] == nil then + self.tickqueue[tick] = {} + end + table.insert(self.tickqueue[tick], gate) +end + +function Simulation:queuegateinput(gate, argv) + self.inputqueue[gate] = self.inputqueue[gate] or {} + table.insert(self.inputqueue[gate], argv) +end + +function Simulation:queuegateinit(gate) + self.initqueue[gate] = gate +end + +function Simulation:queuegroup(group) + self.groupqueue[group] = group +end + +function Simulation:queuegroupfx(group) + self.groupfxqueue[group] = group +end + +function Simulation:queuecallback(gate, ...) + self.callbacks[gate.objref] = {...} +end + +function Simulation:tick() + for k, group in pairs(self.groupqueue) do + local newstate = false + for j, port in pairs(group.out_ports) do + newstate = newstate or port.state + if newstate then + break + end + end + + group:setstate(newstate) + end + self.groupqueue = {} + + for k, gate in pairs(self.initqueue) do + gate.definition.init(gate) + end + self.initqueue = {} + + for gate, inputs in pairs(self.inputqueue) do + for inputidx, argv in ipairs(inputs) do + gate.definition.input(gate, argv) + end + end + self.inputqueue = {} + + if self.tickqueue[self.currenttick] ~= nil then + for i, gate in ipairs(self.tickqueue[self.currenttick]) do + self:queuegate(gate) + end + self.tickqueue[self.currenttick] = nil + end + + for k, gate in pairs(self.gatequeue) do + gate.definition.logic(gate) + end + self.gatequeue = {} + + self.currenttick = self.currenttick + 1 +end + +function Simulation:sendfxupdate() + 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] + + if OPT_FX_UPDATES then + for i, wire in pairs(group.wires) do + data = data .. "\t" .. wire.objref + end + else + for i, wire in pairs(group.wires) do + if wire.isvisual then + data = data .. "\t" .. wire.objref + end + end + end + + client:send("WU\t" .. data .. "\n") + end + end + + self.groupfxqueue = {} +end + +function Simulation:sendcallbacks() + if next(self.callbacks) ~= nil then + local data = "CB" + + for objref, args in pairs(self.callbacks) do + local argc = 0 + local escargs = {} + for argidx, argv in ipairs(args) do + table.insert(escargs, expandescape(tostring(argv))) + argc = argc+1 + end + data = data .. "\t" .. objref .. "\t"..argc..(#escargs>0 and ("\t"..table.concat(escargs, "\t")) or "") + end + + client:send(data .. "\n") + self.callbacks = {} + end +end diff --git a/sim/utility.lua b/sim/utility.lua new file mode 100644 index 0000000..039fd84 --- /dev/null +++ b/sim/utility.lua @@ -0,0 +1,53 @@ + +local escapes = { + {"\\", "b"}, + {"\t", "t"}, + {"\n", "n"}, + {"\r", "r"}, + {"\'", "a"}, + {"\"", "q"}, + {";" , "s"}, + {":" , "c"}, +} + +function expandescape(str) + local ostrt = {} + + local len = #str + for i=1, len do + local ci = str:sub(i, i) + + local co = ci + for escidx, esc in ipairs(escapes) do + if ci==esc[1] then co = "\\"..esc[2] end + end + + table.insert(ostrt, co) + end + + return table.concat(ostrt) +end + +function collapseescape(str) + local ostrt = {} + + local i = 1 + local len = #str + while i<=len do + local ci = str:sub(i, i) + + local co = ci + if ci=="\\" and i