154 lines
6.1 KiB
TypeScript
154 lines
6.1 KiB
TypeScript
import Logging from "@proxnet/undead-logging";
|
|
import type KV from "../../persistence/kv.ts";
|
|
import { type ServerBase } from "../../server.ts";
|
|
import { DatabaseSubroom, FactoryMode, RoomDataTypes, RoomSave, RoomSaveMap, WriteMode } from "./RoomDataTypes.ts";
|
|
|
|
export interface SubroomFactoryOptions {
|
|
mode: FactoryMode,
|
|
writeMode: WriteMode
|
|
id: number
|
|
}
|
|
|
|
const log = new Logging("SubroomFactoryBase");
|
|
|
|
export class SubroomFactory {
|
|
|
|
#server: ServerBase;
|
|
#kv: KV;
|
|
|
|
#subroomId: number | null = null;
|
|
|
|
factoryMode: FactoryMode = FactoryMode.Fetch;
|
|
writeMode: WriteMode = WriteMode.WriteIfFree;
|
|
|
|
#obj: DatabaseSubroom | null = null;
|
|
#saves: RoomSaveMap | null = null;
|
|
|
|
#cannotAccessBeforeInitError = new Error("Cannot access properties before initialization");
|
|
#cannotWriteBeforeInitError = new Error("Cannot write before initialization");
|
|
|
|
constructor(server: ServerBase, kv: KV) {
|
|
|
|
this.#server = server;
|
|
this.#kv = kv;
|
|
|
|
}
|
|
|
|
async init(options: SubroomFactoryOptions) {
|
|
|
|
const data = await this.#kv.getKv().get<DatabaseSubroom>([options.id, 'meta']);
|
|
if (options.mode == FactoryMode.Fetch && data == null) throw new Error("No such subroom");
|
|
|
|
const saves = await this.#kv.getKv().get<RoomSaveMap>([options.id, 'saves']);
|
|
this.#saves = saves.value ?? new Map<number, RoomSave>();
|
|
|
|
this.#obj = options.mode == FactoryMode.Fetch ? data.value : {
|
|
RoomId: 0,
|
|
RoomSceneLocationId: "",
|
|
Name: "Subroom data init failed, contact an admin!",
|
|
IsSandbox: false,
|
|
MaxPlayers: 8,
|
|
CanMatchmakeInto: true,
|
|
LatestSaveId: null
|
|
}; // use template object when writing
|
|
this.#subroomId = options.id;
|
|
|
|
return this;
|
|
}
|
|
|
|
async write() {
|
|
if (!this.#obj) throw this.#cannotWriteBeforeInitError;
|
|
if (!this.#subroomId) throw new Error("No RoomSceneId set");
|
|
|
|
await this.#kv.getKv().set([this.#subroomId, 'meta'], this.#obj);
|
|
await this.#kv.getKv().set([this.#subroomId, 'saves'], this.#saves);
|
|
this.#server.emit('room.subroom.updated', { subroom: this });
|
|
}
|
|
|
|
export(): RoomDataTypes.RoomScene {
|
|
if (!this.#subroomId) throw this.#cannotAccessBeforeInitError;
|
|
const save = this.getLatestSave();
|
|
if (!save) throw new Error("No save to export");
|
|
|
|
return {
|
|
RoomId: this.RoomId,
|
|
RoomSceneId: this.#subroomId,
|
|
RoomSceneLocationId: this.RoomSceneLocationId,
|
|
Name: this.Name,
|
|
IsSandbox: this.IsSandbox,
|
|
MaxPlayers: this.MaxPlayers,
|
|
CanMatchmakeInto: this.CanMatchmakeInto,
|
|
DataModifiedAt: save.SavedAt.toISOString(),
|
|
DataBlobName: save.DataBlobName
|
|
}
|
|
}
|
|
|
|
setSubroomProperties(props: RoomDataTypes.SubroomProps) {
|
|
Object.assign(this, props);
|
|
}
|
|
|
|
get RoomId() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.RoomId; }
|
|
set RoomId(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.RoomId = data }
|
|
|
|
get RoomSceneId() { if (!this.#subroomId) throw this.#cannotAccessBeforeInitError; else return this.#subroomId; }
|
|
|
|
get RoomSceneLocationId() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.RoomSceneLocationId; }
|
|
set RoomSceneLocationId(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.RoomSceneLocationId = data }
|
|
|
|
get Name() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Name; }
|
|
set Name(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.Name = data }
|
|
|
|
get IsSandbox() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.IsSandbox; }
|
|
set IsSandbox(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.IsSandbox = data }
|
|
|
|
get MaxPlayers() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.MaxPlayers; }
|
|
set MaxPlayers(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.MaxPlayers = data }
|
|
|
|
get CanMatchmakeInto() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.CanMatchmakeInto; }
|
|
set CanMatchmakeInto(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.CanMatchmakeInto = data }
|
|
|
|
get LatestSaveId() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.LatestSaveId; }
|
|
set LatestSaveId(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.LatestSaveId = data }
|
|
|
|
#getAvailableSaveId() {
|
|
if (!this.#saves) throw this.#cannotAccessBeforeInitError;
|
|
|
|
let newId = Math.round(Math.random() * Math.pow(2, 31));
|
|
while (this.#saves.has(newId)) newId = this.#getAvailableSaveId();
|
|
return newId;
|
|
}
|
|
/**
|
|
* Add a save (history) to the scene. Automatically resets the LatestSaveId.
|
|
*
|
|
* Use empty string for no datablob
|
|
*
|
|
* @param dataBlobName Filename of datablob on CDN
|
|
*/
|
|
addSave(dataBlobName?: string) {
|
|
if (!this.#saves) throw this.#cannotAccessBeforeInitError;
|
|
|
|
const newId = this.#getAvailableSaveId();
|
|
this.#saves.set(newId, {
|
|
DataBlobName: dataBlobName ?? "",
|
|
SavedAt: new Date()
|
|
});
|
|
|
|
this.LatestSaveId = newId;
|
|
}
|
|
getSaves() {
|
|
if (!this.#saves) throw this.#cannotAccessBeforeInitError;
|
|
return this.#saves;
|
|
}
|
|
getLatestSave() {
|
|
if (!this.#saves || !this.#obj) throw this.#cannotAccessBeforeInitError;
|
|
else if (!this.#obj.LatestSaveId) throw new Error(`No save is marked as the latest save`);
|
|
else {
|
|
if (this.#saves.size === 0) {
|
|
log.w(`No save could be found when fetching the latest save for subroomid ${this.#subroomId}!`);
|
|
return null;
|
|
} else if (this.#saves.size === 1) return this.#saves.values().toArray()[0];
|
|
else return this.#saves.get(this.#obj.LatestSaveId);
|
|
}
|
|
}
|
|
|
|
} |