Files
factorio-clusterio/plugins/player_sync/messages.ts
T
bbassieandClaude Opus 5.5 0e559cf000 Allow inventory sync components to be turned off per instance
Adds an inventory_sync.sync_<component> instance config field for the
controller, force, appearance, inventories, logistics, quick bar,
settings and recipe notifications. All default to on, which keeps the
current behaviour.

A component which is off on an instance is left alone there and its
fields are not stored from that instance. The controller merges each
upload with the stored data using the uploading instance's config, the
instance strips the fields from downloads before sending them over RCON,
and the scenario is told the enabled set on start and when it changes so
it can skip serializing them and skip the steps which replace the
character.

The controller is only synced together with the inventories, as switching
controller can destroy the character holding them. Without the controller
synced the player gets back the controller they had on the instance, only
character inventories move between instances, and a player without a
character keeps their inventory.

Closes #968

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:25:06 +00:00

194 lines
4.8 KiB
TypeScript

import { Type, Static } from "@sinclair/typebox";
import { StringEnum } from "@clusterio/lib";
// .\module\serialize.lua:serialize.serialize_player()
export type IpcPlayerData = {
generation: number,
name: string,
controller?: string,
color?: number[],
chat_color?: number[],
tag?: string,
force?: string,
cheat_mode?: boolean,
flashlight?: boolean,
shortcuts?: Record<string, boolean>,
game_view_settings?: Record<string, boolean>,
ticks_to_respawn?: number,
character?: any,
inventories?: any,
quick_bar?: any,
hotbar?: string[],
personal_logistic_slots?: {name:string, min:number, max:number}[],
crafting_queue?: any,
recipe_notifications?: string,
}
const jsonPlayerData = Type.Object({
generation: Type.Number(),
name: Type.String(),
});
export class AcquireResponse {
constructor(
public status: string,
public generation?: number,
public hasData?: boolean,
public message?: string,
) {
}
static jsonSchema = Type.Object({
"status": StringEnum(["acquired", "error", "busy"]),
"generation": Type.Optional(Type.Integer()),
"hasData": Type.Optional(Type.Boolean()),
"message": Type.Optional(Type.String()),
});
static fromJSON(json: Static<typeof AcquireResponse.jsonSchema>): AcquireResponse {
return new this(json.status, json.generation, json.hasData, json.message);
}
};
export class AcquireRequest {
declare ["constructor"]: typeof AcquireRequest;
static type = "request" as const;
static src = "instance" as const;
static dst = "controller" as const;
static plugin = "player_sync" as const;
static Response = AcquireResponse;
constructor(
public instanceId: number,
public playerName: string,
) {
}
static jsonSchema = Type.Object({
"instanceId": Type.Number(),
"playerName": Type.String(),
});
static fromJSON(json: Static<typeof AcquireRequest.jsonSchema>): AcquireRequest {
return new this(json.instanceId, json.playerName);
}
}
export class ReleaseRequest {
declare ["constructor"]: typeof ReleaseRequest;
static type = "request" as const;
static src = "instance" as const;
static dst = "controller" as const;
static plugin = "player_sync" as const;
constructor(
public instanceId: number,
public playerName: string
) {
}
static jsonSchema = Type.Object({
"instanceId": Type.Integer(),
"playerName": Type.String(),
});
static fromJSON(json: Static<typeof ReleaseRequest.jsonSchema>) {
return new this(json.instanceId, json.playerName);
}
}
export class UploadRequest {
declare ["constructor"]: typeof UploadRequest;
static type = "request" as const;
static src = "instance" as const;
static dst = "controller" as const;
static plugin = "player_sync" as const;
constructor(
public instanceId: number,
public playerName: string,
public playerData: IpcPlayerData,
) {
}
static jsonSchema = Type.Object({
"instanceId": Type.Integer(),
"playerName": Type.String(),
"playerData": jsonPlayerData,
});
static fromJSON(json: Static<typeof UploadRequest.jsonSchema>) {
return new this(json.instanceId, json.playerName, json.playerData as IpcPlayerData);
}
}
export class DownloadResponse {
constructor(
public playerData: IpcPlayerData | null
) {
}
static jsonSchema = Type.Object({
"playerData": Type.Optional(jsonPlayerData),
});
static fromJSON(json: Static<typeof DownloadResponse.jsonSchema>): DownloadResponse {
return new this(json.playerData as IpcPlayerData);
}
}
export class DownloadRequest {
declare ["constructor"]: typeof DownloadRequest;
static type = "request" as const;
static src = "instance" as const;
static dst = "controller" as const;
static plugin = "player_sync" as const;
static Response = DownloadResponse;
constructor(
public instanceId: number,
public playerName: string,
) {
}
static jsonSchema = Type.Object({
"instanceId": Type.Integer(),
"playerName": Type.String(),
});
static fromJSON(json: Static<typeof DownloadRequest.jsonSchema>) {
return new this(json.instanceId, json.playerName);
}
}
export class DatabaseStatsResponse {
constructor(
public databaseSize: number,
public databaseEntries: number,
public largestEntry: { name: string, size: number },
) {
}
static jsonSchema = Type.Object({
"databaseSize": Type.Integer(),
"databaseEntries": Type.Integer(),
"largestEntry": Type.Object({
"name": Type.String(),
"size": Type.Number(),
}),
});
static fromJSON(json: Static<typeof DatabaseStatsResponse.jsonSchema>): DatabaseStatsResponse {
return new this(json.databaseSize, json.databaseEntries, json.largestEntry);
}
}
export class DatabaseStatsRequest {
declare ["constructor"]: typeof DatabaseStatsRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static plugin = "player_sync" as const;
static permission = "player_sync.inventory.view" as const;
static Response = DatabaseStatsResponse;
}