Skip to content

Commit fee90a1

Browse files
committed
feat: command hint
1 parent 2e2002e commit fee90a1

9 files changed

Lines changed: 403 additions & 263 deletions

File tree

electron/main.js

Lines changed: 263 additions & 256 deletions
Large diffs are not rendered by default.

electron/parse.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export function parseRules(wasmBytes, rules) {
4848
) {
4949
if (rule.parsedName) {
5050
if (matches[rule.parsedName] !== undefined) {
51+
if (rule.first) {
52+
continue;
53+
}
5154
throw new Error(
5255
`Rule "${rule.parsedName}" is matching multiple functions: [${matches[rule.parsedName]}, ${funcs[i].name.value}]`
5356
);

electron/rules.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,26 @@
1313
"u32.load8_u",
1414
"i32.eqz"
1515
]
16+
},
17+
{
18+
"parsedName": "commandHint",
19+
"body": [
20+
"get_global",
21+
"i32.const",
22+
"i32.sub",
23+
"tee_local",
24+
"set_global",
25+
"get_local",
26+
"u32.load",
27+
"set_local",
28+
"block",
29+
"get_local",
30+
"i32.const",
31+
"i32.add",
32+
"set_global",
33+
"get_local",
34+
"end"
35+
],
36+
"first": true
1637
}
1738
]

src/commands/builtin/ahelp.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {dispatcher} from "@/commands/dispatcher.ts";
1+
import {commandManager, dispatcher} from "@/commands/dispatcher.ts";
22
import {literal} from "@jsprismarine/brigadier";
33
import {addChatMessage} from "@/gameChat/gameChatUtility.ts";
44

@@ -23,4 +23,6 @@ dispatcher.register(
2323
]);
2424
}
2525
)
26-
)
26+
);
27+
28+
commandManager.registerCommand('ahelp', 'Show help message for AyuScript commands');

src/commands/dispatcher.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,90 @@
11
import {CommandDispatcher} from "@jsprismarine/brigadier";
2+
import {writeString} from "@/memory/utils.ts";
3+
import {canvasTextWidth} from "@/gameChat/gameChatUtility.ts";
4+
5+
class CommandManager {
6+
private commands: Map<string, {
7+
name: string,
8+
description: string,
9+
ptr: number
10+
}>;
11+
constructor() {
12+
this.commands = new Map();
13+
}
14+
registerCommand(name: string, description: string) {
15+
if (window.electron) {
16+
(async () => {
17+
// return;
18+
while (!window.Module?.HEAPU8) {
19+
await new Promise(r => setTimeout(r, 50));
20+
}
21+
22+
const ptr = window.Module._malloc(68);
23+
const desPtr = window.Module._malloc(name.length + description.length + 2);
24+
const desPtr2 = window.Module._malloc(name.length + description.length + 5);
25+
26+
window.Module.HEAPU32[ptr / 4] = desPtr;
27+
window.Module.HEAPU32[(ptr + 4) / 4] = 4;
28+
window.Module.HEAPU32[(ptr + 8) / 4] = 0;
29+
window.Module.HEAPF32[(ptr + 12) / 4] = canvasTextWidth(`/${name} - ${description}`);
30+
window.Module.HEAPU32[(ptr + 16) / 4] = 1;
31+
window.Module.HEAPU32[(ptr + 20) / 4] = 0;
32+
window.Module.HEAPU32[(ptr + 24) / 4] = 0;
33+
window.Module.HEAPU32[(ptr + 28) / 4] = 0;
34+
window.Module.HEAPU32[(ptr + 32) / 4] = desPtr2;
35+
window.Module.HEAPU32[(ptr + 36) / 4] = name.length + description.length + 5;
36+
window.Module.HEAPU32[(ptr + 40) / 4] = 2147483728;
37+
window.Module.HEAPU32[(ptr + 44) / 4] = 0x00f3c4a3; // color
38+
window.Module.HEAPU32[(ptr + 48) / 4] = 0;
39+
window.Module.HEAPU32[(ptr + 52) / 4] = 1039516303;
40+
41+
window.Module.HEAPF32[(ptr + 56) / 4] = 14.00;
42+
43+
window.Module.HEAPU32[(ptr + 60) / 4] = 264;
44+
window.Module.HEAPU32[(ptr + 64) / 4] = 0;
45+
writeString(name, desPtr);
46+
writeString(description, desPtr + name.length + 1);
47+
writeString(`/${name} - ${description}`, desPtr2);
48+
49+
this.commands.set(name, { name, description, ptr });
50+
})()
51+
} else {
52+
this.commands.set(name, {ptr: 0, name, description});
53+
}
54+
}
55+
getAllCommands() {
56+
return this.commands;
57+
}
58+
}
59+
60+
export const commandManager = new CommandManager();
261

362
export const dispatcher = new CommandDispatcher();
463

64+
65+
if (window.electron) {
66+
(async () => {
67+
while (!window.Module?.HEAPU8) {
68+
await new Promise(r => setTimeout(r, 50));
69+
}
70+
71+
let firstPtr = 0;
72+
window.Module.asm.registerHookPre(440);
73+
74+
window.ayuHooks.pre_79 = (arg0: number, arg1: number) => {
75+
if (!firstPtr) {
76+
firstPtr = arg1;
77+
}
78+
if (arg1 == firstPtr) {
79+
for (const [, { ptr }] of commandManager.getAllCommands()) {
80+
window.Module.asm[79]?.(arg0, ptr);
81+
}
82+
}
83+
};
84+
})();
85+
}
86+
87+
588
document.addEventListener("keydown", async (e) => {
689
if (e.key !== "Enter") {
790
return;

src/florr.d.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ interface Cp6 {
66
simulateContextLoss: () => void;
77
}
88

9-
type ModuleType = {
9+
type ModuleType = ({
1010
HEAP8: Int8Array;
1111
HEAP16: Int16Array;
1212
HEAP32: Int32Array;
@@ -15,7 +15,6 @@ type ModuleType = {
1515
HEAPU32: Uint32Array;
1616
HEAPF32: Float32Array;
1717
HEAPF64: Float64Array;
18-
_malloc: (number) => number;
1918
} | {
2019
HEAP8: undefined;
2120
HEAP16: undefined;
@@ -25,6 +24,16 @@ type ModuleType = {
2524
HEAPU32: undefined;
2625
HEAPF32: undefined;
2726
HEAPF64: undefined;
27+
}) & {
28+
_malloc: (number) => number;
29+
asm: {
30+
registerHookPre: (number) => void,
31+
registerHookPost: (number) => void,
32+
unregisterHookPre: (number) => void,
33+
unregisterHookPost: (number) => void,
34+
[key: string]: Function,
35+
[key: number]: Function,
36+
}
2837
}
2938

3039
interface Florrio {

src/gameChat/gameChatUtility.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function utf8Length(str: string) {
1919
return utf8.length;
2020
}
2121

22-
function canvasTextWidth(text: string) {
22+
export function canvasTextWidth(text: string) {
2323
const canvas = document.createElement('canvas');
2424
const ctx = canvas.getContext('2d');
2525
ctx!.font = '14px Game';

src/index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ export interface AyuScriptAPI {
77
}
88
}
99

10+
export interface AyuHooks {
11+
[key: string]: Function;
12+
}
13+
1014
declare global {
1115
export interface Window {
1216
ayuScriptApi: AyuScriptAPI;
17+
ayuHooks: AyuHooks;
1318

1419
electron: true | undefined;
1520

src/memory/utils.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,23 @@ export function getString(ptr: number) {
1111
return decoder.decode(stringBytes);
1212
}
1313

14+
export function writeString(str: string, offset: number) {
15+
if (!window.Module.HEAPU8) {
16+
return;
17+
}
18+
const encoder = new TextEncoder();
19+
const strBytes = encoder.encode(str);
20+
window.Module.HEAPU8.set(strBytes, offset);
21+
window.Module.HEAPU8[offset + strBytes.length] = 0;
22+
}
23+
1424
export function readLeb128(data: Uint8Array, idx: number): { value: number; nextIndex: number } {
1525
let result = 0, shift = 0, byte;
1626
do {
1727
byte = data[idx++];
18-
result |= (byte & 0x7F) << shift;
28+
result |= (byte! & 0x7F) << shift;
1929
shift += 7;
20-
} while (byte & 0x80);
30+
} while (byte! & 0x80);
2131
return { value: result, nextIndex: idx };
2232
}
2333

0 commit comments

Comments
 (0)