@crowdedkingdoms/crowdyjs 12.0.0 → 12.2.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.
Files changed (58) hide show
  1. package/MIGRATION.md +45 -0
  2. package/README.md +70 -1
  3. package/dist/crowdy-studio/agent-dom-shell.d.ts +29 -1
  4. package/dist/crowdy-studio/agent-dom-shell.d.ts.map +1 -1
  5. package/dist/crowdy-studio/agent-dom-shell.js +108 -44
  6. package/dist/crowdy-studio/dom-shell.d.ts +46 -3
  7. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  8. package/dist/crowdy-studio/dom-shell.js +551 -125
  9. package/dist/crowdy-studio/embed/dock.d.ts +56 -0
  10. package/dist/crowdy-studio/embed/dock.d.ts.map +1 -0
  11. package/dist/crowdy-studio/embed/dock.js +231 -0
  12. package/dist/crowdy-studio/embed/embed-styles.d.ts +12 -0
  13. package/dist/crowdy-studio/embed/embed-styles.d.ts.map +1 -0
  14. package/dist/crowdy-studio/embed/embed-styles.js +549 -0
  15. package/dist/crowdy-studio/embed/hud-layer.d.ts +40 -0
  16. package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -0
  17. package/dist/crowdy-studio/embed/hud-layer.js +100 -0
  18. package/dist/crowdy-studio/embed/index.d.ts +5 -0
  19. package/dist/crowdy-studio/embed/index.d.ts.map +1 -0
  20. package/dist/crowdy-studio/embed/index.js +4 -0
  21. package/dist/crowdy-studio/embed/panel.d.ts +162 -0
  22. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -0
  23. package/dist/crowdy-studio/embed/panel.js +642 -0
  24. package/dist/crowdy-studio/index.d.ts +3 -0
  25. package/dist/crowdy-studio/index.d.ts.map +1 -1
  26. package/dist/crowdy-studio/index.js +3 -0
  27. package/dist/crowdy-studio/layout.d.ts +50 -0
  28. package/dist/crowdy-studio/layout.d.ts.map +1 -0
  29. package/dist/crowdy-studio/layout.js +155 -0
  30. package/dist/crowdy-studio/mount.d.ts.map +1 -1
  31. package/dist/crowdy-studio/mount.js +3 -1
  32. package/dist/crowdy-studio/splitter.d.ts +33 -0
  33. package/dist/crowdy-studio/splitter.d.ts.map +1 -0
  34. package/dist/crowdy-studio/splitter.js +133 -0
  35. package/dist/crowdy-studio/styles.d.ts +1 -1
  36. package/dist/crowdy-studio/styles.d.ts.map +1 -1
  37. package/dist/crowdy-studio/styles.js +128 -13
  38. package/dist/domains/gameModel.d.ts +68 -1
  39. package/dist/domains/gameModel.d.ts.map +1 -1
  40. package/dist/domains/gameModel.js +88 -1
  41. package/dist/generated/graphql.d.ts +132 -15
  42. package/dist/generated/graphql.d.ts.map +1 -1
  43. package/dist/generated/graphql.js +12 -0
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/kit/npcs.d.ts.map +1 -1
  48. package/dist/kit/social.d.ts.map +1 -1
  49. package/dist/player-host/control-banner.d.ts +40 -0
  50. package/dist/player-host/control-banner.d.ts.map +1 -0
  51. package/dist/player-host/control-banner.js +252 -0
  52. package/dist/player-host/control-gate.d.ts +77 -0
  53. package/dist/player-host/control-gate.d.ts.map +1 -0
  54. package/dist/player-host/control-gate.js +197 -0
  55. package/dist/player-host/index.d.ts +2 -0
  56. package/dist/player-host/index.d.ts.map +1 -1
  57. package/dist/player-host/index.js +2 -0
  58. package/package.json +6 -1
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The game's synchronous takeover seam around CrowdyJS's lease manager.
3
+ * Capture listeners revoke before the game's own gameplay listeners run, but
4
+ * never cancel or synthesize the human event. Offline Stop stays effective
5
+ * with no GraphQL connection.
6
+ */
7
+ export class PlayerControlGate {
8
+ constructor(clearAgentIntent, options = {}) {
9
+ this.clearAgentIntent = clearAgentIntent;
10
+ this.options = options;
11
+ this.listeners = new Set();
12
+ this.leaseManager = null;
13
+ this.controller = null;
14
+ this.locallyRevokedLeaseIds = new Set();
15
+ this.started = false;
16
+ this.lastHumanInputAt = Number.NEGATIVE_INFINITY;
17
+ this.offlineStop = false;
18
+ this.keyboardInput = (event) => {
19
+ this.takeHumanInput(event.code === 'Escape' ? 'ESCAPE' : 'HUMAN_INPUT');
20
+ };
21
+ this.pointerInput = () => {
22
+ this.takeHumanInput('HUMAN_INPUT');
23
+ };
24
+ this.mouseMove = (event) => {
25
+ if (event.movementX !== 0 || event.movementY !== 0 || event.buttons !== 0) {
26
+ this.takeHumanInput('HUMAN_INPUT');
27
+ }
28
+ };
29
+ this.pageHide = () => {
30
+ this.disconnected();
31
+ };
32
+ this.offline = () => {
33
+ this.disconnected();
34
+ };
35
+ this.visibilityChange = () => {
36
+ if (this.documentValue?.visibilityState === 'hidden')
37
+ this.disconnected();
38
+ };
39
+ this.windowValue =
40
+ options.window ?? (typeof window === 'undefined' ? null : window);
41
+ this.documentValue =
42
+ options.document ?? (typeof document === 'undefined' ? null : document);
43
+ this.now = options.now ?? Date.now;
44
+ }
45
+ start() {
46
+ if (this.started)
47
+ return;
48
+ this.started = true;
49
+ const win = this.windowValue;
50
+ const doc = this.documentValue;
51
+ win?.addEventListener('keydown', this.keyboardInput, true);
52
+ win?.addEventListener('mousedown', this.pointerInput, true);
53
+ win?.addEventListener('pointerdown', this.pointerInput, true);
54
+ win?.addEventListener('mousemove', this.mouseMove, true);
55
+ win?.addEventListener('wheel', this.pointerInput, true);
56
+ win?.addEventListener('touchstart', this.pointerInput, true);
57
+ win?.addEventListener('pagehide', this.pageHide, true);
58
+ win?.addEventListener('offline', this.offline, true);
59
+ doc?.addEventListener('visibilitychange', this.visibilityChange, true);
60
+ }
61
+ destroy() {
62
+ if (this.started) {
63
+ this.started = false;
64
+ const win = this.windowValue;
65
+ const doc = this.documentValue;
66
+ win?.removeEventListener('keydown', this.keyboardInput, true);
67
+ win?.removeEventListener('mousedown', this.pointerInput, true);
68
+ win?.removeEventListener('pointerdown', this.pointerInput, true);
69
+ win?.removeEventListener('mousemove', this.mouseMove, true);
70
+ win?.removeEventListener('wheel', this.pointerInput, true);
71
+ win?.removeEventListener('touchstart', this.pointerInput, true);
72
+ win?.removeEventListener('pagehide', this.pageHide, true);
73
+ win?.removeEventListener('offline', this.offline, true);
74
+ doc?.removeEventListener('visibilitychange', this.visibilityChange, true);
75
+ }
76
+ this.preempt('DISCONNECTED', false);
77
+ this.unbind();
78
+ this.listeners.clear();
79
+ }
80
+ bind(leaseManager, controller) {
81
+ if (this.leaseManager &&
82
+ (this.leaseManager !== leaseManager || this.controller !== controller)) {
83
+ this.preempt('CLIENT_REATTACHED', false);
84
+ }
85
+ this.leaseManager = leaseManager;
86
+ this.controller = controller;
87
+ this.offlineStop = false;
88
+ this.emit();
89
+ return () => {
90
+ if (this.leaseManager === leaseManager &&
91
+ this.controller === controller) {
92
+ this.preempt('DISCONNECTED', false);
93
+ this.unbind();
94
+ }
95
+ };
96
+ }
97
+ subscribe(listener) {
98
+ this.listeners.add(listener);
99
+ listener(this.snapshot());
100
+ return () => this.listeners.delete(listener);
101
+ }
102
+ snapshot() {
103
+ return {
104
+ bound: Boolean(this.leaseManager && this.controller),
105
+ activeLease: this.activeLease(),
106
+ ...(this.lastPreemption ? { lastPreemption: this.lastPreemption } : {}),
107
+ humanInputActive: this.humanInputActive(),
108
+ offlineStop: this.offlineStop,
109
+ };
110
+ }
111
+ humanInputActive() {
112
+ return (this.now() - this.lastHumanInputAt <
113
+ (this.options.humanInputActiveMs ?? 150));
114
+ }
115
+ preempt(reason, notifyServer = true) {
116
+ const lease = this.activeLease();
117
+ if (lease)
118
+ this.locallyRevokedLeaseIds.add(lease.leaseId);
119
+ try {
120
+ if (this.leaseManager)
121
+ this.leaseManager.preempt(reason);
122
+ else
123
+ this.clearAgentIntent(reason);
124
+ }
125
+ catch {
126
+ // CrowdyJS still clears the lease in its own finally block. A host
127
+ // cleanup bug must not interrupt the human event or offline Stop.
128
+ }
129
+ finally {
130
+ this.lastPreemption = reason;
131
+ this.options.onPreempt?.(reason);
132
+ this.emit();
133
+ }
134
+ if (notifyServer && lease && this.controller) {
135
+ void this.controller
136
+ .revokeLease(lease.leaseId, reason)
137
+ .catch(() => undefined);
138
+ }
139
+ }
140
+ /** Immediate local Pause; remote persistence is best effort. */
141
+ pause() {
142
+ this.preempt('HUMAN_STOP', false);
143
+ void this.controller?.pause().catch(() => undefined);
144
+ }
145
+ /** Immediate local Stop remains effective with no GraphQL connection. */
146
+ stop() {
147
+ this.offlineStop = true;
148
+ this.preempt('HUMAN_STOP', false);
149
+ void this.controller?.stop().catch(() => undefined);
150
+ this.emit();
151
+ }
152
+ death() {
153
+ this.preempt('DEATH');
154
+ }
155
+ contextChanged() {
156
+ this.preempt('CONTEXT_CHANGED');
157
+ }
158
+ permissionChanged() {
159
+ this.preempt('PERMISSION_CHANGED');
160
+ }
161
+ controlTargetChanged() {
162
+ this.preempt('CONTROL_TARGET_CHANGED');
163
+ }
164
+ disconnected() {
165
+ this.preempt('DISCONNECTED', false);
166
+ }
167
+ activeLease() {
168
+ const local = this.leaseManager?.snapshot().lease;
169
+ if (local?.status === 'ACTIVE' &&
170
+ !this.locallyRevokedLeaseIds.has(local.leaseId)) {
171
+ return local;
172
+ }
173
+ return (this.controller
174
+ ?.getState()
175
+ .leases.find((lease) => lease.kind === 'PLAY' &&
176
+ lease.status === 'ACTIVE' &&
177
+ !this.locallyRevokedLeaseIds.has(lease.leaseId)) ?? null);
178
+ }
179
+ unbind() {
180
+ this.leaseManager = null;
181
+ this.controller = null;
182
+ this.locallyRevokedLeaseIds.clear();
183
+ this.emit();
184
+ }
185
+ takeHumanInput(reason) {
186
+ this.lastHumanInputAt = this.now();
187
+ if (this.activeLease())
188
+ this.preempt(reason);
189
+ else
190
+ this.emit();
191
+ }
192
+ emit() {
193
+ const snapshot = this.snapshot();
194
+ for (const listener of [...this.listeners])
195
+ listener(snapshot);
196
+ }
197
+ }
@@ -1,5 +1,7 @@
1
1
  export { AgentControlLeaseManager, type AgentControlDispatchV1, type AgentControlLeaseManagerOptionsV1, type AgentControlLeaseSnapshotV1, type AgentObservationDispatchV1, } from './lease-manager.js';
2
2
  export { GAME_COMMAND_RESULT_SCHEMA_V1, GAME_COMMAND_SCHEMAS_V1, GAME_COMMAND_SCHEMA_V1, GAME_OBSERVATION_SCHEMA_V1, OBSERVE_REQUEST_SCHEMA_V1, PLAYER_HOST_CAPABILITIES_SCHEMA_V1, } from './schemas.js';
3
3
  export { createPlayerHostAgentTools, type PlayerHostAgentToolsV1, } from './tools.js';
4
+ export { PlayerControlGate, type PlayerControlGateAgentControl, type PlayerControlGateOptions, type PlayerControlGateSnapshot, } from './control-gate.js';
5
+ export { AGENT_CONTROL_BANNER_STYLES, AgentControlBanner, ensureAgentControlBannerStyles, type AgentControlBannerController, } from './control-banner.js';
4
6
  export type { GameChatSendCommandV1, GameCombatAttackCommandV1, GameCommandResultV1, GameCommandV1, GameCraftCommandV1, GameInteractCommandV1, GameInventoryConsumeCommandV1, GameInventorySelectCommandV1, GameInventoryTransferCommandV1, GameLookCommandV1, GameMountCommandV1, GameMoveCommandV1, GameObservationActorV1, GameObservationInventoryV1, GameObservationV1, GameStopCommandV1, GameTravelTeleportCommandV1, ObserveRequestV1, PlayerHostAdapterV1, PlayerHostCapabilitiesV1, PlayerHostCommandCapabilityV1, PlayerHostCommandKind, PlayerHostLeaseScope, PlayerHostLookV1, PlayerHostVector3V1, ValidatedGateV1, } from './types.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/player-host/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,iCAAiC,EACtC,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,kCAAkC,GACnC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,6BAA6B,EAC7B,4BAA4B,EAC5B,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,0BAA0B,EAC1B,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/player-host/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,iCAAiC,EACtC,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,kCAAkC,GACnC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,8BAA8B,EAC9B,KAAK,4BAA4B,GAClC,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,6BAA6B,EAC7B,4BAA4B,EAC5B,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,0BAA0B,EAC1B,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAChB,MAAM,YAAY,CAAC"}
@@ -1,3 +1,5 @@
1
1
  export { AgentControlLeaseManager, } from './lease-manager.js';
2
2
  export { GAME_COMMAND_RESULT_SCHEMA_V1, GAME_COMMAND_SCHEMAS_V1, GAME_COMMAND_SCHEMA_V1, GAME_OBSERVATION_SCHEMA_V1, OBSERVE_REQUEST_SCHEMA_V1, PLAYER_HOST_CAPABILITIES_SCHEMA_V1, } from './schemas.js';
3
3
  export { createPlayerHostAgentTools, } from './tools.js';
4
+ export { PlayerControlGate, } from './control-gate.js';
5
+ export { AGENT_CONTROL_BANNER_STYLES, AgentControlBanner, ensureAgentControlBannerStyles, } from './control-banner.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "12.0.0",
3
+ "version": "12.2.0",
4
4
  "description": "Browser-first Crowded Kingdoms SDK with GraphQL, realtime, and local Rust authoring support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,6 +29,10 @@
29
29
  "./player-host": {
30
30
  "import": "./dist/player-host/index.js",
31
31
  "types": "./dist/player-host/index.d.ts"
32
+ },
33
+ "./player-glue-worker": {
34
+ "import": "./dist/player-runtime/player-glue-worker.js",
35
+ "types": "./dist/player-runtime/player-glue-worker.d.ts"
32
36
  }
33
37
  },
34
38
  "sideEffects": false,
@@ -88,6 +92,7 @@
88
92
  "@graphql-codegen/typescript-operations": "^5.1.0",
89
93
  "@graphql-tools/merge": "^9.1.10",
90
94
  "@types/node": "^22.10.7",
95
+ "happy-dom": "^20.11.1",
91
96
  "typescript": "^5.7.3",
92
97
  "ws": "^8.21.1"
93
98
  },