mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
research_sync: README for the cluster research queue, review fixes
Document the cluster research queue in the plugin README and the root README. Review fixes: ipairs for queue order, event annotations on the new Lua handlers, an IpcQueue type, a test for clearing the queue, and drop the controller import from test/missing.js. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7175571d5b
commit
1f6e0519bd
6 files changed
+41
-7
No files matched your search
@@ -73,7 +73,7 @@ Plugins add functionality to Factorio servers, Clusterio itself or both.
|
||||
These are the plugins supported and maintained by the Clusterio developers:
|
||||
|
||||
- [Global Chat](/plugins/global_chat/README.md): share the in-game chat between servers.
|
||||
- [Research Sync](/plugins/research_sync/README.md): synchronize research progress and technologies unlocked between servers.
|
||||
- [Research Sync](/plugins/research_sync/README.md): synchronize research progress and technologies unlocked and the research queue between servers.
|
||||
- [Statistics Exporter](/plugins/statistics_exporter/README.md): collect in-game statistics from all the servers and makes it available to the Prometheus endpoint on the controller.
|
||||
- [Subspace Storage](https://github.com/clusterio/subspace_storage): Provide shared storage that can transport items between servers via teleport chests.
|
||||
- [Player Auth](/plugins/player_auth/README.md): Provides authentication to the cluster via logging into a Factorio server.
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
|
||||
Plugin synchronizing research progress between instances and the cluster.
|
||||
|
||||
## Research queue
|
||||
|
||||
Each server can follow one cluster research queue.
|
||||
It is set from the normal research screen: queueing, moving or cancelling a technology there changes it for every following server.
|
||||
So does a lab whose circuit "set research" picks a technology, and so can another mod.
|
||||
|
||||
The "Follow the cluster research queue" checkbox on the lab window turns this on or off per server.
|
||||
Turning it on replaces the server's queue with the cluster's, turning it off clears it.
|
||||
It is on by default.
|
||||
On the first start after installing or upgrading, the first server to start shares its queue as the cluster queue, and later servers take it.
|
||||
Every change prints a chat line on every server.
|
||||
|
||||
It needs Factorio 2.0.56 or later, older servers ignore the queue.
|
||||
To turn it off without the GUI, run `/sc research_sync.set_follow(false)` on that server.
|
||||
|
||||
Known gaps:
|
||||
|
||||
- A script that assigns the queue directly to `{}` isn't seen.
|
||||
- Two following servers whose labs want different research keep flipping the cluster queue, as two labs do on one server.
|
||||
- A technology a server can't queue yet is dropped there, and an edit on that server then drops it for everyone.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type IpcFinished = {
|
||||
name: string,
|
||||
level: number,
|
||||
};
|
||||
type IpcQueue = { queue: unknown, player?: string };
|
||||
|
||||
export default async function(context: InstancePluginContext) {
|
||||
const { instance, logger, plugin } = context;
|
||||
@@ -68,14 +69,14 @@ export default async function(context: InstancePluginContext) {
|
||||
researchFinished(tech).catch(err => unexpectedError(err));
|
||||
});
|
||||
|
||||
instance.server.on("ipc-research_sync:queue", (data: { queue?: unknown, player?: string }) => {
|
||||
instance.server.on("ipc-research_sync:queue", (data: IpcQueue) => {
|
||||
try {
|
||||
instance.sendTo("controller", new QueueUpdateEvent(asArray(data.queue), data.player ?? null));
|
||||
} catch (err: any) {
|
||||
unexpectedError(err);
|
||||
}
|
||||
});
|
||||
instance.server.on("ipc-research_sync:follow", (data: { queue?: unknown, player?: string }) => {
|
||||
instance.server.on("ipc-research_sync:follow", (data: IpcQueue) => {
|
||||
followQueue(asArray(data.queue), data.player ?? null).catch(err => unexpectedError(err));
|
||||
});
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ end
|
||||
--- @class ResearchSyncData
|
||||
--- @field technologies table<string, ResearchSyncTechnology>
|
||||
--- @field ignore_research_finished boolean
|
||||
--- @field follow boolean? nil until first set; nil counts as following
|
||||
--- @field follow boolean? nil until the first queue_state sets it; the checkbox shows ticked but nil does not send or apply
|
||||
--- @field ignore_queue_events boolean
|
||||
--- @field queue_dirty boolean
|
||||
--- @field queue_player string?
|
||||
@@ -159,7 +159,7 @@ end
|
||||
--- @return string[]
|
||||
local function queue_names(force)
|
||||
local names = {}
|
||||
for _, tech in pairs(force.research_queue) do
|
||||
for _, tech in ipairs(force.research_queue) do
|
||||
table.insert(names, tech.name)
|
||||
end
|
||||
return names
|
||||
@@ -217,11 +217,13 @@ local function ensure_gui(player)
|
||||
end
|
||||
|
||||
if queue_sync then
|
||||
--- @param event EventData.on_player_created
|
||||
sync.events[defines.events.on_player_created] = function(event)
|
||||
ensure_gui(game.get_player(event.player_index) --[[@as LuaPlayer]])
|
||||
end
|
||||
sync.events[defines.events.on_player_joined_game] = sync.events[defines.events.on_player_created]
|
||||
|
||||
--- @param event EventData.on_gui_checked_state_changed
|
||||
sync.events[defines.events.on_gui_checked_state_changed] = function(event)
|
||||
if event.element.name == "research_sync_follow" then
|
||||
local player = game.get_player(event.player_index) --[[@as LuaPlayer]]
|
||||
@@ -229,6 +231,7 @@ if queue_sync then
|
||||
end
|
||||
end
|
||||
|
||||
--- @param event EventData.on_research_queued|EventData.on_research_moved|EventData.on_research_cancelled
|
||||
local function on_queue_event(event)
|
||||
if event.force.name ~= "player" then
|
||||
return
|
||||
@@ -247,7 +250,8 @@ if queue_sync then
|
||||
sync.events[defines.events.on_research_cancelled] = on_queue_event
|
||||
|
||||
-- One send per tick however many events fired (a lab raises several at once).
|
||||
sync.events[defines.events.on_tick] = function()
|
||||
--- @param event EventData.on_tick
|
||||
sync.events[defines.events.on_tick] = function(event)
|
||||
local script_data = get_script_data()
|
||||
if not script_data.queue_dirty then
|
||||
return
|
||||
|
||||
@@ -63,6 +63,15 @@ describe("research_sync controller", function() {
|
||||
await update(["a", "b"]);
|
||||
assert.equal(queueEvents().length, 1);
|
||||
});
|
||||
it("should broadcast a clear to an empty queue", async function() {
|
||||
await update(["a"], "Bob");
|
||||
sent.length = 0;
|
||||
await update([], null);
|
||||
assert.equal(queueEvents().length, 1);
|
||||
assert.deepEqual(queueEvents()[0][1].queue, []);
|
||||
assert.equal(queueEvents()[0][1].player, null);
|
||||
assert.deepEqual(await sync(null, null), []);
|
||||
});
|
||||
it("should cap the queue at 7", async function() {
|
||||
await update(["1", "2", "3", "4", "5", "6", "7", "8", "9"]);
|
||||
assert.deepEqual(queueEvents()[0][1].queue, ["1", "2", "3", "4", "5", "6", "7"]);
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
// required here in order to track coverage for them.
|
||||
|
||||
import "../dist/node/instance.js";
|
||||
import "../dist/node/controller.js";
|
||||
Reference in new issue
Block a user