mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Merge pull request #1188 from bbassie/fix/research-queue-message
Print only what changed in the research queue chat line
This commit is contained in:
1 parent
7cf9cf8d3e
commit
bd9c4a5fea
7 files changed
+209
-28
No files matched your search
@@ -16,7 +16,8 @@ It is on by default.
|
|||||||
The first server to turn following on with something queued shares its queue as the cluster queue, and servers that turn it on after that take the cluster's.
|
The first server to turn following on with something queued shares its queue as the cluster queue, and servers that turn it on after that take the cluster's.
|
||||||
A server's first start counts as turning it on; a later restart takes the cluster queue, even an empty one.
|
A server's first start counts as turning it on; a later restart takes the cluster queue, even an empty one.
|
||||||
Whenever the cluster queue is empty (for example, everything in it has finished), the next server that turns following on shares its queue again.
|
Whenever the cluster queue is empty (for example, everything in it has finished), the next server that turns following on shares its queue again.
|
||||||
Every change to the cluster queue prints a chat line on every server.
|
Every change to the cluster queue prints a chat line on every server naming the technologies added, removed or moved, with the level each queued entry researches.
|
||||||
|
Entries dropped because their research finished only print the "Researched" line.
|
||||||
|
|
||||||
It needs Factorio 2.0.56 or later, older servers ignore the queue.
|
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.
|
To turn it off without the GUI, run `/sc research_sync.set_follow(false)` on that server.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
TechnologyProgress,
|
TechnologyProgress,
|
||||||
QueueUpdateEvent,
|
QueueUpdateEvent,
|
||||||
QueueEvent,
|
QueueEvent,
|
||||||
|
QueueChange,
|
||||||
QueueSyncRequest,
|
QueueSyncRequest,
|
||||||
} from "./messages.js";
|
} from "./messages.js";
|
||||||
|
|
||||||
@@ -74,6 +75,59 @@ async function saveQueue(controllerConfig: lib.ControllerConfig, queue: string[]
|
|||||||
await lib.atomicOverwriteFile(filePath, JSON.stringify(queue, null, "\t"));
|
await lib.atomicOverwriteFile(filePath, JSON.stringify(queue, null, "\t"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the entries removed, added and moved between two queues. An entry is identified by its name and how many
|
||||||
|
* entries for the same name come before it, so extra levels of an infinite technology count as added or removed
|
||||||
|
* at the end. Moved entries are those outside the longest common subsequence of the kept entries.
|
||||||
|
*/
|
||||||
|
export function queueChanges(oldQueue: string[], newQueue: string[]): QueueChange[] {
|
||||||
|
function keyed(queue: string[]) {
|
||||||
|
const seen = new Map<string, number>();
|
||||||
|
return queue.map((name, index) => {
|
||||||
|
const offset = seen.get(name) ?? 0;
|
||||||
|
seen.set(name, offset + 1);
|
||||||
|
return { name, offset, position: index + 1, key: `${offset}:${name}` };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const oldEntries = keyed(oldQueue);
|
||||||
|
const newEntries = keyed(newQueue);
|
||||||
|
const oldKeys = new Set(oldEntries.map(e => e.key));
|
||||||
|
const newKeys = new Set(newEntries.map(e => e.key));
|
||||||
|
const keptOld = oldEntries.filter(e => newKeys.has(e.key));
|
||||||
|
const keptNew = newEntries.filter(e => oldKeys.has(e.key));
|
||||||
|
|
||||||
|
// lcs[i][j] is the length of the longest common subsequence of keptOld[i..] and keptNew[j..]
|
||||||
|
const lcs = Array.from({ length: keptOld.length + 1 }, () => new Array<number>(keptNew.length + 1).fill(0));
|
||||||
|
for (let i = keptOld.length - 1; i >= 0; i--) {
|
||||||
|
for (let j = keptNew.length - 1; j >= 0; j--) {
|
||||||
|
lcs[i][j] = keptOld[i].key === keptNew[j].key
|
||||||
|
? lcs[i + 1][j + 1] + 1
|
||||||
|
: Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const inPlace = new Set<string>();
|
||||||
|
for (let i = 0, j = 0; i < keptOld.length && j < keptNew.length;) {
|
||||||
|
if (keptOld[i].key === keptNew[j].key) {
|
||||||
|
inPlace.add(keptOld[i].key);
|
||||||
|
i += 1;
|
||||||
|
j += 1;
|
||||||
|
} else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const change = (type: QueueChange["type"], e: { name: string, offset: number, position: number }) => (
|
||||||
|
new QueueChange(type, e.name, e.offset, e.position)
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
...oldEntries.filter(e => !newKeys.has(e.key)).map(e => change("removed", e)),
|
||||||
|
...newEntries.filter(e => !oldKeys.has(e.key)).map(e => change("added", e)),
|
||||||
|
...keptNew.filter(e => !inPlace.has(e.key)).map(e => change("moved", e)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export default async function(context: ControllerPluginContext) {
|
export default async function(context: ControllerPluginContext) {
|
||||||
const { controller, logger, plugin } = context;
|
const { controller, logger, plugin } = context;
|
||||||
const technologies = await loadTechnologies(controller.config, logger);
|
const technologies = await loadTechnologies(controller.config, logger);
|
||||||
@@ -112,9 +166,10 @@ export default async function(context: ControllerPluginContext) {
|
|||||||
if (newQueue.length === queue.length && newQueue.every((name, i) => name === queue[i])) {
|
if (newQueue.length === queue.length && newQueue.every((name, i) => name === queue[i])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const changes = queueChanges(queue, newQueue);
|
||||||
queue = newQueue;
|
queue = newQueue;
|
||||||
queueDirty = true;
|
queueDirty = true;
|
||||||
controller.sendTo("allInstances", new QueueEvent(queue, player, instanceName(src)));
|
controller.sendTo("allInstances", new QueueEvent(queue, player, instanceName(src), changes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastProgress() {
|
function broadcastProgress() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
SyncTechnologiesRequest,
|
SyncTechnologiesRequest,
|
||||||
QueueUpdateEvent,
|
QueueUpdateEvent,
|
||||||
QueueEvent,
|
QueueEvent,
|
||||||
|
QueueChange,
|
||||||
QueueSyncRequest,
|
QueueSyncRequest,
|
||||||
} from "./messages.js";
|
} from "./messages.js";
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ type IpcFinished = {
|
|||||||
level: number,
|
level: number,
|
||||||
};
|
};
|
||||||
type IpcQueue = { queue: unknown, player?: string };
|
type IpcQueue = { queue: unknown, player?: string };
|
||||||
|
type ApplyQueue = { queue: string[], instance?: string, player?: string, changes?: QueueChange[] };
|
||||||
|
|
||||||
export default async function(context: InstancePluginContext) {
|
export default async function(context: InstancePluginContext) {
|
||||||
const { instance, host, logger, plugin } = context;
|
const { instance, host, logger, plugin } = context;
|
||||||
@@ -49,7 +51,7 @@ export default async function(context: InstancePluginContext) {
|
|||||||
return Array.isArray(v) ? v : [];
|
return Array.isArray(v) ? v : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyQueue(data: { queue: string[], instance?: string, player?: string }) {
|
async function applyQueue(data: ApplyQueue) {
|
||||||
await sendOrderedRcon(`/sc research_sync.apply_queue("${lib.escapeString(JSON.stringify(data))}")`, true);
|
await sendOrderedRcon(`/sc research_sync.apply_queue("${lib.escapeString(JSON.stringify(data))}")`, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,13 +179,16 @@ export default async function(context: InstancePluginContext) {
|
|||||||
if (!syncStarted || !["starting", "running"].includes(instance.status)) {
|
if (!syncStarted || !["starting", "running"].includes(instance.status)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data: { queue: string[], instance?: string, player?: string } = { queue: event.queue };
|
let data: ApplyQueue = { queue: event.queue };
|
||||||
if (event.instance !== null) {
|
if (event.instance !== null) {
|
||||||
data.instance = event.instance;
|
data.instance = event.instance;
|
||||||
}
|
}
|
||||||
if (event.player !== null) {
|
if (event.player !== null) {
|
||||||
data.player = event.player;
|
data.player = event.player;
|
||||||
}
|
}
|
||||||
|
if (event.changes?.length) {
|
||||||
|
data.changes = event.changes;
|
||||||
|
}
|
||||||
queueEventsApplied += 1;
|
queueEventsApplied += 1;
|
||||||
await applyQueue(data);
|
await applyQueue(data);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -160,6 +160,29 @@ export class QueueUpdateEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class QueueChange {
|
||||||
|
constructor(
|
||||||
|
public type: "added" | "removed" | "moved",
|
||||||
|
public name: string,
|
||||||
|
/** Entries for the same technology before this one, the level above the first queued level */
|
||||||
|
public offset: number,
|
||||||
|
/** 1-based position in the new queue, or in the old queue for removed */
|
||||||
|
public position: number,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static jsonSchema = Type.Object({
|
||||||
|
"type": Type.Union([Type.Literal("added"), Type.Literal("removed"), Type.Literal("moved")]),
|
||||||
|
"name": Type.String(),
|
||||||
|
"offset": Type.Integer(),
|
||||||
|
"position": Type.Integer(),
|
||||||
|
});
|
||||||
|
|
||||||
|
static fromJSON(json: Static<typeof QueueChange.jsonSchema>): QueueChange {
|
||||||
|
return new this(json.type, json.name, json.offset, json.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class QueueEvent {
|
export class QueueEvent {
|
||||||
declare ["constructor"]: typeof QueueEvent;
|
declare ["constructor"]: typeof QueueEvent;
|
||||||
static type = "event" as const;
|
static type = "event" as const;
|
||||||
@@ -171,6 +194,8 @@ export class QueueEvent {
|
|||||||
public queue: string[],
|
public queue: string[],
|
||||||
public player: string | null,
|
public player: string | null,
|
||||||
public instance: string | null,
|
public instance: string | null,
|
||||||
|
/** What changed, null for a silent update or an older controller */
|
||||||
|
public changes: QueueChange[] | null = null,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +203,16 @@ export class QueueEvent {
|
|||||||
"queue": Type.Array(Type.String()),
|
"queue": Type.Array(Type.String()),
|
||||||
"player": Type.Union([Type.String(), Type.Null()]),
|
"player": Type.Union([Type.String(), Type.Null()]),
|
||||||
"instance": Type.Union([Type.String(), Type.Null()]),
|
"instance": Type.Union([Type.String(), Type.Null()]),
|
||||||
|
"changes": Type.Optional(Type.Union([Type.Array(QueueChange.jsonSchema), Type.Null()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
static fromJSON(json: Static<typeof QueueEvent.jsonSchema>): QueueEvent {
|
static fromJSON(json: Static<typeof QueueEvent.jsonSchema>): QueueEvent {
|
||||||
return new this(json.queue, json.player, json.instance);
|
return new this(
|
||||||
|
json.queue,
|
||||||
|
json.player,
|
||||||
|
json.instance,
|
||||||
|
json.changes ? json.changes.map(c => QueueChange.fromJSON(c)) : null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,17 +177,43 @@ local function write_queue(force, names)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param names string[]
|
--- localised_name of a levelled technology includes its current level, so build the name with the given level
|
||||||
--- @return string
|
--- @param tech LuaTechnology
|
||||||
local function format_queue(names)
|
--- @param level number
|
||||||
if #names == 0 then
|
--- @return LocalisedString
|
||||||
return "(empty)"
|
local function technology_label(tech, level)
|
||||||
|
if tech.name:find("-%d+$") then
|
||||||
|
return {"", {"technology-name." .. tech.name:gsub("-%d+$", "")}, " ", level}
|
||||||
end
|
end
|
||||||
local tags = {}
|
return tech.localised_name
|
||||||
for _, name in ipairs(names) do
|
end
|
||||||
table.insert(tags, "[technology=" .. name .. "]")
|
|
||||||
|
--- @class ResearchSyncQueueChange
|
||||||
|
--- @field type "added"|"removed"|"moved"
|
||||||
|
--- @field name string
|
||||||
|
--- @field offset integer
|
||||||
|
--- @field position integer
|
||||||
|
|
||||||
|
--- @param changes ResearchSyncQueueChange[]
|
||||||
|
--- @return LocalisedString[]
|
||||||
|
local function format_changes(changes)
|
||||||
|
local force = game.forces["player"]
|
||||||
|
local parts = {}
|
||||||
|
for i, change in ipairs(changes) do
|
||||||
|
local tech = force.technologies[change.name]
|
||||||
|
local label
|
||||||
|
if tech then
|
||||||
|
label = technology_label(tech, tech.level + change.offset)
|
||||||
|
else
|
||||||
|
label = change.name
|
||||||
|
end
|
||||||
|
local part = {"", i > 1 and ", " or "", change.type, " ", label}
|
||||||
|
if change.type == "moved" then
|
||||||
|
table.insert(part, " to position " .. change.position)
|
||||||
|
end
|
||||||
|
table.insert(parts, part)
|
||||||
end
|
end
|
||||||
return table.concat(tags, " ")
|
return parts
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param player LuaPlayer
|
--- @param player LuaPlayer
|
||||||
@@ -392,11 +418,7 @@ function research_sync.research_technology(name, level)
|
|||||||
tech.level = level
|
tech.level = level
|
||||||
tech.researched = true
|
tech.researched = true
|
||||||
|
|
||||||
if tech.name:find("-%d+$") then
|
game.print {"", "Researched ", technology_label(tech, level)}
|
||||||
game.print {"", "Researched ", {"technology-name." .. tech.name:gsub("-%d+$", "")}, " ", level}
|
|
||||||
else
|
|
||||||
game.print {"", "Researched ", {"technology-name." .. tech.name}}
|
|
||||||
end
|
|
||||||
game.play_sound { path = "utility/research_completed" }
|
game.play_sound { path = "utility/research_completed" }
|
||||||
end
|
end
|
||||||
script_data.ignore_research_finished = false
|
script_data.ignore_research_finished = false
|
||||||
@@ -424,19 +446,28 @@ function research_sync.queue_state()
|
|||||||
}))
|
}))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param data string JSON {queue: string[], player?: string, instance?: string}
|
--- @param data string JSON {queue: string[], player?: string, instance?: string, changes?: ResearchSyncQueueChange[]}
|
||||||
function research_sync.apply_queue(data)
|
function research_sync.apply_queue(data)
|
||||||
if not research_queue_api then
|
if not research_queue_api then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local msg = compat.json_to_table(data) --[[@as table]]
|
local msg = compat.json_to_table(data) --[[@as table]]
|
||||||
local queue = msg.queue or {}
|
local queue = msg.queue or {}
|
||||||
if msg.instance then
|
if msg.instance and msg.changes then
|
||||||
local list = format_queue(queue)
|
-- "Bob added X on 0_1", or "Research queue on 0_1: added X"
|
||||||
|
local text = {"", msg.player and msg.player .. " " or "Research queue on " .. msg.instance .. ": "}
|
||||||
|
for _, part in ipairs(format_changes(msg.changes)) do
|
||||||
|
table.insert(text, part)
|
||||||
|
end
|
||||||
if msg.player then
|
if msg.player then
|
||||||
game.print(msg.player .. " changed the research queue on " .. msg.instance .. ": " .. list)
|
table.insert(text, " on " .. msg.instance)
|
||||||
|
end
|
||||||
|
game.print(text)
|
||||||
|
elseif msg.instance then
|
||||||
|
if msg.player then
|
||||||
|
game.print(msg.player .. " changed the research queue on " .. msg.instance)
|
||||||
else
|
else
|
||||||
game.print("The research queue was changed on " .. msg.instance .. ": " .. list)
|
game.print("The research queue was changed on " .. msg.instance)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import path from "node:path";
|
|||||||
|
|
||||||
import * as lib from "@clusterio/lib";
|
import * as lib from "@clusterio/lib";
|
||||||
import * as mock from "../../../test/mock.js";
|
import * as mock from "../../../test/mock.js";
|
||||||
import controllerEntrypoint from "../dist/node/controller.js";
|
import controllerEntrypoint, { queueChanges } from "../dist/node/controller.js";
|
||||||
import { plugin as info } from "../dist/node/index.js";
|
import { plugin as info } from "../dist/node/index.js";
|
||||||
import {
|
import {
|
||||||
ContributionEvent, FinishedEvent, QueueEvent, QueueSyncRequest, QueueUpdateEvent,
|
ContributionEvent, FinishedEvent, QueueChange, QueueEvent, QueueSyncRequest, QueueUpdateEvent,
|
||||||
SyncTechnologiesRequest, TechnologySync,
|
SyncTechnologiesRequest, TechnologySync,
|
||||||
} from "../dist/node/messages.js";
|
} from "../dist/node/messages.js";
|
||||||
|
|
||||||
@@ -58,6 +58,24 @@ describe("research_sync controller", function() {
|
|||||||
assert.equal(sent[0][1].instance, "rs-1");
|
assert.equal(sent[0][1].instance, "rs-1");
|
||||||
assert.deepEqual(await sync(), ["a", "b"]);
|
assert.deepEqual(await sync(), ["a", "b"]);
|
||||||
});
|
});
|
||||||
|
it("should send what changed with an update", async function() {
|
||||||
|
await update(["a", "b"], "Bob");
|
||||||
|
await update(["b", "a", "c"], "Bob");
|
||||||
|
assert.deepEqual(queueEvents().map(([, m]) => m.changes), [
|
||||||
|
[new QueueChange("added", "a", 0, 1), new QueueChange("added", "b", 0, 2)],
|
||||||
|
[new QueueChange("added", "c", 0, 3), new QueueChange("moved", "a", 0, 2)],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it("should send what changed when seeding from a sync request", async function() {
|
||||||
|
await sync(["x"], "Bob");
|
||||||
|
assert.deepEqual(queueEvents()[0][1].changes, [new QueueChange("added", "x", 0, 1)]);
|
||||||
|
});
|
||||||
|
it("should send no changes when dropping a finished technology", async function() {
|
||||||
|
await update(["x", "y"]);
|
||||||
|
sent.length = 0;
|
||||||
|
await mock.getHandler(controller, FinishedEvent)(new FinishedEvent("x", 1), src);
|
||||||
|
assert.equal(queueEvents()[0][1].changes, null);
|
||||||
|
});
|
||||||
it("should not broadcast an unchanged update", async function() {
|
it("should not broadcast an unchanged update", async function() {
|
||||||
await update(["a", "b"]);
|
await update(["a", "b"]);
|
||||||
await update(["a", "b"]);
|
await update(["a", "b"]);
|
||||||
@@ -213,6 +231,41 @@ describe("research_sync controller", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("queueChanges()", function() {
|
||||||
|
const added = (name, offset, position) => new QueueChange("added", name, offset, position);
|
||||||
|
const removed = (name, offset, position) => new QueueChange("removed", name, offset, position);
|
||||||
|
const moved = (name, offset, position) => new QueueChange("moved", name, offset, position);
|
||||||
|
|
||||||
|
it("should return nothing for equal queues", function() {
|
||||||
|
assert.deepEqual(queueChanges(["a", "b"], ["a", "b"]), []);
|
||||||
|
assert.deepEqual(queueChanges([], []), []);
|
||||||
|
});
|
||||||
|
it("should list an entry added anywhere", function() {
|
||||||
|
assert.deepEqual(queueChanges(["a", "b"], ["a", "x", "b"]), [added("x", 0, 2)]);
|
||||||
|
assert.deepEqual(queueChanges([], ["a"]), [added("a", 0, 1)]);
|
||||||
|
});
|
||||||
|
it("should list an entry removed with its old position", function() {
|
||||||
|
assert.deepEqual(queueChanges(["a", "x", "b"], ["a", "b"]), [removed("x", 0, 2)]);
|
||||||
|
assert.deepEqual(queueChanges(["a"], []), [removed("a", 0, 1)]);
|
||||||
|
});
|
||||||
|
it("should list a moved entry with its new position", function() {
|
||||||
|
assert.deepEqual(queueChanges(["a", "b", "c", "d"], ["d", "a", "b", "c"]), [moved("d", 0, 1)]);
|
||||||
|
assert.deepEqual(queueChanges(["a", "b", "c", "d"], ["b", "c", "a", "d"]), [moved("a", 0, 3)]);
|
||||||
|
});
|
||||||
|
it("should give repeated levels increasing offsets", function() {
|
||||||
|
assert.deepEqual(queueChanges(["m", "m"], ["m", "m", "m"]), [added("m", 2, 3)]);
|
||||||
|
assert.deepEqual(queueChanges([], ["m", "a", "m"]), [added("m", 0, 1), added("a", 0, 2), added("m", 1, 3)]);
|
||||||
|
});
|
||||||
|
it("should remove the highest levels when a repeated technology shrinks", function() {
|
||||||
|
assert.deepEqual(queueChanges(["m", "m", "m"], ["m"]), [removed("m", 1, 2), removed("m", 2, 3)]);
|
||||||
|
});
|
||||||
|
it("should list removed, then added, then moved", function() {
|
||||||
|
assert.deepEqual(queueChanges(["a", "b", "c"], ["c", "x", "a"]), [
|
||||||
|
removed("b", 0, 2), added("x", 0, 2), moved("a", 0, 3),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should persist the queue across loads", async function() {
|
it("should persist the queue across loads", async function() {
|
||||||
await update(["a", "b"]);
|
await update(["a", "b"]);
|
||||||
await controller.hooks.save.invoke();
|
await controller.hooks.save.invoke();
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import * as mock from "../../../test/mock.js";
|
|||||||
import loadInstancePlugin from "../dist/node/instance.js";
|
import loadInstancePlugin from "../dist/node/instance.js";
|
||||||
import { plugin as info } from "../dist/node/index.js";
|
import { plugin as info } from "../dist/node/index.js";
|
||||||
import {
|
import {
|
||||||
ContributionEvent, FinishedEvent, QueueEvent, QueueSyncRequest, QueueUpdateEvent, SyncTechnologiesRequest,
|
ContributionEvent, FinishedEvent, QueueChange, QueueEvent, QueueSyncRequest, QueueUpdateEvent,
|
||||||
TechnologySync,
|
SyncTechnologiesRequest, TechnologySync,
|
||||||
} from "../dist/node/messages.js";
|
} from "../dist/node/messages.js";
|
||||||
|
|
||||||
describe("research_sync instance", function() {
|
describe("research_sync instance", function() {
|
||||||
@@ -75,6 +75,11 @@ describe("research_sync instance", function() {
|
|||||||
await handler()(new QueueEvent(["a"], "Bob", "rs-2"));
|
await handler()(new QueueEvent(["a"], "Bob", "rs-2"));
|
||||||
assert.equal(lastCommand(), applyCommand({ queue: ["a"], instance: "rs-2", player: "Bob" }));
|
assert.equal(lastCommand(), applyCommand({ queue: ["a"], instance: "rs-2", player: "Bob" }));
|
||||||
});
|
});
|
||||||
|
it("should pass on the changes", async function() {
|
||||||
|
const changes = [new QueueChange("added", "a", 0, 1)];
|
||||||
|
await handler()(new QueueEvent(["a"], "Bob", "rs-2", changes));
|
||||||
|
assert.equal(lastCommand(), applyCommand({ queue: ["a"], instance: "rs-2", player: "Bob", changes }));
|
||||||
|
});
|
||||||
it("should leave out a null player", async function() {
|
it("should leave out a null player", async function() {
|
||||||
await handler()(new QueueEvent(["a"], null, "rs-2"));
|
await handler()(new QueueEvent(["a"], null, "rs-2"));
|
||||||
assert.equal(lastCommand(), applyCommand({ queue: ["a"], instance: "rs-2" }));
|
assert.equal(lastCommand(), applyCommand({ queue: ["a"], instance: "rs-2" }));
|
||||||
|
|||||||
Reference in new issue
Block a user