Initial commit
This commit is contained in:
commit
b3176c4ccf
4
StartBackend.bat
Normal file
4
StartBackend.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd "sim"
|
||||||
|
luajit "main.lua"
|
||||||
|
pause
|
53
sim/gate.lua
Normal file
53
sim/gate.lua
Normal file
@ -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
|
87
sim/gatedef.lua
Normal file
87
sim/gatedef.lua
Normal file
@ -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
|
147
sim/group.lua
Normal file
147
sim/group.lua
Normal file
@ -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
|
312
sim/main.lua
Normal file
312
sim/main.lua
Normal file
@ -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<br>" .. (obj.group.state and "\\c2ON" or "\\c0OFF") .. "\n" ..
|
||||||
|
"IN PORTS: " ..numportsi.."\n"..
|
||||||
|
"OUT PORTS: "..numportso
|
||||||
|
;
|
||||||
|
else
|
||||||
|
info = "\\c5" .. obj.definition.name .. "<br>"
|
||||||
|
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
|
67
sim/port.lua
Normal file
67
sim/port.lua
Normal file
@ -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
|
343
sim/simulation.lua
Normal file
343
sim/simulation.lua
Normal file
@ -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
|
53
sim/utility.lua
Normal file
53
sim/utility.lua
Normal file
@ -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<len then
|
||||||
|
i = i+1
|
||||||
|
ci = str:sub(i, i)
|
||||||
|
for escidx, esc in ipairs(escapes) do
|
||||||
|
if ci==esc[2] then co = esc[1] end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(ostrt, co)
|
||||||
|
i = i+1
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(ostrt)
|
||||||
|
end
|
27
sim/wire.lua
Normal file
27
sim/wire.lua
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Wire = {
|
||||||
|
logictype = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wire:new(objref, layer, bounds)
|
||||||
|
local o = {
|
||||||
|
objref = objref,
|
||||||
|
layer = layer,
|
||||||
|
group = nil,
|
||||||
|
bounds = bounds
|
||||||
|
}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Wire:setlayer(layer)
|
||||||
|
if self.group ~= nil then
|
||||||
|
self.group:removewire(self)
|
||||||
|
end
|
||||||
|
self.layer = layer
|
||||||
|
sim:connectwire(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Wire:update()
|
||||||
|
client:send("WU\t" .. bool_to_int[self.group.state] .. "\t" .. self.objref .. "\n")
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user