@vampgg/utils 1.0.0-beta.5 → 1.0.0-beta.6
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 +32 -1
- package/dist/async-queue.d.mts +3 -3
- package/dist/async-queue.mjs +6 -6
- package/dist/channel-core.d.mts +2 -2
- package/dist/channel-core.mjs +7 -7
- package/dist/context-logger.d.mts +7 -5
- package/dist/context-logger.mjs +3 -4
- package/dist/create-event-iterator.d.mts +1 -1
- package/dist/create-event-iterator.mjs +3 -3
- package/dist/extension-channel.d.mts +2 -2
- package/dist/extension-router.d.mts +1 -1
- package/dist/pino-logger.d.mts +10 -10
- package/dist/router-core.d.mts +1 -1
- package/dist/ws-channel.mjs +6 -6
- package/package.json +1 -1
- package/schema/behavior.bop +45 -0
- package/schema/pool.bop +1 -1
- package/schema/vec.bop +24 -0
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ vp run build # regenerate schema then build (runs: bebopc build && vp pa
|
|
|
19
19
|
|
|
20
20
|
## Schema (`schema/`)
|
|
21
21
|
|
|
22
|
-
Two bebop types define the wire format for all RPC in this framework.
|
|
22
|
+
Two bebop types define the wire format for all RPC in this framework. The remaining files are entity components that apps import into their own schema.
|
|
23
23
|
|
|
24
24
|
### `Error` (struct — `schema/error.bop`)
|
|
25
25
|
|
|
@@ -50,6 +50,37 @@ The universal envelope for all RPC calls. Fields are optional (bebop message typ
|
|
|
50
50
|
| `credential` | `string` | Serialized credential for storage |
|
|
51
51
|
| `headers` | `string` | HTTP-style metadata header string |
|
|
52
52
|
|
|
53
|
+
### Behavior trees (`schema/behavior.bop`)
|
|
54
|
+
|
|
55
|
+
Entity components for `@vampgg/ecs` behavior trees. Import the file from your
|
|
56
|
+
`entity.bop` (as `vamp init` does), then add `Brain` and `BehaviorTree` fields to
|
|
57
|
+
`Entity`. `vamp generate` merges both of their deltas as last-write-wins, never
|
|
58
|
+
as counters.
|
|
59
|
+
|
|
60
|
+
| Type | Fields | Role |
|
|
61
|
+
| ------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
62
|
+
| `BehaviorNodeKind` | `Selector`, `Sequence`, `Weighted`, `Condition`, `Task`, `Invert`, `Chance`, `Cooldown` | What a node does |
|
|
63
|
+
| `BehaviorNode` | `kind`, `children`, `weight`, `leaf`, `args` | One node; `children` are indices into `nodes` |
|
|
64
|
+
| `BehaviorTree` | `nodes` | A flat tree whose root is `nodes[0]`, stored on an entity |
|
|
65
|
+
| `Brain` | `tree`, `readyAt`, `last` | An agent's tree id, cooldown expiry ticks, and last task |
|
|
66
|
+
|
|
67
|
+
`weight` is a `Chance` percentage or a `Cooldown` length in ticks. A `Weighted`
|
|
68
|
+
node keeps its per-child weights in `args`. `leaf` is the app's `Condition` or
|
|
69
|
+
`Task` enum value.
|
|
70
|
+
|
|
71
|
+
### Vectors (`schema/vec.bop`)
|
|
72
|
+
|
|
73
|
+
Spatial entity components. Import the file from your `entity.bop` (as `vamp init`
|
|
74
|
+
does), then add `Vec2` or `Vec3` fields to `Entity`. `vamp generate` merges their
|
|
75
|
+
deltas as counters, so a delta of `{ x: 0.5 }` adds `0.5` to `x`.
|
|
76
|
+
|
|
77
|
+
| Type | Fields | Role |
|
|
78
|
+
| ----------- | ------------- | ------------------------------------ |
|
|
79
|
+
| `Vec2` | `x`, `y` | A 2D vector, such as a position |
|
|
80
|
+
| `Vec2Delta` | `x`, `y` | Signed `float32` offsets onto a Vec2 |
|
|
81
|
+
| `Vec3` | `x`, `y`, `z` | A 3D vector |
|
|
82
|
+
| `Vec3Delta` | `x`, `y`, `z` | Signed `float32` offsets onto a Vec3 |
|
|
83
|
+
|
|
53
84
|
---
|
|
54
85
|
|
|
55
86
|
## Subpath Exports
|
package/dist/async-queue.d.mts
CHANGED
|
@@ -52,12 +52,12 @@ declare class AsyncQueue<T> implements AsyncIterableIterator<T> {
|
|
|
52
52
|
/** Producer: terminate the stream as a clean completion. Idempotent. */
|
|
53
53
|
close(): void;
|
|
54
54
|
/** Producer: terminate the stream with an error thrown into the consumer. Idempotent. */
|
|
55
|
-
fail(
|
|
55
|
+
fail(cause: unknown): void;
|
|
56
56
|
next(): Promise<IteratorResult<T>>;
|
|
57
57
|
/** Consumer aborted early (break / return). Runs dispose and ends the stream. */
|
|
58
|
-
return(value?:
|
|
58
|
+
return(value?: undefined): Promise<IteratorResult<T>>;
|
|
59
59
|
/** Consumer threw. Propagate after dispose. */
|
|
60
|
-
throw(
|
|
60
|
+
throw(cause?: unknown): Promise<IteratorResult<T>>;
|
|
61
61
|
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
|
62
62
|
private enqueue;
|
|
63
63
|
private dequeue;
|
package/dist/async-queue.mjs
CHANGED
|
@@ -62,15 +62,15 @@ var AsyncQueue = class {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
/** Producer: terminate the stream with an error thrown into the consumer. Idempotent. */
|
|
65
|
-
fail(
|
|
65
|
+
fail(cause) {
|
|
66
66
|
if (this.closed || this.hasError) return;
|
|
67
67
|
this.hasError = true;
|
|
68
|
-
this.error =
|
|
68
|
+
this.error = cause;
|
|
69
69
|
if (this.pendingReject) {
|
|
70
70
|
const reject = this.pendingReject;
|
|
71
71
|
this.pendingResolve = null;
|
|
72
72
|
this.pendingReject = null;
|
|
73
|
-
reject(
|
|
73
|
+
reject(cause);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
async next() {
|
|
@@ -113,16 +113,16 @@ var AsyncQueue = class {
|
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
115
|
/** Consumer threw. Propagate after dispose. */
|
|
116
|
-
async throw(
|
|
116
|
+
async throw(cause) {
|
|
117
117
|
this.closed = true;
|
|
118
118
|
if (this.pendingReject) {
|
|
119
119
|
const reject = this.pendingReject;
|
|
120
120
|
this.pendingResolve = null;
|
|
121
121
|
this.pendingReject = null;
|
|
122
|
-
reject(
|
|
122
|
+
reject(cause);
|
|
123
123
|
}
|
|
124
124
|
await this.dispose();
|
|
125
|
-
throw
|
|
125
|
+
throw cause;
|
|
126
126
|
}
|
|
127
127
|
[Symbol.asyncIterator]() {
|
|
128
128
|
return this;
|
package/dist/channel-core.d.mts
CHANGED
|
@@ -28,7 +28,7 @@ declare abstract class CoreChannel extends BaseChannel {
|
|
|
28
28
|
* Reject callbacks for in-flight unary / client-stream calls, keyed by
|
|
29
29
|
* messageId. Lets close()/error() settle parked requests instead of hanging.
|
|
30
30
|
*/
|
|
31
|
-
readonly pending: Map<string, (
|
|
31
|
+
readonly pending: Map<string, (cause: unknown) => void>;
|
|
32
32
|
protected closed: boolean;
|
|
33
33
|
/** The reason a closed channel settles calls that arrive after teardown. */
|
|
34
34
|
protected closeReason?: TempoError;
|
|
@@ -39,7 +39,7 @@ declare abstract class CoreChannel extends BaseChannel {
|
|
|
39
39
|
* synchronous (worker / WebSocket) or asynchronous (extension runtime); the
|
|
40
40
|
* call sites await or attach rejection handlers as each RPC shape requires.
|
|
41
41
|
*/
|
|
42
|
-
protected abstract sendFrame(message: Message): void | Promise<
|
|
42
|
+
protected abstract sendFrame(message: Message): void | Promise<void>;
|
|
43
43
|
/** Generates the unique id for an outgoing request envelope. */
|
|
44
44
|
protected generateMessageId(): string;
|
|
45
45
|
/**
|
package/dist/channel-core.mjs
CHANGED
|
@@ -308,14 +308,14 @@ var CoreChannel = class extends BaseChannel {
|
|
|
308
308
|
* Runs the shared error-hook + error-mapping tail of every `start*` method.
|
|
309
309
|
* Always throws.
|
|
310
310
|
*/
|
|
311
|
-
async raiseCallError(context,
|
|
312
|
-
if (this.hooks !== void 0 &&
|
|
313
|
-
if (
|
|
314
|
-
if (
|
|
315
|
-
if (
|
|
316
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred",
|
|
311
|
+
async raiseCallError(context, cause) {
|
|
312
|
+
if (this.hooks !== void 0 && cause instanceof Error) await this.hooks.executeErrorHooks(context, cause);
|
|
313
|
+
if (cause instanceof TempoError) throw cause;
|
|
314
|
+
if (cause instanceof Error) {
|
|
315
|
+
if (cause.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", cause);
|
|
316
|
+
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", cause);
|
|
317
317
|
}
|
|
318
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data:
|
|
318
|
+
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: cause });
|
|
319
319
|
}
|
|
320
320
|
/**
|
|
321
321
|
* {@inheritDoc BaseChannel.startUnary}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ConsoleLogger, TempoLogLevel, TempoLogger } from "@tempojs/common";
|
|
2
2
|
|
|
3
3
|
//#region src/context-logger.d.ts
|
|
4
|
+
/** Structured log fields, as TempoLogger's own `write` contract defines them. */
|
|
5
|
+
type LogData = NonNullable<Parameters<TempoLogger["write"]>[2]>;
|
|
4
6
|
declare class ContextLogger extends ConsoleLogger {
|
|
5
|
-
bindings:
|
|
6
|
-
constructor(sourceName: string, logLevel?: TempoLogLevel, parent?: TempoLogger, bindings?:
|
|
7
|
-
correlate<TLogger extends TempoLogger>(sourceName: string, correlationId?: string, asOrphan?: boolean, bindings?:
|
|
8
|
-
clone<TLogger extends TempoLogger>(sourceName: string, asOrphan?: boolean, bindings?:
|
|
7
|
+
bindings: LogData;
|
|
8
|
+
constructor(sourceName: string, logLevel?: TempoLogLevel, parent?: TempoLogger, bindings?: LogData);
|
|
9
|
+
correlate<TLogger extends TempoLogger>(sourceName: string, correlationId?: string, asOrphan?: boolean, bindings?: LogData): TLogger;
|
|
10
|
+
clone<TLogger extends TempoLogger>(sourceName: string, asOrphan?: boolean, bindings?: LogData): TLogger;
|
|
9
11
|
}
|
|
10
12
|
//#endregion
|
|
11
|
-
export { ContextLogger };
|
|
13
|
+
export { ContextLogger, LogData };
|
package/dist/context-logger.mjs
CHANGED
|
@@ -8,10 +8,9 @@ var ContextLogger = class extends ConsoleLogger {
|
|
|
8
8
|
TempoLogger.instances.delete(sourceName.replace(/\s+/g, "_"));
|
|
9
9
|
}
|
|
10
10
|
correlate(sourceName, correlationId, asOrphan, bindings) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
11
|
+
const merged = correlationId ? { correlationId } : {};
|
|
12
|
+
Object.assign(merged, bindings);
|
|
13
|
+
return this.clone(sourceName, asOrphan, merged);
|
|
15
14
|
}
|
|
16
15
|
clone(sourceName, asOrphan, bindings) {
|
|
17
16
|
const logger = Reflect.construct(this.constructor, [
|
|
@@ -4,7 +4,7 @@ import { AsyncQueue, AsyncQueueOptions } from "./async-queue.mjs";
|
|
|
4
4
|
type Context<T> = {
|
|
5
5
|
emit: (value: T) => void; /** Terminate the stream as a clean completion. */
|
|
6
6
|
cancel: () => void; /** Terminate the stream with an error thrown into the consumer's `for await`. */
|
|
7
|
-
error: (
|
|
7
|
+
error: (cause: unknown) => void;
|
|
8
8
|
};
|
|
9
9
|
type CleanupFn = () => void | Promise<void>;
|
|
10
10
|
type Subscriber<T> = (context: Context<T>) => void | CleanupFn | Promise<CleanupFn | void>;
|
|
@@ -34,9 +34,9 @@ function queueIterator(queue, start) {
|
|
|
34
34
|
if (started !== void 0) await started.catch(() => {});
|
|
35
35
|
return queue.return(value);
|
|
36
36
|
},
|
|
37
|
-
async throw(
|
|
37
|
+
async throw(cause) {
|
|
38
38
|
if (started !== void 0) await started.catch(() => {});
|
|
39
|
-
return queue.throw(
|
|
39
|
+
return queue.throw(cause);
|
|
40
40
|
},
|
|
41
41
|
[Symbol.asyncIterator]() {
|
|
42
42
|
return this;
|
|
@@ -57,7 +57,7 @@ function createEventIterator(subscriber, options) {
|
|
|
57
57
|
queue.push(value);
|
|
58
58
|
},
|
|
59
59
|
cancel: () => queue.close(),
|
|
60
|
-
error: (
|
|
60
|
+
error: (cause) => queue.fail(cause)
|
|
61
61
|
};
|
|
62
62
|
return queueIterator(queue, async () => {
|
|
63
63
|
unsubscribe = await subscriber(context);
|
|
@@ -33,8 +33,8 @@ declare class TempoExtensionChannel extends CoreChannel {
|
|
|
33
33
|
/**
|
|
34
34
|
* {@inheritDoc CoreChannel.sendFrame}
|
|
35
35
|
*/
|
|
36
|
-
protected sendFrame(message: Message): Promise<
|
|
37
|
-
protected sendMessage(message: Message): Promise<
|
|
36
|
+
protected sendFrame(message: Message): Promise<void>;
|
|
37
|
+
protected sendMessage(message: Message): Promise<void>;
|
|
38
38
|
}
|
|
39
39
|
//#endregion
|
|
40
40
|
export { TempoExtensionChannel };
|
|
@@ -159,6 +159,6 @@ type ExtensionSenderContext = {
|
|
|
159
159
|
* ignored. Replies travel back through the router's send path, so this never
|
|
160
160
|
* uses `sendResponse`.
|
|
161
161
|
*/
|
|
162
|
-
declare function createExtensionListener(router: Pick<TempoExtensionRouter<ExtensionSenderContext>, "process">, registry: ServiceRegistry):
|
|
162
|
+
declare function createExtensionListener(router: Pick<TempoExtensionRouter<ExtensionSenderContext>, "process">, registry: ServiceRegistry): Runtime.OnMessageListenerNoResponse;
|
|
163
163
|
//#endregion
|
|
164
164
|
export { ExtensionBaseRouter, ExtensionSenderContext, TempoExtensionRouter, TempoExtensionRouterConfiguration, createExtensionListener };
|
package/dist/pino-logger.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContextLogger } from "./context-logger.mjs";
|
|
1
|
+
import { ContextLogger, LogData } from "./context-logger.mjs";
|
|
2
2
|
import { ConsoleLogger, TempoLogLevel, TempoLogger } from "@tempojs/common";
|
|
3
3
|
import pino from "pino";
|
|
4
4
|
|
|
@@ -8,34 +8,34 @@ declare class PinoLogger extends ContextLogger implements ConsoleLogger {
|
|
|
8
8
|
sourceName: string;
|
|
9
9
|
logLevel: TempoLogLevel;
|
|
10
10
|
children: Set<TempoLogger>;
|
|
11
|
-
constructor(sourceName: string, logLevel?: TempoLogLevel, parent?: pino.Logger, bindings?:
|
|
11
|
+
constructor(sourceName: string, logLevel?: TempoLogLevel, parent?: pino.Logger, bindings?: LogData);
|
|
12
12
|
static getPinoLevel(level: string): string;
|
|
13
|
-
clone<TLogger extends TempoLogger>(sourceName: string, asOrphan?: boolean, bindings?:
|
|
14
|
-
write(level: TempoLogLevel, message: string, data?:
|
|
13
|
+
clone<TLogger extends TempoLogger>(sourceName: string, asOrphan?: boolean, bindings?: LogData): TLogger;
|
|
14
|
+
write(level: TempoLogLevel, message: string, data?: LogData, error?: Error): void;
|
|
15
15
|
/**
|
|
16
16
|
* @inheritDoc
|
|
17
17
|
*/
|
|
18
|
-
trace(message: string, data?:
|
|
18
|
+
trace(message: string, data?: LogData, error?: Error): void;
|
|
19
19
|
/**
|
|
20
20
|
* @inheritDoc
|
|
21
21
|
*/
|
|
22
|
-
debug(message: string, data?:
|
|
22
|
+
debug(message: string, data?: LogData, error?: Error): void;
|
|
23
23
|
/**
|
|
24
24
|
* @inheritDoc
|
|
25
25
|
*/
|
|
26
|
-
info(message: string, data?:
|
|
26
|
+
info(message: string, data?: LogData, error?: Error): void;
|
|
27
27
|
/**
|
|
28
28
|
* @inheritDoc
|
|
29
29
|
*/
|
|
30
|
-
warn(message: string, data?:
|
|
30
|
+
warn(message: string, data?: LogData, error?: Error): void;
|
|
31
31
|
/**
|
|
32
32
|
* @inheritDoc
|
|
33
33
|
*/
|
|
34
|
-
error(message: string, data?:
|
|
34
|
+
error(message: string, data?: LogData, error?: Error): void;
|
|
35
35
|
/**
|
|
36
36
|
* @inheritDoc
|
|
37
37
|
*/
|
|
38
|
-
critical(message: string, data?:
|
|
38
|
+
critical(message: string, data?: LogData, error?: Error): void;
|
|
39
39
|
setLogLevel(level: TempoLogLevel): void;
|
|
40
40
|
private tempoPinoLevelMap;
|
|
41
41
|
formatErrorMessage(error: Error, indentLevel?: number): string;
|
package/dist/router-core.d.mts
CHANGED
|
@@ -115,7 +115,7 @@ declare class RouterCore {
|
|
|
115
115
|
* type, populate and deliver the response, and map any failure onto an error
|
|
116
116
|
* frame. Transport-specific behavior is injected via `transport`.
|
|
117
117
|
*/
|
|
118
|
-
processRequest(request: Message, response: Message, env:
|
|
118
|
+
processRequest<TEnv>(request: Message, response: Message, env: TEnv, transport: ProcessTransport, contentType?: BebopContentType): Promise<void>;
|
|
119
119
|
}
|
|
120
120
|
//#endregion
|
|
121
121
|
export { ProcessTransport, RouterCore, RouterCoreHost, StreamWriteOptions };
|
package/dist/ws-channel.mjs
CHANGED
|
@@ -79,7 +79,7 @@ var TempoWSChannel = class TempoWSChannel extends CoreChannel {
|
|
|
79
79
|
*/
|
|
80
80
|
static forAddress(address, options) {
|
|
81
81
|
if (!address) throw new Error("no address");
|
|
82
|
-
if (
|
|
82
|
+
if (!(address instanceof URL)) address = new URL(address);
|
|
83
83
|
options ??= {};
|
|
84
84
|
return new TempoWSChannel(address, options);
|
|
85
85
|
}
|
|
@@ -102,14 +102,15 @@ var TempoWSChannel = class TempoWSChannel extends CoreChannel {
|
|
|
102
102
|
this.close(new TempoError(TempoStatusCode.UNAVAILABLE, "channel transport error"));
|
|
103
103
|
});
|
|
104
104
|
this.ws.addEventListener(WebsocketEvent.message, (_ws, ev) => {
|
|
105
|
-
const
|
|
105
|
+
const data = ev.data;
|
|
106
|
+
const byteLength = data instanceof ArrayBuffer ? data.byteLength : data.length;
|
|
106
107
|
if (byteLength > this.maxReceiveMessageSize) {
|
|
107
108
|
this.logger.error(`inbound frame ${byteLength}B exceeds maxReceiveMessageSize ${this.maxReceiveMessageSize}B; dropping`);
|
|
108
109
|
return;
|
|
109
110
|
}
|
|
110
111
|
let message;
|
|
111
|
-
if (
|
|
112
|
-
else message = Message(
|
|
112
|
+
if (data instanceof ArrayBuffer) message = Message(Message.decode(new Uint8Array(data)));
|
|
113
|
+
else message = Message(JSON.parse(data));
|
|
113
114
|
const messageId = message.messageId;
|
|
114
115
|
this.events.emit(messageId, message);
|
|
115
116
|
this.logger.trace(`received new message ${messageId}`);
|
|
@@ -168,8 +169,7 @@ var TempoWSChannel = class TempoWSChannel extends CoreChannel {
|
|
|
168
169
|
};
|
|
169
170
|
const onError = (ws, event) => {
|
|
170
171
|
teardown();
|
|
171
|
-
const
|
|
172
|
-
const detail = typeof message === "string" ? message : event.type;
|
|
172
|
+
const detail = event.message ?? event.type;
|
|
173
173
|
reject(new TempoError(TempoStatusCode.UNAVAILABLE, `socket error before open: ${detail}`));
|
|
174
174
|
};
|
|
175
175
|
const onClose = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vampgg/utils",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "Tempo RPC, Bebop serialization, transports, and async primitives for @vampgg.",
|
|
5
5
|
"homepage": "https://github.com/sammccord/vamp/tree/main/packages/utils#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Behavior tree built-ins. A BehaviorTree lives on its own entity and is shared
|
|
2
|
+
// by every agent whose Brain points at it. Nodes form a flat array linked by
|
|
3
|
+
// index; nodes[0] is the root.
|
|
4
|
+
enum BehaviorNodeKind {
|
|
5
|
+
Selector = 1; // first child that succeeds
|
|
6
|
+
Sequence = 2; // every child succeeds, in order
|
|
7
|
+
Weighted = 3; // children that pass, picked by weight
|
|
8
|
+
Condition = 4; // leaf: registered predicate
|
|
9
|
+
Task = 5; // leaf: registered task, emits action intents
|
|
10
|
+
Invert = 6;
|
|
11
|
+
Chance = 7; // decorator: passes with probability weight/100
|
|
12
|
+
Cooldown = 8; // decorator: fails until `weight` ticks since its last success
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
message BehaviorNode {
|
|
16
|
+
1 -> BehaviorNodeKind kind;
|
|
17
|
+
2 -> uint16[] children; // indices into BehaviorTree.nodes
|
|
18
|
+
3 -> uint32 weight; // Chance percent or Cooldown ticks
|
|
19
|
+
4 -> uint32 leaf; // Condition / Task id (app enum)
|
|
20
|
+
5 -> float32[] args; // leaf arguments; per-child weights on Weighted
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
message BehaviorTree {
|
|
24
|
+
1 -> BehaviorNode[] nodes;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Behavior tree delta - last-write-wins replace of the whole node array
|
|
28
|
+
message BehaviorTreeDelta {
|
|
29
|
+
1 -> BehaviorNode[] nodes;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
message Brain {
|
|
33
|
+
1 -> string tree; // id of the entity that holds the BehaviorTree
|
|
34
|
+
2 -> uint32[] readyAt; // per-node cooldown expiry tick
|
|
35
|
+
3 -> uint16 last; // last task node that fired
|
|
36
|
+
4 -> uint32 fingerprint; // of the tree that readyAt and last index into
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Brain delta - every field is last-write-wins
|
|
40
|
+
message BrainDelta {
|
|
41
|
+
1 -> string tree;
|
|
42
|
+
2 -> uint32[] readyAt;
|
|
43
|
+
3 -> uint16 last;
|
|
44
|
+
4 -> uint32 fingerprint;
|
|
45
|
+
}
|
package/schema/pool.bop
CHANGED
package/schema/vec.bop
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Vector built-ins for positions, velocities, and other spatial components.
|
|
2
|
+
message Vec2 {
|
|
3
|
+
1 -> float32 x;
|
|
4
|
+
2 -> float32 y;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Vec2 delta - every field is a float CRDT counter (+/-)
|
|
8
|
+
message Vec2Delta {
|
|
9
|
+
1 -> float32 x;
|
|
10
|
+
2 -> float32 y;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
message Vec3 {
|
|
14
|
+
1 -> float32 x;
|
|
15
|
+
2 -> float32 y;
|
|
16
|
+
3 -> float32 z;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Vec3 delta - every field is a float CRDT counter (+/-)
|
|
20
|
+
message Vec3Delta {
|
|
21
|
+
1 -> float32 x;
|
|
22
|
+
2 -> float32 y;
|
|
23
|
+
3 -> float32 z;
|
|
24
|
+
}
|