diff --git a/docs/configuration.md b/docs/configuration.md index 817c5f94..be96b9d1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -610,7 +610,7 @@ Defaults to true. Enable UDP messaging between the host and the Factorio server by passing `--enable-lua-udp` to Factorio. This lets plugins send UDP packets to the server and receive data sent with `clusterio_api.send_udp` in-game. -Requires Factorio 2.0.59 or later. +Requires Factorio 2.1.12 or later, earlier versions crash when a packet is received while the server is paused. Defaults to false. diff --git a/docs/developing-for-clusterio.md b/docs/developing-for-clusterio.md index 8b11ef0c..f096af55 100644 --- a/docs/developing-for-clusterio.md +++ b/docs/developing-for-clusterio.md @@ -255,7 +255,7 @@ Parameters: Send data to Clusterio over the given channel using UDP instead of stdout. Clusterio plugins receive it as `udp-channel_name` events, see the [Communicating over UDP section](writing-plugins.md#communicating-over-udp) in the Writing Plugins document. -Requires `factorio.enable_lua_udp` to be enabled on the instance and Factorio 2.0.59 or later, otherwise the data is silently dropped. +Requires `factorio.enable_lua_udp` to be enabled on the instance and Factorio 2.1.12 or later, otherwise the data is silently dropped. The port to send to is passed to the module with a script command on startup, so `factorio.enable_script_commands` must be enabled as well. **Note**: Delivery is not guaranteed and payloads should be kept well below 32 kB. diff --git a/docs/writing-plugins.md b/docs/writing-plugins.md index 6df458dc..8479c099 100644 --- a/docs/writing-plugins.md +++ b/docs/writing-plugins.md @@ -484,7 +484,8 @@ For RCON, commands longer than 50 characters may end up being executed after sho ### Communicating over UDP -Instances can optionally exchange UDP packets with the Factorio server for low latency fire-and-forget communication, using the `--enable-lua-udp` option of Factorio 2.0.59 and later. +Instances can optionally exchange UDP packets with the Factorio server for low latency fire-and-forget communication, using the `--enable-lua-udp` option of Factorio. +This needs Factorio 2.1.12 or later, earlier versions crash when a packet is received while the server is paused. This is disabled by default and enabled by setting `factorio.enable_lua_udp` on the instance. Sending data out of Factorio also needs `factorio.enable_script_commands`, as the port to send to is passed to the module with a script command on startup. Plugins requiring it should declare the `LuaUdp` feature. diff --git a/packages/host/src/server.ts b/packages/host/src/server.ts index a9aec3a7..55f298a0 100644 --- a/packages/host/src/server.ts +++ b/packages/host/src/server.ts @@ -865,6 +865,13 @@ export class FactorioServer extends events.EventEmitter { this._udpSocket = socket; } + // Factorio before 2.1.12 crashes when a packet is received while paused + _checkLuaUdpVersion() { + if (this.enableLuaUdp && lib.integerFullVersion(this._version!) < lib.integerFullVersion("2.1.12")) { + throw new Error("Lua UDP requires Factorio 2.1.12 or later"); + } + } + _stopUdp() { if (this._udpSocket) { this._udpSocket.close(); @@ -1317,6 +1324,7 @@ export class FactorioServer extends events.EventEmitter { */ async start(save: string) { this._check(["init"]); + this._checkLuaUdpVersion(); this._state = "running"; try { @@ -1370,6 +1378,7 @@ export class FactorioServer extends events.EventEmitter { */ async startScenario(scenario: string, seed?: number, mapGenSettings?: object, mapSettings?: object) { this._check(["init"]); + this._checkLuaUdpVersion(); this._state = "running"; try { diff --git a/packages/lib/src/config/definitions.ts b/packages/lib/src/config/definitions.ts index ad90b3f1..4854734e 100644 --- a/packages/lib/src/config/definitions.ts +++ b/packages/lib/src/config/definitions.ts @@ -611,7 +611,7 @@ export class InstanceConfig extends classes.Config { "factorio.enable_lua_udp": { description: "Enable UDP messaging between the Factorio server and the host. " + - "Required by plugins using UDP. Requires Factorio 2.0.59 or later.", + "Required by plugins using UDP. Requires Factorio 2.1.12 or later.", restartRequired: true, // Because of the cli option "--enable-lua-udp" type: "boolean", initialValue: false, diff --git a/test/host/server.js b/test/host/server.js index 18fd3628..b1a70eb6 100644 --- a/test/host/server.js +++ b/test/host/server.js @@ -365,6 +365,20 @@ describe("host/server", function() { return { lines, restore: () => { server._logger = logger; } }; } + it("should refuse to start on Factorio before 2.1.12", async function() { + const version = server._version; + server._version = "2.1.11"; + server.enableLuaUdp = true; + try { + const error = new Error("Lua UDP requires Factorio 2.1.12 or later"); + await assert.rejects(server.start("save.zip"), error); + await assert.rejects(server.startScenario("test"), error); + assert.equal(server._state, "init"); + } finally { + server._version = version; + server.enableLuaUdp = false; + } + }); it("should not have a host port when not running", function() { assert.equal(server.hostUdpPort, undefined); }); diff --git a/test/integration/instance.js b/test/integration/instance.js index 79278f80..139932a7 100644 --- a/test/integration/instance.js +++ b/test/integration/instance.js @@ -21,9 +21,9 @@ const requireApi = [ "or package.loaded['__level__/modules/clusterio/api.lua']", // 2.0.0 ].join(" "); -// --enable-lua-udp was added in Factorio 2.0.59 +// Lua UDP needs Factorio 2.1.12, see docs/configuration.md function hasLuaUdp() { - return lib.integerFullVersion(getFactorioVersion()) >= lib.integerFullVersion("2.0.59"); + return lib.integerFullVersion(getFactorioVersion()) >= lib.integerFullVersion("2.1.12"); } function getUser(name) { diff --git a/test/integration/server.js b/test/integration/server.js index 8c3b8075..1675ac96 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -125,8 +125,8 @@ describe("Integration of host/src/server", function() { let mapPath = server.writePath("saves", "test.zip"); await assert.doesNotReject(fs.access(mapPath), "save is missing"); - // --enable-lua-udp was added in Factorio 2.0.59 - server.enableLuaUdp = lib.integerFullVersion(server.version) >= lib.integerFullVersion("2.0.59"); + // Lua UDP needs Factorio 2.1.12, see docs/configuration.md + server.enableLuaUdp = lib.integerFullVersion(server.version) >= lib.integerFullVersion("2.1.12"); await server.start("test.zip"); }); }); @@ -182,9 +182,11 @@ describe("Integration of host/src/server", function() { } log("Lua UDP to game"); + // Poll from on_tick like the clusterio module does await server.sendRcon( "/sc script.on_event(defines.events.on_udp_packet_received, " + - "function(event) print('udp:' .. event.payload) end)" + "function(event) print('udp:' .. event.payload) end) " + + "script.on_nth_tick(1, function() helpers.recv_udp(0) end)" ); let pass = false; function filter(output) { @@ -195,8 +197,9 @@ describe("Integration of host/src/server", function() { server.on("output", filter); await server.sendUdp("hello"); + // The server does not tick without players, commands make it tick for (let i = 0; i < 10; i++) { - await server.sendRcon("/sc helpers.recv_udp(0)"); + await server.sendRcon("/sc rcon.print('tick')"); await lib.wait(10); if (pass) { break;