Merge pull request #527 from Hornwitser/autosave-data

Implement autosaving of data held in memory
This commit is contained in:
Hornwitser authored and GitHub committed 2023-12-01 02:30:54 +01:00
commit f802e6ba67
22 files changed
+366 -138

No files matched your search

+11 -13
View File
@@ -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() {
-7
View File
@@ -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",
+1 -1
View File
@@ -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");
}
+14 -1
View File
@@ -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;
}
}
}
+10 -9
View File
@@ -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);
}
}
}
-7
View File
@@ -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",