redscript-mc 1.2.27 → 1.2.28
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 +24 -13
- package/README.zh.md +16 -5
- package/dist/__tests__/cli.test.js +13 -13
- package/dist/__tests__/optimizer-advanced.test.js +4 -4
- package/dist/cli.js +13 -5
- package/dist/codegen/mcfunction/index.d.ts +4 -0
- package/dist/codegen/mcfunction/index.js +9 -4
- package/dist/compile.d.ts +4 -0
- package/dist/compile.js +9 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -6
- package/dist/lowering/index.d.ts +3 -0
- package/dist/lowering/index.js +95 -65
- package/dist/optimizer/commands.d.ts +1 -0
- package/dist/optimizer/commands.js +18 -11
- package/dist/optimizer/structure.d.ts +1 -0
- package/dist/optimizer/structure.js +6 -1
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/math-showcase.mcrs +146 -0
- package/examples/readme-demo.mcrs +92 -0
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +13 -13
- package/src/__tests__/optimizer-advanced.test.ts +4 -4
- package/src/cli.ts +14 -5
- package/src/codegen/mcfunction/index.ts +14 -5
- package/src/compile.ts +15 -2
- package/src/index.ts +17 -7
- package/src/lowering/index.ts +95 -64
- package/src/optimizer/commands.ts +18 -12
- package/src/optimizer/structure.ts +6 -2
package/dist/lowering/index.js
CHANGED
|
@@ -39,7 +39,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.Lowering = void 0;
|
|
42
|
+
exports.Lowering = exports.LOWERING_OBJ = void 0;
|
|
43
|
+
exports.setScoreboardObjective = setScoreboardObjective;
|
|
43
44
|
const builder_1 = require("../ir/builder");
|
|
44
45
|
const diagnostics_1 = require("../diagnostics");
|
|
45
46
|
const path = __importStar(require("path"));
|
|
@@ -54,6 +55,13 @@ const entity_hierarchy_1 = require("../types/entity-hierarchy");
|
|
|
54
55
|
// All builtins support macro parameters - any arg that's a function param
|
|
55
56
|
// will automatically use MC 1.20.2+ macro syntax when needed
|
|
56
57
|
// ---------------------------------------------------------------------------
|
|
58
|
+
// Scoreboard Objective
|
|
59
|
+
// Set per-compilation via setScoreboardObjective() — defaults to 'rs'.
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
/** Current scoreboard objective. Set once per compile() call. */
|
|
62
|
+
exports.LOWERING_OBJ = 'rs';
|
|
63
|
+
function setScoreboardObjective(obj) { exports.LOWERING_OBJ = obj; }
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
57
65
|
// Builtin Functions
|
|
58
66
|
// ---------------------------------------------------------------------------
|
|
59
67
|
const BUILTINS = {
|
|
@@ -73,9 +81,13 @@ const BUILTINS = {
|
|
|
73
81
|
const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ');
|
|
74
82
|
return nbt ? `summon ${type} ${pos} ${nbt}` : `summon ${type} ${pos}`;
|
|
75
83
|
},
|
|
76
|
-
particle: ([name, x, y, z]) => {
|
|
84
|
+
particle: ([name, x, y, z, dx, dy, dz, speed, count]) => {
|
|
77
85
|
const pos = [x ?? '~', y ?? '~', z ?? '~'].join(' ');
|
|
78
|
-
|
|
86
|
+
// dx/dy/dz/speed/count are optional; omit trailing undefineds
|
|
87
|
+
const extra = [dx, dy, dz, speed, count].filter(v => v !== undefined && v !== null);
|
|
88
|
+
return extra.length > 0
|
|
89
|
+
? `particle ${name} ${pos} ${extra.join(' ')}`
|
|
90
|
+
: `particle ${name} ${pos}`;
|
|
79
91
|
},
|
|
80
92
|
playsound: ([sound, source, sel, x, y, z, volume, pitch, minVolume]) => ['playsound', sound, source, sel, x, y, z, volume, pitch, minVolume].filter(Boolean).join(' '),
|
|
81
93
|
tp: () => null, // Special handling
|
|
@@ -453,6 +465,16 @@ class Lowering {
|
|
|
453
465
|
if (expr.kind === 'struct_lit' || expr.kind === 'array_lit') {
|
|
454
466
|
return { str: this.exprToSnbt(expr) };
|
|
455
467
|
}
|
|
468
|
+
// Float literals: preserve the float value for MC commands that accept floats
|
|
469
|
+
// (particle spread, playsound volume/pitch, etc.)
|
|
470
|
+
// We do NOT truncate here — that only applies to scoreboard/IR contexts.
|
|
471
|
+
if (expr.kind === 'float_lit') {
|
|
472
|
+
return { str: expr.value.toString() };
|
|
473
|
+
}
|
|
474
|
+
// Unary minus applied to a float literal (e.g. -0.5)
|
|
475
|
+
if (expr.kind === 'unary' && expr.op === '-' && expr.operand.kind === 'float_lit') {
|
|
476
|
+
return { str: (-expr.operand.value).toString() };
|
|
477
|
+
}
|
|
456
478
|
return { str: this.exprToString(expr) };
|
|
457
479
|
}
|
|
458
480
|
/**
|
|
@@ -466,10 +488,10 @@ class Lowering {
|
|
|
466
488
|
for (let i = 0; i < loweredArgs.length; i++) {
|
|
467
489
|
const operand = loweredArgs[i];
|
|
468
490
|
if (operand.kind === 'const') {
|
|
469
|
-
this.builder.emitRaw(`scoreboard players set $p${i}
|
|
491
|
+
this.builder.emitRaw(`scoreboard players set $p${i} ${exports.LOWERING_OBJ} ${operand.value}`);
|
|
470
492
|
}
|
|
471
493
|
else if (operand.kind === 'var') {
|
|
472
|
-
this.builder.emitRaw(`scoreboard players operation $p${i}
|
|
494
|
+
this.builder.emitRaw(`scoreboard players operation $p${i} ${exports.LOWERING_OBJ} = ${operand.name} ${exports.LOWERING_OBJ}`);
|
|
473
495
|
}
|
|
474
496
|
}
|
|
475
497
|
// Set up NBT storage for each macro param
|
|
@@ -482,14 +504,14 @@ class Lowering {
|
|
|
482
504
|
this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`);
|
|
483
505
|
}
|
|
484
506
|
else if (operand.kind === 'var') {
|
|
485
|
-
this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name}
|
|
507
|
+
this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${exports.LOWERING_OBJ}`);
|
|
486
508
|
}
|
|
487
509
|
}
|
|
488
510
|
// Call with macro storage
|
|
489
511
|
this.builder.emitRaw(`function ${this.namespace}:${fnName} with storage rs:macro_args`);
|
|
490
512
|
// Copy return value (callers may use it)
|
|
491
513
|
const dst = this.builder.freshTemp();
|
|
492
|
-
this.builder.emitRaw(`scoreboard players operation ${dst}
|
|
514
|
+
this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = $ret ${exports.LOWERING_OBJ}`);
|
|
493
515
|
return { kind: 'var', name: dst };
|
|
494
516
|
}
|
|
495
517
|
lower(program) {
|
|
@@ -723,7 +745,7 @@ class Lowering {
|
|
|
723
745
|
const originalInstrs = [...entry.instrs];
|
|
724
746
|
const originalTerm = entry.term;
|
|
725
747
|
entry.instrs = [
|
|
726
|
-
{ op: 'raw', cmd: `scoreboard players add ${counterVar}
|
|
748
|
+
{ op: 'raw', cmd: `scoreboard players add ${counterVar} ${exports.LOWERING_OBJ} 1` },
|
|
727
749
|
];
|
|
728
750
|
// Create conditional jump
|
|
729
751
|
const bodyLabel = 'tick_body';
|
|
@@ -737,13 +759,13 @@ class Lowering {
|
|
|
737
759
|
// Add check instruction
|
|
738
760
|
entry.instrs.push({
|
|
739
761
|
op: 'raw',
|
|
740
|
-
cmd: `execute store success score ${counterVar}_check
|
|
762
|
+
cmd: `execute store success score ${counterVar}_check ${exports.LOWERING_OBJ} if score ${counterVar} ${exports.LOWERING_OBJ} matches ${rate}..`,
|
|
741
763
|
});
|
|
742
764
|
// Body block (original logic + counter reset)
|
|
743
765
|
fn.blocks.push({
|
|
744
766
|
label: bodyLabel,
|
|
745
767
|
instrs: [
|
|
746
|
-
{ op: 'raw', cmd: `scoreboard players set ${counterVar}
|
|
768
|
+
{ op: 'raw', cmd: `scoreboard players set ${counterVar} ${exports.LOWERING_OBJ} 0` },
|
|
747
769
|
...originalInstrs,
|
|
748
770
|
],
|
|
749
771
|
term: originalTerm,
|
|
@@ -857,7 +879,7 @@ class Lowering {
|
|
|
857
879
|
}
|
|
858
880
|
else if (fieldValue.kind === 'var') {
|
|
859
881
|
// Copy from scoreboard to NBT
|
|
860
|
-
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name}
|
|
882
|
+
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${exports.LOWERING_OBJ}`);
|
|
861
883
|
}
|
|
862
884
|
}
|
|
863
885
|
return;
|
|
@@ -890,7 +912,7 @@ class Lowering {
|
|
|
890
912
|
}
|
|
891
913
|
else if (elemValue.kind === 'var') {
|
|
892
914
|
this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value 0`);
|
|
893
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name}
|
|
915
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} ${exports.LOWERING_OBJ}`);
|
|
894
916
|
}
|
|
895
917
|
}
|
|
896
918
|
return;
|
|
@@ -936,7 +958,7 @@ class Lowering {
|
|
|
936
958
|
this.builder.emitRaw(`data modify storage ${path} set value ${fieldValue.value}`);
|
|
937
959
|
}
|
|
938
960
|
else if (fieldValue.kind === 'var') {
|
|
939
|
-
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name}
|
|
961
|
+
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} ${exports.LOWERING_OBJ}`);
|
|
940
962
|
}
|
|
941
963
|
}
|
|
942
964
|
this.builder.emitReturn({ kind: 'const', value: 0 });
|
|
@@ -1103,10 +1125,10 @@ class Lowering {
|
|
|
1103
1125
|
this.varMap.set(stmt.varName, loopVar);
|
|
1104
1126
|
const startVal = this.lowerExpr(stmt.start);
|
|
1105
1127
|
if (startVal.kind === 'const') {
|
|
1106
|
-
this.builder.emitRaw(`scoreboard players set ${loopVar}
|
|
1128
|
+
this.builder.emitRaw(`scoreboard players set ${loopVar} ${exports.LOWERING_OBJ} ${startVal.value}`);
|
|
1107
1129
|
}
|
|
1108
1130
|
else if (startVal.kind === 'var') {
|
|
1109
|
-
this.builder.emitRaw(`scoreboard players operation ${loopVar}
|
|
1131
|
+
this.builder.emitRaw(`scoreboard players operation ${loopVar} ${exports.LOWERING_OBJ} = ${startVal.name} ${exports.LOWERING_OBJ}`);
|
|
1110
1132
|
}
|
|
1111
1133
|
// Call loop function
|
|
1112
1134
|
this.builder.emitRaw(`function ${this.namespace}:${subFnName}`);
|
|
@@ -1123,11 +1145,11 @@ class Lowering {
|
|
|
1123
1145
|
// Body
|
|
1124
1146
|
this.lowerBlock(stmt.body);
|
|
1125
1147
|
// Increment
|
|
1126
|
-
this.builder.emitRaw(`scoreboard players add ${loopVar}
|
|
1148
|
+
this.builder.emitRaw(`scoreboard players add ${loopVar} ${exports.LOWERING_OBJ} 1`);
|
|
1127
1149
|
// Loop condition: execute if score matches ..<end-1> run function
|
|
1128
1150
|
const endVal = this.lowerExpr(stmt.end);
|
|
1129
1151
|
const endNum = endVal.kind === 'const' ? endVal.value - 1 : '?';
|
|
1130
|
-
this.builder.emitRaw(`execute if score ${loopVar}
|
|
1152
|
+
this.builder.emitRaw(`execute if score ${loopVar} ${exports.LOWERING_OBJ} matches ..${endNum} run function ${this.namespace}:${subFnName}`);
|
|
1131
1153
|
if (!this.builder.isBlockSealed()) {
|
|
1132
1154
|
this.builder.emitReturn();
|
|
1133
1155
|
}
|
|
@@ -1217,12 +1239,12 @@ class Lowering {
|
|
|
1217
1239
|
matchCondition = String(patternValue.value);
|
|
1218
1240
|
}
|
|
1219
1241
|
const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
|
|
1220
|
-
this.builder.emitRaw(`execute if score ${matchedVar}
|
|
1242
|
+
this.builder.emitRaw(`execute if score ${matchedVar} ${exports.LOWERING_OBJ} matches ..0 if score ${subject} ${exports.LOWERING_OBJ} matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
|
|
1221
1243
|
this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true);
|
|
1222
1244
|
}
|
|
1223
1245
|
if (defaultArm) {
|
|
1224
1246
|
const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
|
|
1225
|
-
this.builder.emitRaw(`execute if score ${matchedVar}
|
|
1247
|
+
this.builder.emitRaw(`execute if score ${matchedVar} ${exports.LOWERING_OBJ} matches ..0 run function ${this.namespace}:${subFnName}`);
|
|
1226
1248
|
this.emitMatchArmSubFunction(subFnName, matchedVar, defaultArm.body, false);
|
|
1227
1249
|
}
|
|
1228
1250
|
}
|
|
@@ -1237,7 +1259,7 @@ class Lowering {
|
|
|
1237
1259
|
this.blockPosVars = new Map(savedBlockPosVars);
|
|
1238
1260
|
this.builder.startBlock('entry');
|
|
1239
1261
|
if (setMatched) {
|
|
1240
|
-
this.builder.emitRaw(`scoreboard players set ${matchedVar}
|
|
1262
|
+
this.builder.emitRaw(`scoreboard players set ${matchedVar} ${exports.LOWERING_OBJ} 1`);
|
|
1241
1263
|
}
|
|
1242
1264
|
this.lowerBlock(body);
|
|
1243
1265
|
if (!this.builder.isBlockSealed()) {
|
|
@@ -1269,7 +1291,7 @@ class Lowering {
|
|
|
1269
1291
|
}
|
|
1270
1292
|
this.builder.emitAssign(indexVar, { kind: 'const', value: 0 });
|
|
1271
1293
|
this.builder.emitAssign(oneVar, { kind: 'const', value: 1 });
|
|
1272
|
-
this.builder.emitRaw(`execute store result score ${lengthVar}
|
|
1294
|
+
this.builder.emitRaw(`execute store result score ${lengthVar} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}`);
|
|
1273
1295
|
const checkLabel = this.builder.freshLabel('foreach_array_check');
|
|
1274
1296
|
const bodyLabel = this.builder.freshLabel('foreach_array_body');
|
|
1275
1297
|
const exitLabel = this.builder.freshLabel('foreach_array_exit');
|
|
@@ -1282,7 +1304,7 @@ class Lowering {
|
|
|
1282
1304
|
this.builder.emitAssign(bindingVar, element);
|
|
1283
1305
|
this.lowerBlock(stmt.body);
|
|
1284
1306
|
if (!this.builder.isBlockSealed()) {
|
|
1285
|
-
this.builder.emitRaw(`scoreboard players operation ${indexVar}
|
|
1307
|
+
this.builder.emitRaw(`scoreboard players operation ${indexVar} ${exports.LOWERING_OBJ} += ${oneVar} ${exports.LOWERING_OBJ}`);
|
|
1286
1308
|
this.builder.emitJump(checkLabel);
|
|
1287
1309
|
}
|
|
1288
1310
|
this.builder.startBlock(exitLabel);
|
|
@@ -1596,7 +1618,7 @@ class Lowering {
|
|
|
1596
1618
|
if (mapped && mapped.startsWith('@e[tag=__rs_obj_')) {
|
|
1597
1619
|
// World object field access → scoreboard get
|
|
1598
1620
|
const dst = this.builder.freshTemp();
|
|
1599
|
-
this.builder.emitRaw(`scoreboard players operation ${dst}
|
|
1621
|
+
this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${mapped} ${exports.LOWERING_OBJ}`);
|
|
1600
1622
|
return { kind: 'var', name: dst };
|
|
1601
1623
|
}
|
|
1602
1624
|
if (varType?.kind === 'struct') {
|
|
@@ -1604,13 +1626,13 @@ class Lowering {
|
|
|
1604
1626
|
const path = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
|
|
1605
1627
|
const dst = this.builder.freshTemp();
|
|
1606
1628
|
// Read from NBT storage into scoreboard
|
|
1607
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1629
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${path}`);
|
|
1608
1630
|
return { kind: 'var', name: dst };
|
|
1609
1631
|
}
|
|
1610
1632
|
// Array length property
|
|
1611
1633
|
if (varType?.kind === 'array' && expr.field === 'len') {
|
|
1612
1634
|
const dst = this.builder.freshTemp();
|
|
1613
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1635
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${expr.obj.name}`);
|
|
1614
1636
|
return { kind: 'var', name: dst };
|
|
1615
1637
|
}
|
|
1616
1638
|
}
|
|
@@ -1626,10 +1648,10 @@ class Lowering {
|
|
|
1626
1648
|
const value = this.lowerExpr(expr.value);
|
|
1627
1649
|
if (expr.op === '=') {
|
|
1628
1650
|
if (value.kind === 'const') {
|
|
1629
|
-
this.builder.emitRaw(`scoreboard players set ${mapped}
|
|
1651
|
+
this.builder.emitRaw(`scoreboard players set ${mapped} ${exports.LOWERING_OBJ} ${value.value}`);
|
|
1630
1652
|
}
|
|
1631
1653
|
else if (value.kind === 'var') {
|
|
1632
|
-
this.builder.emitRaw(`scoreboard players operation ${mapped}
|
|
1654
|
+
this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} = ${value.name} ${exports.LOWERING_OBJ}`);
|
|
1633
1655
|
}
|
|
1634
1656
|
}
|
|
1635
1657
|
else {
|
|
@@ -1639,10 +1661,10 @@ class Lowering {
|
|
|
1639
1661
|
if (value.kind === 'const') {
|
|
1640
1662
|
const constTemp = this.builder.freshTemp();
|
|
1641
1663
|
this.builder.emitAssign(constTemp, value);
|
|
1642
|
-
this.builder.emitRaw(`scoreboard players operation ${mapped}
|
|
1664
|
+
this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} ${opMap[binOp]} ${constTemp} ${exports.LOWERING_OBJ}`);
|
|
1643
1665
|
}
|
|
1644
1666
|
else if (value.kind === 'var') {
|
|
1645
|
-
this.builder.emitRaw(`scoreboard players operation ${mapped}
|
|
1667
|
+
this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports.LOWERING_OBJ} ${opMap[binOp]} ${value.name} ${exports.LOWERING_OBJ}`);
|
|
1646
1668
|
}
|
|
1647
1669
|
}
|
|
1648
1670
|
return { kind: 'const', value: 0 };
|
|
@@ -1656,16 +1678,16 @@ class Lowering {
|
|
|
1656
1678
|
this.builder.emitRaw(`data modify storage ${path} set value ${value.value}`);
|
|
1657
1679
|
}
|
|
1658
1680
|
else if (value.kind === 'var') {
|
|
1659
|
-
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name}
|
|
1681
|
+
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
|
|
1660
1682
|
}
|
|
1661
1683
|
}
|
|
1662
1684
|
else {
|
|
1663
1685
|
// Compound assignment: read, modify, write back
|
|
1664
1686
|
const dst = this.builder.freshTemp();
|
|
1665
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1687
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${path}`);
|
|
1666
1688
|
const binOp = expr.op.slice(0, -1);
|
|
1667
1689
|
this.builder.emitBinop(dst, { kind: 'var', name: dst }, binOp, value);
|
|
1668
|
-
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst}
|
|
1690
|
+
this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} ${exports.LOWERING_OBJ}`);
|
|
1669
1691
|
}
|
|
1670
1692
|
return { kind: 'const', value: 0 };
|
|
1671
1693
|
}
|
|
@@ -1694,14 +1716,14 @@ class Lowering {
|
|
|
1694
1716
|
this.builder.emitAssign(dst, left);
|
|
1695
1717
|
const rightVar = this.operandToVar(right);
|
|
1696
1718
|
// dst = dst && right → if dst != 0 then dst = right
|
|
1697
|
-
this.builder.emitRaw(`execute if score ${dst}
|
|
1719
|
+
this.builder.emitRaw(`execute if score ${dst} ${exports.LOWERING_OBJ} matches 1.. run scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${rightVar} ${exports.LOWERING_OBJ}`);
|
|
1698
1720
|
}
|
|
1699
1721
|
else {
|
|
1700
1722
|
// Short-circuit OR
|
|
1701
1723
|
this.builder.emitAssign(dst, left);
|
|
1702
1724
|
const rightVar = this.operandToVar(right);
|
|
1703
1725
|
// dst = dst || right → if dst == 0 then dst = right
|
|
1704
|
-
this.builder.emitRaw(`execute if score ${dst}
|
|
1726
|
+
this.builder.emitRaw(`execute if score ${dst} ${exports.LOWERING_OBJ} matches ..0 run scoreboard players operation ${dst} ${exports.LOWERING_OBJ} = ${rightVar} ${exports.LOWERING_OBJ}`);
|
|
1705
1727
|
}
|
|
1706
1728
|
return { kind: 'var', name: dst };
|
|
1707
1729
|
}
|
|
@@ -1719,16 +1741,16 @@ class Lowering {
|
|
|
1719
1741
|
// Divide by 1000 to correct for double scaling
|
|
1720
1742
|
const constDiv = this.builder.freshTemp();
|
|
1721
1743
|
this.builder.emitAssign(constDiv, { kind: 'const', value: 1000 });
|
|
1722
|
-
this.builder.emitRaw(`scoreboard players operation ${dst}
|
|
1744
|
+
this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} /= ${constDiv} ${exports.LOWERING_OBJ}`);
|
|
1723
1745
|
}
|
|
1724
1746
|
else {
|
|
1725
1747
|
// Division: a * 1000 / b
|
|
1726
1748
|
const constMul = this.builder.freshTemp();
|
|
1727
1749
|
this.builder.emitAssign(constMul, { kind: 'const', value: 1000 });
|
|
1728
1750
|
this.builder.emitAssign(dst, left);
|
|
1729
|
-
this.builder.emitRaw(`scoreboard players operation ${dst}
|
|
1751
|
+
this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} *= ${constMul} ${exports.LOWERING_OBJ}`);
|
|
1730
1752
|
const rightVar = this.operandToVar(right);
|
|
1731
|
-
this.builder.emitRaw(`scoreboard players operation ${dst}
|
|
1753
|
+
this.builder.emitRaw(`scoreboard players operation ${dst} ${exports.LOWERING_OBJ} /= ${rightVar} ${exports.LOWERING_OBJ}`);
|
|
1732
1754
|
}
|
|
1733
1755
|
return { kind: 'var', name: dst };
|
|
1734
1756
|
}
|
|
@@ -1798,7 +1820,7 @@ class Lowering {
|
|
|
1798
1820
|
const storagePath = this.getStringStoragePath(expr.args[0]);
|
|
1799
1821
|
if (storagePath) {
|
|
1800
1822
|
const dst = this.builder.freshTemp();
|
|
1801
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1823
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storagePath}`);
|
|
1802
1824
|
return { kind: 'var', name: dst };
|
|
1803
1825
|
}
|
|
1804
1826
|
const staticString = this.resolveStaticString(expr.args[0]);
|
|
@@ -1832,7 +1854,7 @@ class Lowering {
|
|
|
1832
1854
|
const entity = this.exprToString(expr.args[0]);
|
|
1833
1855
|
const tagName = this.exprToString(expr.args[1]);
|
|
1834
1856
|
const dst = this.builder.freshTemp();
|
|
1835
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1857
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} if entity ${entity}[tag=${tagName}]`);
|
|
1836
1858
|
return { kind: 'var', name: dst };
|
|
1837
1859
|
}
|
|
1838
1860
|
// Handle array push
|
|
@@ -1847,7 +1869,7 @@ class Lowering {
|
|
|
1847
1869
|
}
|
|
1848
1870
|
else if (value.kind === 'var') {
|
|
1849
1871
|
this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value 0`);
|
|
1850
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name}
|
|
1872
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
|
|
1851
1873
|
}
|
|
1852
1874
|
}
|
|
1853
1875
|
return { kind: 'const', value: 0 };
|
|
@@ -1856,7 +1878,7 @@ class Lowering {
|
|
|
1856
1878
|
const arrName = this.getArrayStorageName(expr.args[0]);
|
|
1857
1879
|
const dst = this.builder.freshTemp();
|
|
1858
1880
|
if (arrName) {
|
|
1859
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
1881
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrName}[-1]`);
|
|
1860
1882
|
this.builder.emitRaw(`data remove storage rs:heap ${arrName}[-1]`);
|
|
1861
1883
|
}
|
|
1862
1884
|
else {
|
|
@@ -2120,7 +2142,7 @@ class Lowering {
|
|
|
2120
2142
|
const dst = this.builder.freshTemp();
|
|
2121
2143
|
const min = args[0] ? this.exprToLiteral(args[0]) : '0';
|
|
2122
2144
|
const max = args[1] ? this.exprToLiteral(args[1]) : '100';
|
|
2123
|
-
this.builder.emitRaw(`scoreboard players random ${dst}
|
|
2145
|
+
this.builder.emitRaw(`scoreboard players random ${dst} ${exports.LOWERING_OBJ} ${min} ${max}`);
|
|
2124
2146
|
return { kind: 'var', name: dst };
|
|
2125
2147
|
}
|
|
2126
2148
|
// Special case: random_native - /random value (MC 1.20.3+)
|
|
@@ -2128,7 +2150,7 @@ class Lowering {
|
|
|
2128
2150
|
const dst = this.builder.freshTemp();
|
|
2129
2151
|
const min = args[0] ? this.exprToLiteral(args[0]) : '0';
|
|
2130
2152
|
const max = args[1] ? this.exprToLiteral(args[1]) : '100';
|
|
2131
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2153
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run random value ${min} ${max}`);
|
|
2132
2154
|
return { kind: 'var', name: dst };
|
|
2133
2155
|
}
|
|
2134
2156
|
// Special case: random_sequence - /random reset (MC 1.20.3+)
|
|
@@ -2143,7 +2165,7 @@ class Lowering {
|
|
|
2143
2165
|
const dst = this.builder.freshTemp();
|
|
2144
2166
|
const player = this.exprToTargetString(args[0]);
|
|
2145
2167
|
const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan);
|
|
2146
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2168
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run scoreboard players get ${player} ${objective}`);
|
|
2147
2169
|
return { kind: 'var', name: dst };
|
|
2148
2170
|
}
|
|
2149
2171
|
// Special case: scoreboard_set — write to vanilla MC scoreboard
|
|
@@ -2157,7 +2179,7 @@ class Lowering {
|
|
|
2157
2179
|
else if (value.kind === 'var') {
|
|
2158
2180
|
// Read directly from the computed scoreboard temp. Routing through a fresh
|
|
2159
2181
|
// temp here breaks once optimization removes the apparently-dead assign.
|
|
2160
|
-
this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name}
|
|
2182
|
+
this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} ${exports.LOWERING_OBJ}`);
|
|
2161
2183
|
}
|
|
2162
2184
|
return { kind: 'const', value: 0 };
|
|
2163
2185
|
}
|
|
@@ -2220,7 +2242,7 @@ class Lowering {
|
|
|
2220
2242
|
}
|
|
2221
2243
|
if (name === 'bossbar_get_value') {
|
|
2222
2244
|
const dst = this.builder.freshTemp();
|
|
2223
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2245
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run bossbar get ${this.exprToString(args[0])} value`);
|
|
2224
2246
|
return { kind: 'var', name: dst };
|
|
2225
2247
|
}
|
|
2226
2248
|
if (name === 'team_add') {
|
|
@@ -2261,7 +2283,7 @@ class Lowering {
|
|
|
2261
2283
|
: this.exprToString(args[1]);
|
|
2262
2284
|
const path = this.exprToString(args[2]);
|
|
2263
2285
|
const scale = args[3] ? this.exprToString(args[3]) : '1';
|
|
2264
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2286
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get ${targetType} ${target} ${path} ${scale}`);
|
|
2265
2287
|
return { kind: 'var', name: dst };
|
|
2266
2288
|
}
|
|
2267
2289
|
// storage_get_int(storage_ns, array_key, index) -> int
|
|
@@ -2270,7 +2292,7 @@ class Lowering {
|
|
|
2270
2292
|
// array_key : e.g. "sin"
|
|
2271
2293
|
// index : integer index (const or runtime)
|
|
2272
2294
|
//
|
|
2273
|
-
// Const index: execute store result score $dst
|
|
2295
|
+
// Const index: execute store result score $dst ${LOWERING_OBJ} run data get storage math:tables sin[N] 1
|
|
2274
2296
|
// Runtime index: macro sub-function via rs:heap, mirrors readArrayElement.
|
|
2275
2297
|
if (name === 'storage_get_int') {
|
|
2276
2298
|
const storageNs = this.exprToString(args[0]); // "math:tables"
|
|
@@ -2278,7 +2300,7 @@ class Lowering {
|
|
|
2278
2300
|
const indexOperand = this.lowerExpr(args[2]);
|
|
2279
2301
|
const dst = this.builder.freshTemp();
|
|
2280
2302
|
if (indexOperand.kind === 'const') {
|
|
2281
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2303
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`);
|
|
2282
2304
|
}
|
|
2283
2305
|
else {
|
|
2284
2306
|
// Runtime index: store the index into rs:heap under a unique key,
|
|
@@ -2288,13 +2310,13 @@ class Lowering {
|
|
|
2288
2310
|
const indexVar = indexOperand.kind === 'var'
|
|
2289
2311
|
? indexOperand.name
|
|
2290
2312
|
: this.operandToVar(indexOperand);
|
|
2291
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar}
|
|
2313
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
|
|
2292
2314
|
this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
|
|
2293
2315
|
// Prefix \x01 is a sentinel for the MC macro '$' line-start marker.
|
|
2294
2316
|
// We avoid using literal '$execute' here so the pre-alloc pass
|
|
2295
2317
|
// doesn't mistakenly register 'execute' as a scoreboard variable.
|
|
2296
2318
|
// Codegen replaces \x01 → '$' when emitting the mc function file.
|
|
2297
|
-
this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst}
|
|
2319
|
+
this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`);
|
|
2298
2320
|
}
|
|
2299
2321
|
return { kind: 'var', name: dst };
|
|
2300
2322
|
}
|
|
@@ -2316,7 +2338,7 @@ class Lowering {
|
|
|
2316
2338
|
// value : integer value to write (const or runtime)
|
|
2317
2339
|
//
|
|
2318
2340
|
// Const index + const value:
|
|
2319
|
-
// execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V
|
|
2341
|
+
// execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V ${LOWERING_OBJ} V
|
|
2320
2342
|
// Runtime index or value: macro sub-function via rs:heap
|
|
2321
2343
|
if (name === 'storage_set_int') {
|
|
2322
2344
|
const storageNs = this.exprToString(args[0]);
|
|
@@ -2328,7 +2350,7 @@ class Lowering {
|
|
|
2328
2350
|
const valVar = valueOperand.kind === 'var'
|
|
2329
2351
|
? valueOperand.name
|
|
2330
2352
|
: this.operandToVar(valueOperand);
|
|
2331
|
-
this.builder.emitRaw(`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar}
|
|
2353
|
+
this.builder.emitRaw(`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
|
|
2332
2354
|
}
|
|
2333
2355
|
else {
|
|
2334
2356
|
// Runtime index: we need a macro sub-function.
|
|
@@ -2343,16 +2365,16 @@ class Lowering {
|
|
|
2343
2365
|
const valVar = valueOperand.kind === 'var'
|
|
2344
2366
|
? valueOperand.name
|
|
2345
2367
|
: this.operandToVar(valueOperand);
|
|
2346
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar}
|
|
2368
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
|
|
2347
2369
|
// Pin valVar in the optimizer's read-set so the assignment is not dead-code-eliminated.
|
|
2348
2370
|
// The value is stored to rs:heap but NOT used by the macro (the macro reads the scoreboard
|
|
2349
2371
|
// slot directly to avoid the MC 'data modify set value $(n)' macro substitution bug).
|
|
2350
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar}
|
|
2372
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
|
|
2351
2373
|
this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
|
|
2352
2374
|
// Use execute store result (not 'data modify set value $(val)') to avoid MC macro
|
|
2353
2375
|
// substitution bugs with numeric values. The scoreboard slot ${valVar} is hardcoded
|
|
2354
2376
|
// into the macro sub-function at compile time — only the array index is macro-substituted.
|
|
2355
|
-
this.emitRawSubFunction(subFnName, `\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar}
|
|
2377
|
+
this.emitRawSubFunction(subFnName, `\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} ${exports.LOWERING_OBJ}`);
|
|
2356
2378
|
}
|
|
2357
2379
|
return { kind: 'const', value: 0 };
|
|
2358
2380
|
}
|
|
@@ -2398,7 +2420,7 @@ class Lowering {
|
|
|
2398
2420
|
const dst = this.builder.freshTemp();
|
|
2399
2421
|
const setId = this.exprToString(args[0]);
|
|
2400
2422
|
const value = this.exprToString(args[1]);
|
|
2401
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
2423
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} if data storage rs:sets ${setId}[{value:${value}}]`);
|
|
2402
2424
|
return { kind: 'var', name: dst };
|
|
2403
2425
|
}
|
|
2404
2426
|
if (name === 'set_remove') {
|
|
@@ -2663,7 +2685,7 @@ class Lowering {
|
|
|
2663
2685
|
components.push({ text: operand.value.toString() });
|
|
2664
2686
|
return;
|
|
2665
2687
|
}
|
|
2666
|
-
components.push({ score: { name: this.operandToVar(operand), objective:
|
|
2688
|
+
components.push({ score: { name: this.operandToVar(operand), objective: exports.LOWERING_OBJ } });
|
|
2667
2689
|
}
|
|
2668
2690
|
exprToString(expr) {
|
|
2669
2691
|
switch (expr.kind) {
|
|
@@ -2816,11 +2838,19 @@ class Lowering {
|
|
|
2816
2838
|
}
|
|
2817
2839
|
exprToScoreboardObjective(expr, span) {
|
|
2818
2840
|
if (expr.kind === 'mc_name') {
|
|
2819
|
-
|
|
2841
|
+
// 'rs' is the canonical token for the current RS scoreboard objective.
|
|
2842
|
+
// Resolve to LOWERING_OBJ so it respects --scoreboard / namespace default.
|
|
2843
|
+
return expr.value === 'rs' ? exports.LOWERING_OBJ : expr.value;
|
|
2820
2844
|
}
|
|
2821
2845
|
const objective = this.exprToString(expr);
|
|
2822
2846
|
if (objective.startsWith('#') || objective.includes('.')) {
|
|
2823
|
-
|
|
2847
|
+
if (objective.startsWith('#')) {
|
|
2848
|
+
const name = objective.slice(1);
|
|
2849
|
+
// '#rs' is the canonical way to reference the current RS scoreboard objective.
|
|
2850
|
+
// Resolve to LOWERING_OBJ so it tracks the --scoreboard option.
|
|
2851
|
+
return name === 'rs' ? exports.LOWERING_OBJ : name;
|
|
2852
|
+
}
|
|
2853
|
+
return objective;
|
|
2824
2854
|
}
|
|
2825
2855
|
return `${this.getObjectiveNamespace(span)}.${objective}`;
|
|
2826
2856
|
}
|
|
@@ -2836,7 +2866,7 @@ class Lowering {
|
|
|
2836
2866
|
if (!filePath) {
|
|
2837
2867
|
return this.namespace;
|
|
2838
2868
|
}
|
|
2839
|
-
return this.isStdlibFile(filePath) ?
|
|
2869
|
+
return this.isStdlibFile(filePath) ? exports.LOWERING_OBJ : this.namespace;
|
|
2840
2870
|
}
|
|
2841
2871
|
tryGetStdlibInternalObjective(playerExpr, objectiveExpr, span) {
|
|
2842
2872
|
if (!span || !this.currentStdlibCallSite || objectiveExpr.kind !== 'mc_name' || objectiveExpr.value !== 'rs') {
|
|
@@ -2851,7 +2881,7 @@ class Lowering {
|
|
|
2851
2881
|
return null;
|
|
2852
2882
|
}
|
|
2853
2883
|
const hash = this.shortHash(this.serializeCallSite(this.currentStdlibCallSite));
|
|
2854
|
-
return
|
|
2884
|
+
return `${exports.LOWERING_OBJ}._${resourceBase}_${hash}`;
|
|
2855
2885
|
}
|
|
2856
2886
|
getStdlibInternalResourceBase(playerExpr) {
|
|
2857
2887
|
if (!playerExpr || playerExpr.kind !== 'str_lit') {
|
|
@@ -3138,15 +3168,15 @@ class Lowering {
|
|
|
3138
3168
|
readArrayElement(arrayName, index) {
|
|
3139
3169
|
const dst = this.builder.freshTemp();
|
|
3140
3170
|
if (index.kind === 'const') {
|
|
3141
|
-
this.builder.emitRaw(`execute store result score ${dst}
|
|
3171
|
+
this.builder.emitRaw(`execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[${index.value}]`);
|
|
3142
3172
|
return { kind: 'var', name: dst };
|
|
3143
3173
|
}
|
|
3144
3174
|
const macroKey = `__rs_index_${this.foreachCounter++}`;
|
|
3145
3175
|
const subFnName = `${this.currentFn}/array_get_${this.foreachCounter++}`;
|
|
3146
3176
|
const indexVar = index.kind === 'var' ? index.name : this.operandToVar(index);
|
|
3147
|
-
this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar}
|
|
3177
|
+
this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports.LOWERING_OBJ}`);
|
|
3148
3178
|
this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
|
|
3149
|
-
this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst}
|
|
3179
|
+
this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} ${exports.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
|
|
3150
3180
|
return { kind: 'var', name: dst };
|
|
3151
3181
|
}
|
|
3152
3182
|
emitRawSubFunction(name, ...commands) {
|
|
@@ -17,6 +17,7 @@ export interface CommandFunction {
|
|
|
17
17
|
name: string;
|
|
18
18
|
commands: IRCommand[];
|
|
19
19
|
}
|
|
20
|
+
export declare function setOptimizerObjective(obj: string): void;
|
|
20
21
|
export declare function createEmptyOptimizationStats(): OptimizationStats;
|
|
21
22
|
export declare function mergeOptimizationStats(base: OptimizationStats, delta: Partial<OptimizationStats>): void;
|
|
22
23
|
export declare function applyLICM(functions: CommandFunction[]): {
|