1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 12:10:30 +00:00

Fix computer IDs greater than 65535 crashing Rednet (#900)

This commit is contained in:
Alessandro 2021-09-26 18:13:26 +02:00 committed by GitHub
parent eb61c5c5d7
commit 297426419b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 12 deletions

View File

@ -61,6 +61,8 @@
(table space) (table space)
(index no-space)) (index no-space))
(allow-clarifying-parens true)
;; colours imports from colors, and we don't handle that right now. ;; colours imports from colors, and we don't handle that right now.
;; keys is entirely dynamic, so we skip it. ;; keys is entirely dynamic, so we skip it.
(dynamic-modules colours keys _G) (dynamic-modules colours keys _G)

View File

@ -25,10 +25,18 @@ CHANNEL_BROADCAST = 65535
--- The channel used by the Rednet API to repeat messages. --- The channel used by the Rednet API to repeat messages.
CHANNEL_REPEAT = 65533 CHANNEL_REPEAT = 65533
--- The number of channels rednet reserves for computer IDs. Computers with IDs
-- greater or equal to this limit wrap around to 0.
MAX_ID_CHANNELS = 65500
local tReceivedMessages = {} local tReceivedMessages = {}
local tReceivedMessageTimeouts = {} local tReceivedMessageTimeouts = {}
local tHostnames = {} local tHostnames = {}
local function id_as_channel(id)
return (id or os.getComputerID()) % MAX_ID_CHANNELS
end
--[[- Opens a modem with the given @{peripheral} name, allowing it to send and --[[- Opens a modem with the given @{peripheral} name, allowing it to send and
receive messages over rednet. receive messages over rednet.
@ -47,7 +55,7 @@ function open(modem)
if peripheral.getType(modem) ~= "modem" then if peripheral.getType(modem) ~= "modem" then
error("No such modem: " .. modem, 2) error("No such modem: " .. modem, 2)
end end
peripheral.call(modem, "open", os.getComputerID()) peripheral.call(modem, "open", id_as_channel())
peripheral.call(modem, "open", CHANNEL_BROADCAST) peripheral.call(modem, "open", CHANNEL_BROADCAST)
end end
@ -64,7 +72,7 @@ function close(modem)
if peripheral.getType(modem) ~= "modem" then if peripheral.getType(modem) ~= "modem" then
error("No such modem: " .. modem, 2) error("No such modem: " .. modem, 2)
end end
peripheral.call(modem, "close", os.getComputerID()) peripheral.call(modem, "close", id_as_channel())
peripheral.call(modem, "close", CHANNEL_BROADCAST) peripheral.call(modem, "close", CHANNEL_BROADCAST)
else else
-- Close all modems -- Close all modems
@ -87,7 +95,7 @@ function isOpen(modem)
if modem then if modem then
-- Check if a specific modem is open -- Check if a specific modem is open
if peripheral.getType(modem) == "modem" then if peripheral.getType(modem) == "modem" then
return peripheral.call(modem, "isOpen", os.getComputerID()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST) return peripheral.call(modem, "isOpen", id_as_channel()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST)
end end
else else
-- Check if any modem is open -- Check if any modem is open
@ -134,10 +142,11 @@ function send(nRecipient, message, sProtocol)
tReceivedMessageTimeouts[os.startTimer(30)] = nMessageID tReceivedMessageTimeouts[os.startTimer(30)] = nMessageID
-- Create the message -- Create the message
local nReplyChannel = os.getComputerID() local nReplyChannel = id_as_channel()
local tMessage = { local tMessage = {
nMessageID = nMessageID, nMessageID = nMessageID,
nRecipient = nRecipient, nRecipient = nRecipient,
nSender = os.getComputerID(),
message = message, message = message,
sProtocol = sProtocol, sProtocol = sProtocol,
} }
@ -145,10 +154,14 @@ function send(nRecipient, message, sProtocol)
local sent = false local sent = false
if nRecipient == os.getComputerID() then if nRecipient == os.getComputerID() then
-- Loopback to ourselves -- Loopback to ourselves
os.queueEvent("rednet_message", nReplyChannel, message, sProtocol) os.queueEvent("rednet_message", os.getComputerID(), message, sProtocol)
sent = true sent = true
else else
-- Send on all open modems, to the target and to repeaters -- Send on all open modems, to the target and to repeaters
if nRecipient ~= CHANNEL_BROADCAST then
nRecipient = id_as_channel(nRecipient)
end
for _, sModem in ipairs(peripheral.getNames()) do for _, sModem in ipairs(peripheral.getNames()) do
if isOpen(sModem) then if isOpen(sModem) then
peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage) peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage)
@ -390,13 +403,14 @@ function run()
if sEvent == "modem_message" then if sEvent == "modem_message" then
-- Got a modem message, process it and add it to the rednet event queue -- Got a modem message, process it and add it to the rednet event queue
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4 local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
if isOpen(sModem) and (nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then if isOpen(sModem) and (nChannel == id_as_channel() or nChannel == CHANNEL_BROADCAST) then
if type(tMessage) == "table" and type(tMessage.nMessageID) == "number" if type(tMessage) == "table" and type(tMessage.nMessageID) == "number"
and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID] and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID]
and ((tMessage.nRecipient and tMessage.nRecipient == os.getComputerID()) or nChannel == CHANNEL_BROADCAST)
then then
tReceivedMessages[tMessage.nMessageID] = true tReceivedMessages[tMessage.nMessageID] = true
tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID
os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol) os.queueEvent("rednet_message", tMessage.nSender or nReplyChannel, tMessage.message, tMessage.sProtocol)
end end
end end

View File

@ -14,6 +14,10 @@ else
print(#tModems .. " modems found.") print(#tModems .. " modems found.")
end end
local function idAsChannel(id)
return (id or os.getComputerID()) % rednet.MAX_ID_CHANNELS
end
local function open(nChannel) local function open(nChannel)
for n = 1, #tModems do for n = 1, #tModems do
local sModem = tModems[n] local sModem = tModems[n]
@ -53,7 +57,7 @@ local ok, error = pcall(function()
for n = 1, #tModems do for n = 1, #tModems do
local sOtherModem = tModems[n] local sOtherModem = tModems[n]
peripheral.call(sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage) peripheral.call(sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage)
peripheral.call(sOtherModem, "transmit", tMessage.nRecipient, nReplyChannel, tMessage) peripheral.call(sOtherModem, "transmit", idAsChannel(tMessage.nRecipient), nReplyChannel, tMessage)
end end
-- Log the event -- Log the event

View File

@ -84,7 +84,7 @@ describe("The rednet library", function()
end) end)
end) end)
describe("on a fake computers", function() describe("on fake computers", function()
local fake_computer = require "support.fake_computer" local fake_computer = require "support.fake_computer"
local function computer_with_rednet(id, fn, options) local function computer_with_rednet(id, fn, options)
@ -168,5 +168,24 @@ describe("The rednet library", function()
fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 }) fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
end) end)
it("repeats messages between computers with massive ids", function()
local id_1, id_3 = 24283947, 93428798
local computer_1, modem_1 = computer_with_rednet(id_1, function(rednet, _ENV)
rednet.send(id_3, "Hello")
local id, message = rednet.receive()
expect { id, message }:same { id_3, "World" }
end, { open = true })
local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
local computer_3, modem_3 = computer_with_rednet(id_3, function(rednet)
rednet.send(id_1, "World")
local id, message = rednet.receive()
expect { id, message }:same { id_1, "Hello" }
end, { open = true })
fake_computer.add_modem_edge(modem_1, modem_2)
fake_computer.add_modem_edge(modem_2, modem_3)
fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
end)
end) end)
end) end)

View File

@ -70,15 +70,22 @@ local function make_computer(id, fn)
return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co } return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co }
end end
local function parse_channel(c)
if c < 0 or c > 65535 then error("Expected number in range 0-65535", 3) end
return c
end
--- Add a modem to a computer on a particular side --- Add a modem to a computer on a particular side
local function add_modem(owner, side) local function add_modem(owner, side)
local open, adjacent = {}, {} local open, adjacent = {}, {}
local peripheral = setmetatable({ local peripheral = setmetatable({
open = function(channel) open[channel] = true end, open = function(channel) open[parse_channel(channel)] = true end,
close = function(channel) open[channel] = false end, close = function(channel) open[parse_channel(channel)] = false end,
closeAll = function(channel) open = {} end, closeAll = function(channel) open = {} end,
isOpen = function(channel) return open[channel] == true end, isOpen = function(channel) return open[parse_channel(channel)] == true end,
transmit = function(channel, reply_channel, payload) transmit = function(channel, reply_channel, payload)
channel, reply_channel = parse_channel(channel), parse_channel(reply_channel)
for _, adjacent in pairs(adjacent) do for _, adjacent in pairs(adjacent) do
if adjacent.open[channel] then if adjacent.open[channel] then
adjacent.owner.queue_event("modem_message", adjacent.side, channel, reply_channel, payload, 123) adjacent.owner.queue_event("modem_message", adjacent.side, channel, reply_channel, payload, 123)