@crowdedkingdoms/crowdyjs 8.8.0 → 8.10.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 +59 -0
- package/README.md +5 -1
- package/dist/domains/compute.d.ts +13 -2
- package/dist/domains/compute.d.ts.map +1 -1
- package/dist/domains/compute.js +19 -2
- package/dist/generated/graphql.d.ts +60 -0
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/kit/abilities.d.ts +66 -0
- package/dist/kit/abilities.d.ts.map +1 -0
- package/dist/kit/abilities.js +71 -0
- package/dist/kit/blueprints/index.d.ts +3 -1
- package/dist/kit/blueprints/index.d.ts.map +1 -1
- package/dist/kit/blueprints/index.js +3 -1
- package/dist/kit/blueprints/inventory.d.ts +47 -1
- package/dist/kit/blueprints/inventory.d.ts.map +1 -1
- package/dist/kit/blueprints/inventory.js +112 -12
- package/dist/kit/blueprints/liveops.d.ts +42 -0
- package/dist/kit/blueprints/liveops.d.ts.map +1 -0
- package/dist/kit/blueprints/liveops.js +170 -0
- package/dist/kit/blueprints/moderation.d.ts +32 -0
- package/dist/kit/blueprints/moderation.d.ts.map +1 -0
- package/dist/kit/blueprints/moderation.js +140 -0
- package/dist/kit/combat.d.ts +4 -3
- package/dist/kit/combat.d.ts.map +1 -1
- package/dist/kit/combat.js +4 -3
- package/dist/kit/index.d.ts +9 -2
- package/dist/kit/index.d.ts.map +1 -1
- package/dist/kit/index.js +9 -2
- package/dist/kit/inventory.d.ts +13 -0
- package/dist/kit/inventory.d.ts.map +1 -1
- package/dist/kit/inventory.js +33 -4
- package/dist/kit/kit.d.ts +44 -0
- package/dist/kit/kit.d.ts.map +1 -1
- package/dist/kit/kit.js +35 -1
- package/dist/kit/liveops.d.ts +112 -0
- package/dist/kit/liveops.d.ts.map +1 -0
- package/dist/kit/liveops.js +183 -0
- package/dist/kit/loot.d.ts +22 -1
- package/dist/kit/loot.d.ts.map +1 -1
- package/dist/kit/loot.js +41 -1
- package/dist/kit/moderation.d.ts +73 -0
- package/dist/kit/moderation.d.ts.map +1 -0
- package/dist/kit/moderation.js +103 -0
- package/dist/kit/movement.d.ts +78 -0
- package/dist/kit/movement.d.ts.map +1 -0
- package/dist/kit/movement.js +103 -0
- package/dist/kit/npcs.d.ts.map +1 -1
- package/dist/kit/quests.d.ts +4 -0
- package/dist/kit/quests.d.ts.map +1 -1
- package/dist/kit/quests.js +9 -2
- package/dist/kit/racing.d.ts +85 -0
- package/dist/kit/racing.d.ts.map +1 -0
- package/dist/kit/racing.js +99 -0
- package/dist/kit/social.d.ts.map +1 -1
- package/dist/kit/telemetry.d.ts +66 -0
- package/dist/kit/telemetry.d.ts.map +1 -0
- package/dist/kit/telemetry.js +133 -0
- package/dist/kit/territory.d.ts +97 -0
- package/dist/kit/territory.d.ts.map +1 -0
- package/dist/kit/territory.js +114 -0
- package/dist/kit/wire.d.ts +51 -0
- package/dist/kit/wire.d.ts.map +1 -1
- package/dist/kit/wire.js +60 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/kit/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGvD,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;IAOrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;gBAGrC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjC,SAAS,EAAE,YAAY,EACxC,OAAO,GAAE,mBAAwB;IAMnC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAK9D,+DAA+D;IACzD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB5E,qDAAqD;IAC/C,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;YAiB3E,aAAa;CA0B5B;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,eAAe,SAAqB;;;;;;;;;;;;;;EAmCtE"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { kitContainerProperties } from './shared.js';
|
|
2
|
+
/**
|
|
3
|
+
* Game analytics events (matrix P7) — MODEL-THIN by design: `track()`
|
|
4
|
+
* writes a naming-convention row into the existing model event log via a
|
|
5
|
+
* fire-and-forget container property bump, and sampled counters live in
|
|
6
|
+
* ordinary `TelemetryCounter` containers (`event_name` + `count`). Export
|
|
7
|
+
* and BI are platform concerns, deliberately out of scope; the convention
|
|
8
|
+
* is the abstraction.
|
|
9
|
+
*
|
|
10
|
+
* Naming convention: `<area>.<action>` (e.g. `ftue.step_completed`,
|
|
11
|
+
* `shop.purchase`). Keep cardinality low — counters are containers.
|
|
12
|
+
*
|
|
13
|
+
* Obtained via `client.kit(appId).telemetry`.
|
|
14
|
+
*/
|
|
15
|
+
export class TelemetryKit {
|
|
16
|
+
constructor(appId, gameModel, options = {}) {
|
|
17
|
+
this.appId = appId;
|
|
18
|
+
this.gameModel = gameModel;
|
|
19
|
+
/** event name -> counter container id (cached per session). */
|
|
20
|
+
this.counterIds = new Map();
|
|
21
|
+
this.counterTypeName = options.counterTypeName ?? 'TelemetryCounter';
|
|
22
|
+
this.sampleRate = options.sampleRate ?? 1;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Track one event — fire-and-forget (never throws, never blocks
|
|
26
|
+
* gameplay): bumps the event's sampled counter. `props` ride the
|
|
27
|
+
* container's last-props snapshot for spot-debugging (NOT a warehouse).
|
|
28
|
+
*/
|
|
29
|
+
track(name, props = {}) {
|
|
30
|
+
if (Math.random() > this.sampleRate)
|
|
31
|
+
return;
|
|
32
|
+
void this.bump(name, props).catch(() => undefined);
|
|
33
|
+
}
|
|
34
|
+
/** The awaitable form of {@link track} for tests/backfills. */
|
|
35
|
+
async bump(name, props = {}) {
|
|
36
|
+
const containerId = await this.ensureCounter(name);
|
|
37
|
+
const current = await kitContainerProperties(this.gameModel, String(this.appId), containerId);
|
|
38
|
+
await this.gameModel.setProperty({
|
|
39
|
+
appId: this.appId,
|
|
40
|
+
containerId,
|
|
41
|
+
key: 'count',
|
|
42
|
+
valueType: 'int',
|
|
43
|
+
valueJson: String(Number(current.count ?? 0) + 1),
|
|
44
|
+
});
|
|
45
|
+
if (Object.keys(props).length > 0) {
|
|
46
|
+
await this.gameModel.setProperty({
|
|
47
|
+
appId: this.appId,
|
|
48
|
+
containerId,
|
|
49
|
+
key: 'last_props',
|
|
50
|
+
valueType: 'string',
|
|
51
|
+
valueJson: JSON.stringify(JSON.stringify(props).slice(0, 500)),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Read the sampled counters (dashboards, tests). */
|
|
56
|
+
async counters() {
|
|
57
|
+
const containers = await this.gameModel.containers({
|
|
58
|
+
appId: this.appId,
|
|
59
|
+
typeName: this.counterTypeName,
|
|
60
|
+
});
|
|
61
|
+
return Promise.all(containers.map(async (c) => {
|
|
62
|
+
const props = await kitContainerProperties(this.gameModel, String(this.appId), c.containerId);
|
|
63
|
+
return {
|
|
64
|
+
eventName: String(props.event_name ?? ''),
|
|
65
|
+
count: Number(props.count ?? 0),
|
|
66
|
+
lastProps: String(props.last_props ?? ''),
|
|
67
|
+
};
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
async ensureCounter(name) {
|
|
71
|
+
const cached = this.counterIds.get(name);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
const containers = await this.gameModel.containers({
|
|
75
|
+
appId: this.appId,
|
|
76
|
+
typeName: this.counterTypeName,
|
|
77
|
+
});
|
|
78
|
+
for (const c of containers) {
|
|
79
|
+
const props = await kitContainerProperties(this.gameModel, String(this.appId), c.containerId);
|
|
80
|
+
if (String(props.event_name ?? '') === name) {
|
|
81
|
+
this.counterIds.set(name, c.containerId);
|
|
82
|
+
return c.containerId;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const created = await this.gameModel.createContainer({
|
|
86
|
+
appId: this.appId,
|
|
87
|
+
typeName: this.counterTypeName,
|
|
88
|
+
displayName: `telemetry ${name}`,
|
|
89
|
+
properties: [
|
|
90
|
+
{ key: 'event_name', valueType: 'string', valueJson: JSON.stringify(name) },
|
|
91
|
+
{ key: 'count', valueType: 'int', valueJson: '0' },
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
this.counterIds.set(name, created.containerId);
|
|
95
|
+
return created.containerId;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The telemetry blueprint: one counter type; the convention is the point. */
|
|
99
|
+
export function telemetryBlueprint(counterTypeName = 'TelemetryCounter') {
|
|
100
|
+
return {
|
|
101
|
+
name: 'telemetry',
|
|
102
|
+
containerTypes: [
|
|
103
|
+
{
|
|
104
|
+
typeName: counterTypeName,
|
|
105
|
+
displayName: 'Telemetry counter',
|
|
106
|
+
description: "One sampled event counter (event_name + count + last_props). Naming convention: '<area>.<action>'.",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
propertyDefinitions: [
|
|
110
|
+
{
|
|
111
|
+
containerTypeName: counterTypeName,
|
|
112
|
+
key: 'event_name',
|
|
113
|
+
valueType: 'string',
|
|
114
|
+
defaultValueJson: '""',
|
|
115
|
+
description: "The tracked event's convention name ('<area>.<action>').",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
containerTypeName: counterTypeName,
|
|
119
|
+
key: 'count',
|
|
120
|
+
valueType: 'int',
|
|
121
|
+
defaultValueJson: '0',
|
|
122
|
+
description: 'Sampled occurrence count.',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
containerTypeName: counterTypeName,
|
|
126
|
+
key: 'last_props',
|
|
127
|
+
valueType: 'string',
|
|
128
|
+
defaultValueJson: '""',
|
|
129
|
+
description: 'The last tracked props JSON (spot-debugging, not a warehouse).',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { GameModelAPI } from '../domains/gameModel.js';
|
|
2
|
+
import type { Scalars } from '../generated/graphql.js';
|
|
3
|
+
import type { EngineDetector } from './engine.js';
|
|
4
|
+
import { type ControlPointEvent } from './wire.js';
|
|
5
|
+
/** Options for {@link TerritoryKit}. */
|
|
6
|
+
export interface TerritoryKitOptions {
|
|
7
|
+
/** The territory engine module name. Defaults to `'territory'`. */
|
|
8
|
+
moduleName?: string;
|
|
9
|
+
}
|
|
10
|
+
/** A control point as the engine reports it. */
|
|
11
|
+
export interface KitControlPoint {
|
|
12
|
+
pointId: string;
|
|
13
|
+
x: number;
|
|
14
|
+
z: number;
|
|
15
|
+
radius: number;
|
|
16
|
+
owner: string;
|
|
17
|
+
challenger: string;
|
|
18
|
+
progress: number;
|
|
19
|
+
incomePerMin: number;
|
|
20
|
+
siegeOpen: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Runtime helpers for the territory engine (Wave 3): control-point state,
|
|
24
|
+
* faction standings, and the admin CRUD for the map (factions, members,
|
|
25
|
+
* points). Capture itself is pure presence — stand inside a point's radius
|
|
26
|
+
* with your faction; the engine does the rest and announces flips as
|
|
27
|
+
* type-96 events.
|
|
28
|
+
*
|
|
29
|
+
* Obtained via `client.kit(appId).territory`.
|
|
30
|
+
*/
|
|
31
|
+
export declare class TerritoryKit {
|
|
32
|
+
private readonly appId;
|
|
33
|
+
private readonly gameModel;
|
|
34
|
+
private readonly engines;
|
|
35
|
+
private readonly moduleName;
|
|
36
|
+
constructor(appId: Scalars['BigInt']['input'], gameModel: GameModelAPI, engines: EngineDetector, options?: TerritoryKitOptions);
|
|
37
|
+
/** Is the territory engine deployed + enabled (cached per session)? */
|
|
38
|
+
engineAvailable(): Promise<boolean>;
|
|
39
|
+
/** Every control point with live capture state. */
|
|
40
|
+
points(): Promise<KitControlPoint[]>;
|
|
41
|
+
/** Faction standings (id + treasury). */
|
|
42
|
+
factions(): Promise<Array<{
|
|
43
|
+
factionId: string;
|
|
44
|
+
gold: number;
|
|
45
|
+
}>>;
|
|
46
|
+
/** Engine totals (flips, income paid). */
|
|
47
|
+
status(): Promise<Record<string, unknown>>;
|
|
48
|
+
/** STUDIO (admin) — create a faction. */
|
|
49
|
+
defineFaction(factionId: string, displayName?: string): 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
|
+
/** STUDIO (admin) — enroll a player in a faction. */
|
|
61
|
+
enroll(userId: string, factionId: string): Promise<{
|
|
62
|
+
__typename?: "GmContainer";
|
|
63
|
+
containerId: string;
|
|
64
|
+
appId: string;
|
|
65
|
+
sessionId: string | null;
|
|
66
|
+
typeName: string;
|
|
67
|
+
displayName: string;
|
|
68
|
+
description: string | null;
|
|
69
|
+
ownerUserId: string | null;
|
|
70
|
+
metadataJson: string;
|
|
71
|
+
}>;
|
|
72
|
+
/** STUDIO (admin) — create a control point. */
|
|
73
|
+
definePoint(input: {
|
|
74
|
+
pointId: string;
|
|
75
|
+
x: number;
|
|
76
|
+
z: number;
|
|
77
|
+
radius: number;
|
|
78
|
+
incomePerMin?: number;
|
|
79
|
+
siegeFromMin?: number;
|
|
80
|
+
siegeToMin?: number;
|
|
81
|
+
displayName?: string;
|
|
82
|
+
}): Promise<{
|
|
83
|
+
__typename?: "GmContainer";
|
|
84
|
+
containerId: string;
|
|
85
|
+
appId: string;
|
|
86
|
+
sessionId: string | null;
|
|
87
|
+
typeName: string;
|
|
88
|
+
displayName: string;
|
|
89
|
+
description: string | null;
|
|
90
|
+
ownerUserId: string | null;
|
|
91
|
+
metadataJson: string;
|
|
92
|
+
}>;
|
|
93
|
+
/** Parse a type-96 control-point server event (flips). */
|
|
94
|
+
parseControlPoint(bytes: Uint8Array): ControlPointEvent | null;
|
|
95
|
+
private invoke;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=territory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"territory.d.ts","sourceRoot":"","sources":["../../src/kit/territory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAA0B,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE3E,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAIrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGjB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EACjC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,cAAc,EACxC,OAAO,GAAE,mBAAwB;IAKnC,uEAAuE;IACvE,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC,mDAAmD;IAC7C,MAAM,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAiB1C,yCAAyC;IACnC,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAUrE,0CAA0C;IACpC,MAAM;IAIZ,yCAAyC;IACnC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;;;;;;;;;;;IAY3D,qDAAqD;IAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;IAY9C,+CAA+C;IACzC,WAAW,CAAC,KAAK,EAAE;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;;;;;;;;;;;IA0BD,0DAA0D;IAC1D,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI;YAIhD,MAAM;CAOrB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { parseControlPointEvent } from './wire.js';
|
|
2
|
+
/**
|
|
3
|
+
* Runtime helpers for the territory engine (Wave 3): control-point state,
|
|
4
|
+
* faction standings, and the admin CRUD for the map (factions, members,
|
|
5
|
+
* points). Capture itself is pure presence — stand inside a point's radius
|
|
6
|
+
* with your faction; the engine does the rest and announces flips as
|
|
7
|
+
* type-96 events.
|
|
8
|
+
*
|
|
9
|
+
* Obtained via `client.kit(appId).territory`.
|
|
10
|
+
*/
|
|
11
|
+
export class TerritoryKit {
|
|
12
|
+
constructor(appId, gameModel, engines, options = {}) {
|
|
13
|
+
this.appId = appId;
|
|
14
|
+
this.gameModel = gameModel;
|
|
15
|
+
this.engines = engines;
|
|
16
|
+
this.moduleName = options.moduleName ?? 'territory';
|
|
17
|
+
}
|
|
18
|
+
/** Is the territory engine deployed + enabled (cached per session)? */
|
|
19
|
+
engineAvailable() {
|
|
20
|
+
return this.engines.has(this.moduleName);
|
|
21
|
+
}
|
|
22
|
+
/** Every control point with live capture state. */
|
|
23
|
+
async points() {
|
|
24
|
+
const body = await this.invoke('points', {});
|
|
25
|
+
return Array.isArray(body.points)
|
|
26
|
+
? body.points.map((p) => ({
|
|
27
|
+
pointId: String(p.pointId ?? ''),
|
|
28
|
+
x: Number(p.x ?? 0),
|
|
29
|
+
z: Number(p.z ?? 0),
|
|
30
|
+
radius: Number(p.radius ?? 0),
|
|
31
|
+
owner: String(p.owner ?? ''),
|
|
32
|
+
challenger: String(p.challenger ?? ''),
|
|
33
|
+
progress: Number(p.progress ?? 0),
|
|
34
|
+
incomePerMin: Number(p.incomePerMin ?? 0),
|
|
35
|
+
siegeOpen: p.siegeOpen === true,
|
|
36
|
+
}))
|
|
37
|
+
: [];
|
|
38
|
+
}
|
|
39
|
+
/** Faction standings (id + treasury). */
|
|
40
|
+
async factions() {
|
|
41
|
+
const body = await this.invoke('factions', {});
|
|
42
|
+
return Array.isArray(body.factions)
|
|
43
|
+
? body.factions.map((f) => ({
|
|
44
|
+
factionId: String(f.factionId ?? ''),
|
|
45
|
+
gold: Number(f.gold ?? 0),
|
|
46
|
+
}))
|
|
47
|
+
: [];
|
|
48
|
+
}
|
|
49
|
+
/** Engine totals (flips, income paid). */
|
|
50
|
+
async status() {
|
|
51
|
+
return this.invoke('status', {});
|
|
52
|
+
}
|
|
53
|
+
/** STUDIO (admin) — create a faction. */
|
|
54
|
+
async defineFaction(factionId, displayName) {
|
|
55
|
+
return this.gameModel.createContainer({
|
|
56
|
+
appId: this.appId,
|
|
57
|
+
typeName: 'Faction',
|
|
58
|
+
displayName: displayName ?? `faction-${factionId}`,
|
|
59
|
+
properties: [
|
|
60
|
+
{ key: 'faction_id', valueType: 'string', valueJson: JSON.stringify(factionId) },
|
|
61
|
+
{ key: 'gold', valueType: 'int', valueJson: '0' },
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** STUDIO (admin) — enroll a player in a faction. */
|
|
66
|
+
async enroll(userId, factionId) {
|
|
67
|
+
return this.gameModel.createContainer({
|
|
68
|
+
appId: this.appId,
|
|
69
|
+
typeName: 'FactionMember',
|
|
70
|
+
displayName: `member-${userId}`,
|
|
71
|
+
properties: [
|
|
72
|
+
{ key: 'user_id', valueType: 'string', valueJson: JSON.stringify(userId) },
|
|
73
|
+
{ key: 'faction_id', valueType: 'string', valueJson: JSON.stringify(factionId) },
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** STUDIO (admin) — create a control point. */
|
|
78
|
+
async definePoint(input) {
|
|
79
|
+
return this.gameModel.createContainer({
|
|
80
|
+
appId: this.appId,
|
|
81
|
+
typeName: 'ControlPoint',
|
|
82
|
+
displayName: input.displayName ?? `cp-${input.pointId}`,
|
|
83
|
+
properties: [
|
|
84
|
+
{ key: 'point_id', valueType: 'string', valueJson: JSON.stringify(input.pointId) },
|
|
85
|
+
{ key: 'x', valueType: 'int', valueJson: String(Math.round(input.x)) },
|
|
86
|
+
{ key: 'z', valueType: 'int', valueJson: String(Math.round(input.z)) },
|
|
87
|
+
{ key: 'radius', valueType: 'int', valueJson: String(Math.round(input.radius)) },
|
|
88
|
+
{ key: 'owner_faction', valueType: 'string', valueJson: '""' },
|
|
89
|
+
{
|
|
90
|
+
key: 'income_per_min',
|
|
91
|
+
valueType: 'int',
|
|
92
|
+
valueJson: String(input.incomePerMin ?? 0),
|
|
93
|
+
},
|
|
94
|
+
...(input.siegeFromMin !== undefined
|
|
95
|
+
? [{ key: 'siege_from_min', valueType: 'int', valueJson: String(input.siegeFromMin) }]
|
|
96
|
+
: []),
|
|
97
|
+
...(input.siegeToMin !== undefined
|
|
98
|
+
? [{ key: 'siege_to_min', valueType: 'int', valueJson: String(input.siegeToMin) }]
|
|
99
|
+
: []),
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Parse a type-96 control-point server event (flips). */
|
|
104
|
+
parseControlPoint(bytes) {
|
|
105
|
+
return parseControlPointEvent(bytes);
|
|
106
|
+
}
|
|
107
|
+
async invoke(exportName, params) {
|
|
108
|
+
const result = await this.engines.invoke(this.moduleName, exportName, params);
|
|
109
|
+
if (!result.success) {
|
|
110
|
+
throw new Error(`territory.${exportName} failed: ${result.reason ?? 'unknown'}`);
|
|
111
|
+
}
|
|
112
|
+
return result.body;
|
|
113
|
+
}
|
|
114
|
+
}
|
package/dist/kit/wire.d.ts
CHANGED
|
@@ -66,6 +66,16 @@ export declare const EVENT_TURN = 91;
|
|
|
66
66
|
export declare const EVENT_SCORE = 92;
|
|
67
67
|
/** Match proposal (matchmaking → matches handoff). */
|
|
68
68
|
export declare const EVENT_PROPOSAL = 93;
|
|
69
|
+
/** Ability cast/impact (kit-play abilities). */
|
|
70
|
+
export declare const EVENT_ABILITY = 94;
|
|
71
|
+
/** Movement-envelope violation (movement-warden, observe/flag). */
|
|
72
|
+
export declare const EVENT_MOVEMENT_VIOLATION = 95;
|
|
73
|
+
/** Control-point state change (territory). */
|
|
74
|
+
export declare const EVENT_CONTROL_POINT = 96;
|
|
75
|
+
/** Race timing: checkpoint/lap/finish (kit-play timing). */
|
|
76
|
+
export declare const EVENT_RACE_TIMING = 97;
|
|
77
|
+
/** Zone change: shrinking circles, event areas (kit-sim zones). */
|
|
78
|
+
export declare const EVENT_ZONE_CHANGE = 98;
|
|
69
79
|
/** Split an engine server-event payload into its type + JSON body. */
|
|
70
80
|
export declare function parseEngineEvent(bytes: Uint8Array): {
|
|
71
81
|
eventType: number;
|
|
@@ -121,4 +131,45 @@ export interface ProposalEvent {
|
|
|
121
131
|
}
|
|
122
132
|
/** Parse a proposal event; null when the payload is another type. */
|
|
123
133
|
export declare function parseProposalEvent(bytes: Uint8Array): ProposalEvent | null;
|
|
134
|
+
/** A parsed type-94 ability cast/impact event. */
|
|
135
|
+
export interface AbilityEvent {
|
|
136
|
+
/** `'cast'` or `'impact'`. */
|
|
137
|
+
kind: string;
|
|
138
|
+
abilityId: string;
|
|
139
|
+
casterId: string;
|
|
140
|
+
victimId: string | null;
|
|
141
|
+
damage: number;
|
|
142
|
+
body: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
/** Parse an ability event; null when the payload is another type. */
|
|
145
|
+
export declare function parseAbilityEvent(bytes: Uint8Array): AbilityEvent | null;
|
|
146
|
+
/** A parsed type-95 movement-violation event (observe/flag posture). */
|
|
147
|
+
export interface MovementViolationEvent {
|
|
148
|
+
/** `'speed'`, `'teleport'`, or `'bounds'`. */
|
|
149
|
+
kind: string;
|
|
150
|
+
userId: string;
|
|
151
|
+
detail: string;
|
|
152
|
+
body: Record<string, unknown>;
|
|
153
|
+
}
|
|
154
|
+
/** Parse a movement-violation event; null when the payload is another type. */
|
|
155
|
+
export declare function parseMovementViolation(bytes: Uint8Array): MovementViolationEvent | null;
|
|
156
|
+
/** A parsed type-96 control-point state event (territory flips). */
|
|
157
|
+
export interface ControlPointEvent {
|
|
158
|
+
pointId: string;
|
|
159
|
+
owner: string;
|
|
160
|
+
previousOwner: string;
|
|
161
|
+
body: Record<string, unknown>;
|
|
162
|
+
}
|
|
163
|
+
/** Parse a control-point event; null when the payload is another type. */
|
|
164
|
+
export declare function parseControlPointEvent(bytes: Uint8Array): ControlPointEvent | null;
|
|
165
|
+
/** A parsed type-97 race-timing event (checkpoint/lap/finish). */
|
|
166
|
+
export interface RaceTimingEvent {
|
|
167
|
+
/** `'started'`, `'checkpoint'`, `'lap'`, or `'finished'`. */
|
|
168
|
+
kind: string;
|
|
169
|
+
courseId: string;
|
|
170
|
+
userId: string;
|
|
171
|
+
body: Record<string, unknown>;
|
|
172
|
+
}
|
|
173
|
+
/** Parse a race-timing event; null when the payload is another type. */
|
|
174
|
+
export declare function parseRaceTimingEvent(bytes: Uint8Array): RaceTimingEvent | null;
|
|
124
175
|
//# sourceMappingURL=wire.d.ts.map
|
package/dist/kit/wire.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/kit/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,4EAA4E;AAC5E,eAAO,MAAM,aAAa,IAAS,CAAC;AACpC,eAAO,MAAM,QAAQ,IAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,IAAS,CAAC;AAC/B,eAAO,MAAM,cAAc,IAAS,CAAC;AAErC,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAiBtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAqBrE;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,UAAU,CAAC,UAAU,CAiBlD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,CAM5E;AAMD,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,uEAAuE;AACvE,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,oDAAoD;AACpD,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,8CAA8C;AAC9C,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,sDAAsD;AACtD,eAAO,MAAM,cAAc,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/kit/wire.ts"],"names":[],"mappings":"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"}
|
package/dist/kit/wire.js
CHANGED
|
@@ -120,6 +120,16 @@ export const EVENT_TURN = 91;
|
|
|
120
120
|
export const EVENT_SCORE = 92;
|
|
121
121
|
/** Match proposal (matchmaking → matches handoff). */
|
|
122
122
|
export const EVENT_PROPOSAL = 93;
|
|
123
|
+
/** Ability cast/impact (kit-play abilities). */
|
|
124
|
+
export const EVENT_ABILITY = 94;
|
|
125
|
+
/** Movement-envelope violation (movement-warden, observe/flag). */
|
|
126
|
+
export const EVENT_MOVEMENT_VIOLATION = 95;
|
|
127
|
+
/** Control-point state change (territory). */
|
|
128
|
+
export const EVENT_CONTROL_POINT = 96;
|
|
129
|
+
/** Race timing: checkpoint/lap/finish (kit-play timing). */
|
|
130
|
+
export const EVENT_RACE_TIMING = 97;
|
|
131
|
+
/** Zone change: shrinking circles, event areas (kit-sim zones). */
|
|
132
|
+
export const EVENT_ZONE_CHANGE = 98;
|
|
123
133
|
/** Split an engine server-event payload into its type + JSON body. */
|
|
124
134
|
export function parseEngineEvent(bytes) {
|
|
125
135
|
if (bytes.length < 2)
|
|
@@ -204,3 +214,53 @@ export function parseProposalEvent(bytes) {
|
|
|
204
214
|
body: parsed.body,
|
|
205
215
|
};
|
|
206
216
|
}
|
|
217
|
+
/** Parse an ability event; null when the payload is another type. */
|
|
218
|
+
export function parseAbilityEvent(bytes) {
|
|
219
|
+
const parsed = parseEngineEvent(bytes);
|
|
220
|
+
if (!parsed || parsed.eventType !== EVENT_ABILITY)
|
|
221
|
+
return null;
|
|
222
|
+
return {
|
|
223
|
+
kind: String(parsed.body.kind ?? ''),
|
|
224
|
+
abilityId: String(parsed.body.abilityId ?? ''),
|
|
225
|
+
casterId: String(parsed.body.casterId ?? ''),
|
|
226
|
+
victimId: parsed.body.victimId != null ? String(parsed.body.victimId) : null,
|
|
227
|
+
damage: Number(parsed.body.damage ?? 0),
|
|
228
|
+
body: parsed.body,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/** Parse a movement-violation event; null when the payload is another type. */
|
|
232
|
+
export function parseMovementViolation(bytes) {
|
|
233
|
+
const parsed = parseEngineEvent(bytes);
|
|
234
|
+
if (!parsed || parsed.eventType !== EVENT_MOVEMENT_VIOLATION)
|
|
235
|
+
return null;
|
|
236
|
+
return {
|
|
237
|
+
kind: String(parsed.body.kind ?? ''),
|
|
238
|
+
userId: String(parsed.body.userId ?? ''),
|
|
239
|
+
detail: String(parsed.body.detail ?? ''),
|
|
240
|
+
body: parsed.body,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/** Parse a control-point event; null when the payload is another type. */
|
|
244
|
+
export function parseControlPointEvent(bytes) {
|
|
245
|
+
const parsed = parseEngineEvent(bytes);
|
|
246
|
+
if (!parsed || parsed.eventType !== EVENT_CONTROL_POINT)
|
|
247
|
+
return null;
|
|
248
|
+
return {
|
|
249
|
+
pointId: String(parsed.body.pointId ?? ''),
|
|
250
|
+
owner: String(parsed.body.owner ?? ''),
|
|
251
|
+
previousOwner: String(parsed.body.previousOwner ?? ''),
|
|
252
|
+
body: parsed.body,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/** Parse a race-timing event; null when the payload is another type. */
|
|
256
|
+
export function parseRaceTimingEvent(bytes) {
|
|
257
|
+
const parsed = parseEngineEvent(bytes);
|
|
258
|
+
if (!parsed || parsed.eventType !== EVENT_RACE_TIMING)
|
|
259
|
+
return null;
|
|
260
|
+
return {
|
|
261
|
+
kind: String(parsed.body.kind ?? ''),
|
|
262
|
+
courseId: String(parsed.body.courseId ?? ''),
|
|
263
|
+
userId: String(parsed.body.userId ?? ''),
|
|
264
|
+
body: parsed.body,
|
|
265
|
+
};
|
|
266
|
+
}
|