@sharpee/engine 3.1.0 → 3.3.0
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/action-context-factory.d.ts +9 -2
- package/action-context-factory.d.ts.map +1 -1
- package/action-context-factory.js +15 -5
- package/action-context-factory.js.map +1 -1
- package/command-executor.d.ts +10 -4
- package/command-executor.d.ts.map +1 -1
- package/command-executor.js +27 -20
- package/command-executor.js.map +1 -1
- package/game-engine.d.ts +49 -11
- package/game-engine.d.ts.map +1 -1
- package/game-engine.js +133 -87
- package/game-engine.js.map +1 -1
- package/index.d.ts +20 -18
- package/index.d.ts.map +1 -1
- package/index.js +26 -23
- package/index.js.map +1 -1
- package/language-provider-interface.d.ts +1 -1
- package/language-provider-interface.d.ts.map +1 -1
- package/narrative/index.d.ts +1 -1
- package/narrative/index.d.ts.map +1 -1
- package/narrative/index.js +3 -3
- package/narrative/index.js.map +1 -1
- package/package.json +12 -12
- package/platform-operations.d.ts +2 -2
- package/platform-operations.d.ts.map +1 -1
- package/platform-operations.js +4 -4
- package/platform-operations.js.map +1 -1
- package/prose-pipeline/assemble.js +2 -2
- package/prose-pipeline/assemble.js.map +1 -1
- package/prose-pipeline/decorations/index.d.ts +4 -4
- package/prose-pipeline/decorations/index.d.ts.map +1 -1
- package/prose-pipeline/decorations/index.js +7 -7
- package/prose-pipeline/decorations/index.js.map +1 -1
- package/prose-pipeline/decorations/parser.d.ts +1 -1
- package/prose-pipeline/decorations/parser.d.ts.map +1 -1
- package/prose-pipeline/decorations/parser.js +6 -6
- package/prose-pipeline/decorations/parser.js.map +1 -1
- package/prose-pipeline/decorations/resolver.js +2 -2
- package/prose-pipeline/decorations/resolver.js.map +1 -1
- package/prose-pipeline/handlers/audibility.d.ts +1 -1
- package/prose-pipeline/handlers/audibility.d.ts.map +1 -1
- package/prose-pipeline/handlers/audibility.js +7 -7
- package/prose-pipeline/handlers/audibility.js.map +1 -1
- package/prose-pipeline/handlers/banner.d.ts +1 -1
- package/prose-pipeline/handlers/banner.d.ts.map +1 -1
- package/prose-pipeline/handlers/banner.js +8 -8
- package/prose-pipeline/handlers/banner.js.map +1 -1
- package/prose-pipeline/handlers/client-query.d.ts +1 -1
- package/prose-pipeline/handlers/client-query.d.ts.map +1 -1
- package/prose-pipeline/handlers/client-query.js +2 -2
- package/prose-pipeline/handlers/client-query.js.map +1 -1
- package/prose-pipeline/handlers/command-failed.d.ts +1 -1
- package/prose-pipeline/handlers/command-failed.d.ts.map +1 -1
- package/prose-pipeline/handlers/command-failed.js +4 -4
- package/prose-pipeline/handlers/command-failed.js.map +1 -1
- package/prose-pipeline/handlers/domain-message.d.ts +1 -1
- package/prose-pipeline/handlers/domain-message.d.ts.map +1 -1
- package/prose-pipeline/handlers/domain-message.js +15 -8
- package/prose-pipeline/handlers/domain-message.js.map +1 -1
- package/prose-pipeline/handlers/game.d.ts +1 -1
- package/prose-pipeline/handlers/game.d.ts.map +1 -1
- package/prose-pipeline/handlers/game.js +2 -2
- package/prose-pipeline/handlers/game.js.map +1 -1
- package/prose-pipeline/handlers/generic.d.ts +1 -1
- package/prose-pipeline/handlers/generic.d.ts.map +1 -1
- package/prose-pipeline/handlers/generic.js +12 -12
- package/prose-pipeline/handlers/generic.js.map +1 -1
- package/prose-pipeline/handlers/implicit-take.d.ts +1 -1
- package/prose-pipeline/handlers/implicit-take.d.ts.map +1 -1
- package/prose-pipeline/handlers/implicit-take.js +2 -2
- package/prose-pipeline/handlers/implicit-take.js.map +1 -1
- package/prose-pipeline/handlers/index.d.ts +11 -11
- package/prose-pipeline/handlers/index.d.ts.map +1 -1
- package/prose-pipeline/handlers/index.js +21 -21
- package/prose-pipeline/handlers/index.js.map +1 -1
- package/prose-pipeline/handlers/platform.d.ts +1 -1
- package/prose-pipeline/handlers/platform.d.ts.map +1 -1
- package/prose-pipeline/handlers/platform.js +5 -5
- package/prose-pipeline/handlers/platform.js.map +1 -1
- package/prose-pipeline/handlers/revealed.d.ts +1 -1
- package/prose-pipeline/handlers/revealed.d.ts.map +1 -1
- package/prose-pipeline/handlers/revealed.js +4 -4
- package/prose-pipeline/handlers/revealed.js.map +1 -1
- package/prose-pipeline/handlers/room.d.ts +1 -1
- package/prose-pipeline/handlers/room.d.ts.map +1 -1
- package/prose-pipeline/handlers/room.js +9 -9
- package/prose-pipeline/handlers/room.js.map +1 -1
- package/prose-pipeline/handlers/types.d.ts +7 -1
- package/prose-pipeline/handlers/types.d.ts.map +1 -1
- package/prose-pipeline/index.d.ts +11 -9
- package/prose-pipeline/index.d.ts.map +1 -1
- package/prose-pipeline/index.js +36 -32
- package/prose-pipeline/index.js.map +1 -1
- package/prose-pipeline/phrase-render.d.ts +28 -1
- package/prose-pipeline/phrase-render.d.ts.map +1 -1
- package/prose-pipeline/phrase-render.js +29 -2
- package/prose-pipeline/phrase-render.js.map +1 -1
- package/prose-pipeline/pipeline.d.ts +4 -5
- package/prose-pipeline/pipeline.d.ts.map +1 -1
- package/prose-pipeline/pipeline.js +31 -31
- package/prose-pipeline/pipeline.js.map +1 -1
- package/prose-pipeline/render-context.d.ts +2 -0
- package/prose-pipeline/render-context.d.ts.map +1 -1
- package/prose-pipeline/render-context.js.map +1 -1
- package/prose-pipeline/stages/index.d.ts +2 -2
- package/prose-pipeline/stages/index.d.ts.map +1 -1
- package/prose-pipeline/stages/index.js +5 -5
- package/prose-pipeline/stages/index.js.map +1 -1
- package/save-restore-service.d.ts +9 -3
- package/save-restore-service.d.ts.map +1 -1
- package/save-restore-service.js +15 -1
- package/save-restore-service.js.map +1 -1
- package/sound/dispatcher.js +2 -2
- package/sound/dispatcher.js.map +1 -1
- package/sound/index.d.ts +2 -2
- package/sound/index.d.ts.map +1 -1
- package/sound/index.js +6 -6
- package/sound/index.js.map +1 -1
- package/story.d.ts +10 -3
- package/story.d.ts.map +1 -1
- package/story.js +2 -2
- package/story.js.map +1 -1
- package/test-helpers/mock-prose-pipeline.d.ts +1 -1
- package/test-helpers/mock-prose-pipeline.d.ts.map +1 -1
- package/turn-event-processor.d.ts +1 -1
- package/turn-event-processor.d.ts.map +1 -1
package/game-engine.js
CHANGED
|
@@ -10,25 +10,25 @@ exports.splitChainedInput = splitChainedInput;
|
|
|
10
10
|
const world_model_1 = require("@sharpee/world-model");
|
|
11
11
|
const event_processor_1 = require("@sharpee/event-processor");
|
|
12
12
|
const stdlib_1 = require("@sharpee/stdlib");
|
|
13
|
-
const
|
|
13
|
+
const index_js_1 = require("./prose-pipeline/index.js");
|
|
14
14
|
const text_blocks_1 = require("@sharpee/text-blocks");
|
|
15
15
|
const channel_service_1 = require("@sharpee/channel-service");
|
|
16
16
|
const core_1 = require("@sharpee/core");
|
|
17
17
|
const plugins_1 = require("@sharpee/plugins");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
18
|
+
const scene_evaluation_plugin_js_1 = require("./scene-evaluation-plugin.js");
|
|
19
|
+
const types_js_1 = require("./types.js");
|
|
20
|
+
const index_js_2 = require("./narrative/index.js");
|
|
21
|
+
const snippet_validation_js_1 = require("./snippet-validation.js");
|
|
22
|
+
const combatant_health_validation_js_1 = require("./combatant-health-validation.js");
|
|
23
|
+
const command_executor_js_1 = require("./command-executor.js");
|
|
24
|
+
const action_context_factory_js_1 = require("./action-context-factory.js");
|
|
25
|
+
const index_js_3 = require("./sound/index.js");
|
|
26
|
+
const turn_event_processor_js_1 = require("./turn-event-processor.js");
|
|
27
|
+
const parser_interface_js_1 = require("./parser-interface.js");
|
|
28
|
+
const language_provider_interface_js_1 = require("./language-provider-interface.js");
|
|
29
|
+
const vocabulary_manager_js_1 = require("./vocabulary-manager.js");
|
|
30
|
+
const save_restore_service_js_1 = require("./save-restore-service.js");
|
|
31
|
+
const turn_event_processor_js_2 = require("./turn-event-processor.js");
|
|
32
32
|
/**
|
|
33
33
|
* Conservative client-capability profile used when `start()` is called
|
|
34
34
|
* without an explicit `capabilities` option. Mirrors a CLI / text-only
|
|
@@ -97,8 +97,16 @@ class GameEngine {
|
|
|
97
97
|
* room for future extension seams (e.g., custom propagate injection
|
|
98
98
|
* via `setSoundDispatcher` in tests).
|
|
99
99
|
*/
|
|
100
|
-
soundDispatcher = new
|
|
100
|
+
soundDispatcher = new index_js_3.SoundDispatcher();
|
|
101
101
|
random;
|
|
102
|
+
/**
|
|
103
|
+
* Dedicated action RNG stream (ADR-231 D6), exposed to actions as
|
|
104
|
+
* `ActionContext.random`. A separate instance from `random` (the
|
|
105
|
+
* turn-plugin stream) so plugin draws can never shift action rolls;
|
|
106
|
+
* its seed rides the save blob (`IEngineState.actionRngSeed`) so
|
|
107
|
+
* post-restore action outcomes replay deterministically.
|
|
108
|
+
*/
|
|
109
|
+
actionRandom;
|
|
102
110
|
narrativeSettings;
|
|
103
111
|
// Alternate input mode handlers (ADR-137)
|
|
104
112
|
inputModeHandlers = new Map();
|
|
@@ -188,29 +196,32 @@ class GameEngine {
|
|
|
188
196
|
});
|
|
189
197
|
});
|
|
190
198
|
this.pluginRegistry = new plugins_1.PluginRegistry();
|
|
191
|
-
this.pluginRegistry.register(new
|
|
199
|
+
this.pluginRegistry.register(new scene_evaluation_plugin_js_1.SceneEvaluationPlugin());
|
|
192
200
|
this.random = (0, core_1.createSeededRandom)();
|
|
193
|
-
|
|
201
|
+
// ADR-231 D6: dedicated action stream — unseeded construction gives a
|
|
202
|
+
// time-based initial seed (same pattern as `random` above)
|
|
203
|
+
this.actionRandom = (0, core_1.createSeededRandom)();
|
|
204
|
+
this.narrativeSettings = (0, index_js_2.buildNarrativeSettings)(); // Default: 2nd person
|
|
194
205
|
// Initialize extracted services (Phase 4 remediation)
|
|
195
|
-
this.vocabularyManager = (0,
|
|
196
|
-
this.saveRestoreService = (0,
|
|
206
|
+
this.vocabularyManager = (0, vocabulary_manager_js_1.createVocabularyManager)();
|
|
207
|
+
this.saveRestoreService = (0, save_restore_service_js_1.createSaveRestoreService)({
|
|
197
208
|
maxSnapshots: this.config.maxUndoSnapshots ?? 10
|
|
198
209
|
});
|
|
199
|
-
this.turnEventProcessor = (0,
|
|
210
|
+
this.turnEventProcessor = (0, turn_event_processor_js_2.createTurnEventProcessor)(this.perceptionService);
|
|
200
211
|
// Set provided dependencies
|
|
201
212
|
this.languageProvider = options.language;
|
|
202
213
|
this.parser = options.parser;
|
|
203
|
-
this.textService = new
|
|
214
|
+
this.textService = new index_js_1.ProsePipeline(this.languageProvider, this.world);
|
|
204
215
|
// Update action registry with language provider
|
|
205
216
|
this.actionRegistry.setLanguageProvider(this.languageProvider);
|
|
206
217
|
// Wire parser with platform events if supported
|
|
207
|
-
if (this.parser && (0,
|
|
218
|
+
if (this.parser && (0, parser_interface_js_1.hasPlatformEventEmitter)(this.parser)) {
|
|
208
219
|
this.parser.setPlatformEventEmitter((event) => {
|
|
209
220
|
this.platformEvents.addEvent(event);
|
|
210
221
|
});
|
|
211
222
|
}
|
|
212
223
|
// Create command executor with dependencies
|
|
213
|
-
this.commandExecutor = (0,
|
|
224
|
+
this.commandExecutor = (0, command_executor_js_1.createCommandExecutor)(this.world, this.actionRegistry, this.eventProcessor, this.parser, this.systemEventSource, this.actionRandom);
|
|
214
225
|
// ADR-224: auto-register the deadly-room death transformer so every story
|
|
215
226
|
// (TS or Chord) gets the deadly-room verb-allowlist / probabilistic hazard for
|
|
216
227
|
// free — no story wiring needed. It early-returns when the player's room has no
|
|
@@ -230,7 +241,7 @@ class GameEngine {
|
|
|
230
241
|
this.emitGameEvent(loadingEvent);
|
|
231
242
|
this.story = story;
|
|
232
243
|
// Build narrative settings from story config (ADR-089)
|
|
233
|
-
this.narrativeSettings = (0,
|
|
244
|
+
this.narrativeSettings = (0, index_js_2.buildNarrativeSettings)(story.config.narrative);
|
|
234
245
|
// Create player first so initializeWorld() can place them
|
|
235
246
|
const newPlayer = story.createPlayer(this.world);
|
|
236
247
|
this.context.player = newPlayer;
|
|
@@ -254,9 +265,9 @@ class GameEngine {
|
|
|
254
265
|
story.initializeWorld(this.world);
|
|
255
266
|
// ADR-209 AC-5: fail load synchronously (naming room and marker) if any
|
|
256
267
|
// snippet-bearing room's description carries an unbound {snippet:name}.
|
|
257
|
-
(0,
|
|
268
|
+
(0, snippet_validation_js_1.validateRoomSnippets)(this.world);
|
|
258
269
|
// ADR-226 AC-7: every combatant must carry the HealthTrait combat operates on.
|
|
259
|
-
(0,
|
|
270
|
+
(0, combatant_health_validation_js_1.validateCombatantHealth)(this.world);
|
|
260
271
|
// Configure language provider with narrative settings (ADR-089)
|
|
261
272
|
this.configureLanguageProviderNarrative(newPlayer);
|
|
262
273
|
// Update metadata
|
|
@@ -636,6 +647,30 @@ class GameEngine {
|
|
|
636
647
|
});
|
|
637
648
|
this.emit('channel:packet', packet, turn);
|
|
638
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Resume a stopped engine without touching world state.
|
|
652
|
+
*
|
|
653
|
+
* The post-mortem revival seam: after `stop('defeat')`, a harness (or a
|
|
654
|
+
* story resurrection policy) that has restored the world to a live-player
|
|
655
|
+
* snapshot — e.g. the transcript-tester's RETRY block via
|
|
656
|
+
* `world.loadJSON()` — needs turn execution back without any world
|
|
657
|
+
* teardown (a full reboot would clear the world it just restored).
|
|
658
|
+
* Flips `running` back on; emits nothing, rebuilds nothing.
|
|
659
|
+
*
|
|
660
|
+
* No-op when already running. Throws if the engine was never started
|
|
661
|
+
* (no command executor) — resuming presumes a completed `start()`.
|
|
662
|
+
*/
|
|
663
|
+
resume() {
|
|
664
|
+
if (this.running) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
// The channel service exists only once start() has completed — resuming
|
|
668
|
+
// an engine that never started would bypass the whole bootstrap order.
|
|
669
|
+
if (!this.channelService) {
|
|
670
|
+
throw new Error('Engine must have been started before it can resume');
|
|
671
|
+
}
|
|
672
|
+
this.running = true;
|
|
673
|
+
}
|
|
639
674
|
/**
|
|
640
675
|
* Stop the game engine
|
|
641
676
|
*/
|
|
@@ -682,41 +717,22 @@ class GameEngine {
|
|
|
682
717
|
this.emit('game:over', this.context);
|
|
683
718
|
}
|
|
684
719
|
/**
|
|
685
|
-
*
|
|
720
|
+
* Build the restart acknowledgment event (ADR-248).
|
|
686
721
|
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
722
|
+
* On confirmed restart the engine does NOT rebuild in place — it renders
|
|
723
|
+
* this acknowledgment ("The story restarts.") in the final packet, then
|
|
724
|
+
* stops with reason 'restart'; the client owns the reboot via its own
|
|
725
|
+
* boot path. No pre-emptive restart_completed(true) is emitted: the new
|
|
726
|
+
* boot's opening banner is the success signal.
|
|
689
727
|
*/
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
if (this.parser && (0, parser_interface_1.hasPronounContext)(this.parser)) {
|
|
699
|
-
this.parser.resetPronounContext();
|
|
700
|
-
}
|
|
701
|
-
// Clear world state (entities, spatial index, relationships, etc.)
|
|
702
|
-
this.world.clear();
|
|
703
|
-
// Reset engine context
|
|
704
|
-
this.context.currentTurn = 1;
|
|
705
|
-
this.context.history = [];
|
|
706
|
-
this.context.metadata.started = new Date();
|
|
707
|
-
this.context.metadata.lastPlayed = new Date();
|
|
708
|
-
// Clear engine bookkeeping
|
|
709
|
-
this.turnEvents.clear();
|
|
710
|
-
this.pendingPlatformOps = [];
|
|
711
|
-
this.soundBuffer.length = 0;
|
|
712
|
-
this.saveRestoreService.clearUndoSnapshots();
|
|
713
|
-
this.hasEmittedInitialized = false;
|
|
714
|
-
// Clear plugin registry so story can re-register plugins
|
|
715
|
-
this.pluginRegistry.clear();
|
|
716
|
-
// Re-initialize the story (creates entities, player, custom actions, etc.)
|
|
717
|
-
this.setStory(this.story);
|
|
718
|
-
// Start the engine (emits game.initialized + game.started)
|
|
719
|
-
this.start();
|
|
728
|
+
createRestartAckEvent() {
|
|
729
|
+
return {
|
|
730
|
+
id: `restart_ack_${Date.now()}`,
|
|
731
|
+
type: 'game.message',
|
|
732
|
+
timestamp: Date.now(),
|
|
733
|
+
data: { messageId: 'if.action.restarting.game_restarting' },
|
|
734
|
+
entities: {}
|
|
735
|
+
};
|
|
720
736
|
}
|
|
721
737
|
/**
|
|
722
738
|
* Execute a turn
|
|
@@ -734,7 +750,7 @@ class GameEngine {
|
|
|
734
750
|
// the line, matching player expectations from Inform-style interpreters.
|
|
735
751
|
// Skipped while an alternate input mode is active (ADR-137) — mode
|
|
736
752
|
// handlers own the raw line, punctuation and all.
|
|
737
|
-
if (typeof input === 'string' && !this.world.getStateValue(
|
|
753
|
+
if (typeof input === 'string' && !this.world.getStateValue(types_js_1.INPUT_MODE_STATE_KEY)) {
|
|
738
754
|
const statements = splitChainedInput(input);
|
|
739
755
|
if (statements.length > 1) {
|
|
740
756
|
let result;
|
|
@@ -793,7 +809,7 @@ class GameEngine {
|
|
|
793
809
|
}
|
|
794
810
|
this.emit('turn:start', turn, input);
|
|
795
811
|
// Check for alternate input mode (ADR-137)
|
|
796
|
-
const activeModeId = this.world.getStateValue(
|
|
812
|
+
const activeModeId = this.world.getStateValue(types_js_1.INPUT_MODE_STATE_KEY);
|
|
797
813
|
if (activeModeId) {
|
|
798
814
|
const handler = this.inputModeHandlers.get(activeModeId);
|
|
799
815
|
if (handler) {
|
|
@@ -808,7 +824,7 @@ class GameEngine {
|
|
|
808
824
|
if (this.parser) {
|
|
809
825
|
// Set world context for parser
|
|
810
826
|
const player = this.world.getPlayer();
|
|
811
|
-
if (player && (0,
|
|
827
|
+
if (player && (0, parser_interface_js_1.hasWorldContext)(this.parser)) {
|
|
812
828
|
const playerLocation = this.world.getLocation(player.id) || '';
|
|
813
829
|
this.parser.setWorldContext(this.world, player.id, playerLocation);
|
|
814
830
|
}
|
|
@@ -851,7 +867,7 @@ class GameEngine {
|
|
|
851
867
|
locationId: playerLocation
|
|
852
868
|
};
|
|
853
869
|
// Store events for this turn (process through enrichment pipeline)
|
|
854
|
-
let semanticEvents = result.events.map(e => (0,
|
|
870
|
+
let semanticEvents = result.events.map(e => (0, turn_event_processor_js_1.processEvent)(e, enrichmentContext));
|
|
855
871
|
// Apply perception filtering if service is configured
|
|
856
872
|
// This transforms events based on what the player can perceive
|
|
857
873
|
if (this.perceptionService) {
|
|
@@ -873,7 +889,7 @@ class GameEngine {
|
|
|
873
889
|
if (result.success) {
|
|
874
890
|
this.updateCommandHistory(result, input, turn);
|
|
875
891
|
// Update pronoun context for "it"/"them"/"him"/"her" resolution (ADR-089)
|
|
876
|
-
if (this.parser && (0,
|
|
892
|
+
if (this.parser && (0, parser_interface_js_1.hasPronounContext)(this.parser) && result.validatedCommand) {
|
|
877
893
|
this.parser.updatePronounContext(result.validatedCommand, turn);
|
|
878
894
|
}
|
|
879
895
|
}
|
|
@@ -908,6 +924,16 @@ class GameEngine {
|
|
|
908
924
|
const playerLocation = this.world.getLocation(this.context.player.id);
|
|
909
925
|
// Plugin tick loop (ADR-120)
|
|
910
926
|
// Plugins run in priority order (NPC at 100, state machines at 75, scheduler at 50)
|
|
927
|
+
//
|
|
928
|
+
// actionResult.success must reflect GENUINE action success (Phase 7):
|
|
929
|
+
// result.success only checks for action.error events, but modern
|
|
930
|
+
// blocked() paths reuse the primary event type with blocked:true /
|
|
931
|
+
// failed:true — a refused action would otherwise report success and
|
|
932
|
+
// (e.g.) advance state-machine transitions it never earned.
|
|
933
|
+
const actionRefused = semanticEvents.some(e => {
|
|
934
|
+
const data = e.data;
|
|
935
|
+
return data?.blocked === true || data?.failed === true;
|
|
936
|
+
});
|
|
911
937
|
const pluginContext = {
|
|
912
938
|
world: this.world,
|
|
913
939
|
turn,
|
|
@@ -916,7 +942,7 @@ class GameEngine {
|
|
|
916
942
|
random: this.random,
|
|
917
943
|
actionResult: {
|
|
918
944
|
actionId: result.actionId || '',
|
|
919
|
-
success: result.success,
|
|
945
|
+
success: result.success && !actionRefused,
|
|
920
946
|
targetId: result.validatedCommand?.directObject?.entity?.id,
|
|
921
947
|
},
|
|
922
948
|
actionEvents: semanticEvents,
|
|
@@ -1101,7 +1127,7 @@ class GameEngine {
|
|
|
1101
1127
|
}
|
|
1102
1128
|
// Create action context for meta-command execution
|
|
1103
1129
|
const scopeResolver = (0, stdlib_1.createScopeResolver)(this.world);
|
|
1104
|
-
const actionContext = (0,
|
|
1130
|
+
const actionContext = (0, action_context_factory_js_1.createActionContext)(this.world, this.context, command, action, scopeResolver, undefined, this.actionRandom);
|
|
1105
1131
|
// Run action's four-phase pattern
|
|
1106
1132
|
const actionValidation = action.validate(actionContext);
|
|
1107
1133
|
let actionEvents;
|
|
@@ -1267,20 +1293,19 @@ class GameEngine {
|
|
|
1267
1293
|
}
|
|
1268
1294
|
case core_1.PlatformEventType.RESTART_REQUESTED: {
|
|
1269
1295
|
const context = platformOp.payload.context;
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1296
|
+
const shouldRestart = this.saveRestoreHooks?.onRestartRequested
|
|
1297
|
+
? await this.saveRestoreHooks.onRestartRequested(context)
|
|
1298
|
+
: true; // No restart hook — auto-confirm
|
|
1299
|
+
if (shouldRestart) {
|
|
1300
|
+
// ADR-248: ack in the final packet, then stop('restart'). The
|
|
1301
|
+
// client's hook is the reboot trigger; the stop reason is
|
|
1302
|
+
// bookkeeping. No restart_completed(true) — the reboot's opening
|
|
1303
|
+
// banner is the success signal.
|
|
1304
|
+
completionEvents.push(this.createRestartAckEvent());
|
|
1305
|
+
this.stop('restart');
|
|
1279
1306
|
}
|
|
1280
1307
|
else {
|
|
1281
|
-
|
|
1282
|
-
await this.restartGame();
|
|
1283
|
-
completionEvents.push((0, core_1.createRestartCompletedEvent)(true));
|
|
1308
|
+
completionEvents.push((0, core_1.createRestartCompletedEvent)(false));
|
|
1284
1309
|
}
|
|
1285
1310
|
break;
|
|
1286
1311
|
}
|
|
@@ -1406,7 +1431,7 @@ class GameEngine {
|
|
|
1406
1431
|
*/
|
|
1407
1432
|
configureLanguageProviderNarrative(player) {
|
|
1408
1433
|
// Check if language provider supports narrative settings
|
|
1409
|
-
if (!this.languageProvider || !(0,
|
|
1434
|
+
if (!this.languageProvider || !(0, language_provider_interface_js_1.hasNarrativeSettings)(this.languageProvider)) {
|
|
1410
1435
|
return;
|
|
1411
1436
|
}
|
|
1412
1437
|
// Build narrative context for language provider
|
|
@@ -1447,11 +1472,11 @@ class GameEngine {
|
|
|
1447
1472
|
throw new Error(`Cannot sync player state: entity '${newPlayerId}' not found`);
|
|
1448
1473
|
}
|
|
1449
1474
|
this.context.player = newPlayer;
|
|
1450
|
-
if (this.parser && (0,
|
|
1475
|
+
if (this.parser && (0, parser_interface_js_1.hasWorldContext)(this.parser)) {
|
|
1451
1476
|
const playerLocation = this.world.getLocation(newPlayerId) || '';
|
|
1452
1477
|
this.parser.setWorldContext(this.world, newPlayerId, playerLocation);
|
|
1453
1478
|
}
|
|
1454
|
-
if (this.parser && (0,
|
|
1479
|
+
if (this.parser && (0, parser_interface_js_1.hasPronounContext)(this.parser)) {
|
|
1455
1480
|
this.parser.resetPronounContext();
|
|
1456
1481
|
}
|
|
1457
1482
|
this.updateScopeVocabulary();
|
|
@@ -1463,6 +1488,23 @@ class GameEngine {
|
|
|
1463
1488
|
getPluginRegistry() {
|
|
1464
1489
|
return this.pluginRegistry;
|
|
1465
1490
|
}
|
|
1491
|
+
/**
|
|
1492
|
+
* The negotiated client capabilities for this session (ADR-216): the
|
|
1493
|
+
* `client has <capability>` predicate reads these live, and channel
|
|
1494
|
+
* gating uses the same flags at manifest time. Text-only before
|
|
1495
|
+
* `start({ capabilities })` runs or when none were negotiated.
|
|
1496
|
+
*/
|
|
1497
|
+
getClientCapabilities() {
|
|
1498
|
+
return this.clientCapabilities ?? exports.DEFAULT_TEXT_CAPABILITIES;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Get the dedicated action RNG stream (ADR-231 D6). Part of the
|
|
1502
|
+
* ISaveRestoreStateProvider contract — the save service persists this
|
|
1503
|
+
* stream's seed and the restore path re-seeds it.
|
|
1504
|
+
*/
|
|
1505
|
+
getActionRandom() {
|
|
1506
|
+
return this.actionRandom;
|
|
1507
|
+
}
|
|
1466
1508
|
/**
|
|
1467
1509
|
* Get event processor for handler registration (ADR-075)
|
|
1468
1510
|
*/
|
|
@@ -1698,7 +1740,7 @@ class GameEngine {
|
|
|
1698
1740
|
playerId: this.context.player.id,
|
|
1699
1741
|
locationId: playerLocation ?? undefined
|
|
1700
1742
|
};
|
|
1701
|
-
let processed = events.map(e => (0,
|
|
1743
|
+
let processed = events.map(e => (0, turn_event_processor_js_1.processEvent)(e, enrichmentContext));
|
|
1702
1744
|
if (this.perceptionService) {
|
|
1703
1745
|
processed = this.perceptionService.filterEvents(processed, this.context.player, this.world);
|
|
1704
1746
|
}
|
|
@@ -2009,10 +2051,14 @@ class GameEngine {
|
|
|
2009
2051
|
shouldRestart = await this.saveRestoreHooks.onRestartRequested(context);
|
|
2010
2052
|
}
|
|
2011
2053
|
if (shouldRestart) {
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2054
|
+
// ADR-248: ack in the final packet, then stop('restart').
|
|
2055
|
+
// No restart_completed(true) — the client reboots and its
|
|
2056
|
+
// opening banner is the success signal.
|
|
2057
|
+
const ackEvent = this.createRestartAckEvent();
|
|
2058
|
+
this.eventSource.emit(ackEvent);
|
|
2059
|
+
this.turnEvents.get(currentTurn)?.push(ackEvent);
|
|
2060
|
+
this.emit('event', ackEvent);
|
|
2061
|
+
this.stop('restart');
|
|
2016
2062
|
}
|
|
2017
2063
|
else {
|
|
2018
2064
|
const cancelEvent = (0, core_1.createRestartCompletedEvent)(false);
|