@crowdedkingdoms/crowdyjs 8.0.1 → 8.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.
@@ -0,0 +1,106 @@
1
+ import { kitContainerProperties } from './shared.js';
2
+ /**
3
+ * Runtime helpers for the {@link npcBlueprint} conventions: spawn NPC
4
+ * instances, read their server-driven state, and manage/monitor the
5
+ * automations behind them. Behaviors run in the API server — clients only
6
+ * re-read state (or listen for model-driven notifications) and render.
7
+ *
8
+ * Spawning and the automation management/monitoring calls are studio/admin
9
+ * operations (`manage_apps`); reads are player-safe.
10
+ *
11
+ * Obtained via `client.kit(appId).npcs`.
12
+ */
13
+ export class NpcsKit {
14
+ constructor(appId, gameModel, options = {}) {
15
+ this.appId = appId;
16
+ this.gameModel = gameModel;
17
+ this.typeName = options.typeName ?? 'Npc';
18
+ }
19
+ /** Spawn a live NPC instance (admin — the type is admin-instantiable). */
20
+ async spawn(input) {
21
+ const properties = [
22
+ ...(input.role !== undefined
23
+ ? [{ key: 'role', valueType: 'string', valueJson: JSON.stringify(input.role) }]
24
+ : []),
25
+ ...(input.position
26
+ ? [
27
+ { key: 'x', valueType: 'float', valueJson: String(input.position.x) },
28
+ { key: 'y', valueType: 'float', valueJson: String(input.position.y) },
29
+ { key: 'z', valueType: 'float', valueJson: String(input.position.z) },
30
+ ]
31
+ : []),
32
+ ...(input.properties ?? []),
33
+ ];
34
+ return this.gameModel.createContainer({
35
+ appId: this.appId,
36
+ typeName: this.typeName,
37
+ displayName: input.displayName,
38
+ ...(input.sessionId !== undefined ? { sessionId: input.sessionId } : {}),
39
+ ...(properties.length ? { properties } : {}),
40
+ });
41
+ }
42
+ /**
43
+ * List live NPCs with parsed state, optionally filtered by `role`. Fetches
44
+ * each NPC's visible properties in parallel — fine for the bounded NPC
45
+ * populations automations are designed around.
46
+ */
47
+ async list(options = {}) {
48
+ const containers = await this.gameModel.containers({
49
+ appId: this.appId,
50
+ typeName: this.typeName,
51
+ ...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),
52
+ });
53
+ const npcs = await Promise.all(containers.map((c) => this.toNpc(c.containerId, c.displayName)));
54
+ return options.role !== undefined
55
+ ? npcs.filter((n) => n.role === options.role)
56
+ : npcs;
57
+ }
58
+ /** Read one NPC's current server-side state. */
59
+ async state(npcId) {
60
+ const container = await this.gameModel.container({
61
+ appId: this.appId,
62
+ containerId: npcId,
63
+ });
64
+ return this.toNpc(container.containerId, container.displayName);
65
+ }
66
+ /** Run one of the NPC automations immediately (admin; useful for testing). */
67
+ async runNow(automationName) {
68
+ return this.gameModel.runAutomation({ appId: this.appId, name: automationName });
69
+ }
70
+ /**
71
+ * Pause or resume an NPC automation (admin). Re-enabling also resets a
72
+ * tripped failure circuit.
73
+ */
74
+ async setEnabled(automationName, enabled) {
75
+ return this.gameModel.setAutomationEnabled({
76
+ appId: this.appId,
77
+ name: automationName,
78
+ enabled,
79
+ });
80
+ }
81
+ /** Aggregate "what are my NPCs doing" stats over a recent window (admin). */
82
+ async stats(windowMinutes) {
83
+ return this.gameModel.automationStats({
84
+ appId: this.appId,
85
+ ...(windowMinutes !== undefined ? { windowMinutes } : {}),
86
+ });
87
+ }
88
+ /** Recent automation run history, newest first (admin). */
89
+ async runs(options = {}) {
90
+ return this.gameModel.automationRuns({ appId: this.appId, ...options });
91
+ }
92
+ async toNpc(containerId, displayName) {
93
+ const props = await kitContainerProperties(this.gameModel, String(this.appId), containerId);
94
+ return {
95
+ containerId,
96
+ displayName,
97
+ role: String(props.role ?? ''),
98
+ x: Number(props.x ?? 0),
99
+ y: Number(props.y ?? 0),
100
+ z: Number(props.z ?? 0),
101
+ behaviorState: String(props.behavior_state ?? ''),
102
+ health: Number(props.health ?? 0),
103
+ properties: props,
104
+ };
105
+ }
106
+ }
@@ -0,0 +1,116 @@
1
+ import type { GameModelAPI } from '../domains/gameModel.js';
2
+ import type { Scalars, SeedPropertyInput } from '../generated/graphql.js';
3
+ import { type KitInvokeResult } from './shared.js';
4
+ /** Options for {@link ObjectsKit}. Must match the deployed lock blueprint. */
5
+ export interface ObjectsKitOptions {
6
+ /** The `objectTypeName` the lock blueprint was deployed with. Defaults to `'Lockable'`. */
7
+ objectTypeName?: string;
8
+ /** The `keyTypeName` the lock blueprint was deployed with. */
9
+ keyTypeName?: string;
10
+ }
11
+ /**
12
+ * Runtime helpers for the {@link lockBlueprint} conventions: instantiate
13
+ * lockable world objects, grant key items, and operate the objects through
14
+ * their authority-gated `open`/`close` functions. Authorization is decided
15
+ * entirely server-side; a denied attempt resolves with `success: false`.
16
+ *
17
+ * Obtained via `client.kit(appId).objects` (or `client.kit(appId)
18
+ * .objectsFor('Door')` for a non-default type name).
19
+ */
20
+ export declare class ObjectsKit {
21
+ private readonly appId;
22
+ private readonly gameModel;
23
+ private readonly names;
24
+ constructor(appId: Scalars['BigInt']['input'], gameModel: GameModelAPI, options?: ObjectsKitOptions);
25
+ /**
26
+ * Instantiate a lockable object (admin/studio call — the type is
27
+ * admin-instantiable). For key-gated objects set `requiredKeyId` to the key
28
+ * id that opens it; for owner-gated objects set `ownerUserId`; for
29
+ * chunk-permission-gated objects set `chunk` to where the object stands
30
+ * (feeds the `has_chunk_permission` policy).
31
+ */
32
+ create(input: {
33
+ displayName: string;
34
+ requiredKeyId?: string;
35
+ ownerUserId?: Scalars['BigInt']['input'];
36
+ /** The chunk the object occupies (chunkPermission authority). */
37
+ chunk?: {
38
+ x: number;
39
+ y: number;
40
+ z: number;
41
+ };
42
+ properties?: SeedPropertyInput[];
43
+ sessionId?: string;
44
+ }): Promise<{
45
+ __typename?: "GmContainer";
46
+ containerId: string;
47
+ appId: string;
48
+ sessionId: string | null;
49
+ typeName: string;
50
+ displayName: string;
51
+ description: string | null;
52
+ ownerUserId: string | null;
53
+ metadataJson: string;
54
+ }>;
55
+ /**
56
+ * Grant a player a key item (admin/studio call). Creates a key container
57
+ * owned by the player, with the owner mirrored into the `owner_user_id`
58
+ * property that the key condition policy reads.
59
+ */
60
+ grantKey(input: {
61
+ keyId: string;
62
+ toUserId: Scalars['BigInt']['input'];
63
+ displayName?: string;
64
+ }): Promise<{
65
+ __typename?: "GmContainer";
66
+ containerId: string;
67
+ appId: string;
68
+ sessionId: string | null;
69
+ typeName: string;
70
+ displayName: string;
71
+ description: string | null;
72
+ ownerUserId: string | null;
73
+ metadataJson: string;
74
+ }>;
75
+ /** List the key items a player holds. */
76
+ keysOf(userId: Scalars['BigInt']['input']): Promise<{
77
+ __typename?: "GmContainer";
78
+ containerId: string;
79
+ appId: string;
80
+ sessionId: string | null;
81
+ typeName: string;
82
+ displayName: string;
83
+ description: string | null;
84
+ ownerUserId: string | null;
85
+ metadataJson: string;
86
+ }[]>;
87
+ /**
88
+ * Try to open an object. Pass `keyId` (the **container id** of a key the
89
+ * caller holds) when the object is key-gated; owner/grid/group authorities
90
+ * need no params. A denial is not an exception — check `success`.
91
+ */
92
+ open(objectId: string, options?: {
93
+ keyId?: string;
94
+ }): Promise<KitInvokeResult<boolean>>;
95
+ /** Try to close an object; same authority as {@link open}. */
96
+ close(objectId: string, options?: {
97
+ keyId?: string;
98
+ }): Promise<KitInvokeResult<boolean>>;
99
+ /** Read whether an object is currently open. */
100
+ isOpen(objectId: string): Promise<boolean>;
101
+ /** List all objects of this lockable type. */
102
+ list(options?: {
103
+ sessionId?: string;
104
+ }): Promise<{
105
+ __typename?: "GmContainer";
106
+ containerId: string;
107
+ appId: string;
108
+ sessionId: string | null;
109
+ typeName: string;
110
+ displayName: string;
111
+ description: string | null;
112
+ ownerUserId: string | null;
113
+ metadataJson: string;
114
+ }[]>;
115
+ }
116
+ //# sourceMappingURL=objects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../../src/kit/objects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAErB,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;gBAGf,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjC,SAAS,EAAE,YAAY,EACxC,OAAO,GAAE,iBAAsB;IAKjC;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;QACzC,iEAAiE;QACjE,KAAK,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;QACjC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;IA+BD;;;;OAIG;IACG,QAAQ,CAAC,KAAK,EAAE;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;QACrC,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;;;;;;;;;;;IAiBD,yCAAyC;IACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;;;;;;;;;;;IAU/C;;;;OAIG;IACG,IAAI,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IASpC,8DAA8D;IACxD,KAAK,CACT,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IASpC,gDAAgD;IAC1C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShD,8CAA8C;IACxC,IAAI,CAAC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;;;;;;;;;;;CAOhD"}
@@ -0,0 +1,119 @@
1
+ import { lockNames } from './blueprints.js';
2
+ import { kitContainerProperties, kitInvoke, } from './shared.js';
3
+ /**
4
+ * Runtime helpers for the {@link lockBlueprint} conventions: instantiate
5
+ * lockable world objects, grant key items, and operate the objects through
6
+ * their authority-gated `open`/`close` functions. Authorization is decided
7
+ * entirely server-side; a denied attempt resolves with `success: false`.
8
+ *
9
+ * Obtained via `client.kit(appId).objects` (or `client.kit(appId)
10
+ * .objectsFor('Door')` for a non-default type name).
11
+ */
12
+ export class ObjectsKit {
13
+ constructor(appId, gameModel, options = {}) {
14
+ this.appId = appId;
15
+ this.gameModel = gameModel;
16
+ this.names = lockNames(options.objectTypeName, options.keyTypeName);
17
+ }
18
+ /**
19
+ * Instantiate a lockable object (admin/studio call — the type is
20
+ * admin-instantiable). For key-gated objects set `requiredKeyId` to the key
21
+ * id that opens it; for owner-gated objects set `ownerUserId`; for
22
+ * chunk-permission-gated objects set `chunk` to where the object stands
23
+ * (feeds the `has_chunk_permission` policy).
24
+ */
25
+ async create(input) {
26
+ const properties = [
27
+ { key: 'is_open', valueType: 'bool', valueJson: 'false' },
28
+ ...(input.requiredKeyId !== undefined
29
+ ? [
30
+ {
31
+ key: 'required_key_id',
32
+ valueType: 'string',
33
+ valueJson: JSON.stringify(input.requiredKeyId),
34
+ },
35
+ ]
36
+ : []),
37
+ ...(input.chunk
38
+ ? [
39
+ { key: 'cx', valueType: 'int', valueJson: String(input.chunk.x) },
40
+ { key: 'cy', valueType: 'int', valueJson: String(input.chunk.y) },
41
+ { key: 'cz', valueType: 'int', valueJson: String(input.chunk.z) },
42
+ ]
43
+ : []),
44
+ ...(input.properties ?? []),
45
+ ];
46
+ return this.gameModel.createContainer({
47
+ appId: this.appId,
48
+ typeName: this.names.objectType,
49
+ displayName: input.displayName,
50
+ ...(input.ownerUserId !== undefined ? { ownerUserId: input.ownerUserId } : {}),
51
+ ...(input.sessionId !== undefined ? { sessionId: input.sessionId } : {}),
52
+ properties,
53
+ });
54
+ }
55
+ /**
56
+ * Grant a player a key item (admin/studio call). Creates a key container
57
+ * owned by the player, with the owner mirrored into the `owner_user_id`
58
+ * property that the key condition policy reads.
59
+ */
60
+ async grantKey(input) {
61
+ return this.gameModel.createContainer({
62
+ appId: this.appId,
63
+ typeName: this.names.keyType,
64
+ displayName: input.displayName ?? `Key ${input.keyId}`,
65
+ ownerUserId: input.toUserId,
66
+ properties: [
67
+ { key: 'key_id', valueType: 'string', valueJson: JSON.stringify(input.keyId) },
68
+ {
69
+ key: 'owner_user_id',
70
+ valueType: 'int',
71
+ valueJson: String(input.toUserId),
72
+ },
73
+ ],
74
+ });
75
+ }
76
+ /** List the key items a player holds. */
77
+ async keysOf(userId) {
78
+ const containers = await this.gameModel.containers({
79
+ appId: this.appId,
80
+ typeName: this.names.keyType,
81
+ });
82
+ return containers.filter((c) => c.ownerUserId != null && String(c.ownerUserId) === String(userId));
83
+ }
84
+ /**
85
+ * Try to open an object. Pass `keyId` (the **container id** of a key the
86
+ * caller holds) when the object is key-gated; owner/grid/group authorities
87
+ * need no params. A denial is not an exception — check `success`.
88
+ */
89
+ async open(objectId, options = {}) {
90
+ return kitInvoke(this.gameModel, {
91
+ appId: String(this.appId),
92
+ functionName: this.names.openFn,
93
+ selfContainerId: objectId,
94
+ params: options.keyId !== undefined ? { key_id: options.keyId } : {},
95
+ });
96
+ }
97
+ /** Try to close an object; same authority as {@link open}. */
98
+ async close(objectId, options = {}) {
99
+ return kitInvoke(this.gameModel, {
100
+ appId: String(this.appId),
101
+ functionName: this.names.closeFn,
102
+ selfContainerId: objectId,
103
+ params: options.keyId !== undefined ? { key_id: options.keyId } : {},
104
+ });
105
+ }
106
+ /** Read whether an object is currently open. */
107
+ async isOpen(objectId) {
108
+ const props = await kitContainerProperties(this.gameModel, String(this.appId), objectId);
109
+ return props.is_open === true;
110
+ }
111
+ /** List all objects of this lockable type. */
112
+ async list(options = {}) {
113
+ return this.gameModel.containers({
114
+ appId: this.appId,
115
+ typeName: this.names.objectType,
116
+ ...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),
117
+ });
118
+ }
119
+ }
@@ -0,0 +1,81 @@
1
+ import type { GameAppsAPI } from '../domains/gameApps.js';
2
+ import type { GameModelAPI } from '../domains/gameModel.js';
3
+ import type { Scalars, SeedPropertyInput } from '../generated/graphql.js';
4
+ import { type KitInvokeResult } from './shared.js';
5
+ /** Options for {@link PlotsKit}. Must match the deployed plot blueprint. */
6
+ export interface PlotsKitOptions {
7
+ /** The `typeName` the plot blueprint was deployed with. Defaults to `'Plot'`. */
8
+ typeName?: string;
9
+ }
10
+ /** A parsed view of one plot. */
11
+ export interface KitPlot {
12
+ containerId: string;
13
+ displayName: string;
14
+ gridId: number;
15
+ price: number;
16
+ /** 0 when unowned. */
17
+ ownerUserId: number;
18
+ rentPrice?: number;
19
+ rentTtlSeconds?: number;
20
+ }
21
+ /**
22
+ * Runtime helpers for the {@link plotBlueprint} conventions: list/create
23
+ * plots and drive the buy/rent/evict functions whose permission effects grant
24
+ * or revoke real, replication-enforced grid permissions transactionally with
25
+ * the currency mutation. Authorization (wallet ownership, price, plot
26
+ * ownership) is enforced server-side — a denial resolves with
27
+ * `success: false`, never an exception.
28
+ *
29
+ * Obtained via `client.kit(appId).plots`.
30
+ */
31
+ export declare class PlotsKit {
32
+ private readonly appId;
33
+ private readonly gameModel;
34
+ private readonly gameApps;
35
+ private readonly names;
36
+ constructor(appId: Scalars['BigInt']['input'], gameModel: GameModelAPI, gameApps: GameAppsAPI, options?: PlotsKitOptions);
37
+ /**
38
+ * Instantiate a plot over an existing grid (admin — the type is
39
+ * admin-instantiable). Create the grid first (`client.gameApps.createGrid`)
40
+ * and pass its id here.
41
+ */
42
+ create(input: {
43
+ displayName: string;
44
+ gridId: Scalars['BigInt']['input'];
45
+ price: number;
46
+ rentPrice?: number;
47
+ rentTtlSeconds?: number;
48
+ properties?: SeedPropertyInput[];
49
+ }): Promise<{
50
+ __typename?: "GmContainer";
51
+ containerId: string;
52
+ appId: string;
53
+ sessionId: string | null;
54
+ typeName: string;
55
+ displayName: string;
56
+ description: string | null;
57
+ ownerUserId: string | null;
58
+ metadataJson: string;
59
+ }>;
60
+ /** List plots with parsed state (grid, price, current owner). */
61
+ list(): Promise<KitPlot[]>;
62
+ /**
63
+ * Buy a plot: spends the price from the caller's wallet AND grants the
64
+ * blueprint's grid permissions in one transaction. Resolves with the
65
+ * wallet's remaining balance.
66
+ */
67
+ buy(plotId: string, walletId: string): Promise<KitInvokeResult<number>>;
68
+ /**
69
+ * Rent a plot (blueprint deployed with `rentable: true`): like {@link buy}
70
+ * but the grant expires after the plot's `rent_ttl_seconds`.
71
+ */
72
+ rent(plotId: string, walletId: string): Promise<KitInvokeResult<number>>;
73
+ /** Revoke a user's permissions on a plot (plot owner or app admin). */
74
+ evict(plotId: string, targetUserId: Scalars['BigInt']['input']): Promise<KitInvokeResult>;
75
+ /**
76
+ * A user's effective permission keys on a plot's grid (for HUD display —
77
+ * enforcement happens server-side regardless).
78
+ */
79
+ accessOf(userId: Scalars['BigInt']['input'], gridId: Scalars['BigInt']['input']): Promise<string[]>;
80
+ }
81
+ //# sourceMappingURL=plots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plots.d.ts","sourceRoot":"","sources":["../../src/kit/plots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAErB,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iCAAiC;AACjC,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,qBAAa,QAAQ;IAIjB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;gBAGf,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjC,SAAS,EAAE,YAAY,EACvB,QAAQ,EAAE,WAAW,EACtC,OAAO,GAAE,eAAoB;IAK/B;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;KAClC;;;;;;;;;;;IA0BD,iEAAiE;IAC3D,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IA2BhC;;;;OAIG;IACG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAS7E;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAS9E,uEAAuE;IACjE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IAS/F;;;OAGG;IACG,QAAQ,CACZ,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAClC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC;CAQrB"}
@@ -0,0 +1,113 @@
1
+ import { plotNames } from './blueprints.js';
2
+ import { kitContainerProperties, kitInvoke, } from './shared.js';
3
+ /**
4
+ * Runtime helpers for the {@link plotBlueprint} conventions: list/create
5
+ * plots and drive the buy/rent/evict functions whose permission effects grant
6
+ * or revoke real, replication-enforced grid permissions transactionally with
7
+ * the currency mutation. Authorization (wallet ownership, price, plot
8
+ * ownership) is enforced server-side — a denial resolves with
9
+ * `success: false`, never an exception.
10
+ *
11
+ * Obtained via `client.kit(appId).plots`.
12
+ */
13
+ export class PlotsKit {
14
+ constructor(appId, gameModel, gameApps, options = {}) {
15
+ this.appId = appId;
16
+ this.gameModel = gameModel;
17
+ this.gameApps = gameApps;
18
+ this.names = plotNames(options.typeName);
19
+ }
20
+ /**
21
+ * Instantiate a plot over an existing grid (admin — the type is
22
+ * admin-instantiable). Create the grid first (`client.gameApps.createGrid`)
23
+ * and pass its id here.
24
+ */
25
+ async create(input) {
26
+ const properties = [
27
+ { key: 'grid_id', valueType: 'int', valueJson: String(input.gridId) },
28
+ { key: 'price', valueType: 'int', valueJson: String(input.price) },
29
+ ...(input.rentPrice !== undefined
30
+ ? [{ key: 'rent_price', valueType: 'int', valueJson: String(input.rentPrice) }]
31
+ : []),
32
+ ...(input.rentTtlSeconds !== undefined
33
+ ? [
34
+ {
35
+ key: 'rent_ttl_seconds',
36
+ valueType: 'int',
37
+ valueJson: String(input.rentTtlSeconds),
38
+ },
39
+ ]
40
+ : []),
41
+ ...(input.properties ?? []),
42
+ ];
43
+ return this.gameModel.createContainer({
44
+ appId: this.appId,
45
+ typeName: this.names.plotType,
46
+ displayName: input.displayName,
47
+ properties,
48
+ });
49
+ }
50
+ /** List plots with parsed state (grid, price, current owner). */
51
+ async list() {
52
+ const containers = await this.gameModel.containers({
53
+ appId: this.appId,
54
+ typeName: this.names.plotType,
55
+ });
56
+ return Promise.all(containers.map(async (c) => {
57
+ const props = await kitContainerProperties(this.gameModel, String(this.appId), c.containerId);
58
+ return {
59
+ containerId: c.containerId,
60
+ displayName: c.displayName,
61
+ gridId: Number(props.grid_id ?? 0),
62
+ price: Number(props.price ?? 0),
63
+ ownerUserId: Number(props.owner_user_id ?? 0),
64
+ ...(props.rent_price !== undefined ? { rentPrice: Number(props.rent_price) } : {}),
65
+ ...(props.rent_ttl_seconds !== undefined
66
+ ? { rentTtlSeconds: Number(props.rent_ttl_seconds) }
67
+ : {}),
68
+ };
69
+ }));
70
+ }
71
+ /**
72
+ * Buy a plot: spends the price from the caller's wallet AND grants the
73
+ * blueprint's grid permissions in one transaction. Resolves with the
74
+ * wallet's remaining balance.
75
+ */
76
+ async buy(plotId, walletId) {
77
+ return kitInvoke(this.gameModel, {
78
+ appId: String(this.appId),
79
+ functionName: this.names.buyFn,
80
+ selfContainerId: plotId,
81
+ params: { wallet_id: walletId },
82
+ });
83
+ }
84
+ /**
85
+ * Rent a plot (blueprint deployed with `rentable: true`): like {@link buy}
86
+ * but the grant expires after the plot's `rent_ttl_seconds`.
87
+ */
88
+ async rent(plotId, walletId) {
89
+ return kitInvoke(this.gameModel, {
90
+ appId: String(this.appId),
91
+ functionName: this.names.rentFn,
92
+ selfContainerId: plotId,
93
+ params: { wallet_id: walletId },
94
+ });
95
+ }
96
+ /** Revoke a user's permissions on a plot (plot owner or app admin). */
97
+ async evict(plotId, targetUserId) {
98
+ return kitInvoke(this.gameModel, {
99
+ appId: String(this.appId),
100
+ functionName: this.names.evictFn,
101
+ selfContainerId: plotId,
102
+ params: { target_user_id: Number(targetUserId) },
103
+ });
104
+ }
105
+ /**
106
+ * A user's effective permission keys on a plot's grid (for HUD display —
107
+ * enforcement happens server-side regardless).
108
+ */
109
+ async accessOf(userId, gridId) {
110
+ const res = await this.gameApps.userPermissions(String(this.appId), String(gridId), String(userId));
111
+ return [...res.permissionKeys];
112
+ }
113
+ }
@@ -0,0 +1,32 @@
1
+ import type { GameModelAPI } from '../domains/gameModel.js';
2
+ import type { GameModelInvokeMutation } from '../generated/graphql.js';
3
+ /** The raw server result of a `gameModelInvoke` call. */
4
+ export type RawInvokeResult = GameModelInvokeMutation['gameModelInvoke'];
5
+ /**
6
+ * A kit invoke outcome: the server's authority/evaluation verdict plus the
7
+ * parsed return value. Authority denials and expression errors are **not**
8
+ * exceptions — check {@link success}.
9
+ */
10
+ export interface KitInvokeResult<T = unknown> {
11
+ /** `false` when the invoke policy denied the caller or the logic errored (rolled back). */
12
+ success: boolean;
13
+ /** The parsed `returnValueJson`, when present and `success` is true. */
14
+ returnValue?: T;
15
+ /** The server's error message when `success` is false. */
16
+ errorMessage?: string;
17
+ /** The full server result (event id, applied mutations, …). */
18
+ raw: RawInvokeResult;
19
+ }
20
+ /** Wrap a raw invoke result, parsing the JSON return value. */
21
+ export declare function toKitInvokeResult<T>(raw: RawInvokeResult): KitInvokeResult<T>;
22
+ /** Invoke a model function and wrap the result. */
23
+ export declare function kitInvoke<T = unknown>(gameModel: GameModelAPI, input: {
24
+ appId: string;
25
+ functionName: string;
26
+ selfContainerId: string;
27
+ params?: Record<string, unknown>;
28
+ sessionId?: string;
29
+ }): Promise<KitInvokeResult<T>>;
30
+ /** Read a container's visible properties as a parsed object. */
31
+ export declare function kitContainerProperties(gameModel: GameModelAPI, appId: string, containerId: string): Promise<Record<string, unknown>>;
32
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/kit/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,2FAA2F;IAC3F,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,WAAW,CAAC,EAAE,CAAC,CAAC;IAChB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,GAAG,EAAE,eAAe,CAAC;CACtB;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,GAAG,eAAe,CAAC,CAAC,CAAC,CAe7E;AAED,mDAAmD;AACnD,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,SAAS,EAAE,YAAY,EACvB,KAAK,EAAE;IACL,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAS7B;AAED,gEAAgE;AAChE,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,YAAY,EACvB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOlC"}
@@ -0,0 +1,39 @@
1
+ /** Wrap a raw invoke result, parsing the JSON return value. */
2
+ export function toKitInvokeResult(raw) {
3
+ let returnValue;
4
+ if (raw.success && raw.returnValueJson != null) {
5
+ try {
6
+ returnValue = JSON.parse(raw.returnValueJson);
7
+ }
8
+ catch {
9
+ returnValue = undefined;
10
+ }
11
+ }
12
+ return {
13
+ success: raw.success,
14
+ returnValue,
15
+ errorMessage: raw.errorMessage ?? undefined,
16
+ raw,
17
+ };
18
+ }
19
+ /** Invoke a model function and wrap the result. */
20
+ export async function kitInvoke(gameModel, input) {
21
+ const raw = await gameModel.invoke({
22
+ appId: input.appId,
23
+ functionName: input.functionName,
24
+ selfContainerId: input.selfContainerId,
25
+ paramsJson: JSON.stringify(input.params ?? {}),
26
+ ...(input.sessionId !== undefined ? { sessionId: input.sessionId } : {}),
27
+ });
28
+ return toKitInvokeResult(raw);
29
+ }
30
+ /** Read a container's visible properties as a parsed object. */
31
+ export async function kitContainerProperties(gameModel, appId, containerId) {
32
+ const state = await gameModel.containerState({ appId, containerId });
33
+ try {
34
+ return JSON.parse(state.propertiesJson);
35
+ }
36
+ catch {
37
+ return {};
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "8.0.1",
3
+ "version": "8.2.0",
4
4
  "description": "Client SDK for Crowded Kingdoms GraphQL API with UDP proxy support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",