mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 15:24:52 +09:00
Merge pull request #527 from Hornwitser/autosave-data
Implement autosaving of data held in memory
This commit is contained in:
22 files changed
+366
-138
No files matched your search
@@ -38,16 +38,11 @@ async function saveDatabase(
|
||||
export class ControllerPlugin extends BaseControllerPlugin {
|
||||
acquiredPlayers!: Map<string, { instanceId: number, expires?: number }>;
|
||||
playerDatastore!: Map<string, IpcPlayerData>;
|
||||
autosaveId!: ReturnType<typeof setInterval>;
|
||||
playerDatastoreDirty = false;
|
||||
|
||||
async init() {
|
||||
this.acquiredPlayers = new Map();
|
||||
this.playerDatastore = await loadDatabase(this.controller.config, this.logger);
|
||||
this.autosaveId = setInterval(() => {
|
||||
saveDatabase(this.controller.config, this.playerDatastore, this.logger).catch(err => {
|
||||
this.logger.error(`Unexpected error autosaving player data:\n${err.stack}`);
|
||||
});
|
||||
}, this.controller.config.get("inventory_sync.autosave_interval") as number * 1000);
|
||||
|
||||
this.controller.handle(msg.AcquireRequest, this.handleAcquireRequest.bind(this));
|
||||
this.controller.handle(msg.ReleaseRequest, this.handleReleaseRequest.bind(this));
|
||||
@@ -89,7 +84,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
if (
|
||||
!acquisitionRecord
|
||||
|| acquisitionRecord.instanceId === instanceId
|
||||
|| !this.controller.instances!.has(acquisitionRecord.instanceId)
|
||||
|| !this.controller.instances.has(acquisitionRecord.instanceId)
|
||||
|| acquisitionRecord.expires && acquisitionRecord.expires < Date.now()
|
||||
) {
|
||||
this.acquiredPlayers.set(playerName, { instanceId });
|
||||
@@ -103,7 +98,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
let { instanceId, playerName } = request;
|
||||
if (!this.acquire(instanceId, playerName)) {
|
||||
let acquisitionRecord = this.acquiredPlayers.get(playerName);
|
||||
let instance = this.controller.instances!.get(acquisitionRecord!.instanceId)!;
|
||||
let instance = this.controller.instances.get(acquisitionRecord!.instanceId)!;
|
||||
return {
|
||||
status: "busy",
|
||||
message: instance.config.get("instance.name"),
|
||||
@@ -132,7 +127,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
|
||||
async handleUploadRequest(request: msg.UploadRequest) {
|
||||
let { instanceId, playerName, playerData } = request;
|
||||
let instanceName = this.controller.instances!.get(instanceId)!.config.get("instance.name");
|
||||
let instanceName = this.controller.instances.get(instanceId)!.config.get("instance.name");
|
||||
let store = true;
|
||||
let acquisitionRecord = this.acquiredPlayers.get(playerName);
|
||||
if (!acquisitionRecord) {
|
||||
@@ -161,12 +156,13 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
if (store) {
|
||||
this.logger.verbose(`Received player data for ${playerName} from ${instanceName}`);
|
||||
this.playerDatastore.set(playerName, playerData);
|
||||
this.playerDatastoreDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
async handleDownloadRequest(request: msg.DownloadRequest) {
|
||||
let { instanceId, playerName } = request;
|
||||
let instanceName = this.controller.instances!.get(instanceId)!.config.get("instance.name");
|
||||
let instanceName = this.controller.instances.get(instanceId)!.config.get("instance.name");
|
||||
|
||||
let acquisitionRecord = this.acquiredPlayers.get(playerName);
|
||||
if (!acquisitionRecord) {
|
||||
@@ -179,9 +175,11 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
return new msg.DownloadRequest.Response(this.playerDatastore.get(playerName) || null);
|
||||
}
|
||||
|
||||
async onShutdown() {
|
||||
clearInterval(this.autosaveId);
|
||||
await saveDatabase(this.controller.config, this.playerDatastore, this.logger);
|
||||
async onSaveData() {
|
||||
if (this.playerDatastoreDirty) {
|
||||
this.playerDatastoreDirty = false;
|
||||
await saveDatabase(this.controller.config, this.playerDatastore, this.logger);
|
||||
}
|
||||
}
|
||||
|
||||
async handleDatabaseStatsRequest() {
|
||||
|
||||
@@ -4,13 +4,6 @@ import * as messages from "./messages";
|
||||
class ControllerConfigGroup extends lib.PluginConfigGroup { }
|
||||
ControllerConfigGroup.defaultAccess = ["controller", "host", "control"];
|
||||
ControllerConfigGroup.groupName = "inventory_sync";
|
||||
ControllerConfigGroup.define({
|
||||
name: "autosave_interval",
|
||||
title: "Autosave Interval",
|
||||
description: "Interval the player data is autosaved at in seconds.",
|
||||
type: "number",
|
||||
initial_value: 600, // 10 minutes
|
||||
});
|
||||
ControllerConfigGroup.define({
|
||||
name: "player_lock_timeout",
|
||||
title: "Player Lock Timeout",
|
||||
|
||||
@@ -47,7 +47,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
|
||||
this.controller.app.get("/api/player_auth/servers", (req: Request, res: Response) => {
|
||||
let servers: string[] = [];
|
||||
for (let instance of this.controller.instances!.values()) {
|
||||
for (let instance of this.controller.instances.values()) {
|
||||
if (instance.status === "running" && instance.config.get("player_auth.load_plugin")) {
|
||||
servers.push(instance.config.get("factorio.settings")["name"] as string || "unnamed server");
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ async function saveTechnologies(
|
||||
|
||||
export class ControllerPlugin extends BaseControllerPlugin {
|
||||
technologies!: Map<string, Technology>;
|
||||
technologiesDirty = true;
|
||||
progressRateLimiter!: lib.RateLimiter;
|
||||
progressBroadcastId!: ReturnType<typeof setInterval> | null;
|
||||
progressToBroadcast!: Set<string>;
|
||||
@@ -70,9 +71,15 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
this.controller.handle(SyncTechnologiesRequest, this.handleSyncTechnologiesRequest.bind(this));
|
||||
}
|
||||
|
||||
async onSaveData() {
|
||||
if (this.technologiesDirty) {
|
||||
this.technologiesDirty = false;
|
||||
await saveTechnologies(this.controller.config, this.technologies, this.logger);
|
||||
}
|
||||
}
|
||||
|
||||
async onShutdown() {
|
||||
this.progressRateLimiter.cancel();
|
||||
await saveTechnologies(this.controller.config, this.technologies, this.logger);
|
||||
}
|
||||
|
||||
broadcastProgress() {
|
||||
@@ -96,6 +103,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
if (!tech) {
|
||||
tech = { level, progress: 0, researched: false };
|
||||
this.technologies.set(name, tech);
|
||||
this.technologiesDirty = true;
|
||||
|
||||
// Ignore contribution to already researched technologies
|
||||
} else if (tech.level > level || tech.level === level && tech.researched) {
|
||||
@@ -126,6 +134,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
|
||||
this.controller.sendTo("allInstances", new FinishedEvent(name, tech.level));
|
||||
}
|
||||
this.technologiesDirty = true;
|
||||
}
|
||||
|
||||
async handleFinishedEvent(event: FinishedEvent) {
|
||||
@@ -134,6 +143,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
if (!tech || tech.level <= level) {
|
||||
this.progressToBroadcast.delete(name);
|
||||
this.technologies.set(name, { level, progress: null, researched: true });
|
||||
this.technologiesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +161,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
let tech = this.technologies.get(name);
|
||||
if (!tech) {
|
||||
this.technologies.set(name, { level, progress, researched });
|
||||
this.technologiesDirty = true;
|
||||
if (progress) {
|
||||
this.progressToBroadcast.add(name);
|
||||
} else if (researched || baseLevel(name) !== level) {
|
||||
@@ -176,10 +187,12 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
} else {
|
||||
this.progressToBroadcast.delete(name);
|
||||
}
|
||||
this.technologiesDirty = true;
|
||||
|
||||
} else if (tech.progress && progress && tech.progress < progress) {
|
||||
tech.progress = progress;
|
||||
this.progressToBroadcast.add(name);
|
||||
this.technologiesDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
itemUpdateRateLimiter!: lib.RateLimiter;
|
||||
itemsLastUpdate!: Map<string, number>;
|
||||
subscribedControlLinks!: Set<ControlConnection>;
|
||||
autosaveId!: ReturnType<typeof setInterval>;
|
||||
doleMagicId!: ReturnType<typeof setInterval>;
|
||||
neuralDole!: dole.NeuralDole;
|
||||
storageDirty = false;
|
||||
|
||||
async init() {
|
||||
this.items = await loadDatabase(this.controller.config, this.logger);
|
||||
@@ -92,11 +92,6 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
},
|
||||
});
|
||||
this.itemsLastUpdate = new Map(this.items.getEntries());
|
||||
this.autosaveId = setInterval(() => {
|
||||
saveDatabase(this.controller.config, this.items, this.logger).catch(err => {
|
||||
this.logger.error(`Unexpected error autosaving items:\n${err.stack}`);
|
||||
});
|
||||
}, this.controller.config.get("subspace_storage.autosave_interval") as number * 1000);
|
||||
|
||||
this.neuralDole = new dole.NeuralDole({ items: this.items });
|
||||
this.doleMagicId = setInterval(() => {
|
||||
@@ -117,6 +112,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
|
||||
updateStorage() {
|
||||
this.itemUpdateRateLimiter.activate();
|
||||
this.storageDirty = true;
|
||||
}
|
||||
|
||||
broadcastStorage() {
|
||||
@@ -180,7 +176,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
}
|
||||
|
||||
} else {
|
||||
let instance = this.controller.instances!.get(instanceId);
|
||||
let instance = this.controller.instances.get(instanceId);
|
||||
let instanceName = instance ? instance.config.get("instance.name") : "unkonwn";
|
||||
|
||||
// use fancy neural net to calculate a "fair" dole division rate.
|
||||
@@ -254,8 +250,13 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
|
||||
async onShutdown() {
|
||||
this.itemUpdateRateLimiter.cancel();
|
||||
clearInterval(this.autosaveId);
|
||||
clearInterval(this.doleMagicId);
|
||||
await saveDatabase(this.controller.config, this.items, this.logger);
|
||||
}
|
||||
|
||||
async onSaveData() {
|
||||
if (this.storageDirty) {
|
||||
this.storageDirty = false;
|
||||
await saveDatabase(this.controller.config, this.items, this.logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,6 @@ import * as messages from "./messages";
|
||||
class ControllerConfigGroup extends lib.PluginConfigGroup {}
|
||||
ControllerConfigGroup.defaultAccess = ["controller", "host", "control"];
|
||||
ControllerConfigGroup.groupName = "subspace_storage";
|
||||
ControllerConfigGroup.define({
|
||||
name: "autosave_interval",
|
||||
title: "Autosave Interval",
|
||||
description: "Interval the storage is autosaved at in seconds.",
|
||||
type: "number",
|
||||
initial_value: 60,
|
||||
});
|
||||
ControllerConfigGroup.define({
|
||||
name: "division_method",
|
||||
title: "Division Method",
|
||||
|
||||
Reference in new issue
Block a user