mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
The controller and instance plugins are init functions with closures. The controller batches refreshes with setMany and deleteMany, and the instance gates updates on its status instead of a started flag. short_name is now display_name. New always_on_top field sorts servers first in get_servers. The record carries has_password from the game password in factorio.settings, never the password itself, and the description falls back to the one in factorio.settings. A controller welcome is used when the instance has none, and the readme falls back to a default welcome from the locale. Lua gets is_synced for connect.lua. The readme passes an empty detail instead of nil to the join tooltip so the player count stays in __2__, restores the Password status, shows the welcome title as a centered label and shortens long descriptions with the full text as a tooltip. The Lua tests assert the lookups they index, which fixes the lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
159 lines
4.3 KiB
TypeScript
159 lines
4.3 KiB
TypeScript
import * as lib from "@clusterio/lib";
|
|
import { Type, Static } from "@sinclair/typebox";
|
|
|
|
/**
|
|
* What the rest of the cluster knows about one instance.
|
|
*
|
|
* Built by the controller from the instance, its host and its mod pack, so
|
|
* it is never stored. The id is the instance id.
|
|
*/
|
|
export class ServerRecord {
|
|
constructor(
|
|
public id: number,
|
|
public name: string,
|
|
public displayName: string,
|
|
public description: string,
|
|
public welcome: string,
|
|
public resetTime: string,
|
|
public hidden: boolean,
|
|
/** Sorted before the servers which are not */
|
|
public alwaysOnTop: boolean,
|
|
/** The password itself is never sent */
|
|
public hasPassword: boolean,
|
|
/** Empty when the instance has no mod pack */
|
|
public modPackName: string,
|
|
/** Version the server last ran, empty if it has never started */
|
|
public factorioVersion: string,
|
|
public playerCount: number,
|
|
public status: lib.InstanceStatus,
|
|
/** Empty when the host has no public address */
|
|
public address: string,
|
|
public updatedAtMs: number = 0,
|
|
public isDeleted: boolean = false,
|
|
) {}
|
|
|
|
static jsonSchema = Type.Object({
|
|
id: Type.Integer(),
|
|
name: Type.String(),
|
|
display_name: Type.String(),
|
|
description: Type.String(),
|
|
welcome: Type.String(),
|
|
reset_time: Type.String(),
|
|
hidden: Type.Boolean(),
|
|
always_on_top: Type.Boolean(),
|
|
has_password: Type.Boolean(),
|
|
mod_pack_name: Type.String(),
|
|
factorio_version: Type.String(),
|
|
player_count: Type.Integer(),
|
|
status: lib.InstanceStatus,
|
|
address: Type.String(),
|
|
updated_at_ms: Type.Optional(Type.Number()),
|
|
is_deleted: Type.Optional(Type.Boolean()),
|
|
});
|
|
|
|
toJSON() {
|
|
const json: Static<typeof ServerRecord.jsonSchema> = {
|
|
id: this.id,
|
|
name: this.name,
|
|
display_name: this.displayName,
|
|
description: this.description,
|
|
welcome: this.welcome,
|
|
reset_time: this.resetTime,
|
|
hidden: this.hidden,
|
|
always_on_top: this.alwaysOnTop,
|
|
has_password: this.hasPassword,
|
|
mod_pack_name: this.modPackName,
|
|
factorio_version: this.factorioVersion,
|
|
player_count: this.playerCount,
|
|
status: this.status,
|
|
address: this.address,
|
|
};
|
|
|
|
if (this.updatedAtMs) {
|
|
json.updated_at_ms = this.updatedAtMs;
|
|
}
|
|
|
|
if (this.isDeleted) {
|
|
json.is_deleted = true;
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
|
return new this(
|
|
json.id,
|
|
json.name,
|
|
json.display_name,
|
|
json.description,
|
|
json.welcome,
|
|
json.reset_time,
|
|
json.hidden,
|
|
json.always_on_top,
|
|
json.has_password,
|
|
json.mod_pack_name,
|
|
json.factorio_version,
|
|
json.player_count,
|
|
json.status,
|
|
json.address,
|
|
json.updated_at_ms ?? 0,
|
|
json.is_deleted ?? false,
|
|
);
|
|
}
|
|
|
|
/** True when every field other than updatedAtMs matches */
|
|
sameAs(other: ServerRecord) {
|
|
return this.id === other.id
|
|
&& this.name === other.name
|
|
&& this.displayName === other.displayName
|
|
&& this.description === other.description
|
|
&& this.welcome === other.welcome
|
|
&& this.resetTime === other.resetTime
|
|
&& this.hidden === other.hidden
|
|
&& this.alwaysOnTop === other.alwaysOnTop
|
|
&& this.hasPassword === other.hasPassword
|
|
&& this.modPackName === other.modPackName
|
|
&& this.factorioVersion === other.factorioVersion
|
|
&& this.playerCount === other.playerCount
|
|
&& this.status === other.status
|
|
&& this.address === other.address
|
|
&& this.isDeleted === other.isDeleted;
|
|
}
|
|
}
|
|
|
|
export class ServerUpdatesEvent {
|
|
declare ["constructor"]: typeof ServerUpdatesEvent;
|
|
static plugin = "exp_server_list" as const;
|
|
static type = "event" as const;
|
|
static src = "controller" as const;
|
|
static dst = ["control", "instance"] as const;
|
|
static permission = "exp_server_list.list" as const;
|
|
|
|
constructor(
|
|
public updates: ServerRecord[],
|
|
) {}
|
|
|
|
static jsonSchema = Type.Object({
|
|
updates: Type.Array(ServerRecord.jsonSchema),
|
|
});
|
|
|
|
toJSON() {
|
|
return { updates: this.updates.map(server => server.toJSON()) };
|
|
}
|
|
|
|
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
|
return new this(json.updates.map(server => ServerRecord.fromJSON(server)));
|
|
}
|
|
}
|
|
|
|
/** List every instance, hidden ones included. */
|
|
export class ServerListRequest {
|
|
declare ["constructor"]: typeof ServerListRequest;
|
|
static plugin = "exp_server_list" as const;
|
|
static type = "request" as const;
|
|
static src = ["control", "instance"] as const;
|
|
static dst = "controller" as const;
|
|
static permission = "exp_server_list.list" as const;
|
|
static Response = lib.jsonArray(ServerRecord);
|
|
}
|