chore: initial commit

This commit is contained in:
2026-10-22 17:07:08 +08:00
commit 0acef64f48
200 changed files with 35546 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
interface CommandNode {
flags?: {command_node_type?: number};
children?: number[];
redirectNode?: number;
extraNodeData?: {name?: string; parser?: string; properties?: {type?: number | string}};
}
interface CommandTree {nodes: CommandNode[]; rootIndex: number}
const trees = new WeakMap<object, CommandTree>();
export function remember_command_tree(instance: object, packet: unknown): void {
const tree = packet as CommandTree;
if (tree && Array.isArray(tree.nodes) && Number.isSafeInteger(tree.rootIndex)
&& tree.nodes.length <= 100_000 && tree.nodes[tree.rootIndex]) trees.set(instance, tree);
}
export function local_command_completion(instance: object, input: string, players: string[]): {matches: string[]; start: number; length: number} {
const start = input.search(/\S*$/);
const offset = start === 0 && input.startsWith('/') ? 1 : start;
const result = {matches: [] as string[], start: offset, length: input.length - offset};
const tree = trees.get(instance);
if (!tree || !input.startsWith('/')) return result;
// Walk complete arguments; only the final (possibly empty) token is being completed.
const tokens = input.slice(1, start || 1).trim().split(/\s+/).filter(Boolean);
let parents = [tree.rootIndex];
const children = (index: number): number[] => {
const node = tree.nodes[index];
const redirect = node?.redirectNode;
return Number.isSafeInteger(redirect) ? tree.nodes[redirect!]?.children ?? [] : node?.children ?? [];
};
for (const token of tokens) {
const next: number[] = [];
for (const parent of parents) {
for (const index of children(parent)) {
const node = tree.nodes[index];
if (node?.flags?.command_node_type === 1 && node.extraNodeData?.name === token) next.push(index);
else if (node?.flags?.command_node_type === 2) {
const parser = node.extraNodeData?.parser;
const kind = node.extraNodeData?.properties?.type;
// Quoted and greedy strings need Brigadier parsing; leave their suggestions to the server.
if (token.includes('"') || parser === 'minecraft:message' || parser === 'brigadier:string' && (kind === 2 || kind === 'greedy_phrase')) continue;
next.push(index);
}
}
}
parents = [...new Set(next)].slice(0, 256);
}
const prefix = input.slice(offset).toLowerCase();
for (const parent of parents) {
for (const index of children(parent)) {
const node = tree.nodes[index];
const data = node?.extraNodeData;
const suggestions = node?.flags?.command_node_type === 1 ? [data?.name]
: data?.parser === 'brigadier:bool' ? ['true', 'false']
: data?.parser === 'minecraft:entity' || data?.parser === 'minecraft:game_profile' ? players : [];
for (const match of suggestions) {
if (typeof match === 'string' && match.length && match.length <= 256
&& match.toLowerCase().startsWith(prefix) && !result.matches.includes(match)) result.matches.push(match);
if (result.matches.length >= 100) return result;
}
}
}
return result;
}