@crowdedkingdoms/crowdyjs 12.0.0 → 12.1.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/MIGRATION.md +45 -0
- package/README.md +59 -0
- package/dist/crowdy-studio/agent-dom-shell.d.ts +29 -1
- package/dist/crowdy-studio/agent-dom-shell.d.ts.map +1 -1
- package/dist/crowdy-studio/agent-dom-shell.js +108 -44
- package/dist/crowdy-studio/dom-shell.d.ts +46 -3
- package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
- package/dist/crowdy-studio/dom-shell.js +551 -125
- package/dist/crowdy-studio/embed/dock.d.ts +56 -0
- package/dist/crowdy-studio/embed/dock.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/dock.js +231 -0
- package/dist/crowdy-studio/embed/embed-styles.d.ts +12 -0
- package/dist/crowdy-studio/embed/embed-styles.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/embed-styles.js +549 -0
- package/dist/crowdy-studio/embed/hud-layer.d.ts +40 -0
- package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/hud-layer.js +100 -0
- package/dist/crowdy-studio/embed/index.d.ts +5 -0
- package/dist/crowdy-studio/embed/index.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/index.js +4 -0
- package/dist/crowdy-studio/embed/panel.d.ts +162 -0
- package/dist/crowdy-studio/embed/panel.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/panel.js +642 -0
- package/dist/crowdy-studio/index.d.ts +3 -0
- package/dist/crowdy-studio/index.d.ts.map +1 -1
- package/dist/crowdy-studio/index.js +3 -0
- package/dist/crowdy-studio/layout.d.ts +50 -0
- package/dist/crowdy-studio/layout.d.ts.map +1 -0
- package/dist/crowdy-studio/layout.js +155 -0
- package/dist/crowdy-studio/mount.d.ts.map +1 -1
- package/dist/crowdy-studio/mount.js +3 -1
- package/dist/crowdy-studio/splitter.d.ts +33 -0
- package/dist/crowdy-studio/splitter.d.ts.map +1 -0
- package/dist/crowdy-studio/splitter.js +133 -0
- package/dist/crowdy-studio/styles.d.ts +1 -1
- package/dist/crowdy-studio/styles.d.ts.map +1 -1
- package/dist/crowdy-studio/styles.js +128 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/player-host/control-banner.d.ts +40 -0
- package/dist/player-host/control-banner.d.ts.map +1 -0
- package/dist/player-host/control-banner.js +252 -0
- package/dist/player-host/control-gate.d.ts +77 -0
- package/dist/player-host/control-gate.d.ts.map +1 -0
- package/dist/player-host/control-gate.js +197 -0
- package/dist/player-host/index.d.ts +2 -0
- package/dist/player-host/index.d.ts.map +1 -1
- package/dist/player-host/index.js +2 -0
- package/package.json +6 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CrowdyStudioAgentStateV1 } from '../crowdy-agent/controller.js';
|
|
2
|
+
import type { PlayerControlGate } from './control-gate.js';
|
|
3
|
+
export interface AgentControlBannerController {
|
|
4
|
+
getState(): CrowdyStudioAgentStateV1;
|
|
5
|
+
subscribe(listener: (state: CrowdyStudioAgentStateV1) => void): () => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Always-visible-on-control safety surface outside the Studio dock. Text,
|
|
9
|
+
* status, Pause, and Stop remain keyboard/screen-reader accessible over the
|
|
10
|
+
* game canvas; Stop clears locally before any transport promise is attempted.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AgentControlBanner {
|
|
13
|
+
private readonly gate;
|
|
14
|
+
readonly root: HTMLElement;
|
|
15
|
+
private readonly summary;
|
|
16
|
+
private readonly expiry;
|
|
17
|
+
private readonly pauseButton;
|
|
18
|
+
private readonly stopButton;
|
|
19
|
+
private controller;
|
|
20
|
+
private agentState;
|
|
21
|
+
private gateState;
|
|
22
|
+
private unsubscribeAgent;
|
|
23
|
+
private unsubscribeGate;
|
|
24
|
+
private readonly countdown;
|
|
25
|
+
constructor(parent: HTMLElement, gate: PlayerControlGate);
|
|
26
|
+
bind(controller: AgentControlBannerController): () => void;
|
|
27
|
+
showUnavailable(message: string): void;
|
|
28
|
+
unbind(): void;
|
|
29
|
+
destroy(): void;
|
|
30
|
+
private render;
|
|
31
|
+
}
|
|
32
|
+
export declare function ensureAgentControlBannerStyles(doc?: Document | null): void;
|
|
33
|
+
/**
|
|
34
|
+
* Injected presentation for the visible lease safety surface. Games that
|
|
35
|
+
* dock a studio can set `--ck-game-right-inset` on `document.body` so the
|
|
36
|
+
* banner keeps clear of the dock (the Crowdy Studio embed kit does this
|
|
37
|
+
* automatically).
|
|
38
|
+
*/
|
|
39
|
+
export declare const AGENT_CONTROL_BANNER_STYLES = "\n.ck-agent-control-banner {\n position: fixed;\n top: 12px;\n right: calc(12px + var(--ck-game-right-inset, 0px));\n z-index: 65;\n display: grid;\n grid-template-columns: auto minmax(180px, 1fr) auto auto;\n align-items: center;\n gap: 10px;\n max-width: min(760px, calc(100vw - var(--ck-game-right-inset, 0px) - 24px));\n padding: 9px 10px;\n border: 1px solid rgba(110, 231, 183, 0.65);\n border-radius: 10px;\n color: #ecfdf5;\n background: rgba(2, 44, 34, 0.94);\n box-shadow: 0 12px 30px rgba(2, 6, 23, 0.45);\n pointer-events: auto;\n}\n\n.ck-agent-control-banner[hidden] {\n display: none;\n}\n\n.ck-agent-control-banner[data-state=\"unavailable\"] {\n border-color: rgba(251, 191, 36, 0.65);\n background: rgba(69, 26, 3, 0.94);\n}\n\n.ck-agent-control-summary,\n.ck-agent-control-expiry {\n overflow: hidden;\n color: #d1fae5;\n font-size: 12px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ck-agent-control-expiry {\n color: #a7f3d0;\n}\n\n.ck-agent-control-actions {\n display: flex;\n gap: 6px;\n}\n\n.ck-agent-control-actions button {\n padding: 6px 9px;\n border: 1px solid rgba(167, 243, 208, 0.5);\n border-radius: 7px;\n color: #ecfdf5;\n background: rgba(6, 78, 59, 0.85);\n cursor: pointer;\n}\n\n.ck-agent-control-actions .ck-agent-control-stop {\n border-color: rgba(254, 202, 202, 0.75);\n background: rgba(153, 27, 27, 0.9);\n}\n\n.ck-agent-control-actions button:focus-visible {\n outline: 2px solid #fef3c7;\n outline-offset: 2px;\n}\n\n@media (max-width: 900px) {\n .ck-agent-control-banner {\n right: 8px;\n left: 8px;\n grid-template-columns: auto minmax(0, 1fr) auto;\n max-width: none;\n }\n\n .ck-agent-control-expiry {\n display: none;\n }\n}\n\n@media (max-width: 620px) {\n .ck-agent-control-banner {\n grid-template-columns: minmax(0, 1fr) auto;\n }\n\n .ck-agent-control-banner > strong {\n display: none;\n }\n}\n";
|
|
40
|
+
//# sourceMappingURL=control-banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-banner.d.ts","sourceRoot":"","sources":["../../src/player-host/control-banner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,IAAI,wBAAwB,CAAC;IACrC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5E;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAe3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAdvB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;gBAGzD,MAAM,EAAE,WAAW,EACF,IAAI,EAAE,iBAAiB;IAqC1C,IAAI,CAAC,UAAU,EAAE,4BAA4B,GAAG,MAAM,IAAI;IAc1D,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAUtC,MAAM,IAAI,IAAI;IASd,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,MAAM;CAuDf;AA2BD,wBAAgB,8BAA8B,CAC5C,GAAG,GAAE,QAAQ,GAAG,IAAwD,GACvE,IAAI,CAMN;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,+4DAwFvC,CAAC"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Always-visible-on-control safety surface outside the Studio dock. Text,
|
|
3
|
+
* status, Pause, and Stop remain keyboard/screen-reader accessible over the
|
|
4
|
+
* game canvas; Stop clears locally before any transport promise is attempted.
|
|
5
|
+
*/
|
|
6
|
+
export class AgentControlBanner {
|
|
7
|
+
constructor(parent, gate) {
|
|
8
|
+
this.gate = gate;
|
|
9
|
+
this.controller = null;
|
|
10
|
+
this.agentState = null;
|
|
11
|
+
this.unsubscribeAgent = null;
|
|
12
|
+
this.unsubscribeGate = null;
|
|
13
|
+
ensureAgentControlBannerStyles(parent.ownerDocument);
|
|
14
|
+
const root = document.createElement('aside');
|
|
15
|
+
root.className = 'ck-agent-control-banner';
|
|
16
|
+
root.setAttribute('role', 'region');
|
|
17
|
+
root.setAttribute('aria-label', 'Agent player control');
|
|
18
|
+
root.hidden = true;
|
|
19
|
+
const title = document.createElement('strong');
|
|
20
|
+
title.textContent = 'Crowdy Agent';
|
|
21
|
+
this.summary = document.createElement('span');
|
|
22
|
+
this.summary.className = 'ck-agent-control-summary';
|
|
23
|
+
this.summary.setAttribute('role', 'status');
|
|
24
|
+
this.summary.setAttribute('aria-live', 'polite');
|
|
25
|
+
this.expiry = document.createElement('span');
|
|
26
|
+
this.expiry.className = 'ck-agent-control-expiry';
|
|
27
|
+
const controls = document.createElement('div');
|
|
28
|
+
controls.className = 'ck-agent-control-actions';
|
|
29
|
+
this.pauseButton = button('Pause agent');
|
|
30
|
+
this.pauseButton.addEventListener('click', () => this.gate.pause());
|
|
31
|
+
this.stopButton = button('Stop agent');
|
|
32
|
+
this.stopButton.className = 'ck-agent-control-stop';
|
|
33
|
+
this.stopButton.addEventListener('click', () => this.gate.stop());
|
|
34
|
+
controls.append(this.pauseButton, this.stopButton);
|
|
35
|
+
root.append(title, this.summary, this.expiry, controls);
|
|
36
|
+
parent.append(root);
|
|
37
|
+
this.root = root;
|
|
38
|
+
this.gateState = gate.snapshot();
|
|
39
|
+
this.unsubscribeGate = gate.subscribe((state) => {
|
|
40
|
+
this.gateState = state;
|
|
41
|
+
this.render();
|
|
42
|
+
});
|
|
43
|
+
this.countdown = setInterval(() => this.render(), 500);
|
|
44
|
+
}
|
|
45
|
+
bind(controller) {
|
|
46
|
+
this.unbind();
|
|
47
|
+
this.controller = controller;
|
|
48
|
+
this.agentState = controller.getState();
|
|
49
|
+
this.unsubscribeAgent = controller.subscribe((state) => {
|
|
50
|
+
this.agentState = state;
|
|
51
|
+
this.render();
|
|
52
|
+
});
|
|
53
|
+
this.render();
|
|
54
|
+
return () => {
|
|
55
|
+
if (this.controller === controller)
|
|
56
|
+
this.unbind();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
showUnavailable(message) {
|
|
60
|
+
this.unbind();
|
|
61
|
+
this.root.hidden = false;
|
|
62
|
+
this.root.dataset.state = 'unavailable';
|
|
63
|
+
this.summary.textContent = `Agent unavailable · ${message.slice(0, 240)}`;
|
|
64
|
+
this.expiry.textContent = 'Gameplay remains under human control.';
|
|
65
|
+
this.pauseButton.disabled = true;
|
|
66
|
+
this.stopButton.disabled = false;
|
|
67
|
+
}
|
|
68
|
+
unbind() {
|
|
69
|
+
this.unsubscribeAgent?.();
|
|
70
|
+
this.unsubscribeAgent = null;
|
|
71
|
+
this.controller = null;
|
|
72
|
+
this.agentState = null;
|
|
73
|
+
this.root.hidden = true;
|
|
74
|
+
this.root.removeAttribute('data-state');
|
|
75
|
+
}
|
|
76
|
+
destroy() {
|
|
77
|
+
this.unbind();
|
|
78
|
+
this.unsubscribeGate?.();
|
|
79
|
+
this.unsubscribeGate = null;
|
|
80
|
+
clearInterval(this.countdown);
|
|
81
|
+
this.root.remove();
|
|
82
|
+
}
|
|
83
|
+
render() {
|
|
84
|
+
const state = this.agentState;
|
|
85
|
+
if (!state || !this.controller)
|
|
86
|
+
return;
|
|
87
|
+
this.gateState = this.gate.snapshot();
|
|
88
|
+
const lease = activePlayLease(state.leases, this.gateState.activeLease, this.gateState.bound);
|
|
89
|
+
const mode = state.session?.mode ?? 'ASK';
|
|
90
|
+
const unavailable = state.connection === 'ERROR' ||
|
|
91
|
+
(state.lastError !== null && state.connection !== 'CONNECTED');
|
|
92
|
+
this.root.hidden =
|
|
93
|
+
!unavailable &&
|
|
94
|
+
!lease &&
|
|
95
|
+
mode !== 'PLAY' &&
|
|
96
|
+
state.connection === 'DISCONNECTED';
|
|
97
|
+
this.root.dataset.state = unavailable
|
|
98
|
+
? 'unavailable'
|
|
99
|
+
: lease
|
|
100
|
+
? 'active'
|
|
101
|
+
: 'idle';
|
|
102
|
+
this.summary.textContent = unavailable
|
|
103
|
+
? `Agent disabled · ${state.lastError?.code ?? 'backend unavailable'}`
|
|
104
|
+
: [
|
|
105
|
+
`Mode ${mode}`,
|
|
106
|
+
lease
|
|
107
|
+
? `entity ${lease.controlledEntityId ?? 'unknown'}`
|
|
108
|
+
: 'human control',
|
|
109
|
+
lease
|
|
110
|
+
? `scopes ${lease.scopes.join(', ') || 'none'}`
|
|
111
|
+
: 'no Play lease',
|
|
112
|
+
].join(' · ');
|
|
113
|
+
if (lease) {
|
|
114
|
+
const remaining = Math.max(0, Math.ceil((Date.parse(lease.expiresAt) - Date.now()) / 1000));
|
|
115
|
+
this.expiry.textContent = `Control expires in ${remaining} seconds`;
|
|
116
|
+
}
|
|
117
|
+
else if (this.gateState.offlineStop) {
|
|
118
|
+
this.expiry.textContent =
|
|
119
|
+
'Stopped locally; server acknowledgement is not required.';
|
|
120
|
+
}
|
|
121
|
+
else if (state.reconnectRequired) {
|
|
122
|
+
this.expiry.textContent =
|
|
123
|
+
'Explicit resume and a new Play lease are required.';
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.expiry.textContent = state.connection
|
|
127
|
+
.toLowerCase()
|
|
128
|
+
.replace(/_/gu, ' ');
|
|
129
|
+
}
|
|
130
|
+
this.pauseButton.disabled = !lease && state.session?.status !== 'ACTIVE';
|
|
131
|
+
// Stop is a local safety action and intentionally never disabled.
|
|
132
|
+
this.stopButton.disabled = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function activePlayLease(leases, localLease, gateBound) {
|
|
136
|
+
if (localLease?.kind === 'PLAY' && localLease.status === 'ACTIVE') {
|
|
137
|
+
return localLease;
|
|
138
|
+
}
|
|
139
|
+
if (gateBound)
|
|
140
|
+
return null;
|
|
141
|
+
return (leases.find((lease) => lease.kind === 'PLAY' && lease.status === 'ACTIVE') ?? null);
|
|
142
|
+
}
|
|
143
|
+
function button(label) {
|
|
144
|
+
const control = document.createElement('button');
|
|
145
|
+
control.type = 'button';
|
|
146
|
+
control.textContent = label;
|
|
147
|
+
return control;
|
|
148
|
+
}
|
|
149
|
+
const STYLE_ELEMENT_ID = 'ck-agent-control-banner-styles';
|
|
150
|
+
export function ensureAgentControlBannerStyles(doc = typeof document === 'undefined' ? null : document) {
|
|
151
|
+
if (!doc || doc.getElementById(STYLE_ELEMENT_ID))
|
|
152
|
+
return;
|
|
153
|
+
const style = doc.createElement('style');
|
|
154
|
+
style.id = STYLE_ELEMENT_ID;
|
|
155
|
+
style.textContent = AGENT_CONTROL_BANNER_STYLES;
|
|
156
|
+
doc.head.appendChild(style);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Injected presentation for the visible lease safety surface. Games that
|
|
160
|
+
* dock a studio can set `--ck-game-right-inset` on `document.body` so the
|
|
161
|
+
* banner keeps clear of the dock (the Crowdy Studio embed kit does this
|
|
162
|
+
* automatically).
|
|
163
|
+
*/
|
|
164
|
+
export const AGENT_CONTROL_BANNER_STYLES = `
|
|
165
|
+
.ck-agent-control-banner {
|
|
166
|
+
position: fixed;
|
|
167
|
+
top: 12px;
|
|
168
|
+
right: calc(12px + var(--ck-game-right-inset, 0px));
|
|
169
|
+
z-index: 65;
|
|
170
|
+
display: grid;
|
|
171
|
+
grid-template-columns: auto minmax(180px, 1fr) auto auto;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 10px;
|
|
174
|
+
max-width: min(760px, calc(100vw - var(--ck-game-right-inset, 0px) - 24px));
|
|
175
|
+
padding: 9px 10px;
|
|
176
|
+
border: 1px solid rgba(110, 231, 183, 0.65);
|
|
177
|
+
border-radius: 10px;
|
|
178
|
+
color: #ecfdf5;
|
|
179
|
+
background: rgba(2, 44, 34, 0.94);
|
|
180
|
+
box-shadow: 0 12px 30px rgba(2, 6, 23, 0.45);
|
|
181
|
+
pointer-events: auto;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.ck-agent-control-banner[hidden] {
|
|
185
|
+
display: none;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.ck-agent-control-banner[data-state="unavailable"] {
|
|
189
|
+
border-color: rgba(251, 191, 36, 0.65);
|
|
190
|
+
background: rgba(69, 26, 3, 0.94);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.ck-agent-control-summary,
|
|
194
|
+
.ck-agent-control-expiry {
|
|
195
|
+
overflow: hidden;
|
|
196
|
+
color: #d1fae5;
|
|
197
|
+
font-size: 12px;
|
|
198
|
+
text-overflow: ellipsis;
|
|
199
|
+
white-space: nowrap;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.ck-agent-control-expiry {
|
|
203
|
+
color: #a7f3d0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.ck-agent-control-actions {
|
|
207
|
+
display: flex;
|
|
208
|
+
gap: 6px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ck-agent-control-actions button {
|
|
212
|
+
padding: 6px 9px;
|
|
213
|
+
border: 1px solid rgba(167, 243, 208, 0.5);
|
|
214
|
+
border-radius: 7px;
|
|
215
|
+
color: #ecfdf5;
|
|
216
|
+
background: rgba(6, 78, 59, 0.85);
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.ck-agent-control-actions .ck-agent-control-stop {
|
|
221
|
+
border-color: rgba(254, 202, 202, 0.75);
|
|
222
|
+
background: rgba(153, 27, 27, 0.9);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.ck-agent-control-actions button:focus-visible {
|
|
226
|
+
outline: 2px solid #fef3c7;
|
|
227
|
+
outline-offset: 2px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@media (max-width: 900px) {
|
|
231
|
+
.ck-agent-control-banner {
|
|
232
|
+
right: 8px;
|
|
233
|
+
left: 8px;
|
|
234
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
235
|
+
max-width: none;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.ck-agent-control-expiry {
|
|
239
|
+
display: none;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
@media (max-width: 620px) {
|
|
244
|
+
.ck-agent-control-banner {
|
|
245
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.ck-agent-control-banner > strong {
|
|
249
|
+
display: none;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { CrowdyAgentLeaseV1, CrowdyAgentPreemptionReason } from '../crowdy-agent/types.js';
|
|
2
|
+
import type { AgentControlLeaseManager } from './lease-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* The slice of the studio agent controller the gate needs to observe and
|
|
5
|
+
* revoke leases. `CrowdyStudioAgentController` satisfies this structurally.
|
|
6
|
+
*/
|
|
7
|
+
export interface PlayerControlGateAgentControl {
|
|
8
|
+
getState(): {
|
|
9
|
+
readonly leases: readonly CrowdyAgentLeaseV1[];
|
|
10
|
+
};
|
|
11
|
+
revokeLease(leaseId: string, reason?: CrowdyAgentPreemptionReason): Promise<void>;
|
|
12
|
+
pause(): Promise<void>;
|
|
13
|
+
stop(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export interface PlayerControlGateSnapshot {
|
|
16
|
+
readonly bound: boolean;
|
|
17
|
+
readonly activeLease: CrowdyAgentLeaseV1 | null;
|
|
18
|
+
readonly lastPreemption?: CrowdyAgentPreemptionReason;
|
|
19
|
+
readonly humanInputActive: boolean;
|
|
20
|
+
readonly offlineStop: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface PlayerControlGateOptions {
|
|
23
|
+
readonly window?: Window;
|
|
24
|
+
readonly document?: Document;
|
|
25
|
+
readonly now?: () => number;
|
|
26
|
+
readonly humanInputActiveMs?: number;
|
|
27
|
+
readonly onPreempt?: (reason: CrowdyAgentPreemptionReason) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The game's synchronous takeover seam around CrowdyJS's lease manager.
|
|
31
|
+
* Capture listeners revoke before the game's own gameplay listeners run, but
|
|
32
|
+
* never cancel or synthesize the human event. Offline Stop stays effective
|
|
33
|
+
* with no GraphQL connection.
|
|
34
|
+
*/
|
|
35
|
+
export declare class PlayerControlGate {
|
|
36
|
+
private readonly clearAgentIntent;
|
|
37
|
+
private readonly options;
|
|
38
|
+
private readonly windowValue;
|
|
39
|
+
private readonly documentValue;
|
|
40
|
+
private readonly now;
|
|
41
|
+
private readonly listeners;
|
|
42
|
+
private leaseManager;
|
|
43
|
+
private controller;
|
|
44
|
+
private readonly locallyRevokedLeaseIds;
|
|
45
|
+
private started;
|
|
46
|
+
private lastHumanInputAt;
|
|
47
|
+
private lastPreemption?;
|
|
48
|
+
private offlineStop;
|
|
49
|
+
constructor(clearAgentIntent: (reason: CrowdyAgentPreemptionReason) => void, options?: PlayerControlGateOptions);
|
|
50
|
+
start(): void;
|
|
51
|
+
destroy(): void;
|
|
52
|
+
bind(leaseManager: AgentControlLeaseManager, controller: PlayerControlGateAgentControl): () => void;
|
|
53
|
+
subscribe(listener: (snapshot: PlayerControlGateSnapshot) => void): () => void;
|
|
54
|
+
snapshot(): PlayerControlGateSnapshot;
|
|
55
|
+
humanInputActive(): boolean;
|
|
56
|
+
preempt(reason: CrowdyAgentPreemptionReason, notifyServer?: boolean): void;
|
|
57
|
+
/** Immediate local Pause; remote persistence is best effort. */
|
|
58
|
+
pause(): void;
|
|
59
|
+
/** Immediate local Stop remains effective with no GraphQL connection. */
|
|
60
|
+
stop(): void;
|
|
61
|
+
death(): void;
|
|
62
|
+
contextChanged(): void;
|
|
63
|
+
permissionChanged(): void;
|
|
64
|
+
controlTargetChanged(): void;
|
|
65
|
+
disconnected(): void;
|
|
66
|
+
private activeLease;
|
|
67
|
+
private unbind;
|
|
68
|
+
private takeHumanInput;
|
|
69
|
+
private readonly keyboardInput;
|
|
70
|
+
private readonly pointerInput;
|
|
71
|
+
private readonly mouseMove;
|
|
72
|
+
private readonly pageHide;
|
|
73
|
+
private readonly offline;
|
|
74
|
+
private readonly visibilityChange;
|
|
75
|
+
private emit;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=control-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-gate.d.ts","sourceRoot":"","sources":["../../src/player-host/control-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.
|
|
3
|
+
"version": "12.1.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
|
},
|