@defold-typescript/transpiler 0.43.0 → 0.44.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/dist/hsm/async.ts +73 -0
- package/dist/hsm/debug.ts +119 -0
- package/dist/hsm/defold.ts +14 -8
- package/dist/hsm/index.ts +895 -173
- package/dist/hsm-builtin.d.ts +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -7
- package/dist/transpile.d.ts +2 -1
- package/package.json +3 -2
- package/src/hsm-builtin.ts +55 -4
- package/src/index.ts +1 -0
- package/src/transpile.ts +14 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { EventObject, TaskStart } from "./index";
|
|
2
|
+
|
|
3
|
+
/** @noSelf */
|
|
4
|
+
export interface SequenceSignal {
|
|
5
|
+
readonly aborted: boolean;
|
|
6
|
+
readonly wait: (seconds: number) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** @noSelf */
|
|
10
|
+
interface Signal {
|
|
11
|
+
aborted: boolean;
|
|
12
|
+
readonly wait: (seconds: number) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function forget(handles: number[], handle: number): void {
|
|
16
|
+
for (let i = 0; i < handles.length; i++) {
|
|
17
|
+
if (handles[i] === handle) {
|
|
18
|
+
handles[i] = handles[handles.length - 1] as number;
|
|
19
|
+
handles.pop();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A rejected sequence promise has no reader, so its error would vanish; a fresh
|
|
26
|
+
// timer callback raises it where the engine logs it.
|
|
27
|
+
function rethrow(reason: unknown): void {
|
|
28
|
+
timer.delay(0, false, () => {
|
|
29
|
+
throw reason;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The sequence plugs into the state's task, so its cleanup cancels the pending
|
|
34
|
+
// waits on every way out of the state; a wait left pending is never resolved.
|
|
35
|
+
export function sequence<Ctx, E extends EventObject>(
|
|
36
|
+
// biome-ignore lint/suspicious/noConfusingVoidType: `void` lets a sequence end without returning an event.
|
|
37
|
+
run: (ctx: Ctx, signal: SequenceSignal) => Promise<NoInfer<E> | void>,
|
|
38
|
+
): TaskStart<Ctx, E> {
|
|
39
|
+
return (ctx, finish) => {
|
|
40
|
+
const handles: number[] = [];
|
|
41
|
+
const signal: Signal = {
|
|
42
|
+
aborted: false,
|
|
43
|
+
wait: (seconds) =>
|
|
44
|
+
new Promise<void>((resolve) => {
|
|
45
|
+
if (signal.aborted) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
handles.push(
|
|
49
|
+
timer.delay(seconds, false, (_self, handle) => {
|
|
50
|
+
forget(handles, handle);
|
|
51
|
+
if (!signal.aborted) {
|
|
52
|
+
resolve();
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
run(ctx, signal).then(
|
|
59
|
+
(event) => {
|
|
60
|
+
if (event !== undefined) {
|
|
61
|
+
finish(event);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
(reason) => rethrow(reason),
|
|
65
|
+
);
|
|
66
|
+
return () => {
|
|
67
|
+
signal.aborted = true;
|
|
68
|
+
while (handles.length > 0) {
|
|
69
|
+
timer.cancel(handles.pop() as number);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { EventObject, MachineInstance } from "./index";
|
|
2
|
+
|
|
3
|
+
/** @noSelf */
|
|
4
|
+
export interface MachineInspector {
|
|
5
|
+
readonly draw: (target: Hash | Url | string) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SILENT: MachineInspector = {
|
|
9
|
+
draw: () => {},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const TEXT_OFFSET_Y = 40;
|
|
13
|
+
|
|
14
|
+
const labelUses: Record<string, number> = {};
|
|
15
|
+
const issuedLabels: Record<string, boolean> = {};
|
|
16
|
+
|
|
17
|
+
function uniqueLabel(label: string): string {
|
|
18
|
+
let ordinal = labelUses[label] ?? 1;
|
|
19
|
+
let issued = label;
|
|
20
|
+
while (issuedLabels[issued] === true) {
|
|
21
|
+
ordinal++;
|
|
22
|
+
issued = `${label}#${ordinal}`;
|
|
23
|
+
}
|
|
24
|
+
labelUses[label] = ordinal;
|
|
25
|
+
issuedLabels[issued] = true;
|
|
26
|
+
return issued;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const FIELD_ESCAPES: Record<string, string> = {
|
|
30
|
+
"\\": "\\\\",
|
|
31
|
+
"[": "\\[",
|
|
32
|
+
"]": "\\]",
|
|
33
|
+
"(": "\\(",
|
|
34
|
+
")": "\\)",
|
|
35
|
+
",": "\\,",
|
|
36
|
+
">": "\\>",
|
|
37
|
+
":": "\\:",
|
|
38
|
+
"\n": "\\n",
|
|
39
|
+
"\r": "\\r",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// A structural cast keeps each read a plain method call: native in JavaScript, string.sub in Lua.
|
|
43
|
+
interface StringChars {
|
|
44
|
+
charAt(index: number): string;
|
|
45
|
+
sub(first: number, last: number): string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// JavaScript strings hold UTF-16 code units; Lua strings hold UTF-8 bytes. Every escaped
|
|
49
|
+
// character is ASCII, and ASCII never occurs inside a UTF-8 sequence, so both walks agree.
|
|
50
|
+
const UTF16_UNITS = "\u{E000}".length === 1;
|
|
51
|
+
|
|
52
|
+
function charAt(text: string, index: number): string {
|
|
53
|
+
const chars = text as unknown as StringChars;
|
|
54
|
+
return UTF16_UNITS ? chars.charAt(index) : chars.sub(index + 1, index + 1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function escapeField(text: string): string {
|
|
58
|
+
let escaped = "";
|
|
59
|
+
for (let i = 0; i < text.length; i++) {
|
|
60
|
+
const char = charAt(text, i);
|
|
61
|
+
escaped = `${escaped}${FIELD_ESCAPES[char] ?? char}`;
|
|
62
|
+
}
|
|
63
|
+
return escaped;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function raw(text: string): string {
|
|
67
|
+
return text;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function shown(path: string | undefined): string {
|
|
71
|
+
return path === undefined ? "(stopped)" : escapeField(path);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function shownList(leaves: readonly string[], show: (leaf: string) => string): string {
|
|
75
|
+
if (leaves.length === 0) {
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
let text = show(leaves[0] as string);
|
|
79
|
+
for (let i = 1; i < leaves.length; i++) {
|
|
80
|
+
text = `${text}, ${show(leaves[i] as string)}`;
|
|
81
|
+
}
|
|
82
|
+
return text;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function shownLeaves(leaves: readonly string[]): string {
|
|
86
|
+
return leaves.length === 0 ? "(stopped)" : shownList(leaves, raw);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function inspect<Ctx, E extends EventObject, P extends string>(
|
|
90
|
+
instance: MachineInstance<Ctx, E, P>,
|
|
91
|
+
label: string,
|
|
92
|
+
): MachineInspector {
|
|
93
|
+
if (!sys.get_engine_info().is_debug) {
|
|
94
|
+
return SILENT;
|
|
95
|
+
}
|
|
96
|
+
const shownLabel = uniqueLabel(label);
|
|
97
|
+
const loggedLabel = escapeField(shownLabel);
|
|
98
|
+
let frame = 0;
|
|
99
|
+
instance.onMove((from, to, cause, event) => {
|
|
100
|
+
const reason = event === undefined ? cause : event.type;
|
|
101
|
+
print(
|
|
102
|
+
`hsm ${loggedLabel} frame ${frame}: ${escapeField(from)} -> ${shown(to)} (${escapeField(reason)}) [${shownList(instance.leaves, escapeField)}]`,
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
print(
|
|
106
|
+
`hsm ${loggedLabel} frame ${frame}: inspecting [${shownList(instance.leaves, escapeField)}]`,
|
|
107
|
+
);
|
|
108
|
+
return {
|
|
109
|
+
draw: (target) => {
|
|
110
|
+
frame++;
|
|
111
|
+
const p = go.get_world_position(target);
|
|
112
|
+
msg.post("@render:", "draw_debug_text", {
|
|
113
|
+
text: `${shownLabel} ${shownLeaves(instance.leaves)}`,
|
|
114
|
+
position: vmath.vector3(p.x, p.y + TEXT_OFFSET_Y, p.z),
|
|
115
|
+
color: vmath.vector4(1, 1, 1, 1),
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
package/dist/hsm/defold.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type MessageEvent<K extends MessageId> = K extends MessageId
|
|
2
|
-
? { readonly type: K } & Omit<MessagePayload<K>, "type">
|
|
2
|
+
? { readonly type: K; readonly sender: Url } & Omit<MessagePayload<K>, "type" | "sender">
|
|
3
3
|
: never;
|
|
4
4
|
|
|
5
5
|
/** @noSelf */
|
|
@@ -7,6 +7,7 @@ export interface MessageEventMapper<K extends MessageId> {
|
|
|
7
7
|
readonly toEvent: (
|
|
8
8
|
message_id: Hash,
|
|
9
9
|
message: Record<string | number, unknown>,
|
|
10
|
+
sender: Url,
|
|
10
11
|
) => MessageEvent<K> | undefined;
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -16,18 +17,23 @@ export function messageEvents<const K extends MessageId>(ids: readonly K[]): Mes
|
|
|
16
17
|
hashes[i] = hash(ids[i] as K);
|
|
17
18
|
}
|
|
18
19
|
return {
|
|
19
|
-
toEvent: (message_id, message) => {
|
|
20
|
+
toEvent: (message_id, message, sender) => {
|
|
20
21
|
for (let i = 0; i < hashes.length; i++) {
|
|
21
22
|
if (hashes[i] === message_id) {
|
|
22
|
-
|
|
23
|
-
for (const key in message) {
|
|
24
|
-
event[key] = message[key];
|
|
25
|
-
}
|
|
26
|
-
event.type = ids[i];
|
|
27
|
-
return event as unknown as MessageEvent<K>;
|
|
23
|
+
return copyEvent(ids[i] as K, message, sender) as MessageEvent<K>;
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
26
|
return undefined;
|
|
31
27
|
},
|
|
32
28
|
};
|
|
33
29
|
}
|
|
30
|
+
|
|
31
|
+
function copyEvent(type: string, message: Record<string | number, unknown>, sender: Url): unknown {
|
|
32
|
+
const event: { [key: string]: unknown } = {};
|
|
33
|
+
for (const key in message) {
|
|
34
|
+
event[key] = message[key];
|
|
35
|
+
}
|
|
36
|
+
event.type = type;
|
|
37
|
+
event.sender = sender;
|
|
38
|
+
return event;
|
|
39
|
+
}
|