mirror of
https://github.com/zenith391/OCEmu.git
synced 2025-10-03 01:50:06 -04:00
refactor modem code for robustness
This commit is contained in:
parent
101d4feed1
commit
771ae94ad5
@ -24,27 +24,26 @@ modem_host.clients = {}
|
|||||||
-- [port_number] = true when open
|
-- [port_number] = true when open
|
||||||
modem_host.open_ports = {}
|
modem_host.open_ports = {}
|
||||||
|
|
||||||
local function createPacket(type, address, port, ...)
|
function modem_host.createPacketArray(t, address, port, ...)
|
||||||
-- args are not checked here (unlike the modem api methods)
|
compCheckArg(1,t,type(""))
|
||||||
-- address can be nil, which means broadcast
|
compCheckArg(2,address,type(""),type(0))
|
||||||
|
compCheckArg(3,port,type(0))
|
||||||
|
|
||||||
local packed =
|
local packed =
|
||||||
{
|
{
|
||||||
type or "unknown_type",
|
t,
|
||||||
address or "no address",
|
address,
|
||||||
modem_host.id or "no sender",
|
modem_host.id,
|
||||||
port or "no port",
|
port,
|
||||||
0, -- distance
|
0, -- distance
|
||||||
table.unpack(table.pack(...))
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
local datagram = ser.serialize(packed)
|
return packed
|
||||||
return datagram .. '\n'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parsePacket(raw)
|
function modem_host.packetArrayToPacket(packed)
|
||||||
assert(raw)
|
compCheckArg(1,packed,type({}))
|
||||||
local packed = ser.unserialize(raw)
|
|
||||||
assert(packed ~= nil)
|
|
||||||
assert(#packed >= 5)
|
assert(#packed >= 5)
|
||||||
|
|
||||||
local packet = {}
|
local packet = {}
|
||||||
@ -62,7 +61,14 @@ local function parsePacket(raw)
|
|||||||
return packet
|
return packet
|
||||||
end
|
end
|
||||||
|
|
||||||
local function packetToArray(packet)
|
function modem_host.packetArrayToDatagram(packed)
|
||||||
|
compCheckArg(1,packed,type({}))
|
||||||
|
|
||||||
|
local datagram = ser.serialize(packed)
|
||||||
|
return datagram .. '\n'
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.packetToPacketArray(packet)
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
packet.type,
|
packet.type,
|
||||||
@ -70,24 +76,78 @@ local function packetToArray(packet)
|
|||||||
packet.source,
|
packet.source,
|
||||||
packet.port,
|
packet.port,
|
||||||
packet.distance,
|
packet.distance,
|
||||||
table.unpack(packet.payload)
|
table.unpack(packet.payload or {})
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function modem_host.datagramToPacketArray(datagram)
|
||||||
|
compCheckArg(1,datagram,type(""))
|
||||||
|
local packed = ser.unserialize(datagram)
|
||||||
|
return packed
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.datagramToPacket(datagram)
|
||||||
|
local packed = modem_host.datagramToPacketArray(datagram)
|
||||||
|
local packet = modem_host.packedToPacket(packed)
|
||||||
|
return packet
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.packetToDatagram(packet)
|
||||||
|
local packed = modem_host.packetToPacketArray(packet)
|
||||||
|
local datagram = modem_host.packetArrayToDatagram(packed)
|
||||||
|
return datagram
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.readDatagram(client) -- client:receive()
|
||||||
|
local raw, err = client:receive()
|
||||||
|
if raw then cprint("received: " .. raw) end
|
||||||
|
return raw, err
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.readPacketArray(client) -- client:receive()
|
||||||
|
local datagram, err = modem_host.readDatagram(client)
|
||||||
|
if not datagram then return nil, err end
|
||||||
|
return modem_host.datagramToPacketArray(datagram)
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.readPacket(client) -- client:receive()
|
||||||
|
local packed, err = modem_host.readPacketArray(client)
|
||||||
|
if not packed then return nil, err end
|
||||||
|
return modem_host.packetArrayToPacket(packed)
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.sendDatagram(client, datagram)
|
||||||
|
cprint("sending: " .. datagram)
|
||||||
|
return client:send(datagram)
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.sendPacketArray(client, packed)
|
||||||
|
local datagram = modem_host.packetArrayToDatagram(packed)
|
||||||
|
return modem_host.sendDatagram(client, datagram)
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.sendPacket(client, packet)
|
||||||
|
local datagram = modem_host.packetToDatagram(packet)
|
||||||
|
return modem_host.sendDatagram(client, datagram)
|
||||||
|
end
|
||||||
|
|
||||||
function modem_host.broadcast(packet)
|
function modem_host.broadcast(packet)
|
||||||
-- only host broadcasts
|
-- only host broadcasts
|
||||||
-- this method will be hit for all broadcasted messages
|
-- this method will be hit for all broadcasted messages
|
||||||
-- but nonhosting clients will simply not repeat the broadcast
|
-- but nonhosting clients will simply not repeat the broadcast
|
||||||
if modem_host.hosting then
|
if modem_host.hosting then
|
||||||
local plainArray = packetToArray(packet)
|
local datagram = modem_host.packetToDatagram(packet)
|
||||||
local datagram = ser.serialize(plainArray)
|
|
||||||
for addr,client in pairs(modem_host.clients) do
|
for addr,client in pairs(modem_host.clients) do
|
||||||
client:send(datagram)
|
modem_host.sendDatagram(client, datagram)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function modem_host.validTarget(target)
|
function modem_host.validTarget(target)
|
||||||
|
if target == 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
if target == modem_host.id then
|
if target == modem_host.id then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -106,87 +166,112 @@ function modem_host.validTarget(target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- backend private methods, these are not pushed to user machine environments
|
-- backend private methods, these are not pushed to user machine environments
|
||||||
function modem_host.pushMessage(target, datagram)
|
function modem_host.pushMessage(packet)
|
||||||
if not modem_host.validTarget(target) then
|
if not modem_host.validTarget(packet.target) then
|
||||||
return false, "invalid target, no such client listening" --ignored
|
return false, "invalid target, no such client listening" --ignored
|
||||||
end
|
end
|
||||||
|
|
||||||
local packet = parsePacket(datagram)
|
|
||||||
table.insert(modem_host.messages, packet)
|
table.insert(modem_host.messages, packet)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function modem_host.processPendingMessages()
|
function modem_host.dispatchPacket(packet)
|
||||||
modem_host.recvPendingMessages()
|
if packet.target == modem_host.id then
|
||||||
|
if obj.isOpen(packet.port) then
|
||||||
|
table.insert(machine.signals, modem_host.packetToPacketArray(packet))
|
||||||
|
end
|
||||||
|
elseif modem_host.hosting then -- if hosting we will route
|
||||||
|
for source,client in pairs(modem_host.clients) do
|
||||||
|
if source == packet.target then
|
||||||
|
modem_host.sendPacket(client, packet)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else -- not hosting, send to host
|
||||||
|
modem_host.sendPacket(modem_host.socket, packet)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function modem_host.processPendingMessages()
|
||||||
-- computer address seems to be applied late
|
-- computer address seems to be applied late
|
||||||
if not modem_host.id then
|
if not modem_host.id then
|
||||||
modem_host.id = component.list("computer",true)()
|
modem_host.id = component.list("computer",true)()
|
||||||
assert(modem_host.id)
|
assert(modem_host.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local i = 1;
|
modem_host.recvPendingMessages()
|
||||||
while i <= #modem_host.messages do
|
|
||||||
local packet = modem_host.messages[i]
|
for _,packet in pairs(modem_host.messages) do
|
||||||
local move = true
|
|
||||||
|
|
||||||
if packet.type == 'modem_message' then
|
if packet.type == 'modem_message' then
|
||||||
|
|
||||||
-- broadcast if no target
|
-- broadcast if no target
|
||||||
if packet.target == 0 then
|
if packet.target == 0 then
|
||||||
modem_host.broadcast(packet)
|
modem_host.broadcast(packet) -- ignored by clients
|
||||||
|
|
||||||
-- clean up for broadcasting to self
|
-- clean up for broadcasting to self
|
||||||
packet.target = modem_host.id
|
packet.target = modem_host.id
|
||||||
end
|
end
|
||||||
|
|
||||||
if packet.target == modem_host.id then
|
modem_host.dispatchPacket(packet)
|
||||||
if obj.isOpen(packet.port) then
|
|
||||||
table.insert(machine.signals, packetToArray(packet))
|
|
||||||
end
|
|
||||||
move = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if move then
|
modem_host.messages = {}
|
||||||
i = i + 1
|
|
||||||
else
|
|
||||||
table.remove(modem_host.messages, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function modem_host.recvPendingMessages()
|
function modem_host.recvPendingMessages()
|
||||||
if modem_host.hosting then
|
if modem_host.hosting then
|
||||||
while 1 do
|
while true do
|
||||||
local client = modem_host.socket:accept()
|
local client = modem_host.socket:accept()
|
||||||
if not client then
|
if not client then
|
||||||
break;
|
break;
|
||||||
end
|
end
|
||||||
|
|
||||||
local handshakeDatagram, err = client:receive()
|
local handshake, err = modem_host.readPacket(client) -- client:receive()
|
||||||
if err then
|
if not handshake then
|
||||||
client:close()
|
client:close()
|
||||||
else
|
else
|
||||||
client:settimeout(0, 't')
|
|
||||||
|
|
||||||
local handshake = parsePacket(handshakeDatagram)
|
local connectionResponse
|
||||||
|
local accepted = false
|
||||||
|
if handshake.type ~= "handshake" then
|
||||||
|
connectionResponse = modem_host.createPacketArray("handshake", modem_host.id, -1,
|
||||||
|
false, "unsupported message type");
|
||||||
|
elseif modem_host.validTarget(handshake.source) then -- repeated client
|
||||||
|
connectionResponse = modem_host.createPacketArray("handshake", modem_host.id, -1,
|
||||||
|
false, "computer address conflict detected, ignoring connection");
|
||||||
|
else
|
||||||
|
client:settimeout(0, 't')
|
||||||
modem_host.clients[handshake.source] = client
|
modem_host.clients[handshake.source] = client
|
||||||
|
accepted = true
|
||||||
|
|
||||||
|
connectionResponse = modem_host.createPacketArray("handshake", modem_host.id, -1, true);
|
||||||
|
end
|
||||||
|
|
||||||
|
modem_host.sendPacketArray(client, connectionResponse)
|
||||||
|
|
||||||
|
if not accepted then
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- recv all pending packets
|
-- recv all pending packets
|
||||||
for source, client in pairs(modem_host.clients) do
|
for source, client in pairs(modem_host.clients) do
|
||||||
local line, err = client:receive()
|
local packet, err = modem_host.readPacket(client)
|
||||||
if not err then
|
if packet then
|
||||||
modem_host.pushMessage(source, line)
|
modem_host.pushMessage(packet)
|
||||||
|
elseif err ~= "timeout" then
|
||||||
|
client:close()
|
||||||
|
modem_host.clients[source] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif modem_host.socket then
|
elseif modem_host.socket then
|
||||||
while 1 do
|
while true do
|
||||||
local line, err = modem_host.socket:receive()
|
local packet, err = modem_host.readPacket(modem_host.socket)
|
||||||
if not err then
|
if packet then
|
||||||
modem_host.pushMessage(modem_host.id, line)
|
modem_host.pushMessage(packet)
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -210,8 +295,18 @@ function modem_host.joinExistingMessageBoard()
|
|||||||
modem_host.hosting = nil
|
modem_host.hosting = nil
|
||||||
|
|
||||||
-- send handshake data
|
-- send handshake data
|
||||||
local datagram = createPacket("client_handshake")
|
local packed = modem_host.createPacketArray("handshake", 0, -1)
|
||||||
modem_host.send(datagram)
|
local sendResult = modem_host.sendPacketArray(modem_host.socket, packed)
|
||||||
|
|
||||||
|
local response, why = modem_host.readPacket(modem_host.socket)
|
||||||
|
assert(response)
|
||||||
|
assert(response.payload)
|
||||||
|
|
||||||
|
if not response.payload[1] then
|
||||||
|
modem_host.socket:close()
|
||||||
|
modem_host.socket = nil
|
||||||
|
return false, response.payload[2], true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return modem_host.socket, why
|
return modem_host.socket, why
|
||||||
end
|
end
|
||||||
@ -221,12 +316,18 @@ function modem_host.connectMessageBoard()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, reason =
|
local ok, info, critical = modem_host.joinExistingMessageBoard()
|
||||||
modem_host.joinExistingMessageBoard() or
|
|
||||||
modem_host.createNewMessageBoard()
|
if not ok and critical then
|
||||||
|
return nil, info
|
||||||
|
end
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, reason
|
ok, info = modem_host.createNewMessageBoard()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
return nil, info
|
||||||
end
|
end
|
||||||
|
|
||||||
modem_host.socket:settimeout(0, 't') -- accept calls must be already pending
|
modem_host.socket:settimeout(0, 't') -- accept calls must be already pending
|
||||||
@ -237,15 +338,6 @@ function modem_host.connectMessageBoard()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function modem_host.send(datagram)
|
|
||||||
-- if we are the host, we simply call pushMessage directly
|
|
||||||
if modem_host.hosting then
|
|
||||||
return modem_host.pushMessage(modem_host.id, datagram)
|
|
||||||
else
|
|
||||||
return not not modem_host.socket:send(datagram)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local wakeMessage
|
local wakeMessage
|
||||||
local strength
|
local strength
|
||||||
if wireless then
|
if wireless then
|
||||||
@ -265,8 +357,9 @@ function obj.send(address, port, ...) -- Sends the specified data to the specifi
|
|||||||
compCheckArg(2,port,"number")
|
compCheckArg(2,port,"number")
|
||||||
port=checkPort(port)
|
port=checkPort(port)
|
||||||
|
|
||||||
local datagram = createPacket("modem_message", address, port, ...)
|
local packed = modem_host.createPacketArray("modem_message", address, port, ...)
|
||||||
return modem_host.send(datagram)
|
local packet = modem_host.packetArrayToPacket(packed)
|
||||||
|
return modem_host.dispatchPacket(packet)
|
||||||
end
|
end
|
||||||
|
|
||||||
function obj.getWakeMessage() -- Get the current wake-up message.
|
function obj.getWakeMessage() -- Get the current wake-up message.
|
||||||
@ -324,12 +417,14 @@ function obj.open(port) -- Opens the specified port. Returns true if the port wa
|
|||||||
port=checkPort(port)
|
port=checkPort(port)
|
||||||
|
|
||||||
if obj.isOpen(port) then
|
if obj.isOpen(port) then
|
||||||
return false
|
return false, "port already open"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- make sure we are connected to the message board
|
-- make sure we are connected to the message board
|
||||||
if not modem_host.connectMessageBoard() then
|
local ok, why = modem_host.connectMessageBoard()
|
||||||
return false
|
|
||||||
|
if not ok then
|
||||||
|
return false, why
|
||||||
end
|
end
|
||||||
|
|
||||||
modem_host.open_ports[port] = true
|
modem_host.open_ports[port] = true
|
||||||
@ -351,8 +446,9 @@ function obj.broadcast(port, ...) -- Broadcasts the specified data on the specif
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local datagram = createPacket("modem_message", 0, port, ...)
|
local packed = modem_host.createPacketArray("modem_message", 0, port, ...)
|
||||||
return modem_host.send(datagram)
|
local packet = modem_host.packetArrayToPacket(packed)
|
||||||
|
return modem_host.dispatchPacket(packet)
|
||||||
end
|
end
|
||||||
|
|
||||||
local cec = {}
|
local cec = {}
|
||||||
|
@ -49,11 +49,11 @@ if settings.components == nil then
|
|||||||
-- Read component files for parameter documentation
|
-- Read component files for parameter documentation
|
||||||
settings.components = {
|
settings.components = {
|
||||||
{"gpu",nil,0,160,50,3},
|
{"gpu",nil,0,160,50,3},
|
||||||
|
{"modem",nil,1,false},
|
||||||
{"eeprom",nil,9,"lua/bios.lua"},
|
{"eeprom",nil,9,"lua/bios.lua"},
|
||||||
{"filesystem",nil,7,"loot/OpenOS",true},
|
{"filesystem",nil,7,"loot/OpenOS",true},
|
||||||
{"filesystem",nil,nil,"tmpfs",false},
|
{"filesystem",nil,nil,"tmpfs",false},
|
||||||
{"filesystem",nil,5,nil,false},
|
{"filesystem",nil,5,nil,false},
|
||||||
{"modem",nil,nil,false},
|
|
||||||
{"internet"},
|
{"internet"},
|
||||||
{"computer"},
|
{"computer"},
|
||||||
{"ocemu"},
|
{"ocemu"},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user