@crowdedkingdoms/construct 0.3.9-dev.1 → 0.3.10-test.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdedkingdoms/construct",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10-test.1",
|
|
4
4
|
"description": "The Construct framework: the Crowded Kingdoms game platform layer (hosted sign-in, World Stores, voice/webcam/chat, Crowdy Studio dock, app and grid mods, grid programs) and engine loop, shared by the-construct starter and the games built on it. Ships TypeScript source for Vite.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
DEFAULT_VOXEL_STATE,
|
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
bytesToBase64,
|
|
8
8
|
parseVoxelSetArgs,
|
|
9
9
|
routeClientHostCall,
|
|
10
|
+
routeWithFallback,
|
|
10
11
|
voxelStateToWire,
|
|
11
12
|
voxelsFromBase64,
|
|
12
13
|
voxelsListRows,
|
|
@@ -144,6 +145,23 @@ describe('routeClientHostCall', () => {
|
|
|
144
145
|
}
|
|
145
146
|
});
|
|
146
147
|
|
|
148
|
+
it("hands an SDK call to the host's server calls while no session is bound", async () => {
|
|
149
|
+
const { bindModSession } = await import('./modChunkRuntime');
|
|
150
|
+
bindModSession(null);
|
|
151
|
+
const r = reads();
|
|
152
|
+
for (const fn of ['emit_channel', 'channel_join', 'sessions_list']) {
|
|
153
|
+
const call = { fn, args: { channelId: '5' } } as never;
|
|
154
|
+
await expect(routeClientHostCall(call, r.reads, grid)).rejects.toBeInstanceOf(
|
|
155
|
+
HostCallRefusedError,
|
|
156
|
+
);
|
|
157
|
+
const serverCalls = vi.fn(async () => ({ answered: fn }));
|
|
158
|
+
await expect(
|
|
159
|
+
routeWithFallback(call, () => routeClientHostCall(call, r.reads, grid), serverCalls),
|
|
160
|
+
).resolves.toEqual({ answered: fn });
|
|
161
|
+
expect(serverCalls).toHaveBeenCalledWith(call);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
147
165
|
it('drains pointer_clicks when the game supplies an input sink', async () => {
|
|
148
166
|
const r = reads();
|
|
149
167
|
await expect(
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the same route Paint uses. Never hand a mod the raw client.
|
|
14
14
|
*/
|
|
15
15
|
import type { PlayerCodeGridBounds, PlayerCodeHostCall } from '@crowdedkingdoms/crowdyjs';
|
|
16
|
-
import { routeModGameplay } from './modChunkRuntime';
|
|
16
|
+
import { NO_SESSION, routeModGameplay } from './modChunkRuntime';
|
|
17
17
|
import type { ModSceneStore } from './modScene';
|
|
18
18
|
|
|
19
19
|
export interface ClientVoxelState {
|
|
@@ -188,13 +188,14 @@ export async function routeClientHostCall(
|
|
|
188
188
|
return scene.store.setInstances(scene.source, args);
|
|
189
189
|
default: {
|
|
190
190
|
const answered = await routeModGameplay(call);
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
String((answered as { error: unknown }).error)
|
|
196
|
-
|
|
197
|
-
|
|
191
|
+
if (answered && typeof answered === 'object' && 'error' in answered) {
|
|
192
|
+
// A call the mod runtime does not route, or an SDK call in a game that never binds a
|
|
193
|
+
// session (one without StudioService), is the host's to answer: `routeWithFallback`
|
|
194
|
+
// hands a refusal to its server calls.
|
|
195
|
+
const error = String((answered as { error: unknown }).error);
|
|
196
|
+
if (error.startsWith('unrouted') || error === NO_SESSION) {
|
|
197
|
+
throw new HostCallRefusedError(fn);
|
|
198
|
+
}
|
|
198
199
|
}
|
|
199
200
|
return answered;
|
|
200
201
|
}
|
|
@@ -429,6 +429,9 @@ async function restoreAvatar(): Promise<void> {
|
|
|
429
429
|
.catch(() => undefined);
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/** The answer to an SDK call while no session is bound; the host's router hands such a call on. */
|
|
433
|
+
export const NO_SESSION = 'no session';
|
|
434
|
+
|
|
432
435
|
const PASS_SDK = new Set([
|
|
433
436
|
'actors_create',
|
|
434
437
|
'actors_update',
|
|
@@ -494,7 +497,7 @@ const PASS_SDK = new Set([
|
|
|
494
497
|
|
|
495
498
|
async function passSdk(fn: string, args: Record<string, unknown>): Promise<unknown> {
|
|
496
499
|
if (!PASS_SDK.has(fn)) return { ok: false, error: `unrouted host call ${fn}` };
|
|
497
|
-
if (!port) return { ok: false, error:
|
|
500
|
+
if (!port) return { ok: false, error: NO_SESSION };
|
|
498
501
|
const client = port.client;
|
|
499
502
|
switch (fn) {
|
|
500
503
|
case 'actors_create':
|