redscript-mc 2.1.0 → 2.2.1
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/CHANGELOG.md +11 -0
- package/README.md +86 -21
- package/README.zh.md +61 -61
- package/dist/src/__tests__/e2e/basic.test.js +25 -0
- package/dist/src/__tests__/e2e/coroutine.test.js +22 -0
- package/dist/src/__tests__/lsp.test.js +76 -0
- package/dist/src/__tests__/mc-integration.test.js +25 -13
- package/dist/src/__tests__/mc-syntax.test.js +1 -6
- package/dist/src/__tests__/schedule.test.js +105 -0
- package/dist/src/__tests__/stdlib-include.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib-include.test.js +86 -0
- package/dist/src/__tests__/typechecker.test.js +63 -0
- package/dist/src/cli.js +10 -3
- package/dist/src/compile.d.ts +1 -0
- package/dist/src/compile.js +33 -10
- package/dist/src/emit/compile.d.ts +2 -0
- package/dist/src/emit/compile.js +3 -2
- package/dist/src/emit/index.js +3 -1
- package/dist/src/lir/lower.js +26 -0
- package/dist/src/lsp/server.js +51 -0
- package/dist/src/mir/lower.js +341 -12
- package/dist/src/mir/types.d.ts +10 -0
- package/dist/src/optimizer/copy_prop.js +4 -0
- package/dist/src/optimizer/coroutine.d.ts +2 -0
- package/dist/src/optimizer/coroutine.js +33 -1
- package/dist/src/optimizer/dce.js +7 -1
- package/dist/src/optimizer/lir/const_imm.js +1 -1
- package/dist/src/optimizer/lir/dead_slot.js +1 -1
- package/dist/src/typechecker/index.d.ts +2 -0
- package/dist/src/typechecker/index.js +29 -0
- package/docs/ROADMAP.md +35 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +34 -0
- package/examples/coroutine-demo.mcrs +51 -0
- package/examples/enum-demo.mcrs +95 -0
- package/examples/scheduler-demo.mcrs +59 -0
- package/jest.config.js +19 -0
- package/package.json +1 -1
- package/src/__tests__/e2e/basic.test.ts +27 -0
- package/src/__tests__/e2e/coroutine.test.ts +23 -0
- package/src/__tests__/fixtures/array-test.mcrs +21 -22
- package/src/__tests__/fixtures/counter.mcrs +17 -0
- package/src/__tests__/fixtures/foreach-at-test.mcrs +9 -10
- package/src/__tests__/lsp.test.ts +89 -0
- package/src/__tests__/mc-integration.test.ts +25 -13
- package/src/__tests__/mc-syntax.test.ts +1 -7
- package/src/__tests__/schedule.test.ts +112 -0
- package/src/__tests__/stdlib-include.test.ts +61 -0
- package/src/__tests__/typechecker.test.ts +68 -0
- package/src/cli.ts +9 -1
- package/src/compile.ts +44 -15
- package/src/emit/compile.ts +5 -2
- package/src/emit/index.ts +3 -1
- package/src/lir/lower.ts +27 -0
- package/src/lsp/server.ts +55 -0
- package/src/mir/lower.ts +355 -9
- package/src/mir/types.ts +4 -0
- package/src/optimizer/copy_prop.ts +4 -0
- package/src/optimizer/coroutine.ts +37 -1
- package/src/optimizer/dce.ts +6 -1
- package/src/optimizer/lir/const_imm.ts +1 -1
- package/src/optimizer/lir/dead_slot.ts +1 -1
- package/src/stdlib/timer.mcrs +10 -5
- package/src/typechecker/index.ts +39 -0
- package/examples/spiral.mcrs +0 -43
- package/src/examples/arena.mcrs +0 -44
- package/src/examples/counter.mcrs +0 -12
- package/src/examples/new_features_demo.mcrs +0 -193
- package/src/examples/rpg.mcrs +0 -13
- package/src/examples/stdlib_demo.mcrs +0 -181
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to RedScript will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.1.1] - 2026-03-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- stdlib include path: `import "stdlib/math"` without specifying full path
|
|
9
|
+
- `--include <dir>` CLI flag for custom library paths
|
|
10
|
+
- LSP hover for 50+ builtin functions and all decorators (@tick/@load/@coroutine/@schedule/@on_trigger)
|
|
11
|
+
- f-string syntax highlighting in VSCode extension
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Example files cleaned up: removed stale/redundant examples, added coroutine/enum/scheduler demos
|
|
15
|
+
|
|
5
16
|
## [1.2.15] - 2026-03-13
|
|
6
17
|
|
|
7
18
|
### Changed
|
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-2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-2.1.1-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)
|
|
@@ -37,7 +37,7 @@ RedScript is a typed scripting language that compiles to vanilla Minecraft datap
|
|
|
37
37
|
**The demo above?** Five math curves drawn with 64 sample points each. The core logic:
|
|
38
38
|
|
|
39
39
|
```rs
|
|
40
|
-
import "stdlib/math
|
|
40
|
+
import "stdlib/math"
|
|
41
41
|
|
|
42
42
|
let phase: int = 0;
|
|
43
43
|
let frame: int = 0;
|
|
@@ -73,20 +73,32 @@ let frame: int = 0;
|
|
|
73
73
|
- ✅ Builtins accept runtime macro variables in any argument position
|
|
74
74
|
- ✅ f-strings like `f"Score: {points}"` for dynamic output
|
|
75
75
|
- ✅ Public functions are preserved automatically; `_privateFn()` stays private
|
|
76
|
+
- ✅ `enum` types with `match` dispatch (zero-runtime overhead)
|
|
77
|
+
- ✅ Multi-return values: `fn divmod(a: int, b: int): (int, int)`
|
|
78
|
+
- ✅ Generics: `fn max<T>(a: T, b: T): T` (monomorphized)
|
|
79
|
+
- ✅ `Option<T>` null safety: `Some(x)` / `None` / `if let Some(x) = opt`
|
|
80
|
+
- ✅ `@coroutine(batch=N)` for tick-spread loops (no more maxCommandChain hits)
|
|
81
|
+
- ✅ `@schedule(ticks=N)` to delay function execution
|
|
82
|
+
- ✅ Module system: `module math; import math::sin;`
|
|
83
|
+
- ✅ Multi-version targets: `--mc-version 1.20.2`
|
|
84
|
+
- ✅ Source maps: `--source-map` for debugging
|
|
85
|
+
- ✅ Language Server Protocol: diagnostics, hover, go-to-def, completion
|
|
76
86
|
- ✅ One file -> ready-to-use datapack
|
|
77
87
|
|
|
78
88
|
---
|
|
79
89
|
|
|
80
|
-
### What's New in v2.
|
|
90
|
+
### What's New in v2.1.x
|
|
81
91
|
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- `
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
92
|
+
- **`enum` + `match` dispatch** — zero-runtime-overhead state machines; the compiler folds enum variants to integer constants and uses `execute if score` dispatch with no heap allocation
|
|
93
|
+
- **Multi-return values** — `fn divmod(a: int, b: int): (int, int)` unpacks to separate scoreboard slots; no struct wrapper needed
|
|
94
|
+
- **Generics** — `fn max<T>(a: T, b: T): T` is fully monomorphized at compile time; no boxing, no indirection
|
|
95
|
+
- **`Option<T>` null safety** — `Some(x)` / `None` with `if let Some(x) = opt` pattern binding; safe access to optional values without sentinel integers
|
|
96
|
+
- **`@coroutine(batch=N)`** — spreads a heavy loop across multiple ticks (`batch=50` means 50 iterations/tick), avoiding `maxCommandChainLength` hits on large workloads
|
|
97
|
+
- **`@schedule(ticks=N)`** — delays a function call by N game ticks; compiles to `schedule function` with automatic namespace scoping
|
|
98
|
+
- **Module system** — `module math;` declares a named module; consumers `import math::sin` to tree-shake individual symbols
|
|
99
|
+
- **Multi-version targets** — `--mc-version 1.20.2` switches emit strategy (macro sub-functions vs. legacy scoreboards) for the target server version
|
|
100
|
+
- **Source maps** — `--source-map` emits a `.mcrs.map` sidecar that maps each `.mcfunction` line back to the original source; enables in-editor debugging
|
|
101
|
+
- **LSP: hover + completion for builtins & decorators** — 50+ builtin functions and all decorators (`@tick`/`@load`/`@coroutine`/`@schedule`/`@on_trigger`) now show inline docs on hover
|
|
90
102
|
|
|
91
103
|
---
|
|
92
104
|
|
|
@@ -192,6 +204,22 @@ fn on_diamond() {
|
|
|
192
204
|
fn on_death() {
|
|
193
205
|
scoreboard_add(@s, #deaths, 1);
|
|
194
206
|
}
|
|
207
|
+
|
|
208
|
+
// Delay execution by N ticks (20t = 1 second)
|
|
209
|
+
@schedule(ticks=20)
|
|
210
|
+
fn after_one_second(): void {
|
|
211
|
+
title(@a, "One second later!");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Spread a heavy loop across multiple ticks (batch=N iterations/tick)
|
|
215
|
+
@coroutine(batch=50, onDone=all_done)
|
|
216
|
+
fn process_all(): void {
|
|
217
|
+
let i: int = 0;
|
|
218
|
+
while (i < 1000) {
|
|
219
|
+
// work spread over ~20 ticks instead of lagging one tick
|
|
220
|
+
i = i + 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
195
223
|
```
|
|
196
224
|
|
|
197
225
|
#### Control Flow
|
|
@@ -261,6 +289,9 @@ redscript compile <file> Compile to datapack (default) or structure
|
|
|
261
289
|
-o, --output <dir> Output directory [default: ./out]
|
|
262
290
|
--target datapack|structure Output format [default: datapack]
|
|
263
291
|
--namespace <ns> Datapack namespace [default: filename]
|
|
292
|
+
--mc-version <ver> Target MC version [default: 1.21]
|
|
293
|
+
--include <dir> Extra library search path (repeatable)
|
|
294
|
+
--source-map Emit .mcrs.map source map for debugging
|
|
264
295
|
--no-optimize Skip optimizer passes
|
|
265
296
|
--stats Print optimizer statistics
|
|
266
297
|
|
|
@@ -270,39 +301,53 @@ redscript validate <file> Validate MC commands
|
|
|
270
301
|
|
|
271
302
|
---
|
|
272
303
|
|
|
304
|
+
### Stdlib
|
|
305
|
+
|
|
306
|
+
RedScript ships a built-in standard library. Use the short form — no path needed:
|
|
307
|
+
|
|
308
|
+
```rs
|
|
309
|
+
import "stdlib/math" // fixed-point math
|
|
310
|
+
import "stdlib/vec" // 2D/3D vector geometry
|
|
311
|
+
import "stdlib/combat" // damage, kill-check helpers
|
|
312
|
+
import "stdlib/player" // health, alive check, range
|
|
313
|
+
import "stdlib/cooldown" // per-player cooldown tracking
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Custom library paths can be added with `--include <dir>` so your own modules work the same way.
|
|
317
|
+
|
|
273
318
|
### Standard Library
|
|
274
319
|
|
|
275
320
|
All stdlib files use `module library;` — only the functions you actually call are compiled in.
|
|
276
321
|
|
|
277
322
|
```rs
|
|
278
|
-
import "stdlib/math
|
|
323
|
+
import "stdlib/math" // abs, sign, min, max, clamp, lerp, isqrt, sqrt_fixed,
|
|
279
324
|
// pow_int, gcd, lcm, sin_fixed, cos_fixed, map, ceil_div,
|
|
280
325
|
// log2_int, mulfix, divfix, smoothstep, smootherstep
|
|
281
326
|
|
|
282
|
-
import "stdlib/vec
|
|
327
|
+
import "stdlib/vec" // dot2d, cross2d, length2d_fixed, distance2d_fixed,
|
|
283
328
|
// manhattan, chebyshev, atan2_fixed, normalize2d_x/y,
|
|
284
329
|
// rotate2d_x/y, lerp2d_x/y, dot3d, cross3d_x/y/z,
|
|
285
330
|
// length3d_fixed
|
|
286
331
|
|
|
287
|
-
import "stdlib/advanced
|
|
332
|
+
import "stdlib/advanced" // fib, is_prime, collatz_steps, digit_sum, reverse_int,
|
|
288
333
|
// mod_pow, hash_int, noise1d, bezier_quad,
|
|
289
334
|
// mandelbrot_iter, julia_iter, angle_between,
|
|
290
335
|
// clamp_circle_x/y, newton_sqrt, digital_root
|
|
291
336
|
|
|
292
|
-
import "stdlib/bigint
|
|
337
|
+
import "stdlib/bigint" // bigint_init, bigint_from_int_a/b, bigint_add/sub/mul,
|
|
293
338
|
// bigint_compare, bigint_mul_small, bigint_fib
|
|
294
339
|
// — up to 32 decimal digits, runs on MC scoreboard
|
|
295
340
|
|
|
296
|
-
import "stdlib/player
|
|
297
|
-
import "stdlib/timer
|
|
298
|
-
import "stdlib/cooldown
|
|
299
|
-
import "stdlib/mobs
|
|
341
|
+
import "stdlib/player" // is_alive, in_range, get_health
|
|
342
|
+
import "stdlib/timer" // start_timer, tick_timer, has_elapsed
|
|
343
|
+
import "stdlib/cooldown" // set_cooldown, check_cooldown
|
|
344
|
+
import "stdlib/mobs" // ZOMBIE, SKELETON, CREEPER, ... (60+ constants)
|
|
300
345
|
```
|
|
301
346
|
|
|
302
347
|
**Example — computing Fibonacci(50) in-game:**
|
|
303
348
|
|
|
304
349
|
```rs
|
|
305
|
-
import "stdlib/bigint
|
|
350
|
+
import "stdlib/bigint"
|
|
306
351
|
|
|
307
352
|
fn show_fib() {
|
|
308
353
|
bigint_fib(50);
|
|
@@ -316,6 +361,26 @@ fn show_fib() {
|
|
|
316
361
|
|
|
317
362
|
---
|
|
318
363
|
|
|
364
|
+
### Examples
|
|
365
|
+
|
|
366
|
+
The `examples/` directory contains ready-to-compile demos:
|
|
367
|
+
|
|
368
|
+
| File | What it shows |
|
|
369
|
+
|---|---|
|
|
370
|
+
| `readme-demo.mcrs` | Real-time sine wave particles — `@tick`, `foreach`, f-strings, math stdlib |
|
|
371
|
+
| `math-showcase.mcrs` | All stdlib math modules: trig, vectors, BigInt, fractals |
|
|
372
|
+
| `showcase.mcrs` | Full feature tour: structs, enums, `match`, lambdas, `@tick`/`@load` |
|
|
373
|
+
| `coroutine-demo.mcrs` | `@coroutine(batch=50)` — spread 1000 iterations across ~20 ticks |
|
|
374
|
+
| `enum-demo.mcrs` | Enum state machine: NPC AI cycling Idle → Moving → Attacking with `match` |
|
|
375
|
+
| `scheduler-demo.mcrs` | `@schedule(ticks=20)` — delayed events, chained schedules |
|
|
376
|
+
|
|
377
|
+
Compile any example:
|
|
378
|
+
```bash
|
|
379
|
+
node dist/cli.js compile examples/coroutine-demo.mcrs -o ~/mc-server/datapacks/demo --namespace demo
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
319
384
|
### Further Reading
|
|
320
385
|
|
|
321
386
|
| | |
|
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-2.
|
|
5
|
+
<img src="https://img.shields.io/badge/RedScript-2.1.1-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)
|
|
@@ -37,7 +37,7 @@ RedScript 是一门编译到原版 Minecraft 数据包的脚本语言。用变
|
|
|
37
37
|
**上面的演示?** 五条数学曲线,每条 64 个采样点,核心逻辑:
|
|
38
38
|
|
|
39
39
|
```rs
|
|
40
|
-
import "stdlib/math
|
|
40
|
+
import "stdlib/math"
|
|
41
41
|
|
|
42
42
|
let phase: int = 0;
|
|
43
43
|
let frame: int = 0;
|
|
@@ -65,40 +65,36 @@ let frame: int = 0;
|
|
|
65
65
|
|
|
66
66
|
**你得到:**
|
|
67
67
|
- ✅ `let` / `const` 变量(告别 `scoreboard players set`)
|
|
68
|
-
- ✅ `if` / `else` / `for` / `foreach` 完整控制流
|
|
68
|
+
- ✅ `if` / `else` / `for` / `foreach` / `break` / `continue` 完整控制流
|
|
69
69
|
- ✅ `@tick` / `@load` / `@on(Event)` 装饰器
|
|
70
70
|
- ✅ `foreach (p in @a) at @s` — 遍历实体并设置执行上下文
|
|
71
71
|
- ✅ f-strings 如 `f"分数: {points}"` 动态输出
|
|
72
|
+
- ✅ `enum` 类型 + `match` 分发(零运行时开销)
|
|
73
|
+
- ✅ 多返回值:`fn divmod(a: int, b: int): (int, int)`
|
|
74
|
+
- ✅ 泛型:`fn max<T>(a: T, b: T): T`(单态化)
|
|
75
|
+
- ✅ `Option<T>` 空值安全:`Some(x)` / `None` / `if let Some(x) = opt`
|
|
76
|
+
- ✅ `@coroutine(batch=N)` 将循环分散到多个 tick(不再触发 maxCommandChain 限制)
|
|
77
|
+
- ✅ `@schedule(ticks=N)` 延迟执行函数
|
|
78
|
+
- ✅ 模块系统:`module math; import math::sin;`
|
|
79
|
+
- ✅ 多版本目标:`--mc-version 1.20.2`
|
|
80
|
+
- ✅ 源码映射:`--source-map` 支持调试
|
|
81
|
+
- ✅ 语言服务协议(LSP):诊断、悬停提示、跳转定义、代码补全
|
|
72
82
|
- ✅ 一个文件 → 可直接使用的 datapack
|
|
73
83
|
|
|
74
84
|
---
|
|
75
85
|
|
|
76
|
-
###
|
|
86
|
+
### v2.1.x 新增内容
|
|
77
87
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
- **`
|
|
82
|
-
- **`@
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- **高级标准库** (`advanced.mcrs`):数论(`fib`、`is_prime`、`collatz_steps`、`mod_pow`)、哈希/噪声(splitmix32 `hash_int`、`noise1d`)、曲线(`bezier_quad`)、分形(`mandelbrot_iter`、`julia_iter`)、几何实验
|
|
89
|
-
- **BigInt** (`bigint.mcrs`):任意精度整数架构设计 — base 10000 × 8 limbs = 最多 32 位十进制数
|
|
90
|
-
- **编译器修复**:`isqrt` 大数收敛、优化器拷贝传播别名失效、跨函数变量命名冲突、MCRuntime 数组索引正则
|
|
91
|
-
|
|
92
|
-
### v1.2.25 新增内容
|
|
93
|
-
|
|
94
|
-
- `impl` 块与方法,支持围绕结构体构建面向对象风格 API
|
|
95
|
-
- `is` 类型收窄,实体判断更安全
|
|
96
|
-
- 使用 `@on(Event)` 的静态事件系统
|
|
97
|
-
- 面向运行时输出的 f-string
|
|
98
|
-
- `Timer::new(...)` 与实例方法组成的 Timer OOP API
|
|
99
|
-
- `setTimeout(...)` 与 `setInterval(...)` 调度辅助函数
|
|
100
|
-
- 优化器中的死代码消除
|
|
101
|
-
- 标准库新增 313 个 Minecraft 标签常量
|
|
88
|
+
- **`enum` + `match` 分发** — 零运行时开销的状态机;编译器将枚举变体折叠为整数常量,用 `execute if score` 分发,无堆分配
|
|
89
|
+
- **多返回值** — `fn divmod(a: int, b: int): (int, int)` 解包到独立记分板槽位,不需要结构体包装
|
|
90
|
+
- **泛型** — `fn max<T>(a: T, b: T): T` 在编译期完整单态化,无装箱无间接引用
|
|
91
|
+
- **`Option<T>` 空值安全** — `Some(x)` / `None` 与 `if let Some(x) = opt` 模式绑定,不再依赖哨兵整数
|
|
92
|
+
- **`@coroutine(batch=N)`** — 将繁重循环分散到多个 tick(`batch=50` 表示每 tick 执行 50 次迭代),避免触发 `maxCommandChainLength` 限制
|
|
93
|
+
- **`@schedule(ticks=N)`** — 延迟 N 个游戏 tick 后执行函数,编译为带自动命名空间前缀的 `schedule function`
|
|
94
|
+
- **模块系统** — `module math;` 声明命名模块;调用方 `import math::sin` 可按符号树摇
|
|
95
|
+
- **多版本目标** — `--mc-version 1.20.2` 针对目标服务器版本切换发射策略(宏子函数 vs 传统记分板)
|
|
96
|
+
- **源码映射** — `--source-map` 生成 `.mcrs.map` 附属文件,将每行 `.mcfunction` 映射回原始源码,支持编辑器内调试
|
|
97
|
+
- **LSP 悬停 + 补全** — 50+ 内置函数及所有装饰器(`@tick`/`@load`/`@coroutine`/`@schedule`/`@on_trigger`)均支持悬停内联文档
|
|
102
98
|
|
|
103
99
|
---
|
|
104
100
|
|
|
@@ -273,6 +269,9 @@ redscript compile <file> 编译为 datapack(默认)或 structure
|
|
|
273
269
|
-o, --output <dir> 输出目录 [默认: ./out]
|
|
274
270
|
--target datapack|structure 输出格式 [默认: datapack]
|
|
275
271
|
--namespace <ns> Datapack 命名空间 [默认: 文件名]
|
|
272
|
+
--mc-version <ver> 目标 MC 版本 [默认: 1.21]
|
|
273
|
+
--include <dir> 额外库搜索路径(可重复)
|
|
274
|
+
--source-map 生成 .mcrs.map 源码映射文件
|
|
276
275
|
--no-optimize 禁用优化器
|
|
277
276
|
--stats 输出优化器统计信息
|
|
278
277
|
|
|
@@ -284,37 +283,49 @@ redscript validate <file> 验证 MC 命令语法
|
|
|
284
283
|
|
|
285
284
|
### 标准库
|
|
286
285
|
|
|
286
|
+
RedScript 内置标准库,使用短路径直接导入,无需指定完整文件路径:
|
|
287
|
+
|
|
288
|
+
```rs
|
|
289
|
+
import "stdlib/math" // 定点数学
|
|
290
|
+
import "stdlib/vec" // 2D/3D 向量几何
|
|
291
|
+
import "stdlib/combat" // 伤害、击杀检测辅助函数
|
|
292
|
+
import "stdlib/player" // 血量、存活检测、范围判断
|
|
293
|
+
import "stdlib/cooldown" // 每玩家冷却时间管理
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
通过 `--include <dir>` 可添加自定义库路径,自有模块也能用同样的短路径导入。
|
|
297
|
+
|
|
287
298
|
所有标准库文件都使用 `module library;` —— 只有你实际调用的函数才会编译进去。
|
|
288
299
|
|
|
289
300
|
```rs
|
|
290
|
-
import "stdlib/math
|
|
301
|
+
import "stdlib/math" // abs, sign, min, max, clamp, lerp, isqrt, sqrt_fixed,
|
|
291
302
|
// pow_int, gcd, lcm, sin_fixed, cos_fixed, map, ceil_div,
|
|
292
303
|
// log2_int, mulfix, divfix, smoothstep, smootherstep
|
|
293
304
|
|
|
294
|
-
import "stdlib/vec
|
|
305
|
+
import "stdlib/vec" // dot2d, cross2d, length2d_fixed, distance2d_fixed,
|
|
295
306
|
// manhattan, chebyshev, atan2_fixed, normalize2d_x/y,
|
|
296
307
|
// rotate2d_x/y, lerp2d_x/y, dot3d, cross3d_x/y/z,
|
|
297
308
|
// length3d_fixed
|
|
298
309
|
|
|
299
|
-
import "stdlib/advanced
|
|
310
|
+
import "stdlib/advanced" // fib, is_prime, collatz_steps, digit_sum, reverse_int,
|
|
300
311
|
// mod_pow, hash_int, noise1d, bezier_quad,
|
|
301
312
|
// mandelbrot_iter, julia_iter, angle_between,
|
|
302
313
|
// clamp_circle_x/y, newton_sqrt, digital_root
|
|
303
314
|
|
|
304
|
-
import "stdlib/bigint
|
|
315
|
+
import "stdlib/bigint" // bigint_init, bigint_from_int_a/b, bigint_add/sub/mul,
|
|
305
316
|
// bigint_compare, bigint_mul_small, bigint_fib
|
|
306
317
|
// — 最多 32 位十进制数,纯记分板运行
|
|
307
318
|
|
|
308
|
-
import "stdlib/player
|
|
309
|
-
import "stdlib/timer
|
|
310
|
-
import "stdlib/cooldown
|
|
311
|
-
import "stdlib/mobs
|
|
319
|
+
import "stdlib/player" // is_alive, in_range, get_health
|
|
320
|
+
import "stdlib/timer" // start_timer, tick_timer, has_elapsed
|
|
321
|
+
import "stdlib/cooldown" // set_cooldown, check_cooldown
|
|
322
|
+
import "stdlib/mobs" // ZOMBIE, SKELETON, CREEPER ... (60+ 实体常量)
|
|
312
323
|
```
|
|
313
324
|
|
|
314
325
|
**示例 — 游戏内计算斐波那契数列第 50 项:**
|
|
315
326
|
|
|
316
327
|
```rs
|
|
317
|
-
import "stdlib/bigint
|
|
328
|
+
import "stdlib/bigint"
|
|
318
329
|
|
|
319
330
|
fn show_fib() {
|
|
320
331
|
bigint_fib(50);
|
|
@@ -343,32 +354,21 @@ fn show_fib() {
|
|
|
343
354
|
|
|
344
355
|
### 更新日志亮点
|
|
345
356
|
|
|
346
|
-
####
|
|
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
|
-
|
|
351
|
-
#### v1.2.26(2026-03-14)
|
|
352
|
-
|
|
353
|
-
- 完整的数学/向量/高级/BigInt 标准库(详见上方)
|
|
354
|
-
- `module library;` pragma,实现零成本树摇
|
|
355
|
-
- `storage_get_int` / `storage_set_int` 动态 NBT 数组内置函数
|
|
356
|
-
- 编译器修复:`isqrt` 收敛、拷贝传播、变量作用域命名
|
|
357
|
-
|
|
358
|
-
#### v1.2.25(2026-03-13)
|
|
357
|
+
#### v2.1.1(2026-03-16)
|
|
359
358
|
|
|
360
|
-
-
|
|
361
|
-
-
|
|
362
|
-
-
|
|
359
|
+
- stdlib 包含路径支持:`import "stdlib/math"` 无需指定完整路径
|
|
360
|
+
- `--include <dir>` CLI 参数,支持自定义库路径
|
|
361
|
+
- LSP 悬停支持 50+ 内置函数及全部装饰器
|
|
362
|
+
- VSCode 插件支持 f-string 语法高亮
|
|
363
363
|
|
|
364
|
-
####
|
|
364
|
+
#### v2.0.0
|
|
365
365
|
|
|
366
|
-
-
|
|
367
|
-
-
|
|
368
|
-
-
|
|
369
|
-
-
|
|
370
|
-
-
|
|
371
|
-
-
|
|
366
|
+
- 新编译器管线:**AST → HIR → MIR → LIR → emit**
|
|
367
|
+
- 新 LIR 优化器(死槽位消除 + 常量立即数折叠)
|
|
368
|
+
- CLI `compile` 默认使用 v2 管线
|
|
369
|
+
- 完整 `use "..."` 导入解析(含库模块)
|
|
370
|
+
- 宏调用(`^var`/`~var`)在 v2 发射器中端到端支持
|
|
371
|
+
- v2 中完成结构体/impl 降级(字段槽位 + 方法分发)
|
|
372
372
|
|
|
373
373
|
完整发布说明见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
374
374
|
|
|
@@ -136,5 +136,30 @@ describe('e2e: basic compilation', () => {
|
|
|
136
136
|
const tickJson = getFile(result.files, 'tick.json');
|
|
137
137
|
expect(tickJson).toBeUndefined();
|
|
138
138
|
});
|
|
139
|
+
test('array literal emits data modify storage command', () => {
|
|
140
|
+
const source = `
|
|
141
|
+
fn test_arrays(): void {
|
|
142
|
+
let nums: int[] = [10, 20, 30, 40, 50];
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
const result = (0, compile_1.compile)(source, { namespace: 'array_test' });
|
|
146
|
+
const fn = getFile(result.files, 'test_arrays.mcfunction');
|
|
147
|
+
expect(fn).toBeDefined();
|
|
148
|
+
expect(fn).toContain('data modify storage array_test:arrays nums set value [10, 20, 30, 40, 50]');
|
|
149
|
+
});
|
|
150
|
+
test('array index access emits data get storage command', () => {
|
|
151
|
+
const source = `
|
|
152
|
+
fn test_arrays(): void {
|
|
153
|
+
let nums: int[] = [10, 20, 30, 40, 50];
|
|
154
|
+
scoreboard_set("#arr_0", #rs, nums[0]);
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
const result = (0, compile_1.compile)(source, { namespace: 'array_test' });
|
|
158
|
+
const fn = getFile(result.files, 'test_arrays.mcfunction');
|
|
159
|
+
expect(fn).toBeDefined();
|
|
160
|
+
expect(fn).toContain('data modify storage array_test:arrays nums set value [10, 20, 30, 40, 50]');
|
|
161
|
+
expect(fn).toContain('data get storage array_test:arrays nums[0]');
|
|
162
|
+
expect(fn).toContain('#arr_0');
|
|
163
|
+
});
|
|
139
164
|
});
|
|
140
165
|
//# sourceMappingURL=basic.test.js.map
|
|
@@ -109,6 +109,28 @@ describe('e2e: @coroutine', () => {
|
|
|
109
109
|
const helperFn = getFile(result.files, 'helper.mcfunction');
|
|
110
110
|
expect(helperFn).toBeDefined();
|
|
111
111
|
});
|
|
112
|
+
test('@coroutine with macro call_macro is skipped with warning', () => {
|
|
113
|
+
// call_macro: a function that has isMacro params will be called via call_macro
|
|
114
|
+
// We simulate this with raw() containing ${var} which generates __raw:\x01 in MIR
|
|
115
|
+
const source = `
|
|
116
|
+
@coroutine(batch=10)
|
|
117
|
+
fn with_macro_raw(): void {
|
|
118
|
+
let i: int = 0;
|
|
119
|
+
while (i < 100) {
|
|
120
|
+
let x: int = i;
|
|
121
|
+
raw("particle minecraft:end_rod ^$\{x} ^0 ^0 0 0 0 0 1 force @a");
|
|
122
|
+
i = i + 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
const result = (0, compile_1.compile)(source, { namespace: 'corotest' });
|
|
127
|
+
// Should emit a warning about skipping
|
|
128
|
+
expect(result.warnings.some(w => w.includes('@coroutine cannot be applied') && w.includes('with_macro_raw'))).toBe(true);
|
|
129
|
+
// Should NOT generate continuation files — function kept as-is
|
|
130
|
+
const paths = getFileNames(result.files);
|
|
131
|
+
const contFiles = paths.filter(p => p.includes('_coro_'));
|
|
132
|
+
expect(contFiles.length).toBe(0);
|
|
133
|
+
});
|
|
112
134
|
test('default batch value is 10 when not specified', () => {
|
|
113
135
|
// @coroutine without batch should default to batch=10
|
|
114
136
|
// We test by ensuring compilation succeeds
|
|
@@ -10,6 +10,7 @@ const lexer_1 = require("../lexer");
|
|
|
10
10
|
const parser_1 = require("../parser");
|
|
11
11
|
const typechecker_1 = require("../typechecker");
|
|
12
12
|
const diagnostics_1 = require("../diagnostics");
|
|
13
|
+
const metadata_1 = require("../builtins/metadata");
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// Helpers mirrored from lsp/server.ts (tested independently)
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
@@ -231,6 +232,81 @@ fn anotherFn(x: int): int { return x; }
|
|
|
231
232
|
});
|
|
232
233
|
});
|
|
233
234
|
// ---------------------------------------------------------------------------
|
|
235
|
+
// Hover — builtin functions
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
const DECORATOR_DOCS = {
|
|
238
|
+
tick: 'Runs every MC game tick (~20 Hz). No arguments.',
|
|
239
|
+
load: 'Runs on `/reload`. Use for initialization logic.',
|
|
240
|
+
coroutine: 'Splits loops into tick-spread continuations. Arg: `batch=N` (steps per tick, default 1).',
|
|
241
|
+
schedule: 'Schedules the function to run after N ticks. Arg: `ticks=N`.',
|
|
242
|
+
on_trigger: 'Runs when a trigger scoreboard objective is set by a player. Arg: trigger name.',
|
|
243
|
+
keep: 'Prevents the compiler from dead-code-eliminating this function.',
|
|
244
|
+
on: 'Generic event handler decorator.',
|
|
245
|
+
on_advancement: 'Runs when a player earns an advancement. Arg: advancement id.',
|
|
246
|
+
on_craft: 'Runs when a player crafts an item. Arg: item id.',
|
|
247
|
+
on_death: 'Runs when a player dies.',
|
|
248
|
+
on_join_team: 'Runs when a player joins a team. Arg: team name.',
|
|
249
|
+
on_login: 'Runs when a player logs in.',
|
|
250
|
+
};
|
|
251
|
+
describe('LSP hover — builtin functions', () => {
|
|
252
|
+
it('has metadata for say', () => {
|
|
253
|
+
const b = metadata_1.BUILTIN_METADATA['say'];
|
|
254
|
+
expect(b).toBeDefined();
|
|
255
|
+
expect(b.params.length).toBeGreaterThan(0);
|
|
256
|
+
expect(b.returns).toBe('void');
|
|
257
|
+
expect(b.doc).toBeTruthy();
|
|
258
|
+
});
|
|
259
|
+
it('has metadata for kill', () => {
|
|
260
|
+
const b = metadata_1.BUILTIN_METADATA['kill'];
|
|
261
|
+
expect(b).toBeDefined();
|
|
262
|
+
expect(b.returns).toBe('void');
|
|
263
|
+
});
|
|
264
|
+
it('has metadata for tellraw', () => {
|
|
265
|
+
const b = metadata_1.BUILTIN_METADATA['tellraw'];
|
|
266
|
+
expect(b).toBeDefined();
|
|
267
|
+
const paramStr = b.params.map(p => `${p.name}: ${p.type}${p.required ? '' : '?'}`).join(', ');
|
|
268
|
+
const sig = `fn ${b.name}(${paramStr}): ${b.returns}`;
|
|
269
|
+
expect(sig).toMatch(/^fn tellraw/);
|
|
270
|
+
expect(sig).toContain('target');
|
|
271
|
+
});
|
|
272
|
+
it('formats builtin hover markdown', () => {
|
|
273
|
+
const b = metadata_1.BUILTIN_METADATA['particle'];
|
|
274
|
+
expect(b).toBeDefined();
|
|
275
|
+
const paramStr = b.params.map(p => `${p.name}: ${p.type}${p.required ? '' : '?'}`).join(', ');
|
|
276
|
+
const sig = `fn ${b.name}(${paramStr}): ${b.returns}`;
|
|
277
|
+
const markdown = `\`\`\`redscript\n${sig}\n\`\`\`\n${b.doc}`;
|
|
278
|
+
expect(markdown).toContain('```redscript');
|
|
279
|
+
expect(markdown).toContain('fn particle');
|
|
280
|
+
expect(markdown).toContain(b.doc);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Hover — decorators
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
describe('LSP hover — decorators', () => {
|
|
287
|
+
it('has docs for @tick', () => {
|
|
288
|
+
expect(DECORATOR_DOCS['tick']).toContain('tick');
|
|
289
|
+
});
|
|
290
|
+
it('has docs for @load', () => {
|
|
291
|
+
expect(DECORATOR_DOCS['load']).toContain('reload');
|
|
292
|
+
});
|
|
293
|
+
it('has docs for @coroutine', () => {
|
|
294
|
+
expect(DECORATOR_DOCS['coroutine']).toContain('batch');
|
|
295
|
+
});
|
|
296
|
+
it('has docs for @schedule', () => {
|
|
297
|
+
expect(DECORATOR_DOCS['schedule']).toContain('ticks');
|
|
298
|
+
});
|
|
299
|
+
it('has docs for @on_trigger', () => {
|
|
300
|
+
expect(DECORATOR_DOCS['on_trigger']).toContain('trigger');
|
|
301
|
+
});
|
|
302
|
+
it('formats decorator hover markdown', () => {
|
|
303
|
+
const name = 'tick';
|
|
304
|
+
const doc = DECORATOR_DOCS[name];
|
|
305
|
+
const markdown = `**@${name}** — ${doc}`;
|
|
306
|
+
expect(markdown).toBe(`**@tick** — ${DECORATOR_DOCS['tick']}`);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
234
310
|
// Server module import (smoke test — does not start stdio)
|
|
235
311
|
// ---------------------------------------------------------------------------
|
|
236
312
|
describe('LSP server module', () => {
|
|
@@ -105,13 +105,25 @@ beforeAll(async () => {
|
|
|
105
105
|
console.warn(` Then restart the MC server and re-run tests.`);
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
|
-
// ──
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
// ── Clear stale minecraft tag files before writing fixtures ──────────
|
|
109
|
+
for (const tagFile of ['data/minecraft/tags/function/tick.json', 'data/minecraft/tags/function/load.json',
|
|
110
|
+
'data/minecraft/tags/functions/tick.json', 'data/minecraft/tags/functions/load.json']) {
|
|
111
|
+
const p = path.join(DATAPACK_DIR, tagFile);
|
|
112
|
+
if (fs.existsSync(p))
|
|
113
|
+
fs.writeFileSync(p, JSON.stringify({ values: [] }, null, 2));
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// ── Write fixtures + use safe reloadData (no /reload confirm) ───────
|
|
116
|
+
// counter.mcrs (use fixtures if examples was removed)
|
|
117
|
+
const counterSrc = fs.existsSync(path.join(__dirname, '../examples/counter.mcrs'))
|
|
118
|
+
? fs.readFileSync(path.join(__dirname, '../examples/counter.mcrs'), 'utf-8')
|
|
119
|
+
: fs.readFileSync(path.join(__dirname, 'fixtures/counter.mcrs'), 'utf-8');
|
|
120
|
+
writeFixture(counterSrc, 'counter');
|
|
121
|
+
// world_manager.mcrs
|
|
122
|
+
const wmPath = fs.existsSync(path.join(__dirname, '../examples/world_manager.mcrs'))
|
|
123
|
+
? path.join(__dirname, '../examples/world_manager.mcrs')
|
|
124
|
+
: path.join(__dirname, '../src/examples/world_manager.mcrs');
|
|
125
|
+
if (fs.existsSync(wmPath)) {
|
|
126
|
+
writeFixture(fs.readFileSync(wmPath, 'utf-8'), 'world_manager');
|
|
115
127
|
}
|
|
116
128
|
writeFixture(`
|
|
117
129
|
@tick
|
|
@@ -394,9 +406,9 @@ describe('MC Integration Tests', () => {
|
|
|
394
406
|
if (!serverOnline)
|
|
395
407
|
return;
|
|
396
408
|
await mc.fullReset({ clearArea: false, killEntities: true, resetScoreboards: false });
|
|
397
|
-
await mc.command('/summon minecraft:armor_stand 0 65 0');
|
|
398
|
-
await mc.command('/summon minecraft:armor_stand 2 65 0');
|
|
399
|
-
await mc.command('/summon minecraft:armor_stand 4 65 0');
|
|
409
|
+
await mc.command('/summon minecraft:armor_stand 0 65 0 {NoGravity:1b}');
|
|
410
|
+
await mc.command('/summon minecraft:armor_stand 2 65 0 {NoGravity:1b}');
|
|
411
|
+
await mc.command('/summon minecraft:armor_stand 4 65 0 {NoGravity:1b}');
|
|
400
412
|
await mc.ticks(5);
|
|
401
413
|
const stands = await mc.entities('@e[type=minecraft:armor_stand]');
|
|
402
414
|
expect(stands.length).toBe(3);
|
|
@@ -538,21 +550,21 @@ describe('E2E Scenario Tests', () => {
|
|
|
538
550
|
return;
|
|
539
551
|
await mc.command('/function match_test:__load');
|
|
540
552
|
// Test match on value 2
|
|
541
|
-
await mc.command('/scoreboard players set $p0
|
|
553
|
+
await mc.command('/scoreboard players set $p0 __match_test 2');
|
|
542
554
|
await mc.command('/function match_test:classify');
|
|
543
555
|
await mc.ticks(5);
|
|
544
556
|
let out = await mc.scoreboard('#match', 'out');
|
|
545
557
|
expect(out).toBe(20);
|
|
546
558
|
console.log(` match(2) → out=${out} (expect 20) ✓`);
|
|
547
559
|
// Test match on value 3
|
|
548
|
-
await mc.command('/scoreboard players set $p0
|
|
560
|
+
await mc.command('/scoreboard players set $p0 __match_test 3');
|
|
549
561
|
await mc.command('/function match_test:classify');
|
|
550
562
|
await mc.ticks(5);
|
|
551
563
|
out = await mc.scoreboard('#match', 'out');
|
|
552
564
|
expect(out).toBe(30);
|
|
553
565
|
console.log(` match(3) → out=${out} (expect 30) ✓`);
|
|
554
566
|
// Test default branch (value 99)
|
|
555
|
-
await mc.command('/scoreboard players set $p0
|
|
567
|
+
await mc.command('/scoreboard players set $p0 __match_test 99');
|
|
556
568
|
await mc.command('/function match_test:classify');
|
|
557
569
|
await mc.ticks(5);
|
|
558
570
|
out = await mc.scoreboard('#match', 'out');
|
|
@@ -714,7 +726,7 @@ describe('MC Integration - New Features', () => {
|
|
|
714
726
|
expect(armorStands).toBe(2); // 2 armor_stands matched
|
|
715
727
|
expect(items).toBe(1); // 1 item matched
|
|
716
728
|
await mc.command('/function is_check_test:cleanup').catch(() => { });
|
|
717
|
-
});
|
|
729
|
+
}, 30000); // extended timeout: entity spawn + reload can take >5 s
|
|
718
730
|
test('event-test.mcrs: @on(PlayerDeath) compiles and loads', async () => {
|
|
719
731
|
if (!serverOnline)
|
|
720
732
|
return;
|