@futdevpro/fsm-dynamo 1.20.95 → 1.20.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/_modules/game/_collections/build-reservation.util.d.ts +31 -0
- package/build/_modules/game/_collections/build-reservation.util.d.ts.map +1 -0
- package/build/_modules/game/_collections/build-reservation.util.js +77 -0
- package/build/_modules/game/_collections/build-reservation.util.js.map +1 -0
- package/build/_modules/game/_collections/game-save.util.d.ts +46 -0
- package/build/_modules/game/_collections/game-save.util.d.ts.map +1 -0
- package/build/_modules/game/_collections/game-save.util.js +189 -0
- package/build/_modules/game/_collections/game-save.util.js.map +1 -0
- package/build/_modules/game/_models/build-reservation.interface.d.ts +70 -0
- package/build/_modules/game/_models/build-reservation.interface.d.ts.map +1 -0
- package/build/_modules/game/_models/build-reservation.interface.js +7 -0
- package/build/_modules/game/_models/build-reservation.interface.js.map +1 -0
- package/build/_modules/game/_models/flying-effect.control-model.d.ts +7 -0
- package/build/_modules/game/_models/flying-effect.control-model.d.ts.map +1 -1
- package/build/_modules/game/_models/flying-effect.control-model.js +12 -1
- package/build/_modules/game/_models/flying-effect.control-model.js.map +1 -1
- package/build/_modules/game/_models/flying-effect.interface.d.ts +3 -0
- package/build/_modules/game/_models/flying-effect.interface.d.ts.map +1 -1
- package/build/_modules/game/_models/game-save.interface.d.ts +76 -0
- package/build/_modules/game/_models/game-save.interface.d.ts.map +1 -0
- package/build/_modules/game/_models/game-save.interface.js +7 -0
- package/build/_modules/game/_models/game-save.interface.js.map +1 -0
- package/build/_modules/game/_models/simulation-clock.control-model.d.ts +39 -0
- package/build/_modules/game/_models/simulation-clock.control-model.d.ts.map +1 -0
- package/build/_modules/game/_models/simulation-clock.control-model.js +122 -0
- package/build/_modules/game/_models/simulation-clock.control-model.js.map +1 -0
- package/build/_modules/game/_models/simulation-clock.interface.d.ts +44 -0
- package/build/_modules/game/_models/simulation-clock.interface.d.ts.map +1 -0
- package/build/_modules/game/_models/simulation-clock.interface.js +3 -0
- package/build/_modules/game/_models/simulation-clock.interface.js.map +1 -0
- package/build/_modules/game/_models/work-order-scheduler.control-model.d.ts +55 -0
- package/build/_modules/game/_models/work-order-scheduler.control-model.d.ts.map +1 -0
- package/build/_modules/game/_models/work-order-scheduler.control-model.js +201 -0
- package/build/_modules/game/_models/work-order-scheduler.control-model.js.map +1 -0
- package/build/_modules/game/_models/work-order-scheduler.interface.d.ts +43 -0
- package/build/_modules/game/_models/work-order-scheduler.interface.d.ts.map +1 -0
- package/build/_modules/game/_models/work-order-scheduler.interface.js +3 -0
- package/build/_modules/game/_models/work-order-scheduler.interface.js.map +1 -0
- package/build/_modules/game/index.d.ts +8 -0
- package/build/_modules/game/index.d.ts.map +1 -1
- package/build/_modules/game/index.js +8 -0
- package/build/_modules/game/index.js.map +1 -1
- package/build-esm/_modules/game/_collections/build-reservation.util.js +72 -0
- package/build-esm/_modules/game/_collections/game-save.util.js +184 -0
- package/build-esm/_modules/game/_models/build-reservation.interface.js +5 -0
- package/build-esm/_modules/game/_models/flying-effect.control-model.js +12 -1
- package/build-esm/_modules/game/_models/game-save.interface.js +5 -0
- package/build-esm/_modules/game/_models/simulation-clock.control-model.js +117 -0
- package/build-esm/_modules/game/_models/simulation-clock.interface.js +1 -0
- package/build-esm/_modules/game/_models/work-order-scheduler.control-model.js +196 -0
- package/build-esm/_modules/game/_models/work-order-scheduler.interface.js +1 -0
- package/build-esm/_modules/game/index.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BFR-WARFACTORY-011 — headless game-save contracts. No provider credential, platform binding or game payload shape
|
|
3
|
+
* lives here: the payload is opaque (`TPayload`, typically the serialized game state), adapters are the game's.
|
|
4
|
+
*/
|
|
5
|
+
/** The envelope format marker — an unknown one is rejected, never guessed. */
|
|
6
|
+
export type DyFM_GameSave_Format_Type = 'dyfm-game-save/1';
|
|
7
|
+
/** The list / conflict view of a save: everything except the payload. */
|
|
8
|
+
export interface DyFM_GameSave_Metadata {
|
|
9
|
+
format: DyFM_GameSave_Format_Type;
|
|
10
|
+
/** The game (one envelope format serves several games). */
|
|
11
|
+
gameId: string;
|
|
12
|
+
/** The save slot (the adapter-level key). */
|
|
13
|
+
slotId: string;
|
|
14
|
+
/** The GAME's payload schema version (integer ≥ 1) — what migrations step through. */
|
|
15
|
+
schemaVersion: number;
|
|
16
|
+
/** When it was saved (Unix ms, from the caller's clock). */
|
|
17
|
+
savedAtMs: number;
|
|
18
|
+
/** Deterministic checksum of the payload (corruption + identity). */
|
|
19
|
+
checksum: string;
|
|
20
|
+
/** The checksum of the save this one was made FROM (lineage) — lets conflict resolution prove descent. */
|
|
21
|
+
parentChecksum?: string;
|
|
22
|
+
/** Display label (optional). */
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
/** A full save: metadata + the opaque payload. */
|
|
26
|
+
export interface DyFM_GameSave_Envelope<TPayload = unknown> extends DyFM_GameSave_Metadata {
|
|
27
|
+
payload: TPayload;
|
|
28
|
+
}
|
|
29
|
+
/** What a game passes to `seal` — the checksum and the format are computed, never hand-written. */
|
|
30
|
+
export interface DyFM_GameSave_SealInput<TPayload = unknown> {
|
|
31
|
+
gameId: string;
|
|
32
|
+
slotId: string;
|
|
33
|
+
schemaVersion: number;
|
|
34
|
+
savedAtMs: number;
|
|
35
|
+
payload: TPayload;
|
|
36
|
+
parentChecksum?: string;
|
|
37
|
+
label?: string;
|
|
38
|
+
}
|
|
39
|
+
/** A storage adapter (localStorage, file system, Steam, cloud …) — implemented by the game, not here. */
|
|
40
|
+
export interface DyFM_GameSave_Adapter_Interface<TPayload = unknown> {
|
|
41
|
+
/** Writes (overwrites) the slot. */
|
|
42
|
+
write(envelope: DyFM_GameSave_Envelope<TPayload>): Promise<void>;
|
|
43
|
+
/** Reads a slot; `null` when it does not exist. Callers should pass the raw result through `parse`. */
|
|
44
|
+
read(slotId: string): Promise<unknown>;
|
|
45
|
+
/** Lists the slots' metadata (no payloads — the list UI must not load every save into memory). */
|
|
46
|
+
list(): Promise<DyFM_GameSave_Metadata[]>;
|
|
47
|
+
/** Deletes a slot; idempotent. */
|
|
48
|
+
delete(slotId: string): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/** One migration step: the payload of `from` → the payload of `to` (`to` > `from`). Pure; must not mutate its input. */
|
|
51
|
+
export interface DyFM_GameSave_MigrationStep {
|
|
52
|
+
from: number;
|
|
53
|
+
to: number;
|
|
54
|
+
migrate: (payload: unknown) => unknown;
|
|
55
|
+
}
|
|
56
|
+
/** Why a parse / migration failed — a stable code for the game's UI and telemetry. */
|
|
57
|
+
export type DyFM_GameSave_Failure_Type = 'not-an-object' | 'unknown-format' | 'invalid-field' | 'checksum-mismatch' | 'future-version' | 'missing-step' | 'invalid-step' | 'step-failed';
|
|
58
|
+
/** The result of `parse` / `migrate`. */
|
|
59
|
+
export type DyFM_GameSave_Result<TPayload = unknown> = {
|
|
60
|
+
ok: true;
|
|
61
|
+
envelope: DyFM_GameSave_Envelope<TPayload>;
|
|
62
|
+
appliedSteps: string[];
|
|
63
|
+
} | {
|
|
64
|
+
ok: false;
|
|
65
|
+
code: DyFM_GameSave_Failure_Type;
|
|
66
|
+
message: string;
|
|
67
|
+
appliedSteps: string[];
|
|
68
|
+
};
|
|
69
|
+
/** The deterministic conflict decision (the game maps `ask-user` to its conflict dialog). */
|
|
70
|
+
export type DyFM_GameSave_ConflictDecision_Type = 'in-sync' | 'use-local' | 'use-remote' | 'ask-user' | 'none';
|
|
71
|
+
/** The decision + why (for logs and the dialog). */
|
|
72
|
+
export interface DyFM_GameSave_ConflictResult {
|
|
73
|
+
decision: DyFM_GameSave_ConflictDecision_Type;
|
|
74
|
+
reason: string;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=game-save.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-save.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/game/_models/game-save.interface.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8EAA8E;AAC9E,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,CAAC;AAE3D,yEAAyE;AACzE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,yBAAyB,CAAC;IAClC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kDAAkD;AAClD,MAAM,WAAW,sBAAsB,CAAC,QAAQ,GAAG,OAAO,CAAE,SAAQ,sBAAsB;IACxF,OAAO,EAAE,QAAQ,CAAC;CACnB;AAED,mGAAmG;AACnG,MAAM,WAAW,uBAAuB,CAAC,QAAQ,GAAG,OAAO;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,QAAQ,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yGAAyG;AACzG,MAAM,WAAW,+BAA+B,CAAC,QAAQ,GAAG,OAAO;IACjE,oCAAoC;IACpC,KAAK,CAAC,QAAQ,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,uGAAuG;IACvG,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,kGAAkG;IAClG,IAAI,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAC1C,kCAAkC;IAClC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,wHAAwH;AACxH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CACxC;AAED,sFAAsF;AACtF,MAAM,MAAM,0BAA0B,GAClC,eAAe,GAAG,gBAAgB,GAAG,eAAe,GAAG,mBAAmB,GAC1E,gBAAgB,GAAG,cAAc,GAAG,cAAc,GAAG,aAAa,CAAC;AAEvE,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,CAAC,QAAQ,GAAG,OAAO,IAC/C;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAChF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,0BAA0B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE7F,6FAA6F;AAC7F,MAAM,MAAM,mCAAmC,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAE/G,oDAAoD;AACpD,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,mCAAmC,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BFR-WARFACTORY-011 — headless game-save contracts. No provider credential, platform binding or game payload shape
|
|
4
|
+
* lives here: the payload is opaque (`TPayload`, typically the serialized game state), adapters are the game's.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
//# sourceMappingURL=game-save.interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-save.interface.js","sourceRoot":"","sources":["../../../../src/_modules/game/_models/game-save.interface.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DyFM_SimulationClock_AdvanceInput, DyFM_SimulationClock_AdvanceResult, DyFM_SimulationClock_Config, DyFM_SimulationClock_State } from './simulation-clock.interface';
|
|
2
|
+
/**
|
|
3
|
+
* BFR-WARFACTORY-010 — framework-independent, monotonic simulation clock.
|
|
4
|
+
*
|
|
5
|
+
* - The CALLER supplies real delta, speed and pause; the clock never reads wall time (deterministic, replayable).
|
|
6
|
+
* - Fixed-step mode (`fixedStepSeconds`): scaled time goes into an accumulator, whole steps are committed and the
|
|
7
|
+
* remainder carries over — at speed 10 the loop runs ten times as many steps of the same size, so fast-forward is
|
|
8
|
+
* behaviour-neutral. Variable mode (no `fixedStepSeconds`): one step of `realDelta * speed` (not neutral).
|
|
9
|
+
* - Real deltas are clamped (`maxRealDeltaSeconds`) and fixed steps capped (`maxStepsPerAdvance`); what is cut or
|
|
10
|
+
* dropped is reported, never silently lost.
|
|
11
|
+
* - `exportState` / `importState` (versioned; the legacy `{ elapsedSimulationSeconds }` shape is accepted) / `reset`.
|
|
12
|
+
* An invalid snapshot is rejected WITHOUT touching the running clock.
|
|
13
|
+
*/
|
|
14
|
+
export declare class DyFM_SimulationClock_ControlModel {
|
|
15
|
+
private readonly fixedStepSeconds;
|
|
16
|
+
private readonly maxRealDeltaSeconds;
|
|
17
|
+
private readonly maxStepsPerAdvance;
|
|
18
|
+
private elapsedSeconds;
|
|
19
|
+
private accumulatorSeconds;
|
|
20
|
+
/** Creates the clock; an invalid configuration throws (`DYFM-SIM-CLOCK-CONFIG`). */
|
|
21
|
+
constructor(config?: DyFM_SimulationClock_Config);
|
|
22
|
+
/** Total committed simulation seconds. */
|
|
23
|
+
now(): number;
|
|
24
|
+
/** Advances by one real frame. Paused, speed ≤ 0 or a non-finite input ⇒ nothing passes (a zero result). */
|
|
25
|
+
advance(input: DyFM_SimulationClock_AdvanceInput): DyFM_SimulationClock_AdvanceResult;
|
|
26
|
+
/** The save/load snapshot. */
|
|
27
|
+
exportState(): DyFM_SimulationClock_State;
|
|
28
|
+
/**
|
|
29
|
+
* Restores a snapshot; returns `false` (and changes NOTHING) when it is invalid. Accepts the legacy
|
|
30
|
+
* `{ elapsedSimulationSeconds }` shape (no `schemaVersion`) as an empty accumulator.
|
|
31
|
+
*/
|
|
32
|
+
importState(snapshot: unknown): boolean;
|
|
33
|
+
/** A new run: zero elapsed time and accumulator. */
|
|
34
|
+
reset(): void;
|
|
35
|
+
private static isNonNegative;
|
|
36
|
+
private static positive;
|
|
37
|
+
private static configError;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=simulation-clock.control-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simulation-clock.control-model.d.ts","sourceRoot":"","sources":["../../../../src/_modules/game/_models/simulation-clock.control-model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iCAAiC,EACjC,kCAAkC,EAClC,2BAA2B,EAC3B,0BAA0B,EAC3B,MAAM,8BAA8B,CAAC;AAEtC;;;;;;;;;;;GAWG;AACH,qBAAa,iCAAiC;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,kBAAkB,CAAa;IAEvC,oFAAoF;gBACxE,MAAM,GAAE,2BAAgC;IAcpD,0CAA0C;IAC1C,GAAG,IAAI,MAAM;IAIb,4GAA4G;IAC5G,OAAO,CAAC,KAAK,EAAE,iCAAiC,GAAG,kCAAkC;IA4CrF,8BAA8B;IAC9B,WAAW,IAAI,0BAA0B;IAIzC;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO;IAoBvC,oDAAoD;IACpD,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B,OAAO,CAAC,MAAM,CAAC,QAAQ;IAQvB,OAAO,CAAC,MAAM,CAAC,WAAW;CAO3B"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyFM_SimulationClock_ControlModel = void 0;
|
|
4
|
+
const error_control_model_1 = require("../../../_models/control-models/error.control-model");
|
|
5
|
+
/**
|
|
6
|
+
* BFR-WARFACTORY-010 — framework-independent, monotonic simulation clock.
|
|
7
|
+
*
|
|
8
|
+
* - The CALLER supplies real delta, speed and pause; the clock never reads wall time (deterministic, replayable).
|
|
9
|
+
* - Fixed-step mode (`fixedStepSeconds`): scaled time goes into an accumulator, whole steps are committed and the
|
|
10
|
+
* remainder carries over — at speed 10 the loop runs ten times as many steps of the same size, so fast-forward is
|
|
11
|
+
* behaviour-neutral. Variable mode (no `fixedStepSeconds`): one step of `realDelta * speed` (not neutral).
|
|
12
|
+
* - Real deltas are clamped (`maxRealDeltaSeconds`) and fixed steps capped (`maxStepsPerAdvance`); what is cut or
|
|
13
|
+
* dropped is reported, never silently lost.
|
|
14
|
+
* - `exportState` / `importState` (versioned; the legacy `{ elapsedSimulationSeconds }` shape is accepted) / `reset`.
|
|
15
|
+
* An invalid snapshot is rejected WITHOUT touching the running clock.
|
|
16
|
+
*/
|
|
17
|
+
class DyFM_SimulationClock_ControlModel {
|
|
18
|
+
fixedStepSeconds;
|
|
19
|
+
maxRealDeltaSeconds;
|
|
20
|
+
maxStepsPerAdvance;
|
|
21
|
+
elapsedSeconds = 0;
|
|
22
|
+
accumulatorSeconds = 0;
|
|
23
|
+
/** Creates the clock; an invalid configuration throws (`DYFM-SIM-CLOCK-CONFIG`). */
|
|
24
|
+
constructor(config = {}) {
|
|
25
|
+
this.fixedStepSeconds = config.fixedStepSeconds === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: DyFM_SimulationClock_ControlModel.positive(config.fixedStepSeconds, 'fixedStepSeconds');
|
|
28
|
+
this.maxRealDeltaSeconds =
|
|
29
|
+
DyFM_SimulationClock_ControlModel.positive(config.maxRealDeltaSeconds ?? 0.25, 'maxRealDeltaSeconds');
|
|
30
|
+
this.maxStepsPerAdvance = DyFM_SimulationClock_ControlModel.positive(config.maxStepsPerAdvance ?? 240, 'maxStepsPerAdvance');
|
|
31
|
+
if (!Number.isInteger(this.maxStepsPerAdvance)) {
|
|
32
|
+
throw DyFM_SimulationClock_ControlModel.configError('maxStepsPerAdvance must be an integer');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Total committed simulation seconds. */
|
|
36
|
+
now() {
|
|
37
|
+
return this.elapsedSeconds;
|
|
38
|
+
}
|
|
39
|
+
/** Advances by one real frame. Paused, speed ≤ 0 or a non-finite input ⇒ nothing passes (a zero result). */
|
|
40
|
+
advance(input) {
|
|
41
|
+
const idle = {
|
|
42
|
+
simulationDeltaSeconds: 0, steps: 0, stepSeconds: this.fixedStepSeconds ?? 0,
|
|
43
|
+
clampedRealSeconds: 0, droppedSimulationSeconds: 0,
|
|
44
|
+
};
|
|
45
|
+
if (input.paused || !Number.isFinite(input.realDeltaSeconds) || !Number.isFinite(input.speed)
|
|
46
|
+
|| input.speed <= 0 || input.realDeltaSeconds <= 0) {
|
|
47
|
+
return idle;
|
|
48
|
+
}
|
|
49
|
+
const realSeconds = Math.min(input.realDeltaSeconds, this.maxRealDeltaSeconds);
|
|
50
|
+
const clampedRealSeconds = input.realDeltaSeconds - realSeconds;
|
|
51
|
+
const scaledSeconds = realSeconds * input.speed;
|
|
52
|
+
if (this.fixedStepSeconds === undefined) {
|
|
53
|
+
// Variable step: one update of the scaled delta (documented as NOT behaviour-neutral across speeds)
|
|
54
|
+
this.elapsedSeconds += scaledSeconds;
|
|
55
|
+
return {
|
|
56
|
+
simulationDeltaSeconds: scaledSeconds, steps: 1, stepSeconds: scaledSeconds,
|
|
57
|
+
clampedRealSeconds: clampedRealSeconds, droppedSimulationSeconds: 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Fixed step: commit whole steps, carry the remainder, cap the burst (spiral-of-death guard)
|
|
61
|
+
this.accumulatorSeconds += scaledSeconds;
|
|
62
|
+
// the epsilon keeps 3 × (1/60) from rounding down to 2 steps
|
|
63
|
+
const available = Math.floor(this.accumulatorSeconds / this.fixedStepSeconds + 1e-9);
|
|
64
|
+
const steps = Math.min(available, this.maxStepsPerAdvance);
|
|
65
|
+
let droppedSimulationSeconds = 0;
|
|
66
|
+
this.accumulatorSeconds = Math.max(0, this.accumulatorSeconds - available * this.fixedStepSeconds);
|
|
67
|
+
if (available > steps) {
|
|
68
|
+
droppedSimulationSeconds = (available - steps) * this.fixedStepSeconds;
|
|
69
|
+
}
|
|
70
|
+
this.elapsedSeconds += steps * this.fixedStepSeconds;
|
|
71
|
+
return {
|
|
72
|
+
simulationDeltaSeconds: steps * this.fixedStepSeconds, steps: steps, stepSeconds: this.fixedStepSeconds,
|
|
73
|
+
clampedRealSeconds: clampedRealSeconds, droppedSimulationSeconds: droppedSimulationSeconds,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The save/load snapshot. */
|
|
77
|
+
exportState() {
|
|
78
|
+
return { schemaVersion: 1, elapsedSimulationSeconds: this.elapsedSeconds, accumulatorSeconds: this.accumulatorSeconds };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Restores a snapshot; returns `false` (and changes NOTHING) when it is invalid. Accepts the legacy
|
|
82
|
+
* `{ elapsedSimulationSeconds }` shape (no `schemaVersion`) as an empty accumulator.
|
|
83
|
+
*/
|
|
84
|
+
importState(snapshot) {
|
|
85
|
+
if (!snapshot || typeof snapshot !== 'object') {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const state = snapshot;
|
|
89
|
+
const accumulator = state.schemaVersion === undefined ? 0 : state.accumulatorSeconds;
|
|
90
|
+
if ((state.schemaVersion !== undefined && state.schemaVersion !== 1)
|
|
91
|
+
|| !DyFM_SimulationClock_ControlModel.isNonNegative(state.elapsedSimulationSeconds)
|
|
92
|
+
|| !DyFM_SimulationClock_ControlModel.isNonNegative(accumulator)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
this.elapsedSeconds = state.elapsedSimulationSeconds;
|
|
96
|
+
this.accumulatorSeconds = accumulator;
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
/** A new run: zero elapsed time and accumulator. */
|
|
100
|
+
reset() {
|
|
101
|
+
this.elapsedSeconds = 0;
|
|
102
|
+
this.accumulatorSeconds = 0;
|
|
103
|
+
}
|
|
104
|
+
static isNonNegative(value) {
|
|
105
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
106
|
+
}
|
|
107
|
+
static positive(value, field) {
|
|
108
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
109
|
+
throw DyFM_SimulationClock_ControlModel.configError(`${field} must be a finite number > 0 (got ${value})`);
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
static configError(message) {
|
|
114
|
+
return new error_control_model_1.DyFM_Error({
|
|
115
|
+
message: `DyFM_SimulationClock: ${message}`,
|
|
116
|
+
errorCode: 'DYFM-SIM-CLOCK-CONFIG',
|
|
117
|
+
issuerService: 'DyFM_SimulationClock_ControlModel',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.DyFM_SimulationClock_ControlModel = DyFM_SimulationClock_ControlModel;
|
|
122
|
+
//# sourceMappingURL=simulation-clock.control-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simulation-clock.control-model.js","sourceRoot":"","sources":["../../../../src/_modules/game/_models/simulation-clock.control-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** BFR-WARFACTORY-010 — `DyFM_SimulationClock_ControlModel` configuration. */
|
|
2
|
+
export interface DyFM_SimulationClock_Config {
|
|
3
|
+
/**
|
|
4
|
+
* Fixed simulation step in seconds (e.g. `1 / 60`). When set, `advance` accumulates scaled time and returns how many
|
|
5
|
+
* fixed steps to run — fast-forward then runs MORE steps of the SAME size, so gameplay is behaviour-neutral across
|
|
6
|
+
* speeds. When unset, one variable step of `realDelta * speed` is returned (NOT behaviour-neutral across speeds).
|
|
7
|
+
*/
|
|
8
|
+
fixedStepSeconds?: number;
|
|
9
|
+
/** Real-delta clamp in seconds (a tab switch / breakpoint must not become one giant step). Default: `0.25`. */
|
|
10
|
+
maxRealDeltaSeconds?: number;
|
|
11
|
+
/** Fixed mode: at most this many steps per `advance`; the excess is DROPPED and reported. Default: `240`. */
|
|
12
|
+
maxStepsPerAdvance?: number;
|
|
13
|
+
}
|
|
14
|
+
/** BFR-WARFACTORY-010 — the input of one `advance` (the caller owns real time, speed and pause). */
|
|
15
|
+
export interface DyFM_SimulationClock_AdvanceInput {
|
|
16
|
+
/** Real (wall) seconds since the previous frame — measured by the caller; the clock never reads wall time. */
|
|
17
|
+
realDeltaSeconds: number;
|
|
18
|
+
/** Simulation speed multiplier (`0` = stopped). The UI's speed choices stay the caller's. */
|
|
19
|
+
speed: number;
|
|
20
|
+
/** Paused ⇒ no simulation time passes. */
|
|
21
|
+
paused: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** BFR-WARFACTORY-010 — what one `advance` produced. */
|
|
24
|
+
export interface DyFM_SimulationClock_AdvanceResult {
|
|
25
|
+
/** Simulation seconds committed by this call (`steps * stepSeconds`). */
|
|
26
|
+
simulationDeltaSeconds: number;
|
|
27
|
+
/** How many simulation updates to run now (fixed mode: 0..maxStepsPerAdvance; variable mode: 0 or 1). */
|
|
28
|
+
steps: number;
|
|
29
|
+
/** The size of each step in seconds. */
|
|
30
|
+
stepSeconds: number;
|
|
31
|
+
/** Real seconds cut off by `maxRealDeltaSeconds`. */
|
|
32
|
+
clampedRealSeconds: number;
|
|
33
|
+
/** Simulation seconds dropped by `maxStepsPerAdvance` (fixed mode). */
|
|
34
|
+
droppedSimulationSeconds: number;
|
|
35
|
+
}
|
|
36
|
+
/** BFR-WARFACTORY-010 — the save/load snapshot (versioned). */
|
|
37
|
+
export interface DyFM_SimulationClock_State {
|
|
38
|
+
schemaVersion: 1;
|
|
39
|
+
/** Total committed simulation seconds. */
|
|
40
|
+
elapsedSimulationSeconds: number;
|
|
41
|
+
/** Fixed mode: scaled time not yet committed to a step (carried over, so no time is lost between frames). */
|
|
42
|
+
accumulatorSeconds: number;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=simulation-clock.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simulation-clock.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/game/_models/simulation-clock.interface.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+GAA+G;IAC/G,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6GAA6G;IAC7G,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,oGAAoG;AACpG,MAAM,WAAW,iCAAiC;IAChD,8GAA8G;IAC9G,gBAAgB,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wDAAwD;AACxD,MAAM,WAAW,kCAAkC;IACjD,yEAAyE;IACzE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,yGAAyG;IACzG,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,+DAA+D;AAC/D,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,CAAC,CAAC;IACjB,0CAA0C;IAC1C,wBAAwB,EAAE,MAAM,CAAC;IACjC,6GAA6G;IAC7G,kBAAkB,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simulation-clock.interface.js","sourceRoot":"","sources":["../../../../src/_modules/game/_models/simulation-clock.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { DyFM_WorkOrder_State, DyFM_WorkOrderScheduler_AdvanceResult, DyFM_WorkOrderScheduler_CancelResult, DyFM_WorkOrderScheduler_Config, DyFM_WorkOrderScheduler_HealReport, DyFM_WorkOrderScheduler_State } from './work-order-scheduler.interface';
|
|
2
|
+
/**
|
|
3
|
+
* BFR-WARFACTORY-012 — headless, cancel-safe scheduler of interchangeable workers over parallel work orders.
|
|
4
|
+
*
|
|
5
|
+
* Guarantees (each pinned by a spec):
|
|
6
|
+
* - **Release is structural, not a happy-path step:** an assignment exists only as `worker → order`; cancelling an
|
|
7
|
+
* order, completing it or removing (destroying) a worker deletes the entry in the same call ⇒ no stranded worker.
|
|
8
|
+
* - **Every pending order gets ≥ 1 worker when possible:** idle workers first; if none is idle, one is moved from the
|
|
9
|
+
* order that has the most (only from an order with > 1) — so one plan cannot starve the others ("frozen" plans).
|
|
10
|
+
* - **Stable least-assigned distribution:** extra workers go to the order with the fewest (tie → the earliest order);
|
|
11
|
+
* existing assignments are not reshuffled.
|
|
12
|
+
* - **Self-healing import:** stale ownership (unknown worker / order, duplicates) is dropped and REPORTED; importing
|
|
13
|
+
* the same state twice gives the same result.
|
|
14
|
+
* No clock, no RNG: time comes in through `advance(simulationSeconds)` (e.g. from `DyFM_SimulationClock_ControlModel`).
|
|
15
|
+
*/
|
|
16
|
+
export declare class DyFM_WorkOrderScheduler_ControlModel {
|
|
17
|
+
private readonly rate;
|
|
18
|
+
private orders;
|
|
19
|
+
private workerIds;
|
|
20
|
+
/** workerId → orderId (the ONLY ownership record). */
|
|
21
|
+
private assignments;
|
|
22
|
+
/** Creates the scheduler; an invalid rate throws `DYFM-WORK-ORDER-CONFIG`. */
|
|
23
|
+
constructor(config?: DyFM_WorkOrderScheduler_Config);
|
|
24
|
+
/** Adds a work order at the end of the FIFO; a duplicate id or non-positive work throws `DYFM-WORK-ORDER-INVALID`. */
|
|
25
|
+
addOrder(order: {
|
|
26
|
+
orderId: string;
|
|
27
|
+
work: number;
|
|
28
|
+
progress?: number;
|
|
29
|
+
}): void;
|
|
30
|
+
/** Adds an (idle) worker; duplicates are ignored. */
|
|
31
|
+
addWorker(workerId: string): void;
|
|
32
|
+
/** Removes (destroys) a worker; its assignment goes with it. Idempotent. */
|
|
33
|
+
removeWorker(workerId: string): void;
|
|
34
|
+
/** Cancels an order: removes it and releases its workers in the same call. Idempotent (unknown → found: false). */
|
|
35
|
+
cancel(orderId: string): DyFM_WorkOrderScheduler_CancelResult;
|
|
36
|
+
/** Advances every order by its workers × rate × seconds; completed orders leave and free their workers. */
|
|
37
|
+
advance(simulationSeconds: number): DyFM_WorkOrderScheduler_AdvanceResult;
|
|
38
|
+
/** The workers of an order, in assignment order. */
|
|
39
|
+
workersOf(orderId: string): string[];
|
|
40
|
+
/** The order a worker is on (`undefined` = idle). */
|
|
41
|
+
orderOf(workerId: string): string | undefined;
|
|
42
|
+
/** Current orders (copies, FIFO). */
|
|
43
|
+
getOrders(): DyFM_WorkOrder_State[];
|
|
44
|
+
/** The save/load snapshot. */
|
|
45
|
+
exportState(): DyFM_WorkOrderScheduler_State;
|
|
46
|
+
/**
|
|
47
|
+
* Restores a snapshot and HEALS stale ownership (assignments to unknown workers / orders, duplicates): dropped and
|
|
48
|
+
* reported, then the distribution rules re-apply. An unreadable snapshot throws `DYFM-WORK-ORDER-STATE`.
|
|
49
|
+
*/
|
|
50
|
+
importState(state: DyFM_WorkOrderScheduler_State): DyFM_WorkOrderScheduler_HealReport;
|
|
51
|
+
/** Applies the distribution rules (stable: existing valid assignments are kept). */
|
|
52
|
+
private rebalance;
|
|
53
|
+
private static error;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=work-order-scheduler.control-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-order-scheduler.control-model.d.ts","sourceRoot":"","sources":["../../../../src/_modules/game/_models/work-order-scheduler.control-model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,qCAAqC,EACrC,oCAAoC,EACpC,8BAA8B,EAC9B,kCAAkC,EAClC,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,qBAAa,oCAAoC;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,SAAS,CAAgB;IACjC,sDAAsD;IACtD,OAAO,CAAC,WAAW,CAAkC;IAErD,8EAA8E;gBAClE,MAAM,GAAE,8BAAmC;IASvD,sHAAsH;IACtH,QAAQ,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAU3E,qDAAqD;IACrD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOjC,4EAA4E;IAC5E,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMpC,mHAAmH;IACnH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,oCAAoC;IAgB7D,2GAA2G;IAC3G,OAAO,CAAC,iBAAiB,EAAE,MAAM,GAAG,qCAAqC;IA0BzE,oDAAoD;IACpD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAKpC,qDAAqD;IACrD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C,qCAAqC;IACrC,SAAS,IAAI,oBAAoB,EAAE;IAInC,8BAA8B;IAC9B,WAAW,IAAI,6BAA6B;IAS5C;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,6BAA6B,GAAG,kCAAkC;IAmDrF,oFAAoF;IACpF,OAAO,CAAC,SAAS;IA0CjB,OAAO,CAAC,MAAM,CAAC,KAAK;CAOrB"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyFM_WorkOrderScheduler_ControlModel = void 0;
|
|
4
|
+
const error_control_model_1 = require("../../../_models/control-models/error.control-model");
|
|
5
|
+
/**
|
|
6
|
+
* BFR-WARFACTORY-012 — headless, cancel-safe scheduler of interchangeable workers over parallel work orders.
|
|
7
|
+
*
|
|
8
|
+
* Guarantees (each pinned by a spec):
|
|
9
|
+
* - **Release is structural, not a happy-path step:** an assignment exists only as `worker → order`; cancelling an
|
|
10
|
+
* order, completing it or removing (destroying) a worker deletes the entry in the same call ⇒ no stranded worker.
|
|
11
|
+
* - **Every pending order gets ≥ 1 worker when possible:** idle workers first; if none is idle, one is moved from the
|
|
12
|
+
* order that has the most (only from an order with > 1) — so one plan cannot starve the others ("frozen" plans).
|
|
13
|
+
* - **Stable least-assigned distribution:** extra workers go to the order with the fewest (tie → the earliest order);
|
|
14
|
+
* existing assignments are not reshuffled.
|
|
15
|
+
* - **Self-healing import:** stale ownership (unknown worker / order, duplicates) is dropped and REPORTED; importing
|
|
16
|
+
* the same state twice gives the same result.
|
|
17
|
+
* No clock, no RNG: time comes in through `advance(simulationSeconds)` (e.g. from `DyFM_SimulationClock_ControlModel`).
|
|
18
|
+
*/
|
|
19
|
+
class DyFM_WorkOrderScheduler_ControlModel {
|
|
20
|
+
rate;
|
|
21
|
+
orders = [];
|
|
22
|
+
workerIds = [];
|
|
23
|
+
/** workerId → orderId (the ONLY ownership record). */
|
|
24
|
+
assignments = new Map();
|
|
25
|
+
/** Creates the scheduler; an invalid rate throws `DYFM-WORK-ORDER-CONFIG`. */
|
|
26
|
+
constructor(config = {}) {
|
|
27
|
+
const rate = config.workPerWorkerPerSecond ?? 1;
|
|
28
|
+
if (!Number.isFinite(rate) || rate <= 0) {
|
|
29
|
+
throw DyFM_WorkOrderScheduler_ControlModel.error('DYFM-WORK-ORDER-CONFIG', `workPerWorkerPerSecond must be > 0 (${rate})`);
|
|
30
|
+
}
|
|
31
|
+
this.rate = rate;
|
|
32
|
+
}
|
|
33
|
+
/** Adds a work order at the end of the FIFO; a duplicate id or non-positive work throws `DYFM-WORK-ORDER-INVALID`. */
|
|
34
|
+
addOrder(order) {
|
|
35
|
+
if (!order?.orderId || this.orders.some((o) => o.orderId === order.orderId)
|
|
36
|
+
|| !Number.isFinite(order.work) || order.work <= 0) {
|
|
37
|
+
throw DyFM_WorkOrderScheduler_ControlModel.error('DYFM-WORK-ORDER-INVALID', `order needs a unique orderId and work > 0 (${order?.orderId})`);
|
|
38
|
+
}
|
|
39
|
+
this.orders.push({ orderId: order.orderId, work: order.work, progress: Math.max(0, order.progress ?? 0) });
|
|
40
|
+
this.rebalance();
|
|
41
|
+
}
|
|
42
|
+
/** Adds an (idle) worker; duplicates are ignored. */
|
|
43
|
+
addWorker(workerId) {
|
|
44
|
+
if (workerId && !this.workerIds.includes(workerId)) {
|
|
45
|
+
this.workerIds.push(workerId);
|
|
46
|
+
this.rebalance();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Removes (destroys) a worker; its assignment goes with it. Idempotent. */
|
|
50
|
+
removeWorker(workerId) {
|
|
51
|
+
this.workerIds = this.workerIds.filter((id) => id !== workerId);
|
|
52
|
+
this.assignments.delete(workerId);
|
|
53
|
+
this.rebalance();
|
|
54
|
+
}
|
|
55
|
+
/** Cancels an order: removes it and releases its workers in the same call. Idempotent (unknown → found: false). */
|
|
56
|
+
cancel(orderId) {
|
|
57
|
+
const order = this.orders.find((o) => o.orderId === orderId);
|
|
58
|
+
if (!order) {
|
|
59
|
+
return { found: false, releasedWorkerIds: [], progressRatio: 0 };
|
|
60
|
+
}
|
|
61
|
+
const released = this.workersOf(orderId);
|
|
62
|
+
this.orders = this.orders.filter((o) => o.orderId !== orderId);
|
|
63
|
+
released.forEach((workerId) => this.assignments.delete(workerId));
|
|
64
|
+
this.rebalance();
|
|
65
|
+
return { found: true, releasedWorkerIds: released, progressRatio: Math.min(1, order.progress / order.work) };
|
|
66
|
+
}
|
|
67
|
+
/** Advances every order by its workers × rate × seconds; completed orders leave and free their workers. */
|
|
68
|
+
advance(simulationSeconds) {
|
|
69
|
+
if (!Number.isFinite(simulationSeconds) || simulationSeconds <= 0) {
|
|
70
|
+
return { completedOrderIds: [] };
|
|
71
|
+
}
|
|
72
|
+
const completed = [];
|
|
73
|
+
for (const order of this.orders) {
|
|
74
|
+
order.progress += this.workersOf(order.orderId).length * this.rate * simulationSeconds;
|
|
75
|
+
if (order.progress >= order.work) {
|
|
76
|
+
completed.push(order.orderId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (completed.length) {
|
|
80
|
+
this.orders = this.orders.filter((o) => !completed.includes(o.orderId));
|
|
81
|
+
for (const [workerId, orderId] of [...this.assignments]) {
|
|
82
|
+
if (completed.includes(orderId)) {
|
|
83
|
+
this.assignments.delete(workerId);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.rebalance();
|
|
87
|
+
}
|
|
88
|
+
return { completedOrderIds: completed };
|
|
89
|
+
}
|
|
90
|
+
/** The workers of an order, in assignment order. */
|
|
91
|
+
workersOf(orderId) {
|
|
92
|
+
return [...this.assignments].filter(([, id]) => id === orderId)
|
|
93
|
+
.map(([workerId]) => workerId);
|
|
94
|
+
}
|
|
95
|
+
/** The order a worker is on (`undefined` = idle). */
|
|
96
|
+
orderOf(workerId) {
|
|
97
|
+
return this.assignments.get(workerId);
|
|
98
|
+
}
|
|
99
|
+
/** Current orders (copies, FIFO). */
|
|
100
|
+
getOrders() {
|
|
101
|
+
return this.orders.map((o) => ({ ...o }));
|
|
102
|
+
}
|
|
103
|
+
/** The save/load snapshot. */
|
|
104
|
+
exportState() {
|
|
105
|
+
return {
|
|
106
|
+
schemaVersion: 1,
|
|
107
|
+
orders: this.getOrders(),
|
|
108
|
+
workerIds: [...this.workerIds],
|
|
109
|
+
assignments: Object.fromEntries(this.assignments),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Restores a snapshot and HEALS stale ownership (assignments to unknown workers / orders, duplicates): dropped and
|
|
114
|
+
* reported, then the distribution rules re-apply. An unreadable snapshot throws `DYFM-WORK-ORDER-STATE`.
|
|
115
|
+
*/
|
|
116
|
+
importState(state) {
|
|
117
|
+
if (!state || state.schemaVersion !== 1 || !Array.isArray(state.orders) || !Array.isArray(state.workerIds)
|
|
118
|
+
|| !state.assignments || typeof state.assignments !== 'object') {
|
|
119
|
+
throw DyFM_WorkOrderScheduler_ControlModel.error('DYFM-WORK-ORDER-STATE', 'unreadable scheduler snapshot');
|
|
120
|
+
}
|
|
121
|
+
const report = { droppedUnknownWorkers: [], droppedUnknownOrders: [], skippedDuplicates: [] };
|
|
122
|
+
const orders = [];
|
|
123
|
+
for (const order of state.orders) {
|
|
124
|
+
if (!order?.orderId || orders.some((o) => o.orderId === order.orderId)
|
|
125
|
+
|| !Number.isFinite(order.work) || order.work <= 0) {
|
|
126
|
+
report.skippedDuplicates.push(String(order?.orderId));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (Number(order.progress) >= order.work) {
|
|
130
|
+
continue; // a completed order is not "active" — its assignments heal below
|
|
131
|
+
}
|
|
132
|
+
orders.push({ orderId: order.orderId, work: order.work, progress: Math.max(0, Number(order.progress) || 0) });
|
|
133
|
+
}
|
|
134
|
+
const workerIds = [];
|
|
135
|
+
for (const workerId of state.workerIds) {
|
|
136
|
+
if (!workerId || workerIds.includes(workerId)) {
|
|
137
|
+
report.skippedDuplicates.push(String(workerId));
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
workerIds.push(workerId);
|
|
141
|
+
}
|
|
142
|
+
const assignments = new Map();
|
|
143
|
+
for (const [workerId, orderId] of Object.entries(state.assignments)) {
|
|
144
|
+
if (!workerIds.includes(workerId)) {
|
|
145
|
+
report.droppedUnknownWorkers.push(workerId);
|
|
146
|
+
}
|
|
147
|
+
else if (!orders.some((o) => o.orderId === orderId)) {
|
|
148
|
+
report.droppedUnknownOrders.push(`${workerId}→${orderId}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
assignments.set(workerId, orderId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
this.orders = orders;
|
|
155
|
+
this.workerIds = workerIds;
|
|
156
|
+
this.assignments = assignments;
|
|
157
|
+
this.rebalance();
|
|
158
|
+
return report;
|
|
159
|
+
}
|
|
160
|
+
/** Applies the distribution rules (stable: existing valid assignments are kept). */
|
|
161
|
+
rebalance() {
|
|
162
|
+
const count = (orderId) => this.workersOf(orderId).length;
|
|
163
|
+
const idle = () => this.workerIds.filter((id) => !this.assignments.has(id));
|
|
164
|
+
// 1) every order without a worker gets one: an idle worker, else one moved from the richest order (> 1)
|
|
165
|
+
for (const order of this.orders) {
|
|
166
|
+
if (count(order.orderId) > 0) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const free = idle()[0];
|
|
170
|
+
if (free) {
|
|
171
|
+
this.assignments.set(free, order.orderId);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const donor = [...this.orders]
|
|
175
|
+
.filter((o) => count(o.orderId) > 1)
|
|
176
|
+
.sort((a, b) => count(b.orderId) - count(a.orderId) || this.orders.indexOf(b) - this.orders.indexOf(a))[0];
|
|
177
|
+
if (donor) {
|
|
178
|
+
const moved = this.workersOf(donor.orderId).slice(-1)[0];
|
|
179
|
+
this.assignments.set(moved, order.orderId);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// 2) the remaining idle workers go to the least-assigned order (tie → the earliest)
|
|
183
|
+
for (const workerId of idle()) {
|
|
184
|
+
const target = [...this.orders]
|
|
185
|
+
.sort((a, b) => count(a.orderId) - count(b.orderId) || this.orders.indexOf(a) - this.orders.indexOf(b))[0];
|
|
186
|
+
if (!target) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.assignments.set(workerId, target.orderId);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
static error(code, message) {
|
|
193
|
+
return new error_control_model_1.DyFM_Error({
|
|
194
|
+
message: `DyFM_WorkOrderScheduler: ${message}`,
|
|
195
|
+
errorCode: code,
|
|
196
|
+
issuerService: 'DyFM_WorkOrderScheduler_ControlModel',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.DyFM_WorkOrderScheduler_ControlModel = DyFM_WorkOrderScheduler_ControlModel;
|
|
201
|
+
//# sourceMappingURL=work-order-scheduler.control-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-order-scheduler.control-model.js","sourceRoot":"","sources":["../../../../src/_modules/game/_models/work-order-scheduler.control-model.ts"],"names":[],"mappings":"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"}
|