lua-logic/sim/main.lua
2021-05-25 17:18:57 -05:00

379 lines
9.1 KiB
Lua

assert(getmetatable(_G)==nil, "_G already has a metatable")
setmetatable(_G, {
__index = function(t, i) error("attempt to access nil variable "..i, 2) end
})
OPT_SAVE_DIR = arg[1] or error("must specify save location")
OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("\\", "/")
OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("/$", "")
print("Save location set to \""..OPT_SAVE_DIR.."\"")
local socket = require("socket")
local ffi = require("ffi")
dofile("iosafe.lua")
FFI = ffi
dofile("utility.lua")
dofile("simulation.lua")
dofile("group.lua")
dofile("wire.lua")
dofile("gatedef.lua")
dofile("port.lua")
dofile("gate.lua")
dofile("save.lua")
dofile("compile.lua")
FFI = nil
OPT_TICK_ENABLED = true
OPT_TICK_TIME = 0.032
OPT_TICK_MULT = 1
OPT_FX_UPDATES = true
OPT_FX_TIME = 0.032
OPT_TICK_INF = 29
local tickdelay = 0
local ticksperinterval = 0
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
local sim = Simulation.new(Simulation)
GSim = sim
local units = {
"uHz",
"mHz",
"Hz",
"kHz",
"MHz",
"GHz",
}
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
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(";;", "; ;")
line = line:gsub(";$", "; ")
for str in string.gmatch(line, "([^;]+)") do
data[i] = str or ""
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(Wire, tonumber(data[i+1]), tonumber(data[i+2]), bounds)
Simulation.addwire(sim, wire)
i = i + 4
elseif data[i] == "G" then
local objref = tonumber(data[i+1])
local definition = Simulation.getdefinitionbyref(sim, tonumber(data[i+2]))
assert(definition, "No gate definition for objref "..objref)
local position = vectotable(data[i+3])
local rotation = tonumber(data[i+4])
local gate = GateDefinition.constructgate(definition, objref, position, rotation)
Simulation.addgate(sim, gate)
--print(gate.objref)
--Gate.init(gate)
--Gate.logic(gate)
i = i + 4
elseif data[i] == "RW" then
Simulation.removewire(sim, tonumber(data[i+1]))
i = i + 1
elseif data[i] == "RG" then
Simulation.removegate(sim, tonumber(data[i+1]))
i = i + 1
elseif data[i] == "GD" then
--print("---------------------------------------[[[[")
--print(table.concat(data, "]]]]\n[[[[", i, math.min(#data, i+100)))
--print("]]]]---------------------------------------")
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 portd = {
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] = portd
if not portd.direction then print(line) end
end
local definition = GateDefinition.new(GateDefinition, objref, name, desc, init, logic, input, global, ports)
Simulation.addgatedefinition(sim, definition)
i = i + 8 + numports*5
elseif data[i] == "SL" then
local wire = Simulation.getwirebyref(sim, tonumber(data[i+1]))
if wire ~= nil then
Wire.setlayer(wire, tonumber(data[i+2]))
end
i = i + 2
elseif data[i] == "SP" then
local gate = Simulation.getgatebyref(sim, tonumber(data[i+1]))
if gate ~= nil then
Port.setstate(gate.ports[tonumber(data[i+2])], toboolean(data[i+3]))
end
i = i + 3
elseif data[i] == "SG" then
local wire = Simulation.getwirebyref(sim, tonumber(data[i+1]))
if wire ~= nil then
Group.setstate(Wire.getgroup(wire), 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
if value<=0.001 then value = 0.0001 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
elseif option=="TICK_MULT" then
OPT_TICK_MULT = value
end
i = i + 2
elseif data[i] == "GINFO" then
local userid = data[i+1]
local objref = tonumber(data[i+2])
local info = ""
local wire = Simulation.getwirebyref(sim, objref)
if wire then
local group = Wire.getgroup(wire)
local numwires = 0; for k, wire2 in pairs(group.wires ) do numwires = numwires +1 end
local numportsi = 0; for k, port in pairs(group.in_ports ) do numportsi = numportsi+1 end
local numportsu = #group.in_ports_update
local numportso = 0; local numportson=0;
for k, port in pairs(group.out_ports) do
numportso = numportso+1
if Port.getstate(port) then numportson = numportson+1 end
end
info = "\\c5Net " .. tostring(group):match("table: 0x(.+)"):upper() .. "\n" .. (Wire.getgroup(wire).state and "\\c2On" or "\\c0Off") .. "\n" ..
"Wires: "..numwires.."\n"..
"In Ports: " ..numportsi.."\n"..
"Out Ports: "..numportso.."\n"..
"In Ports Update: "..numportsu.."\n"..
"Out Ports On: "..(group.state_num)
;
end
local gate = Simulation.getgatebyref(sim, objref)
if gate then
local def = Gate.getdefinition(gate)
info = "\\c5" .. def.name .. "<br>"
for i = 1, #gate.ports do
info = info .. (gate.ports[i].state and "\\c2" or "\\c0") .. def.ports[i].name .. (i ~= #gate.ports and " " or "")
end
end
if info ~= "" then
client:send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n")
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
Simulation.tick(sim)
ticks = ticks + 1
elseif data[i] == "IN" then
local gate = Simulation.getgatebyref(sim, 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
if gate then
Simulation.queuegateinput(sim, gate, argv)
end
i = i+2+argc
elseif data[i] == "SAVE" then
print("saving all data")
logicsave()
else
print("invalid data "..data[i])
end
i = i + 1
end
elseif err == "closed" then
sim = Simulation.new(Simulation)
acceptclient()
end
local time = os.clock()
if OPT_TICK_ENABLED then
if time-lastticktime >= OPT_TICK_TIME then
lastticktime = time
if OPT_TICK_TIME==0 then
for i = 1, OPT_TICK_INF do
Simulation.tick(sim)
end
ticks = ticks+OPT_TICK_INF
else
for i = 1, OPT_TICK_MULT do
Simulation.tick(sim)
ticks = ticks+1
local elapsed = os.clock()-time
if elapsed>0.1 then
break
end
end
end
local sleeptime = time+OPT_TICK_TIME-os.clock()-0.005
if sleeptime>0 then socket.sleep(sleeptime) end
end
else
socket.sleep(0.05)
end
if time-lastfxtime >= OPT_FX_TIME then
if OPT_FX_UPDATES then
Simulation.sendfxupdate(sim)
end
Simulation.sendcallbacks(sim)
lastfxtime = time
end
if time-lastmeasuretime >= 0.1 then
if #avgticks >= 10 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