@crowdedkingdoms/construct 0.2.1-dev.1 → 0.2.3-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 +10 -4
- package/package.json +3 -3
- package/src/index.ts +9 -2
- package/src/platform/studio/ClientModLifecycle.ts +5 -0
- package/src/platform/studio/StudioService.ts +180 -168
- package/src/platform/studio/clientHalves.test.ts +328 -0
- package/src/platform/studio/clientHalves.ts +291 -0
- package/src/platform/studio/clientModHost.test.ts +40 -1
- package/src/platform/studio/clientModHost.ts +33 -21
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The host side of CLIENT mods: what a sandboxed player module
|
|
3
|
-
*
|
|
2
|
+
* The host side of CLIENT mods: what a sandboxed player module (the Studio's
|
|
3
|
+
* preview, or a grid's CLIENT half, `clientHalves.ts`) may ask the game for.
|
|
4
4
|
*
|
|
5
5
|
* The SDK's `PlayerCodeBroker` has already validated every call against the
|
|
6
6
|
* platform allowlist, clamped chunk coordinates to the mod's grid AABB, rate-
|
|
7
7
|
* limited it, and answered `grid_info` / `hud_set` / `overlay_draw` itself.
|
|
8
8
|
* What reaches `routeClientHostCall` is world_read, `voxel_set`, and
|
|
9
|
-
* `pointer_clicks
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the raw client.
|
|
9
|
+
* `pointer_clicks`, in the argument shapes `crowdy-client-sdk` sends. Reads
|
|
10
|
+
* return only what the running player can already lawfully see. Writes go
|
|
11
|
+
* through ChunkStore.setVoxel (optimistic + UDP) and markDirty (durable
|
|
12
|
+
* write-back), the same route Paint uses. Never hand a mod the raw client.
|
|
13
|
+
*
|
|
14
|
+
* `runConsentedGridMod` starts a legacy grid-attached client mod, until
|
|
15
|
+
* CrowdyJS 18 removes them.
|
|
13
16
|
*/
|
|
14
17
|
import {
|
|
15
18
|
PlayerCodeBroker,
|
|
@@ -54,12 +57,19 @@ export const OFFERED_HOST_CALLS = [
|
|
|
54
57
|
/** One zero byte, base64: the smallest non-empty voxel state the API accepts. */
|
|
55
58
|
export const DEFAULT_VOXEL_STATE = 'AA==';
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
/** `actors_list_radius` reaches this far, as the platform clamps it. */
|
|
61
|
+
const MAX_RADIUS_XZ = 3;
|
|
62
|
+
const MAX_RADIUS_Y = 1;
|
|
58
63
|
|
|
59
64
|
function toBigInt(value: unknown): bigint {
|
|
60
65
|
return BigInt(value as string | number | bigint);
|
|
61
66
|
}
|
|
62
67
|
|
|
68
|
+
function radius(value: unknown, max: number): bigint {
|
|
69
|
+
const requested = Number(value ?? 0);
|
|
70
|
+
return BigInt(Number.isFinite(requested) ? Math.max(0, Math.min(max, Math.floor(requested))) : 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
export class HostCallRefusedError extends Error {
|
|
64
74
|
constructor(readonly fn: string) {
|
|
65
75
|
super(`host call '${fn}' is not offered by this game`);
|
|
@@ -80,22 +90,20 @@ export async function routeClientHostCall(
|
|
|
80
90
|
return { actors: reads.actorsInChunk(toBigInt(args.x), toBigInt(args.y), toBigInt(args.z)) };
|
|
81
91
|
case 'actors_list_radius': {
|
|
82
92
|
const origin = { x: toBigInt(args.x), y: toBigInt(args.y), z: toBigInt(args.z) };
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
Number.isFinite(requested) ? Math.max(0, Math.min(MAX_RADIUS, Math.floor(requested))) : 0,
|
|
86
|
-
);
|
|
93
|
+
const rXz = radius(args.radiusXz ?? args.radius ?? args.r, MAX_RADIUS_XZ);
|
|
94
|
+
const rY = radius(args.radiusY, MAX_RADIUS_Y);
|
|
87
95
|
const low = grid
|
|
88
96
|
? {
|
|
89
|
-
x: maxBig(grid.low.x, origin.x -
|
|
90
|
-
y: maxBig(grid.low.y, origin.y -
|
|
91
|
-
z: maxBig(grid.low.z, origin.z -
|
|
97
|
+
x: maxBig(grid.low.x, origin.x - rXz),
|
|
98
|
+
y: maxBig(grid.low.y, origin.y - rY),
|
|
99
|
+
z: maxBig(grid.low.z, origin.z - rXz),
|
|
92
100
|
}
|
|
93
101
|
: origin;
|
|
94
102
|
const high = grid
|
|
95
103
|
? {
|
|
96
|
-
x: minBig(grid.high.x, origin.x +
|
|
97
|
-
y: minBig(grid.high.y, origin.y +
|
|
98
|
-
z: minBig(grid.high.z, origin.z +
|
|
104
|
+
x: minBig(grid.high.x, origin.x + rXz),
|
|
105
|
+
y: minBig(grid.high.y, origin.y + rY),
|
|
106
|
+
z: minBig(grid.high.z, origin.z + rXz),
|
|
99
107
|
}
|
|
100
108
|
: origin;
|
|
101
109
|
const actors: Array<Record<string, unknown>> = [];
|
|
@@ -166,7 +174,7 @@ function asCoord3(value: unknown): { x: unknown; y: unknown; z: unknown } | null
|
|
|
166
174
|
return null;
|
|
167
175
|
}
|
|
168
176
|
|
|
169
|
-
/** Accept the
|
|
177
|
+
/** Accept the SDKs' flattened host-call JSON (the broker clamps its chunk) and the tuple shape. */
|
|
170
178
|
export function parseVoxelSetArgs(args: Record<string, unknown>): {
|
|
171
179
|
chunk: { x: number; y: number; z: number };
|
|
172
180
|
x: number;
|
|
@@ -185,9 +193,9 @@ export function parseVoxelSetArgs(args: Record<string, unknown>): {
|
|
|
185
193
|
const voxelRaw =
|
|
186
194
|
asCoord3(args.voxel) ??
|
|
187
195
|
({
|
|
188
|
-
x: coordField(args, 'x', 'vx'),
|
|
189
|
-
y: coordField(args, 'y', 'vy'),
|
|
190
|
-
z: coordField(args, 'z', 'vz'),
|
|
196
|
+
x: coordField(args, 'voxelX', 'x', 'vx'),
|
|
197
|
+
y: coordField(args, 'voxelY', 'y', 'vy'),
|
|
198
|
+
z: coordField(args, 'voxelZ', 'z', 'vz'),
|
|
191
199
|
} as { x: unknown; y: unknown; z: unknown });
|
|
192
200
|
const cx = intInRange(chunkRaw.x, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
193
201
|
const cy = intInRange(chunkRaw.y, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
@@ -248,6 +256,10 @@ const artifactCache = new Map<string, CachedArtifact>();
|
|
|
248
256
|
* circuit breaker as the Studio's own Test/Run path. Its HUD output lands in
|
|
249
257
|
* the persistent text HUD. Returns null (fails closed) when the artifact
|
|
250
258
|
* cannot be fetched.
|
|
259
|
+
*
|
|
260
|
+
* @deprecated A legacy grid-attached client mod (`marketplace.gridClientMods`),
|
|
261
|
+
* which CrowdyJS 18 removes. A ck-exec mod's CLIENT half runs through
|
|
262
|
+
* `GridClientHalves` (`clientHalves.ts`).
|
|
251
263
|
*/
|
|
252
264
|
export async function runConsentedGridMod(options: {
|
|
253
265
|
client: CrowdyClient;
|