mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Add opt-in UDP messaging between the host and the Factorio server using the --enable-lua-udp option of Factorio 2.0.59+. When factorio.enable_lua_udp is set the instance binds a localhost UDP socket, starts Factorio with --enable-lua-udp on factorio.lua_udp_port (random if null, like the RCON port) and tells the clusterio module the host port via clusterio_private.update_instance. Data from Factorio is sent with clusterio_api.send_udp(channel, data) in the same IPC format as send_json, so plugins receive it through the existing ipc-<channel> events. Packets not sent from the Lua UDP port are ignored, as that is the only unauthenticated way into the IPC handler. Data to Factorio is sent with the new sendUdp method on instances and instance plugins, and is dispatched in-game as on_udp_packet_received events by polling helpers.recv_udp every tick from the clusterio module. Plugins requiring UDP declare the new LuaUdp feature flag, which like SavePatching and ScriptCommands refuses to load the plugin on instances without the option enabled. Verified against a headless 2.1.17 server: send_udp and recv_udp are silent no-ops when the option is off, so a stale port in a save that is moved to an instance without UDP does no harm. The integration test matrix now starts the instance with UDP enabled on Factorio 2.0.59+ and round trips a player event over it. Closes #969 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
83 lines
2.0 KiB
Lua
83 lines
2.0 KiB
Lua
-- Important: Keep this API in sync with modules/clusterio/api.lua
|
|
local compat = require("compat")
|
|
local api = {}
|
|
|
|
|
|
local initialized = false
|
|
function api.init()
|
|
if initialized then
|
|
return
|
|
end
|
|
initialized = true
|
|
|
|
if remote.interfaces.clusterio_api then
|
|
api.events = remote.call("clusterio_api", "get_events")
|
|
else
|
|
local null_event = script.generate_event_name()
|
|
api.events = {
|
|
on_instance_updated = null_event,
|
|
on_server_startup = null_event,
|
|
}
|
|
end
|
|
end
|
|
|
|
local function call_api(fn, ...)
|
|
if remote.interfaces.clusterio_api and remote.interfaces.clusterio_api[fn] then
|
|
return remote.call("clusterio_api", fn, ...)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
function api.get_instance_name()
|
|
return call_api("get_instance_name")
|
|
end
|
|
|
|
function api.get_instance_id()
|
|
return call_api("get_instance_id")
|
|
end
|
|
|
|
|
|
local function escape_channel(channel)
|
|
-- Escape bad characters. The question mark is used for separating the
|
|
-- channel name from the payload.
|
|
return channel:gsub("([\x00-\x1f?\\])", function(match)
|
|
return "\\x" .. string.format("%02x", match:byte())
|
|
end)
|
|
end
|
|
|
|
function api.send_json(channel, data)
|
|
if not remote.interfaces.clusterio_api then
|
|
return
|
|
end
|
|
|
|
channel = escape_channel(channel)
|
|
data = compat.table_to_json(data)
|
|
|
|
-- If there's more than about 4kB of data users running with the Windows
|
|
-- console open will start to experience stuttering, otherwise we could
|
|
-- output about 1 MB of data at a time through stdout.
|
|
if #data < 4000 then
|
|
print("\f$ipc:" .. channel .. "?j" .. data)
|
|
else
|
|
local file_no = remote.call("clusterio_api", "get_file_no")
|
|
local file_name = "clst_" .. file_no .. ".json"
|
|
compat.write_file(file_name, data, false, 0)
|
|
print("\f$ipc:" .. channel .. "?f" .. file_name)
|
|
end
|
|
end
|
|
|
|
function api.send_udp(channel, data)
|
|
local port = call_api("get_host_udp_port")
|
|
if not port then
|
|
return -- Lua UDP is not enabled for this instance
|
|
end
|
|
|
|
channel = escape_channel(channel)
|
|
data = compat.table_to_json(data)
|
|
helpers.send_udp(port, "\f$ipc:" .. channel .. "?j" .. data, 0)
|
|
end
|
|
|
|
|
|
return api
|