feat: LazyEntityUpdater

This commit is contained in:
2024-03-09 04:50:55 +00:00
parent a38f72fa46
commit fa29486af6
8 changed files with 126 additions and 42 deletions
+16 -2
View File
@@ -1,8 +1,22 @@
import type { ICityEntity } from "./CityEntity.js";
import { LazyEntityUpdater } from "./LazyEntityUpdater.js";
import type { IResourceController } from "./ResourceController.js";
import type { CityID } from "./defs.js"; import type { CityID } from "./defs.js";
export class City{ export class City extends LazyEntityUpdater<ICityEntity>{
protected readonly _indirectNames: (keyof ICityEntity)[] = ["id", "nationID"];
protected readonly _entityType = "City";
protected readonly _raw: ICityEntity;
constructor( constructor(
public readonly id: CityID raw: ICityEntity,
protected readonly resourceController: IResourceController
){ ){
super();
this._raw = raw;
}
get id(): CityID {
return this._raw.id;
} }
} }
+7
View File
@@ -0,0 +1,7 @@
import type { CityID, CityName, NationID } from "./defs.js";
export interface ICityEntity{
id: CityID;
name: CityName;
nationID: NationID;
}
+18 -27
View File
@@ -6,21 +6,22 @@ import { DiplomaticPermission, type CityID, type GeneralID, type GeneralName, ty
import { must } from "@sammo/util"; import { must } from "@sammo/util";
import { Nation } from "./Nation.js"; import { Nation } from "./Nation.js";
import { Troop } from "./Troop.js"; import { Troop } from "./Troop.js";
import { LazyEntityUpdater } from "./LazyEntityUpdater.js";
export class General { export class General extends LazyEntityUpdater<IGeneralEntity> {
private _raw: IGeneralEntity; protected readonly _indirectNames: (keyof IGeneralEntity)[] = ["id", "nationID", "troopID", "nationID"];
private _city: WeakRef<City>; protected readonly _entityType = "General";
private _troop: WeakRef<Troop> | undefined; protected readonly _raw: IGeneralEntity;
private _nation: WeakRef<Nation>;
public get raw(): Readonly<IGeneralEntity> { protected _city: WeakRef<City>;
return this._raw; protected _troop: WeakRef<Troop> | undefined;
} protected _nation: WeakRef<Nation>;
constructor( constructor(
raw: IGeneralEntity, raw: IGeneralEntity,
private resourceController: IResourceController protected readonly resourceController: IResourceController
){ ){
super();
this._raw = raw; this._raw = raw;
this._city = new WeakRef(must(resourceController.getCity(raw.cityID))); this._city = new WeakRef(must(resourceController.getCity(raw.cityID)));
this._nation = new WeakRef(must(resourceController.getNation(raw.nationID))); this._nation = new WeakRef(must(resourceController.getNation(raw.nationID)));
@@ -38,26 +39,16 @@ export class General {
return this._raw.name; return this._raw.name;
} }
public update(callback: (oldRaw: IGeneralEntity) => void, force = false) { public get city(): City {
return must(this._city.deref());
}
const oldCityID = this._raw.cityID; public get troop(): Troop | undefined {
const oldTroopID = this._raw.troopID; return this._troop?.deref();
const oldNationID = this._raw.nationID; }
callback(this._raw); public get nation(): Nation {
return must(this._nation.deref());
if(!force){
if(oldCityID !== this._raw.cityID){
throw new Error("City change by update() is not allowed");
}
if(oldTroopID !== this._raw.troopID){
throw new Error("Troop change by update() is not allowed");
}
if(oldNationID !== this._raw.nationID){
throw new Error("Nation change by update() is not allowed");
}
}
this.resourceController.setDirtyGeneral(this._raw);
} }
public changeCity(city: CityID | City){ public changeCity(city: CityID | City){
@@ -0,0 +1,31 @@
import type { IResourceController, ValidEntity, ValidEntityType } from "./ResourceController.js";
export abstract class LazyEntityUpdater<Entity extends ValidEntity>{
protected abstract readonly _indirectNames: (keyof Entity)[];
protected abstract readonly _entityType: ValidEntityType<Entity>;
protected abstract readonly _raw: Entity;
protected abstract readonly resourceController: IResourceController;
public get raw(): Readonly<Entity> {
return this._raw;
}
public get entityType(): ValidEntityType<Entity> {
return this._entityType;
}
public update(callback: (oldRaw: Entity) => void, force = false) {
const oldRaw = { ...this._raw };
callback(this._raw);
if (!force) {
for (const indirectName of this._indirectNames) {
if (oldRaw[indirectName] !== this._raw[indirectName]) {
throw new Error(`${String(indirectName)} change by update() is not allowed`);
}
}
}
this.resourceController.setDirty(this);
}
}
+23 -6
View File
@@ -1,9 +1,26 @@
import type { NationID, NationName, NationType } from "./defs.js"; import { LazyEntityUpdater } from "./LazyEntityUpdater.js";
import type { INationEntity } from "./NationEntity.js";
import type { IResourceController } from "./ResourceController.js";
import type { NationID, NationName } from "./defs.js";
export class Nation extends LazyEntityUpdater<INationEntity> {
protected readonly _indirectNames: (keyof INationEntity)[] = ["id"];
protected readonly _entityType = "Nation";
protected readonly _raw: INationEntity;
export class Nation {
constructor( constructor(
public readonly id: NationID, raw: INationEntity,
public readonly name: NationName, protected readonly resourceController: IResourceController
public readonly type: NationType, ){
) { } super();
this._raw = raw;
}
public get id(): NationID {
return this._raw.id;
}
public get name(): NationName {
return this._raw.name;
}
} }
+9
View File
@@ -0,0 +1,9 @@
import type { CityID, NationID, NationName, NationType } from "./defs.js";
export interface INationEntity{
id: NationID;
name: NationName;
type: NationType;
capitalID: CityID;
}
@@ -6,6 +6,19 @@ import type { GameEnv } from "./GameEnv.js";
import type { IGeneralEntity } from "./GeneralEntity.js"; import type { IGeneralEntity } from "./GeneralEntity.js";
import type { Troop } from "./Troop.js"; import type { Troop } from "./Troop.js";
import type { ITroopEntity } from "./TroopEntity.js"; import type { ITroopEntity } from "./TroopEntity.js";
import type { LazyEntityUpdater } from "./LazyEntityUpdater.js";
import type { ICityEntity } from "./CityEntity.js";
import type { INationEntity } from "./NationEntity.js";
export type ValidEntityList = {
'General': IGeneralEntity;
'City': ICityEntity;
'Nation': INationEntity;
'Troop': ITroopEntity;
};
export type ValidEntity = ValidEntityList[keyof ValidEntityList];
export type ValidEntityType<Entity> = Entity extends ValidEntityList[infer T extends keyof ValidEntityList] ? T : never;
export interface IResourceController{ export interface IResourceController{
addCity(city: City): boolean; addCity(city: City): boolean;
@@ -38,6 +51,8 @@ export interface IResourceController{
setDirtyTroop(troop: Troop | TroopID): void; setDirtyTroop(troop: Troop | TroopID): void;
setDirtyNation(nation: Nation): void; setDirtyNation(nation: Nation): void;
setDirty(entity: LazyEntityUpdater<any>): void;
createGeneral(raw: IGeneralEntity): General; createGeneral(raw: IGeneralEntity): General;
createTroop(raw: ITroopEntity): Troop; createTroop(raw: ITroopEntity): Troop;
//createNation //createNation
+7 -7
View File
@@ -3,20 +3,20 @@ import type { General } from "./General.js";
import type { IResourceController } from "./ResourceController.js"; import type { IResourceController } from "./ResourceController.js";
import type { ITroopEntity } from "./TroopEntity.js"; import type { ITroopEntity } from "./TroopEntity.js";
import type { GeneralID } from "./defs.js"; import type { GeneralID } from "./defs.js";
import { LazyEntityUpdater } from "./LazyEntityUpdater.js";
export class Troop { export class Troop extends LazyEntityUpdater<ITroopEntity> {
private _raw: ITroopEntity protected readonly _indirectNames: (keyof ITroopEntity)[] = ["id"];
protected readonly _entityType = "Troop";
protected readonly _raw: ITroopEntity;
private _leader: WeakRef<General>; private _leader: WeakRef<General>;
private _members: Map<GeneralID, WeakRef<General>>; private _members: Map<GeneralID, WeakRef<General>>;
public get raw(): Readonly<ITroopEntity> {
return this._raw;
}
constructor( constructor(
raw: ITroopEntity, raw: ITroopEntity,
private resourceController: IResourceController protected resourceController: IResourceController
){ ){
super();
this._raw = raw; this._raw = raw;
const members = resourceController.findGeneralByTroop(raw.id); const members = resourceController.findGeneralByTroop(raw.id);