@crowdedkingdoms/construct 0.3.0-dev.1 → 0.3.2-dev.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/README.md
CHANGED
|
@@ -16,7 +16,7 @@ and the games built on it share.
|
|
|
16
16
|
|
|
17
17
|
It ships **TypeScript source** for Vite (it uses `?worker&url` and
|
|
18
18
|
`import.meta.env`). Peer dependencies: `@crowdedkingdoms/crowdyjs` 18 (0.3.0
|
|
19
|
-
and later; the 0.2 line peers on 17) and, for the Studio agent pane,
|
|
19
|
+
and later, 18.0.1 from 0.3.1; the 0.2 line peers on 17) and, for the Studio agent pane,
|
|
20
20
|
`@crowdedkingdoms/crowdy-dsh` 0.4+. On a
|
|
21
21
|
prerelease tier the ranges name each `X.Y.Z` line they admit, because npm
|
|
22
22
|
matches a prerelease only against a comparator on the same `X.Y.Z`. A game can
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdedkingdoms/construct",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2-dev.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",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"./grid/*": "./src/grid/*.ts"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@crowdedkingdoms/crowdyjs": ">=18.0.0-dev.0 <19",
|
|
48
|
+
"@crowdedkingdoms/crowdyjs": ">=18.0.0-dev.0 <19 || >=18.0.1-dev.0 <19",
|
|
49
49
|
"@crowdedkingdoms/crowdy-dsh": ">=0.4.0-dev.0 <1 || >=0.4.1-dev.0 <1 || >=0.4.2-dev.0 <1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependenciesMeta": {
|
package/src/index.ts
CHANGED
|
@@ -91,4 +91,5 @@ export {
|
|
|
91
91
|
type ClientHalvesGrid,
|
|
92
92
|
type GridClientHalvesOptions,
|
|
93
93
|
} from './platform/studio/clientHalves';
|
|
94
|
+
export { gridServerCalls, type GridServerCallsOptions } from './platform/studio/gridServerCalls';
|
|
94
95
|
export { GridProgramRunner, type GridProgramRunnerOptions } from './grid/GridProgramRunner';
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
* is missing, saying so in the HUD, rather than letting a worker fail silently.
|
|
25
25
|
*/
|
|
26
26
|
import {
|
|
27
|
-
createGridHostCalls,
|
|
28
27
|
type ExecClientHalfPrompt,
|
|
29
28
|
type ExecGridClientMod,
|
|
30
29
|
type PlayerCodeHostCall,
|
|
@@ -39,6 +38,7 @@ import glueWorkerAssetUrl from '@crowdedkingdoms/crowdyjs/player-glue-worker?wor
|
|
|
39
38
|
|
|
40
39
|
import { API_HTTP_URL, API_WS_URL, CLIENT_MODS_ENABLED, GAME_NAME, STUDIO_ORIGIN } from '../config';
|
|
41
40
|
import { GridProgramRunner } from '../../grid/GridProgramRunner';
|
|
41
|
+
import { gridServerCalls } from './gridServerCalls';
|
|
42
42
|
import type { GameSession } from '../GameSession';
|
|
43
43
|
import { messageOf } from '../network/NetworkManager';
|
|
44
44
|
import { isTextEntry } from '../../engine/Input';
|
|
@@ -422,15 +422,14 @@ export class StudioService {
|
|
|
422
422
|
gridId: string,
|
|
423
423
|
bounds: GridBounds,
|
|
424
424
|
): (call: PlayerCodeHostCall) => Promise<unknown> {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
},
|
|
425
|
+
return gridServerCalls({
|
|
426
|
+
game: this.session.network.game,
|
|
427
|
+
appId: this.session.appId,
|
|
428
|
+
gridId,
|
|
429
|
+
bounds,
|
|
430
|
+
userId: this.session.network.user?.userId,
|
|
431
|
+
permissionKeys: () =>
|
|
432
|
+
this.currentGrid?.gridId === gridId ? this.currentGrid.permissions.effectiveKeys : [],
|
|
434
433
|
});
|
|
435
434
|
}
|
|
436
435
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// The SDK barrel pulls Monaco, which does not load under happy-dom; the host-call module is
|
|
4
|
+
// plain, so the test drives the real one.
|
|
5
|
+
vi.mock('@crowdedkingdoms/crowdyjs', async () => {
|
|
6
|
+
const dist = '../../../../../node_modules/@crowdedkingdoms/crowdyjs/dist';
|
|
7
|
+
const calls = await import(/* @vite-ignore */ `${dist}/grid-mods/grid-host-calls.js`);
|
|
8
|
+
return { createGridHostCalls: calls.createGridHostCalls };
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { gridServerCalls } = await import('./gridServerCalls');
|
|
12
|
+
|
|
13
|
+
const BOUNDS = { low: { x: '5', y: '0', z: '5' }, high: { x: '5', y: '0', z: '5' } };
|
|
14
|
+
|
|
15
|
+
function game() {
|
|
16
|
+
const grid = vi.fn((appId: string, gridId: string, box: unknown) => ({
|
|
17
|
+
appId,
|
|
18
|
+
gridId,
|
|
19
|
+
bounds: box,
|
|
20
|
+
assertContains: () => {},
|
|
21
|
+
channels: { list: async () => [] },
|
|
22
|
+
}));
|
|
23
|
+
return { grid } as never;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const check = (userId: string, permissionKey: string) => ({
|
|
27
|
+
fn: 'grid_permission_check',
|
|
28
|
+
args: { userId, gridId: '42', permissionKey },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('gridServerCalls', () => {
|
|
32
|
+
it("answers grid_permission_check for the signed-in player from the grid's keys", async () => {
|
|
33
|
+
const calls = gridServerCalls({
|
|
34
|
+
game: game(),
|
|
35
|
+
appId: '1',
|
|
36
|
+
gridId: '42',
|
|
37
|
+
bounds: BOUNDS,
|
|
38
|
+
userId: '9',
|
|
39
|
+
permissionKeys: () => ['write_client_code', 'run_client_code'],
|
|
40
|
+
});
|
|
41
|
+
await expect(calls(check('9', 'run_client_code') as never)).resolves.toBe(true);
|
|
42
|
+
await expect(calls(check('9', 'write_server_code') as never)).resolves.toBe(false);
|
|
43
|
+
await expect(calls(check('8', 'run_client_code') as never)).rejects.toThrow(/another player/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('refuses it when no player is signed in', async () => {
|
|
47
|
+
const calls = gridServerCalls({
|
|
48
|
+
game: game(),
|
|
49
|
+
appId: '1',
|
|
50
|
+
gridId: '42',
|
|
51
|
+
bounds: BOUNDS,
|
|
52
|
+
userId: null,
|
|
53
|
+
permissionKeys: () => ['run_client_code'],
|
|
54
|
+
});
|
|
55
|
+
await expect(calls(check('9', 'run_client_code') as never)).rejects.toThrow();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLIENT host calls a game's own router does not answer (DN-10): channels, sessions, user
|
|
3
|
+
* and avatar state, spatial sends and `grid_permission_check`, through CrowdyJS's
|
|
4
|
+
* `createGridHostCalls`, confined to one grid and answered as the player running the module.
|
|
5
|
+
* `grid_permission_check` needs both the player's user id and the keys the game holds for them
|
|
6
|
+
* on the grid; without either the SDK refuses it.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
createGridHostCalls,
|
|
10
|
+
type CrowdyClient,
|
|
11
|
+
type PlayerCodeHostCall,
|
|
12
|
+
} from '@crowdedkingdoms/crowdyjs';
|
|
13
|
+
|
|
14
|
+
import { toBrokerBounds, type GridBounds } from './permissions';
|
|
15
|
+
|
|
16
|
+
export interface GridServerCallsOptions {
|
|
17
|
+
game: CrowdyClient;
|
|
18
|
+
appId: string;
|
|
19
|
+
gridId: string;
|
|
20
|
+
bounds: GridBounds;
|
|
21
|
+
/** The signed-in player, the only one `grid_permission_check` answers about. */
|
|
22
|
+
userId: string | null | undefined;
|
|
23
|
+
/** The permission keys the game holds for that player on this grid. */
|
|
24
|
+
permissionKeys: () => Iterable<string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function gridServerCalls(
|
|
28
|
+
options: GridServerCallsOptions,
|
|
29
|
+
): (call: PlayerCodeHostCall) => Promise<unknown> {
|
|
30
|
+
const { game, appId, gridId, bounds, userId, permissionKeys } = options;
|
|
31
|
+
return createGridHostCalls({
|
|
32
|
+
scope: game.grid(appId, gridId, toBrokerBounds(bounds)),
|
|
33
|
+
client: game,
|
|
34
|
+
...(userId ? { userId: String(userId) } : {}),
|
|
35
|
+
local: { gridPermissionKeys: permissionKeys },
|
|
36
|
+
});
|
|
37
|
+
}
|