redscript-mc 1.0.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/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
- package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
- package/.github/workflows/ci.yml +29 -0
- package/.github/workflows/publish-extension.yml +35 -0
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/README.zh.md +261 -0
- package/dist/__tests__/cli.test.d.ts +1 -0
- package/dist/__tests__/cli.test.js +140 -0
- package/dist/__tests__/codegen.test.d.ts +1 -0
- package/dist/__tests__/codegen.test.js +121 -0
- package/dist/__tests__/diagnostics.test.d.ts +4 -0
- package/dist/__tests__/diagnostics.test.js +149 -0
- package/dist/__tests__/e2e.test.d.ts +6 -0
- package/dist/__tests__/e2e.test.js +1528 -0
- package/dist/__tests__/lexer.test.d.ts +1 -0
- package/dist/__tests__/lexer.test.js +316 -0
- package/dist/__tests__/lowering.test.d.ts +1 -0
- package/dist/__tests__/lowering.test.js +819 -0
- package/dist/__tests__/mc-integration.test.d.ts +12 -0
- package/dist/__tests__/mc-integration.test.js +395 -0
- package/dist/__tests__/mc-syntax.test.d.ts +1 -0
- package/dist/__tests__/mc-syntax.test.js +112 -0
- package/dist/__tests__/nbt.test.d.ts +1 -0
- package/dist/__tests__/nbt.test.js +82 -0
- package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
- package/dist/__tests__/optimizer-advanced.test.js +124 -0
- package/dist/__tests__/optimizer.test.d.ts +1 -0
- package/dist/__tests__/optimizer.test.js +118 -0
- package/dist/__tests__/parser.test.d.ts +1 -0
- package/dist/__tests__/parser.test.js +717 -0
- package/dist/__tests__/repl.test.d.ts +1 -0
- package/dist/__tests__/repl.test.js +27 -0
- package/dist/__tests__/runtime.test.d.ts +1 -0
- package/dist/__tests__/runtime.test.js +276 -0
- package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
- package/dist/__tests__/structure-optimizer.test.js +33 -0
- package/dist/__tests__/typechecker.test.d.ts +1 -0
- package/dist/__tests__/typechecker.test.js +364 -0
- package/dist/ast/types.d.ts +357 -0
- package/dist/ast/types.js +9 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +407 -0
- package/dist/codegen/cmdblock/index.d.ts +26 -0
- package/dist/codegen/cmdblock/index.js +45 -0
- package/dist/codegen/mcfunction/index.d.ts +34 -0
- package/dist/codegen/mcfunction/index.js +413 -0
- package/dist/codegen/structure/index.d.ts +18 -0
- package/dist/codegen/structure/index.js +249 -0
- package/dist/compile.d.ts +30 -0
- package/dist/compile.js +152 -0
- package/dist/data/arena/function/__load.mcfunction +6 -0
- package/dist/data/arena/function/__tick.mcfunction +2 -0
- package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
- package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
- package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
- package/dist/data/arena/function/arena_tick.mcfunction +11 -0
- package/dist/data/counter/function/__load.mcfunction +5 -0
- package/dist/data/counter/function/__tick.mcfunction +2 -0
- package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
- package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
- package/dist/data/counter/function/counter_tick.mcfunction +11 -0
- package/dist/data/minecraft/tags/function/load.json +5 -0
- package/dist/data/minecraft/tags/function/tick.json +5 -0
- package/dist/data/quiz/function/__load.mcfunction +16 -0
- package/dist/data/quiz/function/__tick.mcfunction +6 -0
- package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/answer_a.mcfunction +4 -0
- package/dist/data/quiz/function/answer_b.mcfunction +4 -0
- package/dist/data/quiz/function/answer_c.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
- package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question.mcfunction +7 -0
- package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
- package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
- package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
- package/dist/data/shop/function/__load.mcfunction +7 -0
- package/dist/data/shop/function/__tick.mcfunction +3 -0
- package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
- package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
- package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
- package/dist/data/turret/function/__load.mcfunction +5 -0
- package/dist/data/turret/function/__tick.mcfunction +4 -0
- package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
- package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
- package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
- package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
- package/dist/data/turret/function/turret_tick.mcfunction +5 -0
- package/dist/diagnostics/index.d.ts +44 -0
- package/dist/diagnostics/index.js +140 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +126 -0
- package/dist/ir/builder.d.ts +32 -0
- package/dist/ir/builder.js +99 -0
- package/dist/ir/types.d.ts +117 -0
- package/dist/ir/types.js +15 -0
- package/dist/lexer/index.d.ts +36 -0
- package/dist/lexer/index.js +458 -0
- package/dist/lowering/index.d.ts +106 -0
- package/dist/lowering/index.js +2041 -0
- package/dist/mc-test/client.d.ts +128 -0
- package/dist/mc-test/client.js +174 -0
- package/dist/mc-test/runner.d.ts +28 -0
- package/dist/mc-test/runner.js +150 -0
- package/dist/mc-test/setup.d.ts +11 -0
- package/dist/mc-test/setup.js +98 -0
- package/dist/mc-validator/index.d.ts +17 -0
- package/dist/mc-validator/index.js +322 -0
- package/dist/nbt/index.d.ts +86 -0
- package/dist/nbt/index.js +250 -0
- package/dist/optimizer/commands.d.ts +36 -0
- package/dist/optimizer/commands.js +349 -0
- package/dist/optimizer/passes.d.ts +34 -0
- package/dist/optimizer/passes.js +227 -0
- package/dist/optimizer/structure.d.ts +8 -0
- package/dist/optimizer/structure.js +344 -0
- package/dist/pack.mcmeta +6 -0
- package/dist/parser/index.d.ts +76 -0
- package/dist/parser/index.js +1193 -0
- package/dist/repl.d.ts +16 -0
- package/dist/repl.js +165 -0
- package/dist/runtime/index.d.ts +101 -0
- package/dist/runtime/index.js +1288 -0
- package/dist/typechecker/index.d.ts +42 -0
- package/dist/typechecker/index.js +629 -0
- package/docs/COMPILATION_STATS.md +142 -0
- package/docs/IMPLEMENTATION_GUIDE.md +512 -0
- package/docs/LANGUAGE_REFERENCE.md +415 -0
- package/docs/MC_MAPPING.md +280 -0
- package/docs/STRUCTURE_TARGET.md +80 -0
- package/docs/mc-reference/commands.md +259 -0
- package/editors/vscode/.vscodeignore +10 -0
- package/editors/vscode/LICENSE +21 -0
- package/editors/vscode/README.md +78 -0
- package/editors/vscode/build.mjs +28 -0
- package/editors/vscode/icon.png +0 -0
- package/editors/vscode/mcfunction-language-configuration.json +28 -0
- package/editors/vscode/out/extension.js +7236 -0
- package/editors/vscode/package-lock.json +566 -0
- package/editors/vscode/package.json +137 -0
- package/editors/vscode/redscript-language-configuration.json +28 -0
- package/editors/vscode/snippets/redscript.json +114 -0
- package/editors/vscode/src/codeactions.ts +89 -0
- package/editors/vscode/src/completion.ts +130 -0
- package/editors/vscode/src/extension.ts +239 -0
- package/editors/vscode/src/hover.ts +1120 -0
- package/editors/vscode/src/symbols.ts +207 -0
- package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
- package/editors/vscode/tsconfig.json +13 -0
- package/jest.config.js +5 -0
- package/package.json +38 -0
- package/src/__tests__/cli.test.ts +130 -0
- package/src/__tests__/codegen.test.ts +128 -0
- package/src/__tests__/diagnostics.test.ts +195 -0
- package/src/__tests__/e2e.test.ts +1721 -0
- package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
- package/src/__tests__/formatter.test.ts +46 -0
- package/src/__tests__/lexer.test.ts +356 -0
- package/src/__tests__/lowering.test.ts +962 -0
- package/src/__tests__/mc-integration.test.ts +409 -0
- package/src/__tests__/mc-syntax.test.ts +96 -0
- package/src/__tests__/nbt.test.ts +58 -0
- package/src/__tests__/optimizer-advanced.test.ts +144 -0
- package/src/__tests__/optimizer.test.ts +129 -0
- package/src/__tests__/parser.test.ts +800 -0
- package/src/__tests__/repl.test.ts +33 -0
- package/src/__tests__/runtime.test.ts +289 -0
- package/src/__tests__/structure-optimizer.test.ts +38 -0
- package/src/__tests__/typechecker.test.ts +395 -0
- package/src/ast/types.ts +248 -0
- package/src/cli.ts +445 -0
- package/src/codegen/cmdblock/index.ts +63 -0
- package/src/codegen/mcfunction/index.ts +471 -0
- package/src/codegen/structure/index.ts +305 -0
- package/src/compile.ts +188 -0
- package/src/diagnostics/index.ts +186 -0
- package/src/examples/README.md +77 -0
- package/src/examples/SHOWCASE_GAME.md +43 -0
- package/src/examples/arena.rs +44 -0
- package/src/examples/counter.rs +12 -0
- package/src/examples/pvp_arena.rs +131 -0
- package/src/examples/quiz.rs +90 -0
- package/src/examples/rpg.rs +13 -0
- package/src/examples/shop.rs +30 -0
- package/src/examples/showcase_game.rs +552 -0
- package/src/examples/stdlib_demo.rs +181 -0
- package/src/examples/turret.rs +27 -0
- package/src/examples/world_manager.rs +23 -0
- package/src/formatter/index.ts +22 -0
- package/src/index.ts +161 -0
- package/src/ir/builder.ts +114 -0
- package/src/ir/types.ts +119 -0
- package/src/lexer/index.ts +555 -0
- package/src/lowering/index.ts +2406 -0
- package/src/mc-test/client.ts +259 -0
- package/src/mc-test/runner.ts +140 -0
- package/src/mc-test/setup.ts +70 -0
- package/src/mc-validator/index.ts +367 -0
- package/src/nbt/index.ts +321 -0
- package/src/optimizer/commands.ts +416 -0
- package/src/optimizer/passes.ts +233 -0
- package/src/optimizer/structure.ts +441 -0
- package/src/parser/index.ts +1437 -0
- package/src/repl.ts +165 -0
- package/src/runtime/index.ts +1403 -0
- package/src/stdlib/README.md +156 -0
- package/src/stdlib/combat.rs +20 -0
- package/src/stdlib/cooldown.rs +45 -0
- package/src/stdlib/math.rs +49 -0
- package/src/stdlib/mobs.rs +99 -0
- package/src/stdlib/player.rs +29 -0
- package/src/stdlib/strings.rs +7 -0
- package/src/stdlib/timer.rs +51 -0
- package/src/templates/README.md +126 -0
- package/src/templates/combat.rs +96 -0
- package/src/templates/economy.rs +40 -0
- package/src/templates/mini-game-framework.rs +117 -0
- package/src/templates/quest.rs +78 -0
- package/src/test_programs/zombie_game.rs +25 -0
- package/src/typechecker/index.ts +737 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Code generator: IR → mcfunction datapack
|
|
4
|
+
*
|
|
5
|
+
* Output structure:
|
|
6
|
+
* <namespace>/
|
|
7
|
+
* functions/
|
|
8
|
+
* <fn_name>.mcfunction
|
|
9
|
+
* <fn_name>/<block_label>.mcfunction (for control-flow continuations)
|
|
10
|
+
* load.mcfunction (objective setup)
|
|
11
|
+
*
|
|
12
|
+
* Variable mapping:
|
|
13
|
+
* scoreboard objective: "rs"
|
|
14
|
+
* fake player: "$<varname>"
|
|
15
|
+
* temporaries: "$t0", "$t1", ...
|
|
16
|
+
* return value: "$ret"
|
|
17
|
+
* parameters: "$p0", "$p1", ...
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.countMcfunctionCommands = countMcfunctionCommands;
|
|
21
|
+
exports.generateDatapackWithStats = generateDatapackWithStats;
|
|
22
|
+
exports.generateDatapack = generateDatapack;
|
|
23
|
+
const commands_1 = require("../../optimizer/commands");
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Utilities
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const OBJ = 'rs'; // scoreboard objective name
|
|
28
|
+
function varRef(name) {
|
|
29
|
+
// Ensure fake player prefix
|
|
30
|
+
return name.startsWith('$') ? name : `$${name}`;
|
|
31
|
+
}
|
|
32
|
+
function operandToScore(op) {
|
|
33
|
+
if (op.kind === 'var')
|
|
34
|
+
return `${varRef(op.name)} ${OBJ}`;
|
|
35
|
+
if (op.kind === 'const')
|
|
36
|
+
return `$const_${op.value} ${OBJ}`;
|
|
37
|
+
throw new Error(`Cannot convert storage operand to score: ${op.path}`);
|
|
38
|
+
}
|
|
39
|
+
function constSetup(value) {
|
|
40
|
+
return `scoreboard players set $const_${value} ${OBJ} ${value}`;
|
|
41
|
+
}
|
|
42
|
+
// Collect all constants used in a function for pre-setup
|
|
43
|
+
function collectConsts(fn) {
|
|
44
|
+
const consts = new Set();
|
|
45
|
+
for (const block of fn.blocks) {
|
|
46
|
+
for (const instr of block.instrs) {
|
|
47
|
+
if (instr.op === 'assign' && instr.src.kind === 'const')
|
|
48
|
+
consts.add(instr.src.value);
|
|
49
|
+
if (instr.op === 'binop') {
|
|
50
|
+
if (instr.lhs.kind === 'const')
|
|
51
|
+
consts.add(instr.lhs.value);
|
|
52
|
+
if (instr.rhs.kind === 'const')
|
|
53
|
+
consts.add(instr.rhs.value);
|
|
54
|
+
}
|
|
55
|
+
if (instr.op === 'cmp') {
|
|
56
|
+
if (instr.lhs.kind === 'const')
|
|
57
|
+
consts.add(instr.lhs.value);
|
|
58
|
+
if (instr.rhs.kind === 'const')
|
|
59
|
+
consts.add(instr.rhs.value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const t = block.term;
|
|
63
|
+
if (t.op === 'return' && t.value?.kind === 'const')
|
|
64
|
+
consts.add(t.value.value);
|
|
65
|
+
}
|
|
66
|
+
return consts;
|
|
67
|
+
}
|
|
68
|
+
// MC scoreboard operation suffix
|
|
69
|
+
const BOP_OP = {
|
|
70
|
+
'+': '+=', '-': '-=', '*': '*=', '/': '/=', '%': '%=',
|
|
71
|
+
};
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Instruction codegen
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
function emitInstr(instr, ns) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
switch (instr.op) {
|
|
78
|
+
case 'assign': {
|
|
79
|
+
const dst = varRef(instr.dst);
|
|
80
|
+
const src = instr.src;
|
|
81
|
+
if (src.kind === 'const') {
|
|
82
|
+
lines.push(`scoreboard players set ${dst} ${OBJ} ${src.value}`);
|
|
83
|
+
}
|
|
84
|
+
else if (src.kind === 'var') {
|
|
85
|
+
lines.push(`scoreboard players operation ${dst} ${OBJ} = ${varRef(src.name)} ${OBJ}`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
lines.push(`execute store result score ${dst} ${OBJ} run data get storage ${src.path}`);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'binop': {
|
|
93
|
+
const dst = varRef(instr.dst);
|
|
94
|
+
const bop = BOP_OP[instr.bop] ?? '+=';
|
|
95
|
+
// Copy lhs → dst, then apply op with rhs
|
|
96
|
+
lines.push(...emitInstr({ op: 'assign', dst: instr.dst, src: instr.lhs }, ns));
|
|
97
|
+
lines.push(`scoreboard players operation ${dst} ${OBJ} ${bop} ${operandToScore(instr.rhs)}`);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case 'cmp': {
|
|
101
|
+
// MC doesn't have a direct compare-to-register; use execute store
|
|
102
|
+
const dst = varRef(instr.dst);
|
|
103
|
+
const lhsScore = operandToScore(instr.lhs);
|
|
104
|
+
const rhsScore = operandToScore(instr.rhs);
|
|
105
|
+
lines.push(`scoreboard players set ${dst} ${OBJ} 0`);
|
|
106
|
+
switch (instr.cop) {
|
|
107
|
+
case '==':
|
|
108
|
+
lines.push(`execute if score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
109
|
+
break;
|
|
110
|
+
case '!=':
|
|
111
|
+
lines.push(`execute unless score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
112
|
+
break;
|
|
113
|
+
case '<':
|
|
114
|
+
lines.push(`execute if score ${lhsScore} < ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
115
|
+
break;
|
|
116
|
+
case '<=':
|
|
117
|
+
lines.push(`execute if score ${lhsScore} <= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
118
|
+
break;
|
|
119
|
+
case '>':
|
|
120
|
+
lines.push(`execute if score ${lhsScore} > ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
121
|
+
break;
|
|
122
|
+
case '>=':
|
|
123
|
+
lines.push(`execute if score ${lhsScore} >= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'call': {
|
|
129
|
+
// Push args as fake players $p0, $p1, ...
|
|
130
|
+
for (let i = 0; i < instr.args.length; i++) {
|
|
131
|
+
lines.push(...emitInstr({ op: 'assign', dst: `$p${i}`, src: instr.args[i] }, ns));
|
|
132
|
+
}
|
|
133
|
+
lines.push(`function ${ns}:${instr.fn}`);
|
|
134
|
+
if (instr.dst) {
|
|
135
|
+
lines.push(`scoreboard players operation ${varRef(instr.dst)} ${OBJ} = $ret ${OBJ}`);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'raw':
|
|
140
|
+
lines.push(instr.cmd);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
return lines;
|
|
144
|
+
}
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Terminator codegen
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
function emitTerm(term, ns, fnName) {
|
|
149
|
+
const lines = [];
|
|
150
|
+
switch (term.op) {
|
|
151
|
+
case 'jump':
|
|
152
|
+
lines.push(`function ${ns}:${fnName}/${term.target}`);
|
|
153
|
+
break;
|
|
154
|
+
case 'jump_if':
|
|
155
|
+
lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.then}`);
|
|
156
|
+
lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.else_}`);
|
|
157
|
+
break;
|
|
158
|
+
case 'jump_unless':
|
|
159
|
+
lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.then}`);
|
|
160
|
+
lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.else_}`);
|
|
161
|
+
break;
|
|
162
|
+
case 'return':
|
|
163
|
+
if (term.value) {
|
|
164
|
+
lines.push(...emitInstr({ op: 'assign', dst: '$ret', src: term.value }, ns));
|
|
165
|
+
}
|
|
166
|
+
// In MC 1.20+, use `return` command
|
|
167
|
+
if (term.value?.kind === 'const') {
|
|
168
|
+
lines.push(`return ${term.value.value}`);
|
|
169
|
+
}
|
|
170
|
+
else if (term.value?.kind === 'var') {
|
|
171
|
+
lines.push(`return run scoreboard players get ${varRef(term.value.name)} ${OBJ}`);
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
case 'tick_yield':
|
|
175
|
+
lines.push(`schedule function ${ns}:${fnName}/${term.continuation} 1t replace`);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
return lines;
|
|
179
|
+
}
|
|
180
|
+
function toFunctionName(file) {
|
|
181
|
+
const match = file.path.match(/^data\/[^/]+\/function\/(.+)\.mcfunction$/);
|
|
182
|
+
return match?.[1] ?? null;
|
|
183
|
+
}
|
|
184
|
+
function applyFunctionOptimization(files) {
|
|
185
|
+
const functionFiles = files
|
|
186
|
+
.map(file => {
|
|
187
|
+
const functionName = toFunctionName(file);
|
|
188
|
+
if (!functionName)
|
|
189
|
+
return null;
|
|
190
|
+
const commands = file.content
|
|
191
|
+
.split('\n')
|
|
192
|
+
.map(line => line.trim())
|
|
193
|
+
.filter(line => line !== '' && !line.startsWith('#'))
|
|
194
|
+
.map(cmd => ({ cmd }));
|
|
195
|
+
return { file, functionName, commands };
|
|
196
|
+
})
|
|
197
|
+
.filter((entry) => entry !== null);
|
|
198
|
+
const optimized = (0, commands_1.optimizeCommandFunctions)(functionFiles.map(entry => ({
|
|
199
|
+
name: entry.functionName,
|
|
200
|
+
commands: entry.commands,
|
|
201
|
+
})));
|
|
202
|
+
const commandMap = new Map(optimized.functions.map(fn => [fn.name, fn.commands]));
|
|
203
|
+
return {
|
|
204
|
+
files: files.map(file => {
|
|
205
|
+
const functionName = toFunctionName(file);
|
|
206
|
+
if (!functionName)
|
|
207
|
+
return file;
|
|
208
|
+
const commands = commandMap.get(functionName);
|
|
209
|
+
if (!commands)
|
|
210
|
+
return file;
|
|
211
|
+
const lines = file.content.split('\n');
|
|
212
|
+
const header = lines.filter(line => line.trim().startsWith('#'));
|
|
213
|
+
return {
|
|
214
|
+
...file,
|
|
215
|
+
content: [...header, ...commands.map(command => command.cmd)].join('\n'),
|
|
216
|
+
};
|
|
217
|
+
}),
|
|
218
|
+
stats: optimized.stats,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function countMcfunctionCommands(files) {
|
|
222
|
+
return files.reduce((sum, file) => {
|
|
223
|
+
if (!toFunctionName(file)) {
|
|
224
|
+
return sum;
|
|
225
|
+
}
|
|
226
|
+
return sum + file.content
|
|
227
|
+
.split('\n')
|
|
228
|
+
.map(line => line.trim())
|
|
229
|
+
.filter(line => line !== '' && !line.startsWith('#'))
|
|
230
|
+
.length;
|
|
231
|
+
}, 0);
|
|
232
|
+
}
|
|
233
|
+
function generateDatapackWithStats(module, options = {}) {
|
|
234
|
+
const { optimizeCommands = true } = options;
|
|
235
|
+
const files = [];
|
|
236
|
+
const advancements = [];
|
|
237
|
+
const ns = module.namespace;
|
|
238
|
+
// Collect all trigger handlers
|
|
239
|
+
const triggerHandlers = module.functions.filter(fn => fn.isTriggerHandler && fn.triggerName);
|
|
240
|
+
const triggerNames = new Set(triggerHandlers.map(fn => fn.triggerName));
|
|
241
|
+
// Collect all tick functions
|
|
242
|
+
const tickFunctionNames = [];
|
|
243
|
+
for (const fn of module.functions) {
|
|
244
|
+
if (fn.isTickLoop) {
|
|
245
|
+
tickFunctionNames.push(fn.name);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// pack.mcmeta
|
|
249
|
+
files.push({
|
|
250
|
+
path: 'pack.mcmeta',
|
|
251
|
+
content: JSON.stringify({
|
|
252
|
+
pack: { pack_format: 26, description: `${ns} datapack — compiled by redscript` }
|
|
253
|
+
}, null, 2),
|
|
254
|
+
});
|
|
255
|
+
// __load.mcfunction — create scoreboard objective + trigger registrations
|
|
256
|
+
const loadLines = [
|
|
257
|
+
`# RedScript runtime init`,
|
|
258
|
+
`scoreboard objectives add ${OBJ} dummy`,
|
|
259
|
+
];
|
|
260
|
+
for (const g of module.globals) {
|
|
261
|
+
loadLines.push(`scoreboard players set ${varRef(g)} ${OBJ} 0`);
|
|
262
|
+
}
|
|
263
|
+
// Add trigger objectives
|
|
264
|
+
for (const triggerName of triggerNames) {
|
|
265
|
+
loadLines.push(`scoreboard objectives add ${triggerName} trigger`);
|
|
266
|
+
loadLines.push(`scoreboard players enable @a ${triggerName}`);
|
|
267
|
+
}
|
|
268
|
+
// Generate trigger dispatch functions
|
|
269
|
+
for (const triggerName of triggerNames) {
|
|
270
|
+
const handlers = triggerHandlers.filter(fn => fn.triggerName === triggerName);
|
|
271
|
+
// __trigger_{name}_dispatch.mcfunction
|
|
272
|
+
const dispatchLines = [
|
|
273
|
+
`# Trigger dispatch for ${triggerName}`,
|
|
274
|
+
];
|
|
275
|
+
for (const handler of handlers) {
|
|
276
|
+
dispatchLines.push(`function ${ns}:${handler.name}`);
|
|
277
|
+
}
|
|
278
|
+
dispatchLines.push(`scoreboard players set @s ${triggerName} 0`);
|
|
279
|
+
dispatchLines.push(`scoreboard players enable @s ${triggerName}`);
|
|
280
|
+
files.push({
|
|
281
|
+
path: `data/${ns}/function/__trigger_${triggerName}_dispatch.mcfunction`,
|
|
282
|
+
content: dispatchLines.join('\n'),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
// Generate each function (and collect constants for load)
|
|
286
|
+
for (const fn of module.functions) {
|
|
287
|
+
// Constant setup — place constants in __load.mcfunction
|
|
288
|
+
const consts = collectConsts(fn);
|
|
289
|
+
if (consts.size > 0) {
|
|
290
|
+
loadLines.push(...Array.from(consts).map(constSetup));
|
|
291
|
+
}
|
|
292
|
+
// Entry block → <fn_name>.mcfunction
|
|
293
|
+
// Continuation blocks → <fn_name>/<label>.mcfunction
|
|
294
|
+
for (let i = 0; i < fn.blocks.length; i++) {
|
|
295
|
+
const block = fn.blocks[i];
|
|
296
|
+
const lines = [`# block: ${block.label}`];
|
|
297
|
+
// Param setup in entry block
|
|
298
|
+
if (i === 0) {
|
|
299
|
+
for (let j = 0; j < fn.params.length; j++) {
|
|
300
|
+
lines.push(`scoreboard players operation ${varRef(fn.params[j])} ${OBJ} = $p${j} ${OBJ}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const instr of block.instrs) {
|
|
304
|
+
lines.push(...emitInstr(instr, ns));
|
|
305
|
+
}
|
|
306
|
+
lines.push(...emitTerm(block.term, ns, fn.name));
|
|
307
|
+
const filePath = i === 0
|
|
308
|
+
? `data/${ns}/function/${fn.name}.mcfunction`
|
|
309
|
+
: `data/${ns}/function/${fn.name}/${block.label}.mcfunction`;
|
|
310
|
+
files.push({ path: filePath, content: lines.join('\n') });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Write __load.mcfunction
|
|
314
|
+
files.push({
|
|
315
|
+
path: `data/${ns}/function/__load.mcfunction`,
|
|
316
|
+
content: loadLines.join('\n'),
|
|
317
|
+
});
|
|
318
|
+
// minecraft:load tag pointing to __load
|
|
319
|
+
files.push({
|
|
320
|
+
path: `data/minecraft/tags/function/load.json`,
|
|
321
|
+
content: JSON.stringify({ values: [`${ns}:__load`] }, null, 2),
|
|
322
|
+
});
|
|
323
|
+
// __tick.mcfunction — calls all @tick functions + trigger check
|
|
324
|
+
const tickLines = ['# RedScript tick dispatcher'];
|
|
325
|
+
// Call all @tick functions
|
|
326
|
+
for (const fnName of tickFunctionNames) {
|
|
327
|
+
tickLines.push(`function ${ns}:${fnName}`);
|
|
328
|
+
}
|
|
329
|
+
// Call trigger check if there are triggers
|
|
330
|
+
if (triggerNames.size > 0) {
|
|
331
|
+
tickLines.push(`# Trigger checks`);
|
|
332
|
+
for (const triggerName of triggerNames) {
|
|
333
|
+
tickLines.push(`execute as @a[scores={${triggerName}=1..}] run function ${ns}:__trigger_${triggerName}_dispatch`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Only generate __tick if there's something to run
|
|
337
|
+
if (tickFunctionNames.length > 0 || triggerNames.size > 0) {
|
|
338
|
+
files.push({
|
|
339
|
+
path: `data/${ns}/function/__tick.mcfunction`,
|
|
340
|
+
content: tickLines.join('\n'),
|
|
341
|
+
});
|
|
342
|
+
// minecraft:tick tag pointing to __tick
|
|
343
|
+
files.push({
|
|
344
|
+
path: `data/minecraft/tags/function/tick.json`,
|
|
345
|
+
content: JSON.stringify({ values: [`${ns}:__tick`] }, null, 2),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
for (const fn of module.functions) {
|
|
349
|
+
const eventTrigger = fn.eventTrigger;
|
|
350
|
+
if (!eventTrigger) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
let path = '';
|
|
354
|
+
let criteria = {};
|
|
355
|
+
switch (eventTrigger.kind) {
|
|
356
|
+
case 'advancement':
|
|
357
|
+
path = `data/${ns}/advancements/on_advancement_${fn.name}.json`;
|
|
358
|
+
criteria = {
|
|
359
|
+
trigger: {
|
|
360
|
+
trigger: `minecraft:${eventTrigger.value}`,
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
break;
|
|
364
|
+
case 'craft':
|
|
365
|
+
path = `data/${ns}/advancements/on_craft_${fn.name}.json`;
|
|
366
|
+
criteria = {
|
|
367
|
+
crafted: {
|
|
368
|
+
trigger: 'minecraft:inventory_changed',
|
|
369
|
+
conditions: {
|
|
370
|
+
items: [
|
|
371
|
+
{
|
|
372
|
+
items: [eventTrigger.value],
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
break;
|
|
379
|
+
case 'death':
|
|
380
|
+
path = `data/${ns}/advancements/on_death_${fn.name}.json`;
|
|
381
|
+
criteria = {
|
|
382
|
+
death: {
|
|
383
|
+
trigger: 'minecraft:entity_killed_player',
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
break;
|
|
387
|
+
case 'login':
|
|
388
|
+
case 'join_team':
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
advancements.push({
|
|
392
|
+
path,
|
|
393
|
+
content: JSON.stringify({
|
|
394
|
+
criteria,
|
|
395
|
+
rewards: {
|
|
396
|
+
function: `${ns}:${fn.name}`,
|
|
397
|
+
},
|
|
398
|
+
}, null, 2),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
const stats = (0, commands_1.createEmptyOptimizationStats)();
|
|
402
|
+
if (!optimizeCommands) {
|
|
403
|
+
return { files, advancements, stats };
|
|
404
|
+
}
|
|
405
|
+
const optimized = applyFunctionOptimization(files);
|
|
406
|
+
(0, commands_1.mergeOptimizationStats)(stats, optimized.stats);
|
|
407
|
+
return { files: optimized.files, advancements, stats };
|
|
408
|
+
}
|
|
409
|
+
function generateDatapack(module) {
|
|
410
|
+
const generated = generateDatapackWithStats(module);
|
|
411
|
+
return [...generated.files, ...generated.advancements];
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type OptimizationStats } from '../../optimizer/commands';
|
|
2
|
+
import type { IRModule } from '../../ir/types';
|
|
3
|
+
import type { DatapackFile } from '../mcfunction';
|
|
4
|
+
export interface StructureBlockInfo {
|
|
5
|
+
command: string;
|
|
6
|
+
conditional: boolean;
|
|
7
|
+
state: number;
|
|
8
|
+
functionName: string;
|
|
9
|
+
lineNumber: number;
|
|
10
|
+
}
|
|
11
|
+
export interface StructureCompileResult {
|
|
12
|
+
buffer: Buffer;
|
|
13
|
+
blockCount: number;
|
|
14
|
+
blocks: StructureBlockInfo[];
|
|
15
|
+
stats?: OptimizationStats;
|
|
16
|
+
}
|
|
17
|
+
export declare function generateStructure(input: IRModule | DatapackFile[]): StructureCompileResult;
|
|
18
|
+
export declare function compileToStructure(source: string, namespace: string, filePath?: string): StructureCompileResult;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateStructure = generateStructure;
|
|
4
|
+
exports.compileToStructure = compileToStructure;
|
|
5
|
+
const lexer_1 = require("../../lexer");
|
|
6
|
+
const parser_1 = require("../../parser");
|
|
7
|
+
const lowering_1 = require("../../lowering");
|
|
8
|
+
const nbt_1 = require("../../nbt");
|
|
9
|
+
const commands_1 = require("../../optimizer/commands");
|
|
10
|
+
const passes_1 = require("../../optimizer/passes");
|
|
11
|
+
const structure_1 = require("../../optimizer/structure");
|
|
12
|
+
const compile_1 = require("../../compile");
|
|
13
|
+
const DATA_VERSION = 3953;
|
|
14
|
+
const MAX_WIDTH = 16;
|
|
15
|
+
const OBJ = 'rs';
|
|
16
|
+
const PALETTE_IMPULSE = 0;
|
|
17
|
+
const PALETTE_CHAIN_UNCONDITIONAL = 1;
|
|
18
|
+
const PALETTE_CHAIN_CONDITIONAL = 2;
|
|
19
|
+
const PALETTE_REPEAT = 3;
|
|
20
|
+
const palette = [
|
|
21
|
+
{ Name: 'minecraft:command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
22
|
+
{ Name: 'minecraft:chain_command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
23
|
+
{ Name: 'minecraft:chain_command_block', Properties: { conditional: 'true', facing: 'east' } },
|
|
24
|
+
{ Name: 'minecraft:repeating_command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
25
|
+
{ Name: 'minecraft:air', Properties: {} },
|
|
26
|
+
];
|
|
27
|
+
function escapeJsonString(value) {
|
|
28
|
+
return JSON.stringify(value).slice(1, -1);
|
|
29
|
+
}
|
|
30
|
+
function varRef(name) {
|
|
31
|
+
return name.startsWith('$') ? name : `$${name}`;
|
|
32
|
+
}
|
|
33
|
+
function collectConsts(fn) {
|
|
34
|
+
const consts = new Set();
|
|
35
|
+
for (const block of fn.blocks) {
|
|
36
|
+
for (const instr of block.instrs) {
|
|
37
|
+
if (instr.op === 'assign' && instr.src.kind === 'const')
|
|
38
|
+
consts.add(instr.src.value);
|
|
39
|
+
if (instr.op === 'binop') {
|
|
40
|
+
if (instr.lhs.kind === 'const')
|
|
41
|
+
consts.add(instr.lhs.value);
|
|
42
|
+
if (instr.rhs.kind === 'const')
|
|
43
|
+
consts.add(instr.rhs.value);
|
|
44
|
+
}
|
|
45
|
+
if (instr.op === 'cmp') {
|
|
46
|
+
if (instr.lhs.kind === 'const')
|
|
47
|
+
consts.add(instr.lhs.value);
|
|
48
|
+
if (instr.rhs.kind === 'const')
|
|
49
|
+
consts.add(instr.rhs.value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (block.term.op === 'return' && block.term.value?.kind === 'const') {
|
|
53
|
+
consts.add(block.term.value.value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return consts;
|
|
57
|
+
}
|
|
58
|
+
function constSetup(value) {
|
|
59
|
+
return `scoreboard players set $const_${value} ${OBJ} ${value}`;
|
|
60
|
+
}
|
|
61
|
+
function collectCommandEntriesFromModule(module) {
|
|
62
|
+
const entries = [];
|
|
63
|
+
const triggerHandlers = module.functions.filter(fn => fn.isTriggerHandler && fn.triggerName);
|
|
64
|
+
const triggerNames = new Set(triggerHandlers.map(fn => fn.triggerName));
|
|
65
|
+
const loadCommands = [
|
|
66
|
+
`scoreboard objectives add ${OBJ} dummy`,
|
|
67
|
+
...module.globals.map(globalName => `scoreboard players set ${varRef(globalName)} ${OBJ} 0`),
|
|
68
|
+
...Array.from(triggerNames).flatMap(triggerName => [
|
|
69
|
+
`scoreboard objectives add ${triggerName} trigger`,
|
|
70
|
+
`scoreboard players enable @a ${triggerName}`,
|
|
71
|
+
]),
|
|
72
|
+
...Array.from(new Set(module.functions.flatMap(fn => Array.from(collectConsts(fn))))).map(constSetup),
|
|
73
|
+
];
|
|
74
|
+
const sections = [];
|
|
75
|
+
if (loadCommands.length > 0) {
|
|
76
|
+
sections.push({
|
|
77
|
+
name: '__load',
|
|
78
|
+
commands: loadCommands.map(cmd => ({ cmd })),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
for (const triggerName of triggerNames) {
|
|
82
|
+
const handlers = triggerHandlers.filter(fn => fn.triggerName === triggerName);
|
|
83
|
+
sections.push({
|
|
84
|
+
name: `__trigger_${triggerName}_dispatch`,
|
|
85
|
+
commands: [
|
|
86
|
+
...handlers.map(handler => ({ cmd: `function ${module.namespace}:${handler.name}` })),
|
|
87
|
+
{ cmd: `scoreboard players set @s ${triggerName} 0` },
|
|
88
|
+
{ cmd: `scoreboard players enable @s ${triggerName}` },
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
for (const fn of module.functions) {
|
|
93
|
+
if (!fn.commands || fn.commands.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
sections.push({
|
|
96
|
+
name: fn.name,
|
|
97
|
+
commands: fn.commands,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const tickCommands = [];
|
|
101
|
+
for (const fn of module.functions.filter(candidate => candidate.isTickLoop)) {
|
|
102
|
+
tickCommands.push({ cmd: `function ${module.namespace}:${fn.name}` });
|
|
103
|
+
}
|
|
104
|
+
if (triggerNames.size > 0) {
|
|
105
|
+
for (const triggerName of triggerNames) {
|
|
106
|
+
tickCommands.push({
|
|
107
|
+
cmd: `execute as @a[scores={${triggerName}=1..}] run function ${module.namespace}:__trigger_${triggerName}_dispatch`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (tickCommands.length > 0) {
|
|
112
|
+
sections.push({
|
|
113
|
+
name: '__tick',
|
|
114
|
+
commands: tickCommands,
|
|
115
|
+
repeat: true,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
for (const section of sections) {
|
|
119
|
+
for (let i = 0; i < section.commands.length; i++) {
|
|
120
|
+
const command = section.commands[i];
|
|
121
|
+
const state = i === 0
|
|
122
|
+
? (section.repeat ? PALETTE_REPEAT : PALETTE_IMPULSE)
|
|
123
|
+
: (command.conditional ? PALETTE_CHAIN_CONDITIONAL : PALETTE_CHAIN_UNCONDITIONAL);
|
|
124
|
+
entries.push({
|
|
125
|
+
functionName: section.name,
|
|
126
|
+
lineNumber: i + 1,
|
|
127
|
+
command: command.cmd,
|
|
128
|
+
conditional: Boolean(command.conditional),
|
|
129
|
+
state,
|
|
130
|
+
isRepeat: Boolean(section.repeat && i === 0),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return entries;
|
|
135
|
+
}
|
|
136
|
+
function toFunctionName(file) {
|
|
137
|
+
const match = file.path.match(/^data\/[^/]+\/function\/(.+)\.mcfunction$/);
|
|
138
|
+
return match?.[1] ?? null;
|
|
139
|
+
}
|
|
140
|
+
function collectCommandEntriesFromFiles(files) {
|
|
141
|
+
const entries = [];
|
|
142
|
+
for (const file of files) {
|
|
143
|
+
const functionName = toFunctionName(file);
|
|
144
|
+
if (!functionName)
|
|
145
|
+
continue;
|
|
146
|
+
const lines = file.content.split('\n');
|
|
147
|
+
let isFirstCommand = true;
|
|
148
|
+
const isTickFunction = functionName === '__tick';
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
const command = lines[i].trim();
|
|
151
|
+
if (command === '' || command.startsWith('#'))
|
|
152
|
+
continue;
|
|
153
|
+
const state = isFirstCommand
|
|
154
|
+
? (isTickFunction ? PALETTE_REPEAT : PALETTE_IMPULSE)
|
|
155
|
+
: PALETTE_CHAIN_UNCONDITIONAL;
|
|
156
|
+
entries.push({
|
|
157
|
+
functionName,
|
|
158
|
+
lineNumber: i + 1,
|
|
159
|
+
command,
|
|
160
|
+
conditional: false,
|
|
161
|
+
state,
|
|
162
|
+
isRepeat: isTickFunction && isFirstCommand,
|
|
163
|
+
});
|
|
164
|
+
isFirstCommand = false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return entries;
|
|
168
|
+
}
|
|
169
|
+
function createPaletteTag() {
|
|
170
|
+
return palette.map(entry => nbt_1.nbt.compound({
|
|
171
|
+
Name: nbt_1.nbt.string(entry.Name),
|
|
172
|
+
Properties: nbt_1.nbt.compound(Object.fromEntries(Object.entries(entry.Properties).map(([key, value]) => [key, nbt_1.nbt.string(value)]))),
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
function createBlockEntityTag(entry) {
|
|
176
|
+
return nbt_1.nbt.compound({
|
|
177
|
+
id: nbt_1.nbt.string('minecraft:command_block'),
|
|
178
|
+
Command: nbt_1.nbt.string(entry.command),
|
|
179
|
+
auto: nbt_1.nbt.byte(entry.isRepeat ? 1 : 0),
|
|
180
|
+
powered: nbt_1.nbt.byte(0),
|
|
181
|
+
conditionMet: nbt_1.nbt.byte(0),
|
|
182
|
+
UpdateLastExecution: nbt_1.nbt.byte(1),
|
|
183
|
+
LastExecution: nbt_1.nbt.long(0n),
|
|
184
|
+
TrackOutput: nbt_1.nbt.byte(1),
|
|
185
|
+
SuccessCount: nbt_1.nbt.int(0),
|
|
186
|
+
LastOutput: nbt_1.nbt.string(''),
|
|
187
|
+
CustomName: nbt_1.nbt.string(`{"text":"${escapeJsonString(`${entry.functionName}:${entry.lineNumber}`)}"}`),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function createBlockTag(entry, index) {
|
|
191
|
+
const x = index % MAX_WIDTH;
|
|
192
|
+
const z = Math.floor(index / MAX_WIDTH) % MAX_WIDTH;
|
|
193
|
+
const y = Math.floor(index / (MAX_WIDTH * MAX_WIDTH));
|
|
194
|
+
return nbt_1.nbt.compound({
|
|
195
|
+
pos: nbt_1.nbt.list(3 /* TagType.Int */, [nbt_1.nbt.int(x), nbt_1.nbt.int(y), nbt_1.nbt.int(z)]),
|
|
196
|
+
state: nbt_1.nbt.int(entry.state),
|
|
197
|
+
nbt: createBlockEntityTag(entry),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function generateStructure(input) {
|
|
201
|
+
const entries = Array.isArray(input)
|
|
202
|
+
? collectCommandEntriesFromFiles(input)
|
|
203
|
+
: collectCommandEntriesFromModule(input);
|
|
204
|
+
const blockTags = entries.map(createBlockTag);
|
|
205
|
+
const sizeX = Math.max(1, Math.min(MAX_WIDTH, entries.length || 1));
|
|
206
|
+
const sizeZ = Math.max(1, Math.min(MAX_WIDTH, Math.ceil(entries.length / MAX_WIDTH) || 1));
|
|
207
|
+
const sizeY = Math.max(1, Math.ceil(entries.length / (MAX_WIDTH * MAX_WIDTH)) || 1);
|
|
208
|
+
const root = nbt_1.nbt.compound({
|
|
209
|
+
DataVersion: nbt_1.nbt.int(DATA_VERSION),
|
|
210
|
+
size: nbt_1.nbt.list(3 /* TagType.Int */, [nbt_1.nbt.int(sizeX), nbt_1.nbt.int(sizeY), nbt_1.nbt.int(sizeZ)]),
|
|
211
|
+
palette: nbt_1.nbt.list(10 /* TagType.Compound */, createPaletteTag()),
|
|
212
|
+
blocks: nbt_1.nbt.list(10 /* TagType.Compound */, blockTags),
|
|
213
|
+
entities: nbt_1.nbt.list(10 /* TagType.Compound */, []),
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
buffer: (0, nbt_1.writeNbt)(root, ''),
|
|
217
|
+
blockCount: entries.length,
|
|
218
|
+
blocks: entries.map(entry => ({
|
|
219
|
+
command: entry.command,
|
|
220
|
+
conditional: entry.conditional,
|
|
221
|
+
state: entry.state,
|
|
222
|
+
functionName: entry.functionName,
|
|
223
|
+
lineNumber: entry.lineNumber,
|
|
224
|
+
})),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function compileToStructure(source, namespace, filePath) {
|
|
228
|
+
const preprocessedSource = (0, compile_1.preprocessSource)(source, { filePath });
|
|
229
|
+
const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
|
|
230
|
+
const ast = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
|
|
231
|
+
const ir = new lowering_1.Lowering(namespace).lower(ast);
|
|
232
|
+
const stats = (0, commands_1.createEmptyOptimizationStats)();
|
|
233
|
+
const optimizedIRFunctions = ir.functions.map(fn => {
|
|
234
|
+
const optimized = (0, passes_1.optimizeWithStats)(fn);
|
|
235
|
+
(0, commands_1.mergeOptimizationStats)(stats, optimized.stats);
|
|
236
|
+
return optimized.fn;
|
|
237
|
+
});
|
|
238
|
+
const structureOptimized = (0, structure_1.optimizeForStructureWithStats)(optimizedIRFunctions, namespace);
|
|
239
|
+
(0, commands_1.mergeOptimizationStats)(stats, structureOptimized.stats);
|
|
240
|
+
const optimizedModule = {
|
|
241
|
+
...ir,
|
|
242
|
+
functions: structureOptimized.functions,
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
245
|
+
...generateStructure(optimizedModule),
|
|
246
|
+
stats,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=index.js.map
|