diff --git a/README.md b/README.md index 2a8e38d..0063d05 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,8 @@ change in a future release. - `sscsm.com_send(player_or_name, channel, msg)`: Sends `msg` (a JSON-compatible object) to `player_or_name` on the SSCSM com channel `channel`. Channel names should be `modname` or `modname:name` to prevent - conflicts. + conflicts. *Although the limit for server-to-client messages is 128MB, I + strongly recommend not sending large messages when not necessary.* - `sscsm.com_send_all(channel, msg)`: Sends `msg` to all clients that are running SSCSMs. - `sscsm.register_on_com_receive(channel, function(name, msg))`: Registers a diff --git a/init.lua b/init.lua index f0a75e7..2734af1 100644 --- a/init.lua +++ b/init.lua @@ -202,6 +202,7 @@ local function validate_channel(channel) end end +local msgids = {} function sscsm.com_send(pname, channel, msg) if minetest.is_player(pname) then pname = pname:get_player_name() @@ -212,8 +213,34 @@ function sscsm.com_send(pname, channel, msg) else msg = assert(minetest.write_json(msg)) end - minetest.chat_send_player(pname, '\001SSCSM_COM\001' .. channel .. '\001' - .. msg) + + -- Short messages can be sent all at once + local prefix = '\001SSCSM_COM\001' .. channel .. '\001' + if #msg < 65300 then + minetest.chat_send_player(pname, prefix .. msg) + return + end + + -- You should never send messages over 128MB to clients + assert(#msg < 134217728) + + -- Otherwise split the message into multiple chunks + prefix = prefix .. '\001' + local id = #msgids + 1 + local i = 0 + msgids[id] = true + local total_msgs = math.ceil(#msg / 65000) + repeat + i = i + 1 + minetest.chat_send_player(pname, prefix .. id .. '\001' .. i .. + '\001' .. total_msgs .. '\001' .. msg:sub(1, 65000)) + msg = msg:sub(65001) + until msg == "" + + -- Allow the ID to be reused on the next globalstep. + minetest.after(0, function() + msgids[id] = nil + end) end local registered_on_receive = {} @@ -272,7 +299,7 @@ function sscsm.com_send_all(channel, msg) end -- Testing -minetest.after(1, function() +minetest.after(0, function() -- Check if any other SSCSMs have been registered. local c = 0 for k, v in pairs(sscsm.registered_csms) do diff --git a/sscsm_init.lua b/sscsm_init.lua index 3a470d5..d8bfb1e 100644 --- a/sscsm_init.lua +++ b/sscsm_init.lua @@ -212,6 +212,30 @@ function sscsm.register_on_com_receive(channel, func) table.insert(registered_on_receive[channel], func) end +-- Load split messages +local incoming_messages = {} +local function load_split_message(chan, msg) + print('Got split message chunk') + local id, i, l, pkt = msg:match('^\1([^\1]+)\1([^\1]+)\1([^\1]+)\1(.*)$') + id, i, l = tonumber(id), tonumber(i), tonumber(l) + + if not incoming_messages[id] then + incoming_messages[id] = {} + end + local msgs = incoming_messages[id] + msgs[i] = pkt + + -- Return true if all the messages have been received + if #msgs < l then return end + for i = 1, l do + if not msgs[i] then + return + end + end + incoming_messages[id] = nil + return table.concat(msgs, '') +end + -- Detect messages and handle them minetest.register_on_receiving_chat_message(function(message) local chan, msg = message:match('^\001SSCSM_COM\001([^\001]*)\001(.*)$') @@ -222,7 +246,16 @@ minetest.register_on_receiving_chat_message(function(message) if not callbacks then return end -- Load the message - if msg:sub(1, 1) == '\002' then + local prefix = msg:sub(1, 1) + if prefix == '\001' then + msg = load_split_message(chan, msg) + if not msg then + return true + end + prefix = msg:sub(1, 1) + end + + if prefix == '\002' then msg = msg:sub(2) else msg = minetest.parse_json(msg) @@ -230,7 +263,10 @@ minetest.register_on_receiving_chat_message(function(message) -- Run callbacks for _, func in ipairs(callbacks) do - func(msg) + local ok, msg = pcall(func, msg) + if not ok then + minetest.log('error', '[SSCSM] ' .. tostring(msg)) + end end return true end) diff --git a/sscsm_testing.lua b/sscsm_testing.lua index 1ce597e..4a723b0 100644 --- a/sscsm_testing.lua +++ b/sscsm_testing.lua @@ -89,6 +89,10 @@ sscsm.every(60, function(param1) end, 123) sscsm.register_on_com_receive('sscsm:testing', function(msg) - print('Got ' .. minetest.serialize(msg):sub(8) .. ' from the server') - sscsm.com_send('sscsm:testing', msg) + if #msg > 400 then + print('Got large message of length ' .. #msg .. ' from the server') + else + sscsm.com_send('sscsm:testing', msg) + print('Got ' .. minetest.serialize(msg):sub(8) .. ' from the server') + end end)