diff --git a/src/apis/component.lua b/src/apis/component.lua index 2e6b070..2461762 100644 --- a/src/apis/component.lua +++ b/src/apis/component.lua @@ -17,9 +17,11 @@ local doclist = {} component = {} -function component.connect(...) +function component.connect(info, ...) local address - local info = table.pack(...) + if type(info) ~= "table" then + info = table.pack(info, ...) + end checkArg(2,info[2],"string","number") if type(info[2]) == "string" then address = info[2] @@ -109,10 +111,10 @@ function component.cecinvoke(address, method, ...) end -- Load components -local components = conf.components +local components = settings.components for k,v in pairs(components) do v[2] = v[2] or k - component.connect(table.unpack(v)) + component.connect(v) end env.component = {list = component.list} diff --git a/src/apis/computer.lua b/src/apis/computer.lua index 9a58266..08f1430 100644 --- a/src/apis/computer.lua +++ b/src/apis/computer.lua @@ -35,12 +35,12 @@ end function env.computer.freeMemory() --STUB cprint("computer.freeMemory") - return 10000 + return machine.totalMemory end function env.computer.totalMemory() --STUB cprint("computer.totalMemory") - return 10000 + return machine.totalMemory end function env.computer.pushSignal(name, ...) cprint("computer.pushSignal", name, ...) @@ -73,6 +73,7 @@ function env.computer.energy() return math.huge end function env.computer.maxEnergy() + -- TODO: What is this ... cprint("computer.maxEnergy") - return config.get("power.buffer.computer",500) + return 1500 end diff --git a/src/apis/system.lua b/src/apis/system.lua index a53ec54..bc721b6 100644 --- a/src/apis/system.lua +++ b/src/apis/system.lua @@ -4,9 +4,9 @@ env.system = {} function env.system.allowBytecode() cprint("system.allowBytecode") - return config.get("computer.lua.allowBytecode",false) + return settings.allowBytecode end function env.system.timeout() cprint("system.timeout") - return config.get("computer.timeout",5) + return settings.timeout end diff --git a/src/component/internet.lua b/src/component/internet.lua index fc307eb..ccfd09e 100644 --- a/src/component/internet.lua +++ b/src/component/internet.lua @@ -38,18 +38,18 @@ end function obj.isTcpEnabled() -- Returns whether TCP connections can be made (config setting). cprint("internet.isTcpEnabled") - return config.get("internet.enableTcp",true) + return settings.tcpEnabled end function obj.isHttpEnabled() -- Returns whether HTTP requests can be made (config setting). cprint("internet.isHttpEnabled") - return config.get("internet.enableHttp",true) + return settings.httpEnabled end function obj.connect(address, port) -- Opens a new TCP connection. Returns the handle of the connection. cprint("internet.connect",address, port) if port == nil then port = -1 end compCheckArg(1,address,"string") compCheckArg(2,port,"number") - if not config.get("internet.enableTcp",true) then + if not settings.tcpEnabled then return nil, "tcp connections are unavailable" end -- TODO Check for too many connections @@ -120,7 +120,7 @@ end function obj.request(url, postData) -- Starts an HTTP request. If this returns true, further results will be pushed using `http_response` signals. cprint("internet.request",url, postData) compCheckArg(1,url,"string") - if not config.get("internet.enableHttp",true) then + if not settings.httpEnabled then return nil, "http requests are unavailable" end -- TODO: Check for too many connections diff --git a/src/component/modem.lua b/src/component/modem.lua new file mode 100644 index 0000000..c2a3cf5 --- /dev/null +++ b/src/component/modem.lua @@ -0,0 +1,107 @@ +local address, _, wireless = ... +compCheckArg(1,wireless,"boolean") + +local wakeMessage +local strength +if wireless then + strength = settings.maxWirelessRange +end + +local function checkPort(port) + if port < 1 and port >= 65536 then + error("invalid port number",4) + end + return math.floor(port) +end + +-- modem component +local obj = {} + +function obj.send(address, port, ...) -- Sends the specified data to the specified target. + --STUB + cprint("modem.send",address, port, ...) + compCheckArg(1,address,"string") + compCheckArg(2,port,"number") + port=checkPort(port) + return true +end +function obj.getWakeMessage() -- Get the current wake-up message. + --STUB + cprint("modem.getWakeMessage") + return wakeMessage +end +function obj.setWakeMessage(message) -- Set the wake-up message. + --STUB + cprint("modem.setWakeMessage",message) + compCheckArg(1,message,"string","nil") + wakeMessage = message +end +function obj.close(port) -- Closes the specified port (default: all ports). Returns true if ports were closed. + --STUB + cprint("modem.close",port) + compCheckArg(1,port,"number","nil") + if port ~= nil then + port=checkPort(port) + end + return false +end +function obj.maxPacketSize() -- Gets the maximum packet size (config setting). + cprint("modem.maxPacketSize") + return settings.maxNetworkPacketSize +end +if wireless then + function obj.getStrength() -- Get the signal strength (range) used when sending messages. + --STUB + cprint("modem.getStrength") + return strength + end + function obj.setStrength(newstrength) -- Set the signal strength (range) used when sending messages. + --STUB + cprint("modem.setStrength",newstrength) + compCheckArg(1,newstrength,"number") + strength = newstrength + end +end +function obj.isOpen(port) -- Whether the specified port is open. + --STUB + cprint("modem.isOpen",port) + compCheckArg(1,port,"number") + return false +end +function obj.open(port) -- Opens the specified port. Returns true if the port was opened. + --STUB + cprint("modem.open",port) + compCheckArg(1,port,"number") + port=checkPort(port) + return false +end +function obj.isWireless() -- Whether this is a wireless network card. + --STUB + cprint("modem.isWireless") + return wireless +end +function obj.broadcast(port, ...) -- Broadcasts the specified data on the specified port. + --STUB + cprint("modem.broadcast",port, ...) + compCheckArg(1,port,"number") + port=checkPort(port) + return true +end + +local cec = {} + +local doc = { + ["send"]="function(address:string, port:number, data...) -- Sends the specified data to the specified target.", + ["getWakeMessage"]="function():string -- Get the current wake-up message.", + ["setWakeMessage"]="function(message:string):string -- Set the wake-up message.", + ["close"]="function([port:number]):boolean -- Closes the specified port (default: all ports). Returns true if ports were closed.", + ["maxPacketSize"]="function():number -- Gets the maximum packet size (config setting).", + ["getStrength"]="function():number -- Get the signal strength (range) used when sending messages.", + ["setStrength"]="function(strength:number):number -- Set the signal strength (range) used when sending messages.", + ["isOpen"]="function(port:number):boolean -- Whether the specified port is open.", + ["open"]="function(port:number):boolean -- Opens the specified port. Returns true if the port was opened.", + ["isWireless"]="function():boolean -- Whether this is a wireless network card.", + ["broadcast"]="function(port:number, data...) -- Broadcasts the specified data on the specified port.", +} + +return obj,cec,doc diff --git a/src/config.lua b/src/config.lua index bd734f8..32c955e 100644 --- a/src/config.lua +++ b/src/config.lua @@ -6,15 +6,21 @@ local comments = { ["computer.lua"]="Settings specific to the Lua architecture.", ["computer.lua.allowBytecode"]="Whether to allow loading precompiled bytecode via Lua's `load` function, or related functions (`loadfile`, `dofile`). Enable this only if you absolutely trust all users on your server and all Lua code you run. This can be a MASSIVE SECURITY RISK, since precompiled code can easily be used for exploits, running arbitrary code on the real server! I cannot stress this enough: only enable this is you know what you're doing.", ["computer.timeout"]="The time in seconds a program may run without yielding before it is forcibly aborted. This is used to avoid stupidly written or malicious programs blocking other computers by locking down the executor threads. Note that changing this won't have any effect on computers that are already running - they'll have to be rebooted for this to take effect.", +["emulator"]="Emulator related settings. Components, accuracy, and debugging.", +["emulator.components"]="Default components available to the computer.", +["emulator.debug"]="Whether to enable the emulator's extremely verbose logging.", ["internet.enableHttp"]="Whether to allow HTTP requests via internet cards. When enabled, the `request` method on internet card components becomes available.", ["internet.enableTcp"]="Whether to allow TCP connections via internet cards. When enabled, the `connect` method on internet card components becomes available.", +["misc"]="Other settings that you might find useful to tweak.", +["misc.maxNetworkPacketSize"]="The maximum size of network packets to allow sending via network cards. This has *nothing to do* with real network traffic, it's just a limit for the network cards, mostly to reduce the chance of computer with a lot of RAM killing those with less by sending huge packets. This does not apply to HTTP traffic.", +["misc.maxWirelessRange"]="The maximum distance a wireless message can be sent. In other words, this is the maximum signal strength a wireless network card supports. This is used to limit the search range in which to check for modems, which may or may not lead to performance issues for ridiculous ranges - like, you know, more than the loaded area. See also: `wirelessCostPerRange`.", } local function writeComment(text,file,size) file:write(string.rep(" ",size) .. "--") local line = "" for word in text:gmatch("[%S]+") do - if #line + #word + 1 > 78 then + if #line + #word + 1 > 78-size then line = "" file:write("\n" .. string.rep(" ",size) .. "--") end @@ -40,10 +46,32 @@ function serialize(tbl,key,path,file,size) if comments[spath] then writeComment(comments[spath],file,size) end - if type(v) == "table" then + if spath == "emulator.components" then + file:write(string.rep(" ",size) .. k .. " {\n\n") + for i = 1,#v do + local comp = v[i] + file:write(string.rep(" ",size+2) .. "{") + for i = 1,#comp do + if type(comp[i]) == "string" then + file:write(string.format("%q",comp[i])) + else + file:write(tostring(comp[i])) + end + if i < #comp then + file:write(", ") + end + end + file:write("}") + if i < #v then + file:write(",") + end + file:write("\n") + end + file:write(string.rep(" ",size) .. "}\n") + elseif type(v) == "table" then local list = true for k,l in pairs(v) do - if type(l) ~= "number" then + if type(k) ~= "number" or type(l) ~= "number" then list = false break end @@ -81,19 +109,19 @@ function config.load() rawdata = (rawdata .. "\n"):reverse():match("\n+(.*)"):reverse() local data = "" for line in (rawdata .. "\n"):gmatch("(.-)\n") do - if line:sub(-2) == " {" then - line = line:sub(1,-3) .. "={" + if line:match("{%s-$") then + line = line:gsub("{%s-$","={") end - if line:sub(-1) == "[" then - line = line:sub(1,-2) .. "{" + if line:match("%[%s-$") then + line = line:gsub("%[%s-$","{") end - if line:sub(-1) == "]" then - line = line:sub(1,-2) .. "}" + if line:match("%]%s-$") then + line = line:gsub("%]%s-$","}") end - if line ~= "" and line:sub(-1) ~= "{" and line:sub(-1) ~= "[" and line:sub(-1) ~= "," then + if line:gsub("%s","") ~= "" and not line:match("{%s-$") and not line:match(",%s-$") then line = line .. "," end - if line == "ocemu={" then + if line:match("ocemu%s-=%s-{") then line = "return {" end data = data .. line .. "\n" diff --git a/src/main.lua b/src/main.lua index dbc588d..64ac711 100644 --- a/src/main.lua +++ b/src/main.lua @@ -7,45 +7,6 @@ function math.trunc(n) return n < 0 and math.ceil(n) or math.floor(n) end --- load configuration -elsa.filesystem.load("config.lua")() -config.load() - -elsa.cleanup = {} -function elsa.quit() - config.save() - for k,v in pairs(elsa.cleanup) do - v() - end -end - -conf = { - -- Format: string:type, (string or number or nil):address, (number or nil):slot, component parameters - -- Read component files for parameter documentation - components = { - {"gpu",nil,0,160,50,3}, - {"eeprom",nil,9,"lua/bios.lua"}, - {"filesystem",nil,7,"loot/OpenOS",true}, - {"filesystem",nil,nil,"tmpfs",false}, - {"filesystem",nil,5,nil,false}, - {"internet"}, - {"computer"}, - {"ocemu"}, - } -} -if elsa.SDL then - table.insert(conf.components, {"screen_sdl2",nil,nil,80,25,3}) - table.insert(conf.components, {"keyboard_sdl2"}) -else - -- TODO: Alternatives -end - -machine = { - starttime = elsa.timer.getTime(), - deadline = elsa.timer.getTime(), - signals = {}, -} - local function check(have, want, ...) if not want then return false @@ -70,7 +31,49 @@ function compCheckArg(n, have, ...) end end -if true then +-- load configuration +elsa.filesystem.load("config.lua")() +config.load() +elsa.filesystem.load("settings.lua")() + +elsa.cleanup = {} +function elsa.quit() + config.save() + for k,v in pairs(elsa.cleanup) do + v() + end +end + +if settings.components == nil then + -- Format: string:type, (string or number or nil):address, (number or nil):slot, component parameters + -- Read component files for parameter documentation + settings.components = { + {"gpu",nil,0,160,50,3}, + {"eeprom",nil,9,"lua/bios.lua"}, + {"filesystem",nil,7,"loot/OpenOS",true}, + {"filesystem",nil,nil,"tmpfs",false}, + {"filesystem",nil,5,nil,false}, + {"internet"}, + {"computer"}, + {"ocemu"}, + } + if elsa.SDL then + table.insert(settings.components, {"screen_sdl2",nil,nil,80,25,3}) + table.insert(settings.components, {"keyboard_sdl2"}) + else + -- TODO: Alternatives + end + config.set("emulator.components",settings.components) +end + +machine = { + starttime = elsa.timer.getTime(), + deadline = elsa.timer.getTime(), + signals = {}, + totalMemory = 2*1024*1024, +} + +if settings.emulatorDebug then cprint = print else cprint = function() end @@ -292,6 +295,7 @@ function resume_thread(...) if results[2] then boot_machine() else + elsa.quit() error("Machine power off",0) end end diff --git a/src/settings.lua b/src/settings.lua new file mode 100644 index 0000000..39bbc17 --- /dev/null +++ b/src/settings.lua @@ -0,0 +1,13 @@ +settings = { + allowBytecode = config.get("computer.lua.allowBytecode",false), + timeout = config.get("computer.timeout",5), + + components = config.get("emulator.components"), + emulatorDebug = config.get("emulator.debug",true), + + httpEnabled = config.get("internet.enableHttp",true), + tcpEnabled = config.get("internet.enableTcp",true), + + maxNetworkPacketSize = config.get("misc.maxNetworkPacketSize",8192), + maxWirelessRange = config.get("misc.maxWirelessRange",400), +}