Move save and load into config class

This commit is contained in:
Cooldude2606 committed 2025-05-12 21:32:44 +01:00
1 parent 17db53403a
commit 726a3538c2
8 files changed
+55 -34

No files matched your search

+2 -5
View File
@@ -346,8 +346,7 @@ async function initialize(): Promise<InitializeParameters> {
const controllerConfigPath = args.config;
logger.info(`Loading config from ${controllerConfigPath}`);
try {
let jsonConfig = JSON.parse(await fs.readFile(controllerConfigPath, { encoding: "utf8" }));
controllerConfig = lib.ControllerConfig.fromJSON(jsonConfig, "controller", controllerConfigPath);
controllerConfig = await lib.ControllerConfig.fromFile("controller", controllerConfigPath);
} catch (err: any) {
if (err.code === "ENOENT") {
@@ -366,9 +365,7 @@ async function initialize(): Promise<InitializeParameters> {
let asyncRandomBytes = util.promisify(crypto.randomBytes);
let bytes = await asyncRandomBytes(256);
controllerConfig.set("controller.auth_secret", bytes.toString("base64"));
await lib.safeOutputFile(
controllerConfigPath, JSON.stringify(controllerConfig, null, "\t")
);
await controllerConfig.save();
}
if (command === "config") {
+1 -5
View File
@@ -581,12 +581,8 @@ export default class Controller {
});
private async _saveDataInternal() {
if (this.config.dirty) {
this.config.dirty = false;
await lib.safeOutputFile(this.config.filepath!, JSON.stringify(this.config, null, "\t"));
}
await Promise.all([
this.config.save(),
this.systems.save(),
this.hosts.save(),
this.instances.save(),
+16 -20
View File
@@ -43,12 +43,13 @@ async function discoverInstances(instancesDir: string) {
let instanceInfos = new Map<number, { path: string, config: lib.InstanceConfig }>();
for (let entry of await fs.readdir(instancesDir, { withFileTypes: true })) {
if (entry.isDirectory()) {
let instanceConfig = new lib.InstanceConfig("host");
let configPath = path.join(instancesDir, entry.name, "instance.json");
let instanceConfig = new lib.InstanceConfig("host", Host.instanceConfigWarning);
const instancePath = path.join(instancesDir, entry.name);
const configPath = path.join(instancePath, "instance.json");
try {
const jsonConfig = JSON.parse(await fs.readFile(configPath, "utf8"));
instanceConfig = lib.InstanceConfig.fromJSON(jsonConfig, "host", configPath);
instanceConfig = await lib.InstanceConfig.fromFile("host", configPath);
instanceConfig.update(Host.instanceConfigWarning, false); // File might be missing the warning
} catch (err: any) {
if (err.code === "ENOENT") {
@@ -64,7 +65,6 @@ async function discoverInstances(instancesDir: string) {
continue;
}
let instancePath = path.join(instancesDir, entry.name);
logger.verbose(`found instance ${instanceConfig.get("instance.name")} in ${instancePath}`);
instanceInfos.set(instanceConfig.get("instance.id"), {
path: instancePath,
@@ -278,6 +278,10 @@ export default class Host extends lib.Link {
_disconnecting = false;
_shuttingDown = false;
static instanceConfigWarning = {
"_warning": "Changes to this file will be overwritten by the controller's copy.",
};
static async bootstrap(hostConfig: lib.HostConfig) {
const modsDirectory = hostConfig.get("host.mods_directory");
await fs.ensureDir(modsDirectory);
@@ -652,7 +656,7 @@ export default class Host extends lib.Link {
instanceInfo.config.update(config, true, "controller");
} else {
let instanceConfig = new lib.InstanceConfig("host");
let instanceConfig = new lib.InstanceConfig("host", Host.instanceConfigWarning);
instanceConfig.update(config, false, "controller");
let instanceDir = await this._createNewInstanceDir(instanceConfig.get("instance.name"));
@@ -691,15 +695,7 @@ export default class Host extends lib.Link {
await this.sendSaveListUpdate(instanceId, path.join(instanceInfo.path, "saves"));
// save a copy of the instance config
await this.saveInstanceConfig(instanceInfo);
}
async saveInstanceConfig(instanceInfo: { path: string, config: lib.InstanceConfig }) {
let warnedOutput = {
_warning: "Changes to this file will be overwritten by the controller's copy.",
...instanceInfo.config.toJSON(),
};
await lib.safeOutputFile(instanceInfo.config.filepath!, JSON.stringify(warnedOutput, null, "\t"));
await instanceInfo.config.save();
}
async handleInstanceUnassignInternalRequest(request: lib.InstanceUnassignInternalRequest) {
@@ -1152,11 +1148,11 @@ export default class Host extends lib.Link {
}
logger.info("Saving config");
await lib.safeOutputFile(this.config.filepath!, JSON.stringify(this.config, null, "\t"));
for (const instanceInfo of this.instanceInfos.values()) {
// Save in case host_assigned_game_port changed in the config.
await this.saveInstanceConfig(instanceInfo);
}
await this.config.save();
// Save host side in case host_assigned_game_port changed in the config.
await Promise.all(
[...this.instanceInfos.values()].map(instanceInfo => instanceInfo.config.save())
);
try {
// Clear silly interval in pidfile library.
+28 -3
View File
@@ -1,11 +1,13 @@
// Configuration classes
import { Type, Static } from "@sinclair/typebox";
import TypedEventEmitter from "../TypedEventEmitter";
import * as fs from "fs-extra";
import TypedEventEmitter from "../TypedEventEmitter";
import isDeepStrictEqual from "../is_deep_strict_equal";
import { basicType } from "../helpers";
import * as libSchema from "../schema";
import { StringEnum } from "../data/composites";
import { safeOutputFile } from "../file_ops";
const ConfigLocation = StringEnum(["controller", "host", "control"]);
export type ConfigLocation = Static<typeof ConfigLocation>;
@@ -206,7 +208,7 @@ export class Config<
private _unknownFields: Record<string, FieldValue> = {};
/** Set to true when a field in the config is changed. */
dirty = false;
private dirty = false;
/** Set to true when a 'restart required' field in the config is changed. */
restartRequired = false;
@@ -301,8 +303,31 @@ export class Config<
}
}
static jsonSchema = ConfigSchema;
/**
* Create config from file
*
* @param location - Location used for access control.
* @param filepath - Filepath for the config to save to.
* @returns Instance of this config
*/
static async fromFile(location: ConfigLocation, filepath: string) {
const fields = await fs.readJson(filepath, { encoding: "utf8" });
return this.fromJSON(fields, location, filepath);
}
/** Saves this config to file if there are unsaved changes */
async save() {
if (!this.dirty) {
return;
}
if (!this.filepath) {
throw new Error("Cannot save config which has no filepath");
}
await safeOutputFile(this.filepath, JSON.stringify(this, null, "\t"));
this.dirty = false;
}
static jsonSchema = ConfigSchema;
static validate = libSchema.compile(this.jsonSchema as any);
/**
+5
View File
@@ -4,6 +4,7 @@ import type { PluginNodeEnvInfo, PluginWebpackEnvInfo } from "../plugin";
import { Static } from "@sinclair/typebox";
type configFromJSON<T> = (...args: Parameters<typeof classes.Config.fromJSON>) => T;
type configFromFile<T> = (...args: Parameters<typeof classes.Config.fromFile>) => Promise<T>;
export interface ControllerConfigFields {
"controller.version": string;
@@ -39,6 +40,7 @@ export interface ControllerConfigFields {
*/
export class ControllerConfig extends classes.Config<ControllerConfigFields> {
declare static fromJSON: configFromJSON<ControllerConfig>;
declare static fromFile: configFromFile<ControllerConfig>;
static migrations(config: Static<typeof this.jsonSchema>) {
if (config.hasOwnProperty("controller.external_address")) {
config["controller.public_url"] = config["controller.external_address"];
@@ -246,6 +248,7 @@ export interface HostConfigFields {
*/
export class HostConfig extends classes.Config<HostConfigFields> {
declare static fromJSON: configFromJSON<HostConfig>;
declare static fromFile: configFromFile<HostConfig>;
static fieldDefinitions: classes.ConfigDefs<HostConfigFields> = {
"host.version": {
type: "string",
@@ -378,6 +381,7 @@ export interface InstanceConfigFields {
*/
export class InstanceConfig extends classes.Config<InstanceConfigFields> {
declare static fromJSON: configFromJSON<InstanceConfig>;
declare static fromFile: configFromFile<InstanceConfig>;
static migrations(config: Static<typeof this.jsonSchema>) {
function boolToEnableDisable(name: string) {
if (config.hasOwnProperty(name) && typeof config[name] === "boolean") {
@@ -571,6 +575,7 @@ export interface ControlConfigFields {
*/
export class ControlConfig extends classes.Config<ControlConfigFields> {
declare static fromJSON: configFromJSON<ControlConfig>;
declare static fromFile: configFromFile<ControlConfig>;
static fieldDefinitions: classes.ConfigDefs<ControlConfigFields> = {
"control.controller_url": {
description: "URL to connect to the controller at",
+1 -1
View File
@@ -164,7 +164,7 @@ export async function handleConfigCommand(
try {
instance.set(args.field as string, args.value);
await libFileOps.safeOutputFile(instance.filepath!, JSON.stringify(instance, null, "\t"));
await instance.save();
} catch (err) {
if (err instanceof libConfig.InvalidField || err instanceof libConfig.InvalidValue) {
logger.error(err.message);
+1
View File
@@ -20,6 +20,7 @@
"./src/schema.ts",
"./src/subscriptions.ts",
"./src/zip_ops.ts",
"./src/file_ops.ts",
"./src/ExponentialBackoff.ts",
"./src/RateLimiter.ts",
"./src/TypedEventEmitter.ts",
+1
View File
@@ -15,6 +15,7 @@ describe("Host testing", function() {
const configPath = path.join(instancePath, "test", "instance.json");
const referenceConfig = new lib.InstanceConfig("host", {
...Host.instanceConfigWarning,
"instance.id": 1,
"instance.name": "test",
}, configPath);