redscript-mc 1.2.27 → 1.2.29
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 +5 -0
- package/dist/compile.js +9 -1
- package/dist/index.d.ts +5 -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/out/extension.js +1797 -1157
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/editors/vscode/src/hover.ts +18 -0
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +12 -3
- 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 +16 -2
- package/src/index.ts +18 -7
- package/src/lowering/index.ts +95 -64
- package/src/optimizer/commands.ts +18 -12
- package/src/optimizer/structure.ts +6 -2
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<img src="./logo.png" alt="RedScript Logo" width="64" />
|
|
4
4
|
|
|
5
|
-
<img src="https://img.shields.io/badge/RedScript-1.2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-1.2.27-red?style=for-the-badge&logo=minecraft&logoColor=white" alt="RedScript" />
|
|
6
6
|
|
|
7
7
|
**A typed scripting language that compiles to Minecraft datapacks.**
|
|
8
8
|
|
|
9
9
|
Write clean game logic. RedScript handles the scoreboard spaghetti.
|
|
10
10
|
|
|
11
11
|
[](https://github.com/bkmashiro/redscript/actions/workflows/ci.yml)
|
|
12
|
-
[](https://github.com/bkmashiro/redscript)
|
|
13
13
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
14
14
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
15
15
|
[](https://marketplace.visualstudio.com/items?itemName=bkmashiro.redscript-vscode)
|
|
@@ -76,17 +76,6 @@ fn stop() {
|
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
79
|
-
### What's New in v1.2.26
|
|
80
|
-
|
|
81
|
-
- **Math stdlib** (`math.mcrs`): 18 fixed-point functions — `abs`, `sign`, `min`, `max`, `clamp`, `lerp`, `isqrt`, `sqrt_fixed`, `pow_int`, `gcd`, `lcm`, `sin_fixed`, `cos_fixed`, `map`, `ceil_div`, `log2_int`, `mulfix`, `divfix`, `smoothstep`, `smootherstep`
|
|
82
|
-
- **Vector stdlib** (`vec.mcrs`): 2D and 3D geometry — dot/cross products, `length2d_fixed`, `atan2_fixed` (binary search, O(log 46)), `normalize2d`, `rotate2d`, `lerp2d`, full 3D cross product
|
|
83
|
-
- **Advanced stdlib** (`advanced.mcrs`): number theory (`fib`, `is_prime`, `collatz_steps`, `gcd`, `mod_pow`), hash/noise (`hash_int` splitmix32, `noise1d`), curves (`bezier_quad`), fractals (`mandelbrot_iter`, `julia_iter`), geometry experiments
|
|
84
|
-
- **BigInt** (`bigint.mcrs`): arbitrary precision integers — base 10,000 × 8 limbs = up to 32 decimal digits; `bigint_add/sub/compare/mul/fib` running on MC scoreboard + NBT storage
|
|
85
|
-
- **`module library;` pragma**: declare a file as a library; functions are tree-shaken out unless called — stdlib never bloats your pack
|
|
86
|
-
- **`storage_get_int` / `storage_set_int` builtins**: dynamic NBT int array read/write with runtime indices via MC 1.20.2 macro sub-functions
|
|
87
|
-
- **`@require_on_load(fn)` decorator**: declarative load-time dependency tracking for stdlib initializers (sin/cos table setup etc.)
|
|
88
|
-
- **Compiler fixes**: `isqrt` large-number convergence, optimizer copy propagation alias invalidation, cross-function variable collision, MCRuntime array-index regex
|
|
89
|
-
|
|
90
79
|
### What's New in v1.2.25
|
|
91
80
|
|
|
92
81
|
- `impl` blocks and methods for object-style APIs on structs
|
|
@@ -343,8 +332,30 @@ fn show_fib() {
|
|
|
343
332
|
|
|
344
333
|
---
|
|
345
334
|
|
|
335
|
+
### What's New in v1.2.27
|
|
336
|
+
|
|
337
|
+
- **BigInt confirmed working in real Minecraft** (`bigint.mcrs`): arbitrary precision integers on MC scoreboard + NBT — base 10,000 × 8 limbs = up to 32 decimal digits; `bigint_add`, `bigint_sub`, `bigint_compare`, `bigint_mul`, `bigint_fib(50)` = 12,586,269,025 all verified on Paper 1.21.4
|
|
338
|
+
- **`storage_set_int` macro fix**: dynamic NBT array writes now use `execute store result storage` instead of `data modify set value $(n)` — avoids a silent Minecraft macro substitution bug with integer values
|
|
339
|
+
- **Full stdlib** (`math.mcrs`, `vec.mcrs`, `advanced.mcrs`, `bigint.mcrs`, `showcase.mcrs`): 18 math functions, 14 vector geometry functions, 20+ advanced number-theory and fractal functions
|
|
340
|
+
- **`module library;` pragma**: tree-shaking for library files — stdlib never bloats your pack
|
|
341
|
+
- **`storage_get_int` / `storage_set_int` builtins**: dynamic NBT int array read/write with runtime indices via MC 1.20.2+ macro sub-functions
|
|
342
|
+
- **`@require_on_load(fn)` decorator**: declarative load-time dependency tracking for sin/cos/atan table initializers
|
|
343
|
+
|
|
344
|
+
### What's New in v1.2.26
|
|
345
|
+
|
|
346
|
+
- **Math stdlib** (`math.mcrs`): 18 fixed-point functions — `abs`, `sign`, `min`, `max`, `clamp`, `lerp`, `isqrt`, `sqrt_fixed`, `pow_int`, `gcd`, `lcm`, `sin_fixed`, `cos_fixed`, `map`, `ceil_div`, `log2_int`, `mulfix`, `divfix`, `smoothstep`, `smootherstep`
|
|
347
|
+
- **Vector stdlib** (`vec.mcrs`): 2D and 3D geometry — dot/cross products, `length2d_fixed`, `atan2_fixed` (binary search, O(log 46)), `normalize2d`, `rotate2d`, `lerp2d`, full 3D cross product
|
|
348
|
+
- **Advanced stdlib** (`advanced.mcrs`): number theory (`fib`, `is_prime`, `collatz_steps`, `gcd`, `mod_pow`), hash/noise (`hash_int` splitmix32, `noise1d`), curves (`bezier_quad`), fractals (`mandelbrot_iter`, `julia_iter`), geometry experiments
|
|
349
|
+
- **BigInt** (`bigint.mcrs`): arbitrary precision integers — base 10,000 × 8 limbs = up to 32 decimal digits; `bigint_add/sub/compare/mul/fib` running on MC scoreboard + NBT storage
|
|
350
|
+
- **Compiler fixes**: `isqrt` large-number convergence, optimizer copy propagation alias invalidation, cross-function variable collision, MCRuntime array-index regex
|
|
351
|
+
|
|
346
352
|
### Changelog Highlights
|
|
347
353
|
|
|
354
|
+
#### v1.2.27 (2026-03-14)
|
|
355
|
+
|
|
356
|
+
- **BigInt real-MC fix**: `storage_set_int` macro now uses `execute store result storage` instead of `data modify set value $(n)` — avoids a Minecraft macro substitution bug with integer values; BigInt confirmed working on Paper 1.21.4
|
|
357
|
+
- **showcase**: `atan2_fixed` returns degrees (0–360), not millidegrees; fixed over-division in examples; `mod_pow` test cases use small safe-range moduli (no INT32 overflow)
|
|
358
|
+
|
|
348
359
|
#### v1.2.26 (2026-03-14)
|
|
349
360
|
|
|
350
361
|
- Full math/vector/advanced/bigint standard library (see above)
|
package/README.zh.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<img src="./logo.png" alt="RedScript Logo" width="64" />
|
|
4
4
|
|
|
5
|
-
<img src="https://img.shields.io/badge/RedScript-1.2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-1.2.27-red?style=for-the-badge&logo=minecraft&logoColor=white" alt="RedScript" />
|
|
6
6
|
|
|
7
7
|
**一个编译到 Minecraft Datapack 的类型化脚本语言。**
|
|
8
8
|
|
|
9
9
|
写干净的游戏逻辑,把记分板的面条代码交给 RedScript 处理。
|
|
10
10
|
|
|
11
11
|
[](https://github.com/bkmashiro/redscript/actions/workflows/ci.yml)
|
|
12
|
-
[](https://github.com/bkmashiro/redscript)
|
|
13
13
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
14
14
|
[](https://www.npmjs.com/package/redscript-mc)
|
|
15
15
|
[](https://marketplace.visualstudio.com/items?itemName=bkmashiro.redscript-vscode)
|
|
@@ -73,14 +73,20 @@ let running: bool = false;
|
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
+
### v1.2.27 新增内容
|
|
77
|
+
|
|
78
|
+
- **BigInt 实机验证通过** (`bigint.mcrs`):任意精度整数在 MC 记分板 + NBT 上完整运行 — base 10000 × 8 limbs = 最多 32 位十进制数;`bigint_fib(50)` = 12,586,269,025 在 Paper 1.21.4 实机验证正确
|
|
79
|
+
- **`storage_set_int` 宏修复**:动态 NBT 数组写入改用 `execute store result storage` 而非 `data modify set value $(n)` — 规避 Minecraft 宏机制对整数值的静默替换 bug
|
|
80
|
+
- **完整标准库** (`math.mcrs`、`vec.mcrs`、`advanced.mcrs`、`bigint.mcrs`、`showcase.mcrs`):18 个数学函数、14 个向量几何函数、20+ 数论与分形函数
|
|
81
|
+
- **`module library;` pragma**:库文件零成本树摇 — 标准库永远不会撑大你的数据包
|
|
82
|
+
- **`@require_on_load(fn)` 装饰器**:sin/cos/atan 查找表初始化器的声明式加载依赖跟踪
|
|
83
|
+
|
|
76
84
|
### v1.2.26 新增内容
|
|
77
85
|
|
|
78
86
|
- **数学标准库** (`math.mcrs`):18 个定点数函数 — `abs`、`sign`、`min`、`max`、`clamp`、`lerp`、`isqrt`、`sqrt_fixed`、`pow_int`、`gcd`、`lcm`、`sin_fixed`、`cos_fixed`、`map`、`ceil_div`、`log2_int`、`mulfix`、`divfix`、`smoothstep`、`smootherstep`
|
|
79
87
|
- **向量标准库** (`vec.mcrs`):2D / 3D 几何 — 点积/叉积、`length2d_fixed`、`atan2_fixed`(二分搜索正切表,O(log 46))、`normalize2d`、`rotate2d`、`lerp2d`、完整 3D 叉积
|
|
80
88
|
- **高级标准库** (`advanced.mcrs`):数论(`fib`、`is_prime`、`collatz_steps`、`mod_pow`)、哈希/噪声(splitmix32 `hash_int`、`noise1d`)、曲线(`bezier_quad`)、分形(`mandelbrot_iter`、`julia_iter`)、几何实验
|
|
81
|
-
- **BigInt** (`bigint.mcrs`)
|
|
82
|
-
- **`module library;` pragma**:将文件声明为库,未被调用的函数会被树摇消除 — 标准库永远不会撑大你的数据包
|
|
83
|
-
- **`storage_get_int` / `storage_set_int` 内置函数**:通过 MC 1.20.2 宏子函数实现动态 NBT int 数组读写
|
|
89
|
+
- **BigInt** (`bigint.mcrs`):任意精度整数架构设计 — base 10000 × 8 limbs = 最多 32 位十进制数
|
|
84
90
|
- **编译器修复**:`isqrt` 大数收敛、优化器拷贝传播别名失效、跨函数变量命名冲突、MCRuntime 数组索引正则
|
|
85
91
|
|
|
86
92
|
### v1.2.25 新增内容
|
|
@@ -337,6 +343,11 @@ fn show_fib() {
|
|
|
337
343
|
|
|
338
344
|
### 更新日志亮点
|
|
339
345
|
|
|
346
|
+
#### v1.2.27(2026-03-14)
|
|
347
|
+
|
|
348
|
+
- **BigInt 实机修复**:`storage_set_int` 宏改用 `execute store result storage`,规避 MC 宏整数替换 bug;BigInt 在 Paper 1.21.4 实机验证通过
|
|
349
|
+
- showcase 示例修复:`atan2_fixed` 返回度数(0–360),更正不必要的除以 1000;`mod_pow` 测试改用小 modulus 避免 INT32 溢出
|
|
350
|
+
|
|
340
351
|
#### v1.2.26(2026-03-14)
|
|
341
352
|
|
|
342
353
|
- 完整的数学/向量/高级/BigInt 标准库(详见上方)
|
|
@@ -77,7 +77,7 @@ describe('CLI API', () => {
|
|
|
77
77
|
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
78
78
|
const result = (0, index_1.compile)(source, { namespace: 'mygame', filePath: mainPath });
|
|
79
79
|
const tickTimer = result.files.find(file => file.path.endsWith('/tick_timer.mcfunction'));
|
|
80
|
-
expect(tickTimer?.content).toContain('scoreboard players set #rs
|
|
80
|
+
expect(tickTimer?.content).toContain('scoreboard players set #rs __mygame.timer_ticks 1');
|
|
81
81
|
});
|
|
82
82
|
it('adds a call-site hash for stdlib internal scoreboard objectives', () => {
|
|
83
83
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stdlib-hash-'));
|
|
@@ -106,7 +106,7 @@ describe('CLI API', () => {
|
|
|
106
106
|
const timerFns = result.files.filter(file => /timer_start__callsite_[0-9a-f]{4}\.mcfunction$/.test(file.path));
|
|
107
107
|
expect(timerFns).toHaveLength(2);
|
|
108
108
|
const objectives = timerFns
|
|
109
|
-
.flatMap(file => [...file.content.matchAll(/
|
|
109
|
+
.flatMap(file => [...file.content.matchAll(/mygame\._timer_([0-9a-f]{4})/g)].map(match => match[0]));
|
|
110
110
|
expect(new Set(objectives).size).toBe(2);
|
|
111
111
|
});
|
|
112
112
|
it('Timer::new creates timer', () => {
|
|
@@ -125,8 +125,8 @@ describe('CLI API', () => {
|
|
|
125
125
|
const result = (0, index_1.compile)(source, { namespace: 'timernew', filePath: mainPath });
|
|
126
126
|
expect(result.typeErrors).toEqual([]);
|
|
127
127
|
const newFn = result.files.find(file => file.path.endsWith('/Timer_new.mcfunction'));
|
|
128
|
-
expect(newFn?.content).toContain('scoreboard players set timer_ticks
|
|
129
|
-
expect(newFn?.content).toContain('scoreboard players set timer_active
|
|
128
|
+
expect(newFn?.content).toContain('scoreboard players set timer_ticks __timernew 0');
|
|
129
|
+
expect(newFn?.content).toContain('scoreboard players set timer_active __timernew 0');
|
|
130
130
|
});
|
|
131
131
|
it('Timer.start/pause/reset', () => {
|
|
132
132
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-state-'));
|
|
@@ -149,9 +149,9 @@ describe('CLI API', () => {
|
|
|
149
149
|
const startFn = result.files.find(file => file.path.endsWith('/Timer_start.mcfunction'));
|
|
150
150
|
const pauseFn = result.files.find(file => file.path.endsWith('/Timer_pause.mcfunction'));
|
|
151
151
|
const resetFn = result.files.find(file => file.path.endsWith('/Timer_reset.mcfunction'));
|
|
152
|
-
expect(startFn?.content).toContain('scoreboard players set timer_active
|
|
153
|
-
expect(pauseFn?.content).toContain('scoreboard players set timer_active
|
|
154
|
-
expect(resetFn?.content).toContain('scoreboard players set timer_ticks
|
|
152
|
+
expect(startFn?.content).toContain('scoreboard players set timer_active __timerstate 1');
|
|
153
|
+
expect(pauseFn?.content).toContain('scoreboard players set timer_active __timerstate 0');
|
|
154
|
+
expect(resetFn?.content).toContain('scoreboard players set timer_ticks __timerstate 0');
|
|
155
155
|
});
|
|
156
156
|
it('Timer.done returns bool', () => {
|
|
157
157
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-done-'));
|
|
@@ -174,9 +174,9 @@ describe('CLI API', () => {
|
|
|
174
174
|
expect(result.typeErrors).toEqual([]);
|
|
175
175
|
const doneFn = result.files.find(file => file.path.endsWith('/Timer_done.mcfunction'));
|
|
176
176
|
const mainFn = result.files.find(file => file.path.endsWith('/main.mcfunction'));
|
|
177
|
-
expect(doneFn?.content).toContain('scoreboard players get timer_ticks
|
|
177
|
+
expect(doneFn?.content).toContain('scoreboard players get timer_ticks __timerdone');
|
|
178
178
|
expect(doneFn?.content).toContain('return run scoreboard players get');
|
|
179
|
-
expect(mainFn?.content).toContain('execute if score $main_finished
|
|
179
|
+
expect(mainFn?.content).toContain('execute if score $main_finished __timerdone matches 1..');
|
|
180
180
|
});
|
|
181
181
|
it('Timer.tick increments', () => {
|
|
182
182
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-tick-'));
|
|
@@ -199,10 +199,10 @@ describe('CLI API', () => {
|
|
|
199
199
|
.filter(file => file.path.includes('/Timer_tick'))
|
|
200
200
|
.map(file => file.content)
|
|
201
201
|
.join('\n');
|
|
202
|
-
expect(tickOutput).toContain('scoreboard players get timer_active
|
|
203
|
-
expect(tickOutput).toContain('scoreboard players get timer_ticks
|
|
204
|
-
expect(tickOutput).toContain(' += $const_1
|
|
205
|
-
expect(tickOutput).toContain('execute store result score timer_ticks
|
|
202
|
+
expect(tickOutput).toContain('scoreboard players get timer_active __timertick');
|
|
203
|
+
expect(tickOutput).toContain('scoreboard players get timer_ticks __timertick');
|
|
204
|
+
expect(tickOutput).toContain(' += $const_1 __timertick');
|
|
205
|
+
expect(tickOutput).toContain('execute store result score timer_ticks __timertick run scoreboard players get $_');
|
|
206
206
|
});
|
|
207
207
|
});
|
|
208
208
|
describe('compile()', () => {
|
|
@@ -26,7 +26,7 @@ fn turret_tick() {
|
|
|
26
26
|
const result = (0, index_1.compile)(source, { namespace: 'test' });
|
|
27
27
|
const parent = getFileContent(result.files, 'data/test/function/turret_tick.mcfunction');
|
|
28
28
|
const loopBody = getFileContent(result.files, 'data/test/function/turret_tick/foreach_0.mcfunction');
|
|
29
|
-
const hoistedRead = 'execute store result score $_0
|
|
29
|
+
const hoistedRead = 'execute store result score $_0 __test run scoreboard players get config test.turret_range';
|
|
30
30
|
const executeCall = 'execute as @e[tag=turret] run function test:turret_tick/foreach_0';
|
|
31
31
|
expect(parent).toContain(hoistedRead);
|
|
32
32
|
expect(parent.indexOf(hoistedRead)).toBeLessThan(parent.indexOf(executeCall));
|
|
@@ -48,7 +48,7 @@ fn read_twice() {
|
|
|
48
48
|
const fn = getFileContent(result.files, 'data/test/function/read_twice.mcfunction');
|
|
49
49
|
const readMatches = fn.match(/scoreboard players get @s test\.coins/g) ?? [];
|
|
50
50
|
expect(readMatches).toHaveLength(1);
|
|
51
|
-
expect(fn).toContain('scoreboard players operation $_1
|
|
51
|
+
expect(fn).toContain('scoreboard players operation $_1 __test = $_0 __test');
|
|
52
52
|
});
|
|
53
53
|
test('reuses duplicate arithmetic sequences', () => {
|
|
54
54
|
const source = `
|
|
@@ -63,9 +63,9 @@ fn math() {
|
|
|
63
63
|
`;
|
|
64
64
|
const result = (0, index_1.compile)(source, { namespace: 'test' });
|
|
65
65
|
const fn = getFileContent(result.files, 'data/test/function/math.mcfunction');
|
|
66
|
-
const addMatches = fn.match(/\+= \$const_2
|
|
66
|
+
const addMatches = fn.match(/\+= \$const_2 __test/g) ?? [];
|
|
67
67
|
expect(addMatches).toHaveLength(1);
|
|
68
|
-
expect(fn).toContain('scoreboard players operation $_1
|
|
68
|
+
expect(fn).toContain('scoreboard players operation $_1 __test = $_0 __test');
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
describe('setblock batching', () => {
|
package/dist/cli.js
CHANGED
|
@@ -60,7 +60,7 @@ function printUsage() {
|
|
|
60
60
|
RedScript Compiler
|
|
61
61
|
|
|
62
62
|
Usage:
|
|
63
|
-
redscript compile <file> [-o <out>] [--output-nbt <file>] [--namespace <ns>] [--target <target>] [--no-dce]
|
|
63
|
+
redscript compile <file> [-o <out>] [--output-nbt <file>] [--namespace <ns>] [--scoreboard <obj>] [--target <target>] [--no-dce]
|
|
64
64
|
redscript watch <dir> [-o <outdir>] [--namespace <ns>] [--hot-reload <url>]
|
|
65
65
|
redscript check <file>
|
|
66
66
|
redscript fmt <file.mcrs> [file2.mcrs ...]
|
|
@@ -85,6 +85,9 @@ Options:
|
|
|
85
85
|
--target <target> Output target: datapack (default), cmdblock, or structure
|
|
86
86
|
--no-dce Disable AST dead code elimination
|
|
87
87
|
--no-mangle Disable variable name mangling (use readable names)
|
|
88
|
+
--scoreboard <obj> Scoreboard objective for variables (default: namespace).
|
|
89
|
+
Each datapack automatically uses its namespace as the objective
|
|
90
|
+
so multiple datapacks can coexist without collisions.
|
|
88
91
|
--stats Print optimizer statistics
|
|
89
92
|
--hot-reload <url> After each successful compile, POST to <url>/reload
|
|
90
93
|
(use with redscript-testharness; e.g. http://localhost:25561)
|
|
@@ -218,6 +221,10 @@ function parseArgs(args) {
|
|
|
218
221
|
result.mangle = false;
|
|
219
222
|
i++;
|
|
220
223
|
}
|
|
224
|
+
else if (arg === '--scoreboard') {
|
|
225
|
+
result.scoreboardObjective = args[++i];
|
|
226
|
+
i++;
|
|
227
|
+
}
|
|
221
228
|
else if (arg === '--hot-reload') {
|
|
222
229
|
result.hotReload = args[++i];
|
|
223
230
|
i++;
|
|
@@ -271,7 +278,7 @@ function printOptimizationStats(stats) {
|
|
|
271
278
|
console.log(` constant folding: ${stats.constantFolds} constants folded`);
|
|
272
279
|
console.log(` Total mcfunction commands: ${stats.totalCommandsBefore} -> ${stats.totalCommandsAfter} (${formatReduction(stats.totalCommandsBefore, stats.totalCommandsAfter)} reduction)`);
|
|
273
280
|
}
|
|
274
|
-
function compileCommand(file, output, namespace, target = 'datapack', showStats = false, dce = true, mangle = true) {
|
|
281
|
+
function compileCommand(file, output, namespace, target = 'datapack', showStats = false, dce = true, mangle = true, scoreboardObjective = undefined) {
|
|
275
282
|
// Read source file
|
|
276
283
|
if (!fs.existsSync(file)) {
|
|
277
284
|
console.error(`Error: File not found: ${file}`);
|
|
@@ -280,7 +287,7 @@ function compileCommand(file, output, namespace, target = 'datapack', showStats
|
|
|
280
287
|
const source = fs.readFileSync(file, 'utf-8');
|
|
281
288
|
try {
|
|
282
289
|
if (target === 'cmdblock') {
|
|
283
|
-
const result = (0, index_1.compile)(source, { namespace, filePath: file, dce, mangle });
|
|
290
|
+
const result = (0, index_1.compile)(source, { namespace, filePath: file, dce, mangle, scoreboardObjective });
|
|
284
291
|
printWarnings(result.warnings);
|
|
285
292
|
// Generate command block JSON
|
|
286
293
|
const hasTick = result.files.some(f => f.path.includes('__tick.mcfunction'));
|
|
@@ -309,7 +316,7 @@ function compileCommand(file, output, namespace, target = 'datapack', showStats
|
|
|
309
316
|
}
|
|
310
317
|
}
|
|
311
318
|
else {
|
|
312
|
-
const result = (0, index_1.compile)(source, { namespace, filePath: file, dce, mangle });
|
|
319
|
+
const result = (0, index_1.compile)(source, { namespace, filePath: file, dce, mangle, scoreboardObjective });
|
|
313
320
|
printWarnings(result.warnings);
|
|
314
321
|
// Default: generate datapack
|
|
315
322
|
// Create output directory
|
|
@@ -483,7 +490,8 @@ async function main() {
|
|
|
483
490
|
const output = target === 'structure'
|
|
484
491
|
? (parsed.outputNbt ?? parsed.output ?? `./${namespace}.nbt`)
|
|
485
492
|
: (parsed.output ?? './dist');
|
|
486
|
-
compileCommand(parsed.file, output, namespace, target, parsed.stats, parsed.dce, parsed.mangle)
|
|
493
|
+
compileCommand(parsed.file, output, namespace, target, parsed.stats, parsed.dce, parsed.mangle, parsed.scoreboardObjective // undefined = derive from namespace in compile()
|
|
494
|
+
);
|
|
487
495
|
}
|
|
488
496
|
break;
|
|
489
497
|
case 'watch':
|
|
@@ -30,6 +30,10 @@ export interface DatapackGenerationResult {
|
|
|
30
30
|
export interface DatapackGenerationOptions {
|
|
31
31
|
optimizeCommands?: boolean;
|
|
32
32
|
mangle?: boolean;
|
|
33
|
+
/** Scoreboard objective used for all scoreboard variables.
|
|
34
|
+
* Defaults to 'rs'. Override per-datapack to avoid collisions
|
|
35
|
+
* when multiple RedScript datapacks are loaded simultaneously. */
|
|
36
|
+
scoreboardObjective?: string;
|
|
33
37
|
}
|
|
34
38
|
export declare function countMcfunctionCommands(files: DatapackFile[]): number;
|
|
35
39
|
export declare function generateDatapackWithStats(module: IRModule, options?: DatapackGenerationOptions): DatapackGenerationResult;
|
|
@@ -26,7 +26,8 @@ const var_allocator_1 = require("../var-allocator");
|
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
// Utilities
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
29
|
+
// Default scoreboard objective — overridden per-compilation via DatapackGenerationOptions.scoreboardObjective
|
|
30
|
+
let OBJ = 'rs';
|
|
30
31
|
function operandToScore(op, alloc) {
|
|
31
32
|
if (op.kind === 'var')
|
|
32
33
|
return `${alloc.alloc(op.name)} ${OBJ}`;
|
|
@@ -335,7 +336,11 @@ function preAllocTerm(term, alloc) {
|
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
338
|
function generateDatapackWithStats(module, options = {}) {
|
|
338
|
-
const { optimizeCommands = true, mangle = false } = options;
|
|
339
|
+
const { optimizeCommands = true, mangle = false, scoreboardObjective = 'rs' } = options;
|
|
340
|
+
// Set module-level OBJ so all helper functions in this module use the correct objective.
|
|
341
|
+
// This is safe because compilation is synchronous.
|
|
342
|
+
OBJ = scoreboardObjective;
|
|
343
|
+
(0, commands_1.setOptimizerObjective)(scoreboardObjective);
|
|
339
344
|
const alloc = new var_allocator_1.VarAllocator(mangle);
|
|
340
345
|
const files = [];
|
|
341
346
|
const advancements = [];
|
|
@@ -375,10 +380,10 @@ function generateDatapackWithStats(module, options = {}) {
|
|
|
375
380
|
for (const eventType of eventTypes) {
|
|
376
381
|
const detection = types_1.EVENT_TYPES[eventType].detection;
|
|
377
382
|
if (eventType === 'PlayerDeath') {
|
|
378
|
-
loadLines.push(
|
|
383
|
+
loadLines.push(`scoreboard objectives add ${OBJ}.deaths deathCount`);
|
|
379
384
|
}
|
|
380
385
|
else if (eventType === 'EntityKill') {
|
|
381
|
-
loadLines.push(
|
|
386
|
+
loadLines.push(`scoreboard objectives add ${OBJ}.kills totalKillCount`);
|
|
382
387
|
}
|
|
383
388
|
else if (eventType === 'ItemUse') {
|
|
384
389
|
loadLines.push('# ItemUse detection requires a project-specific objective/tag setup');
|
package/dist/compile.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export interface CompileOptions {
|
|
|
13
13
|
optimize?: boolean;
|
|
14
14
|
dce?: boolean;
|
|
15
15
|
mangle?: boolean;
|
|
16
|
+
/** Scoreboard objective used for all variable slots.
|
|
17
|
+
* Defaults to '__<namespace>' (e.g. '__mathshow') — the double-underscore
|
|
18
|
+
* prefix signals compiler-internal and avoids occupying the user's namespace.
|
|
19
|
+
* Each datapack gets a unique objective automatically; no manual setup needed. */
|
|
20
|
+
scoreboardObjective?: string;
|
|
16
21
|
/** Additional source files that should be treated as *library* code.
|
|
17
22
|
* Functions in these files are DCE-eligible: they are only compiled into
|
|
18
23
|
* the datapack when actually called from user code. Each string is parsed
|
package/dist/compile.js
CHANGED
|
@@ -209,6 +209,11 @@ function compile(source, options = {}) {
|
|
|
209
209
|
}
|
|
210
210
|
const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst) : { program: parsedAst, warnings: [] };
|
|
211
211
|
const ast = dceResult.program;
|
|
212
|
+
// Configure scoreboard objective for this compilation.
|
|
213
|
+
// Default: use the datapack namespace so each datapack gets its own objective
|
|
214
|
+
// automatically, preventing variable collisions when multiple datapacks coexist.
|
|
215
|
+
const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`;
|
|
216
|
+
(0, lowering_1.setScoreboardObjective)(scoreboardObj);
|
|
212
217
|
// Lowering
|
|
213
218
|
const ir = new lowering_1.Lowering(namespace, preprocessed.ranges).lower(ast);
|
|
214
219
|
// Optimization
|
|
@@ -217,7 +222,10 @@ function compile(source, options = {}) {
|
|
|
217
222
|
: ir;
|
|
218
223
|
// Code generation — mangle=true by default to prevent cross-function
|
|
219
224
|
// scoreboard variable collisions in the global MC scoreboard namespace.
|
|
220
|
-
const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized, {
|
|
225
|
+
const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized, {
|
|
226
|
+
mangle: options.mangle ?? true,
|
|
227
|
+
scoreboardObjective: scoreboardObj,
|
|
228
|
+
});
|
|
221
229
|
return {
|
|
222
230
|
success: true,
|
|
223
231
|
files: [...generated.files, ...generated.advancements],
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export interface CompileOptions {
|
|
|
17
17
|
filePath?: string;
|
|
18
18
|
dce?: boolean;
|
|
19
19
|
mangle?: boolean;
|
|
20
|
+
/** Scoreboard objective used for all variable slots.
|
|
21
|
+
* Defaults to '__<namespace>' (e.g. '__mathshow') to avoid collisions when
|
|
22
|
+
* multiple RedScript datapacks are loaded simultaneously, without occupying
|
|
23
|
+
* the user's own namespace. Override only if you need a specific name. */
|
|
24
|
+
scoreboardObjective?: string;
|
|
20
25
|
}
|
|
21
26
|
export interface CompileResult {
|
|
22
27
|
files: DatapackFile[];
|
package/dist/index.js
CHANGED
|
@@ -66,11 +66,16 @@ function compile(source, options = {}) {
|
|
|
66
66
|
const checker = new typechecker_1.TypeChecker(preprocessedSource, filePath);
|
|
67
67
|
typeErrors = checker.check(ast);
|
|
68
68
|
}
|
|
69
|
+
// Configure scoreboard objective for this compilation.
|
|
70
|
+
// Default: use the datapack namespace so each datapack gets its own objective
|
|
71
|
+
// automatically, preventing variable collisions when multiple datapacks coexist.
|
|
72
|
+
const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`;
|
|
73
|
+
(0, lowering_1.setScoreboardObjective)(scoreboardObj);
|
|
69
74
|
// Lowering to IR
|
|
70
75
|
const lowering = new lowering_1.Lowering(namespace, preprocessed.ranges);
|
|
71
76
|
const ir = lowering.lower(ast);
|
|
72
77
|
let optimizedIR = ir;
|
|
73
|
-
let generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: shouldOptimize, mangle });
|
|
78
|
+
let generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: shouldOptimize, mangle, scoreboardObjective: scoreboardObj });
|
|
74
79
|
let optimizationStats;
|
|
75
80
|
if (shouldOptimize) {
|
|
76
81
|
const stats = (0, commands_1.createEmptyOptimizationStats)();
|
|
@@ -86,10 +91,10 @@ function compile(source, options = {}) {
|
|
|
86
91
|
}
|
|
87
92
|
const copyPropagatedIR = { ...ir, functions: copyPropagatedFunctions };
|
|
88
93
|
optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions };
|
|
89
|
-
const baselineGenerated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle });
|
|
90
|
-
const beforeDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(copyPropagatedIR, { optimizeCommands: false, mangle });
|
|
91
|
-
const afterDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: false, mangle });
|
|
92
|
-
generated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: true, mangle });
|
|
94
|
+
const baselineGenerated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
|
|
95
|
+
const beforeDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(copyPropagatedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
|
|
96
|
+
const afterDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
|
|
97
|
+
generated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: true, mangle, scoreboardObjective: scoreboardObj });
|
|
93
98
|
stats.deadCodeRemoved =
|
|
94
99
|
(0, mcfunction_1.countMcfunctionCommands)(beforeDceGenerated.files) - (0, mcfunction_1.countMcfunctionCommands)(afterDceGenerated.files);
|
|
95
100
|
stats.licmHoists = generated.stats.licmHoists;
|
|
@@ -105,7 +110,7 @@ function compile(source, options = {}) {
|
|
|
105
110
|
}
|
|
106
111
|
else {
|
|
107
112
|
optimizedIR = ir;
|
|
108
|
-
generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle });
|
|
113
|
+
generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
|
|
109
114
|
}
|
|
110
115
|
return {
|
|
111
116
|
files: [...generated.files, ...generated.advancements],
|
package/dist/lowering/index.d.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import type { IRModule } from '../ir/types';
|
|
8
8
|
import type { SourceRange } from '../compile';
|
|
9
9
|
import type { Program } from '../ast/types';
|
|
10
|
+
/** Current scoreboard objective. Set once per compile() call. */
|
|
11
|
+
export declare let LOWERING_OBJ: string;
|
|
12
|
+
export declare function setScoreboardObjective(obj: string): void;
|
|
10
13
|
export interface Warning {
|
|
11
14
|
message: string;
|
|
12
15
|
code: string;
|