mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 07:04:52 +09:00
Add bidirectional whitelist sync
This commit is contained in:
4 files changed
+91
-10
No files matched your search
@@ -266,6 +266,15 @@ export default class Instance extends lib.Link {
|
||||
}
|
||||
});
|
||||
|
||||
this.server.on("whitelist-change", (added: string[], removed: string[]) => {
|
||||
for (const player of added) {
|
||||
this._recordUserUpdate("WHITELISTED", player);
|
||||
}
|
||||
for (const player of removed) {
|
||||
this._recordUserUpdate("UNWHITELISTED", player);
|
||||
}
|
||||
});
|
||||
|
||||
this.handle(lib.InstanceExtractPlayersRequest, this.handleInstanceExtractPlayersRequest.bind(this));
|
||||
this.handle(lib.InstanceAdminlistUpdateEvent, this.handleInstanceAdminlistUpdateEvent.bind(this));
|
||||
this.handle(lib.InstanceBanlistUpdateEvent, this.handleInstanceBanlistUpdateEvent.bind(this));
|
||||
@@ -1031,7 +1040,6 @@ end`.replace(/\r?\n/g, " ");
|
||||
name: string,
|
||||
reason: string = ""
|
||||
) {
|
||||
// TODO: Implement bidirectional whitelist sync, likely by watching the json file
|
||||
const addr = lib.Address.fromShorthand("allInstances");
|
||||
const expectedIndex = this._expectedUserUpdates.findIndex(
|
||||
expected => expected.name === name && expected.action === action && expected.reason === reason
|
||||
|
||||
@@ -500,6 +500,10 @@ export class FactorioServer extends events.EventEmitter {
|
||||
_version: string | null = null;
|
||||
_dataDir: string | null = null;
|
||||
|
||||
// Due to inconsistencies in the factorio api, we must manually watch the whitelist
|
||||
// https://forums.factorio.com/viewtopic.php?t=123673
|
||||
_whitelistWatcher: fs.FSWatcher | null = null;
|
||||
_whitelist = new Set<string>();
|
||||
|
||||
_logger: lib.Logger;
|
||||
_targetVersion: string;
|
||||
@@ -741,6 +745,62 @@ export class FactorioServer extends events.EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
_watchWhitelist() {
|
||||
if (this._whitelistWatcher !== null) {
|
||||
throw new Error("Whitelist watcher already started");
|
||||
}
|
||||
|
||||
if (!this.enableWhitelist) {
|
||||
return; // The file is not updated if the whitelist is disabled
|
||||
}
|
||||
|
||||
const filePath = this.writePath("server-whitelist.json");
|
||||
try {
|
||||
this._whitelistWatcher = fs.watch(filePath);
|
||||
} catch (err: any) {
|
||||
this._logger.error(`Unable to watch whitelist, bidirectional sync will not be available:\n${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._whitelistWatcher.unref();
|
||||
this._whitelistWatcher.on("error", (err) => {
|
||||
this._whitelistWatcher = null;
|
||||
this._logger.error(`Whitelist Watcher has errored:\n${err?.stack ?? err?.message ?? err}`);
|
||||
});
|
||||
this._whitelistWatcher.on("close", () => {
|
||||
this._whitelistWatcher = null;
|
||||
});
|
||||
this._whitelistWatcher.on("change", async (eventType) => {
|
||||
if (eventType !== "change") {
|
||||
this._logger.warn(`Unexpected file watcher event: ${eventType}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let newWhitelistJson;
|
||||
try {
|
||||
newWhitelistJson = await fs.readJSON(filePath);
|
||||
} catch (err: any) {
|
||||
this._logger.error(`Unable to read whitelist, bidirectional sync will not be available:\n${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(newWhitelistJson instanceof Array) || !newWhitelistJson.every(e => typeof e === "string")) {
|
||||
// The whitelist must be an array of strings
|
||||
this._logger.error("Unable to read whitelist, bidirectional sync will not be available:" +
|
||||
"Unexpected whitelist format");
|
||||
return;
|
||||
}
|
||||
|
||||
const newWhitelist = new Set(newWhitelistJson);
|
||||
// Set.difference added in node v22 and so we can not use this due to v20 support
|
||||
const added = newWhitelistJson.filter(e => !this._whitelist.has(e));
|
||||
const removed = [...this._whitelist.values()].filter(e => !newWhitelist.has(e));
|
||||
this._whitelist = newWhitelist;
|
||||
|
||||
this.emit("whitelist-change", added, removed);
|
||||
});
|
||||
}
|
||||
|
||||
/** Maximum number of RCON commands transmitted in parallel on the RCON connection */
|
||||
get maxConcurrentCommands() {
|
||||
return this._maxConcurrentCommands;
|
||||
@@ -854,6 +914,10 @@ export class FactorioServer extends events.EventEmitter {
|
||||
this._rconClient.end().catch(() => {});
|
||||
}
|
||||
|
||||
if (this._whitelistWatcher) {
|
||||
this._whitelistWatcher.close();
|
||||
}
|
||||
|
||||
this._resetState();
|
||||
this.emit("exit");
|
||||
});
|
||||
@@ -868,6 +932,10 @@ export class FactorioServer extends events.EventEmitter {
|
||||
this._rconClient.end().catch(() => {});
|
||||
}
|
||||
|
||||
if (this._whitelistWatcher) {
|
||||
this._whitelistWatcher.close();
|
||||
}
|
||||
|
||||
this._resetState();
|
||||
this.emit("exit");
|
||||
});
|
||||
@@ -986,6 +1054,7 @@ export class FactorioServer extends events.EventEmitter {
|
||||
stdio: "pipe",
|
||||
}
|
||||
);
|
||||
this._watchWhitelist();
|
||||
this._attachStdio();
|
||||
this._watchExit();
|
||||
/* eslint-enable node/no-sync */
|
||||
@@ -1036,12 +1105,14 @@ export class FactorioServer extends events.EventEmitter {
|
||||
...(this.enableWhitelist ? ["--use-server-whitelist"] : []),
|
||||
...(this.enableAuthserverBans ? ["--use-authserver-bans"] : []),
|
||||
...(this.verboseLogging ? ["--verbose"] : []),
|
||||
...(this.consoleLogging ? ["--console-log", this.writePath("console.log")] : []),
|
||||
],
|
||||
{
|
||||
detached: true,
|
||||
stdio: "pipe",
|
||||
}
|
||||
);
|
||||
this._watchWhitelist();
|
||||
this._attachStdio();
|
||||
this._watchExit();
|
||||
/* eslint-enable node/no-sync */
|
||||
@@ -1117,6 +1188,9 @@ export class FactorioServer extends events.EventEmitter {
|
||||
if (this._rconClient) {
|
||||
this._rconClient.end().catch(() => {});
|
||||
}
|
||||
if (this._whitelistWatcher) {
|
||||
this._whitelistWatcher.close();
|
||||
}
|
||||
this._server!.kill("SIGKILL");
|
||||
}
|
||||
|
||||
|
||||
@@ -476,12 +476,13 @@ export class InstanceConfig extends classes.Config<InstanceConfigFields> {
|
||||
},
|
||||
"factorio.enable_whitelist": {
|
||||
description: "Turn on whitelist for joining the server.",
|
||||
restartRequired: true, // Because of the cli option "--use-server-whitelist"
|
||||
type: "boolean",
|
||||
initialValue: false,
|
||||
},
|
||||
"factorio.enable_authserver_bans": {
|
||||
description: "Turn on Factorio.com based multiplayer bans.",
|
||||
restartRequired: true,
|
||||
restartRequired: true, // Because of the cli option "--use-authserver-bans"
|
||||
type: "boolean",
|
||||
initialValue: false,
|
||||
},
|
||||
@@ -499,13 +500,13 @@ export class InstanceConfig extends classes.Config<InstanceConfigFields> {
|
||||
},
|
||||
"factorio.verbose_logging": {
|
||||
description: "Enable verbose logging on the Factorio server",
|
||||
restartRequired: true,
|
||||
restartRequired: true, // Because of the cli option "--verbose"
|
||||
type: "boolean",
|
||||
initialValue: false,
|
||||
},
|
||||
"factorio.console_logging": {
|
||||
description: "Enable console logging to a separate file, useful for 3rd party integrations",
|
||||
restartRequired: true,
|
||||
restartRequired: true, // Because of the cli option "--console-log"
|
||||
type: "boolean",
|
||||
initialValue: false,
|
||||
},
|
||||
@@ -522,10 +523,10 @@ export class InstanceConfig extends classes.Config<InstanceConfigFields> {
|
||||
initialValue: "bidirectional",
|
||||
},
|
||||
"factorio.sync_whitelist": {
|
||||
description: "Synchronize whitelist with the controller",
|
||||
description: "Synchronize whitelist with the controller (whitelist must be enabled)",
|
||||
type: "string",
|
||||
enum: ["disabled", "enabled"], // TODO: Implement bidirectional
|
||||
initialValue: "enabled",
|
||||
enum: ["disabled", "enabled", "bidirectional"],
|
||||
initialValue: "bidirectional",
|
||||
},
|
||||
"factorio.sync_banlist": {
|
||||
description: "Synchronize banlist with the controller",
|
||||
|
||||
@@ -200,7 +200,7 @@ describe("class Instance", function() {
|
||||
beforeEach(function() {
|
||||
instance.config.set("factorio.sync_banlist", "bidirectional");
|
||||
instance.config.set("factorio.sync_adminlist", "bidirectional");
|
||||
// instance.config.set("factorio.sync_whitelist", "bidirectional"); // Not Implemented
|
||||
instance.config.set("factorio.sync_whitelist", "bidirectional");
|
||||
});
|
||||
it("should send InstanceBanlistUpdateEvent for bans", function() {
|
||||
instance._recordUserUpdate("BAN", "player", "reason");
|
||||
@@ -257,7 +257,6 @@ describe("class Instance", function() {
|
||||
);
|
||||
});
|
||||
it("should send InstanceWhitelistUpdateEvent for whitelist add", function() {
|
||||
this.skip(); // Bidirectional not implemented
|
||||
instance._recordUserUpdate("WHITELISTED", "player");
|
||||
assert.deepEqual(
|
||||
connector.sentMessages[0],
|
||||
@@ -271,7 +270,6 @@ describe("class Instance", function() {
|
||||
);
|
||||
});
|
||||
it("should send InstanceWhitelistUpdateEvent for whitelist remove", function() {
|
||||
this.skip(); // Bidirectional not implemented
|
||||
instance._recordUserUpdate("UNWHITELISTED", "player");
|
||||
assert.deepEqual(
|
||||
connector.sentMessages[0],
|
||||
|
||||
Reference in new issue
Block a user