redscript-mc 1.2.21 → 1.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish-extension-on-ci.yml +100 -0
- package/dist/__tests__/entity-types.test.d.ts +1 -0
- package/dist/__tests__/entity-types.test.js +203 -0
- package/dist/__tests__/var-allocator.test.d.ts +1 -0
- package/dist/__tests__/var-allocator.test.js +69 -0
- package/dist/ast/types.d.ts +2 -1
- package/dist/cli.js +17 -6
- package/dist/codegen/mcfunction/index.d.ts +2 -0
- package/dist/codegen/mcfunction/index.js +52 -43
- package/dist/codegen/structure/index.d.ts +4 -1
- package/dist/codegen/structure/index.js +8 -12
- package/dist/codegen/var-allocator.d.ts +28 -0
- package/dist/codegen/var-allocator.js +78 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -6
- package/dist/lowering/index.d.ts +2 -0
- package/dist/lowering/index.js +62 -3
- package/dist/parser/index.js +22 -1
- package/dist/typechecker/index.js +30 -0
- package/dist/types/entity-hierarchy.d.ts +29 -0
- package/dist/types/entity-hierarchy.js +107 -0
- package/editors/vscode/package-lock.json +6 -4
- package/editors/vscode/package.json +3 -3
- package/package.json +1 -1
- package/src/__tests__/entity-types.test.ts +236 -0
- package/src/__tests__/var-allocator.test.ts +75 -0
- package/src/ast/types.ts +8 -4
- package/src/cli.ts +20 -6
- package/src/codegen/mcfunction/index.ts +60 -48
- package/src/codegen/structure/index.ts +9 -14
- package/src/codegen/var-allocator.ts +75 -0
- package/src/examples/capture_the_flag.mcrs +34 -34
- package/src/examples/hunger_games.mcrs +59 -59
- package/src/examples/new_features_demo.mcrs +32 -32
- package/src/examples/parkour_race.mcrs +58 -58
- package/src/index.ts +10 -6
- package/src/lowering/index.ts +73 -8
- package/src/parser/index.ts +20 -1
- package/src/typechecker/index.ts +30 -0
- package/src/types/entity-hierarchy.ts +120 -0
- package/dist/data/arena/function/__load.mcfunction +0 -6
- package/dist/data/arena/function/__tick.mcfunction +0 -2
- package/dist/data/arena/function/announce_leaders/else_1.mcfunction +0 -3
- package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +0 -3
- package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +0 -7
- package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +0 -4
- package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +0 -6
- package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/then_0.mcfunction +0 -4
- package/dist/data/arena/function/announce_leaders.mcfunction +0 -6
- package/dist/data/arena/function/arena_tick/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/arena_tick/then_0.mcfunction +0 -4
- package/dist/data/arena/function/arena_tick.mcfunction +0 -11
- package/dist/data/counter/function/__load.mcfunction +0 -5
- package/dist/data/counter/function/__tick.mcfunction +0 -2
- package/dist/data/counter/function/counter_tick/merge_2.mcfunction +0 -1
- package/dist/data/counter/function/counter_tick/then_0.mcfunction +0 -3
- package/dist/data/counter/function/counter_tick.mcfunction +0 -11
- package/dist/data/minecraft/tags/function/load.json +0 -5
- package/dist/data/minecraft/tags/function/tick.json +0 -5
- package/dist/data/quiz/function/__load.mcfunction +0 -16
- package/dist/data/quiz/function/__tick.mcfunction +0 -6
- package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/answer_a.mcfunction +0 -4
- package/dist/data/quiz/function/answer_b.mcfunction +0 -4
- package/dist/data/quiz/function/answer_c.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/else_1.mcfunction +0 -5
- package/dist/data/quiz/function/ask_question/else_4.mcfunction +0 -5
- package/dist/data/quiz/function/ask_question/else_7.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/merge_2.mcfunction +0 -1
- package/dist/data/quiz/function/ask_question/merge_5.mcfunction +0 -2
- package/dist/data/quiz/function/ask_question/merge_8.mcfunction +0 -2
- package/dist/data/quiz/function/ask_question/then_0.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/then_3.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/then_6.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question.mcfunction +0 -7
- package/dist/data/quiz/function/finish_quiz.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/else_1.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/else_10.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_16.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_4.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_7.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +0 -8
- package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/then_0.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_12.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_15.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/then_3.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/then_6.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_9.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer.mcfunction +0 -11
- package/dist/data/quiz/function/start_quiz.mcfunction +0 -5
- package/dist/data/shop/function/__load.mcfunction +0 -7
- package/dist/data/shop/function/__tick.mcfunction +0 -3
- package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/else_1.mcfunction +0 -5
- package/dist/data/shop/function/complete_purchase/else_4.mcfunction +0 -5
- package/dist/data/shop/function/complete_purchase/else_7.mcfunction +0 -3
- package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/then_0.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/then_3.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/then_6.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase.mcfunction +0 -7
- package/dist/data/shop/function/handle_shop_trigger.mcfunction +0 -3
- package/dist/data/turret/function/__load.mcfunction +0 -5
- package/dist/data/turret/function/__tick.mcfunction +0 -4
- package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +0 -4
- package/dist/data/turret/function/deploy_turret.mcfunction +0 -8
- package/dist/data/turret/function/turret_tick/at_1.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/tick_body.mcfunction +0 -3
- package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +0 -1
- package/dist/data/turret/function/turret_tick.mcfunction +0 -5
- package/dist/pack.mcmeta +0 -6
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// ============================================
|
|
2
|
-
// Parkour Race -
|
|
2
|
+
// Parkour Race - Parkour Race
|
|
3
3
|
// ============================================
|
|
4
|
-
//
|
|
5
|
-
//
|
|
4
|
+
// Scenario: Players race from start to finish, recording the fastest time
|
|
5
|
+
// Multiple checkpoints set; falling respawns the player at the nearest checkpoint
|
|
6
6
|
// Scenario: Race from start to finish with checkpoints
|
|
7
7
|
// ============================================
|
|
8
8
|
|
|
@@ -11,28 +11,28 @@ import "../stdlib/world.mcrs"
|
|
|
11
11
|
import "../stdlib/bossbar.mcrs"
|
|
12
12
|
import "../stdlib/particles.mcrs"
|
|
13
13
|
|
|
14
|
-
// =====
|
|
14
|
+
// ===== Configuration =====
|
|
15
15
|
const START_X: int = 0;
|
|
16
16
|
const START_Y: int = 64;
|
|
17
17
|
const START_Z: int = 0;
|
|
18
|
-
const FALL_Y: int = 50; //
|
|
18
|
+
const FALL_Y: int = 50; // Below this height is considered a fall
|
|
19
19
|
|
|
20
20
|
const CHECKPOINT_COUNT: int = 5;
|
|
21
21
|
|
|
22
|
-
// =====
|
|
23
|
-
//
|
|
24
|
-
// - pk_time:
|
|
25
|
-
// - pk_best:
|
|
26
|
-
// - pk_checkpoint:
|
|
27
|
-
// - pk_running:
|
|
22
|
+
// ===== Game State =====
|
|
23
|
+
// Scoreboards:
|
|
24
|
+
// - pk_time: Current elapsed time (ticks)
|
|
25
|
+
// - pk_best: Best record
|
|
26
|
+
// - pk_checkpoint: Current checkpoint
|
|
27
|
+
// - pk_running: Whether in a race
|
|
28
28
|
|
|
29
|
-
//
|
|
30
|
-
// CP0: 0,64,0 (
|
|
29
|
+
// Checkpoint coordinates (simplified to linear parkour)
|
|
30
|
+
// CP0: 0,64,0 (start)
|
|
31
31
|
// CP1: 0,64,50
|
|
32
32
|
// CP2: 0,70,100
|
|
33
33
|
// CP3: 0,75,150
|
|
34
34
|
// CP4: 0,80,200
|
|
35
|
-
// CP5: 0,64,250 (
|
|
35
|
+
// CP5: 0,64,250 (finish)
|
|
36
36
|
|
|
37
37
|
@load
|
|
38
38
|
fn init() {
|
|
@@ -41,21 +41,21 @@ fn init() {
|
|
|
41
41
|
scoreboard_add_objective("pk_checkpoint", "dummy");
|
|
42
42
|
scoreboard_add_objective("pk_running", "dummy");
|
|
43
43
|
|
|
44
|
-
//
|
|
44
|
+
// Display best time
|
|
45
45
|
scoreboard_display("sidebar", "pk_best");
|
|
46
46
|
|
|
47
47
|
set_day();
|
|
48
48
|
weather_clear();
|
|
49
49
|
|
|
50
|
-
announce("§b[
|
|
50
|
+
announce("§b[Parkour] §fLoaded! Step on pressure plate to start");
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// =====
|
|
53
|
+
// ===== Start Race =====
|
|
54
54
|
fn start_race(player: selector) {
|
|
55
|
-
//
|
|
55
|
+
// Check if already in a race
|
|
56
56
|
let running: int = scoreboard_get(player, "pk_running");
|
|
57
57
|
if (running == 1) {
|
|
58
|
-
tell(player, "§
|
|
58
|
+
tell(player, "§cYou are already in a race!");
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -63,53 +63,53 @@ fn start_race(player: selector) {
|
|
|
63
63
|
scoreboard_set(player, "pk_time", 0);
|
|
64
64
|
scoreboard_set(player, "pk_checkpoint", 0);
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Teleport to start
|
|
67
67
|
tp(player, START_X, START_Y, START_Z);
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Clear effects, give speed
|
|
70
70
|
effect_clear(player);
|
|
71
71
|
|
|
72
|
-
title(player, "§
|
|
73
|
-
subtitle(player, "§
|
|
72
|
+
title(player, "§bGo!");
|
|
73
|
+
subtitle(player, "§7Race to the finish!");
|
|
74
74
|
|
|
75
|
-
//
|
|
76
|
-
tell(player, "§b[
|
|
75
|
+
// Create timer bossbar
|
|
76
|
+
tell(player, "§b[Parkour] §aRace started! Reach the finish to complete");
|
|
77
77
|
|
|
78
78
|
sparkles(player);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// =====
|
|
81
|
+
// ===== Every Tick =====
|
|
82
82
|
@tick
|
|
83
83
|
fn race_tick() {
|
|
84
|
-
//
|
|
84
|
+
// For each player in a race
|
|
85
85
|
foreach (p in @a) {
|
|
86
86
|
let running: int = scoreboard_get(p, "pk_running");
|
|
87
87
|
if (running == 1) {
|
|
88
|
-
//
|
|
88
|
+
// Increment time
|
|
89
89
|
scoreboard_add(p, "pk_time", 1);
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// Display current time
|
|
92
92
|
let time: int = scoreboard_get(p, "pk_time");
|
|
93
93
|
let seconds: int = time / 20;
|
|
94
94
|
let ms: int = (time % 20) * 5;
|
|
95
|
-
actionbar(p, "§e⏱ " + seconds + "." + ms + "
|
|
95
|
+
actionbar(p, "§e⏱ " + seconds + "." + ms + "s");
|
|
96
96
|
|
|
97
|
-
//
|
|
97
|
+
// Check fall
|
|
98
98
|
check_fall(p);
|
|
99
99
|
|
|
100
|
-
//
|
|
100
|
+
// Check checkpoints
|
|
101
101
|
check_checkpoints(p);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
fn check_fall(player: selector) {
|
|
107
|
-
//
|
|
108
|
-
//
|
|
107
|
+
// If player is below FALL_Y, teleport back to checkpoint
|
|
108
|
+
// Using execute positioned to check
|
|
109
109
|
execute if entity player[y=..50] run {
|
|
110
110
|
let cp: int = scoreboard_get(player, "pk_checkpoint");
|
|
111
111
|
respawn_at_checkpoint(player, cp);
|
|
112
|
-
tell(player, "§
|
|
112
|
+
tell(player, "§cFell! Returning to checkpoint " + cp);
|
|
113
113
|
angry(player);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -117,35 +117,35 @@ fn check_fall(player: selector) {
|
|
|
117
117
|
fn check_checkpoints(player: selector) {
|
|
118
118
|
let current_cp: int = scoreboard_get(player, "pk_checkpoint");
|
|
119
119
|
|
|
120
|
-
//
|
|
120
|
+
// Checkpoint 1 (0, 64, 50)
|
|
121
121
|
if (current_cp == 0) {
|
|
122
122
|
execute if entity player[x=-2..2, y=62..68, z=48..52] run {
|
|
123
123
|
reach_checkpoint(player, 1);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
//
|
|
127
|
+
// Checkpoint 2 (0, 70, 100)
|
|
128
128
|
if (current_cp == 1) {
|
|
129
129
|
execute if entity player[x=-2..2, y=68..74, z=98..102] run {
|
|
130
130
|
reach_checkpoint(player, 2);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
//
|
|
134
|
+
// Checkpoint 3 (0, 75, 150)
|
|
135
135
|
if (current_cp == 2) {
|
|
136
136
|
execute if entity player[x=-2..2, y=73..79, z=148..152] run {
|
|
137
137
|
reach_checkpoint(player, 3);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
//
|
|
141
|
+
// Checkpoint 4 (0, 80, 200)
|
|
142
142
|
if (current_cp == 3) {
|
|
143
143
|
execute if entity player[x=-2..2, y=78..84, z=198..202] run {
|
|
144
144
|
reach_checkpoint(player, 4);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
//
|
|
148
|
+
// Finish (0, 64, 250)
|
|
149
149
|
if (current_cp == 4) {
|
|
150
150
|
execute if entity player[x=-2..2, y=62..68, z=248..252] run {
|
|
151
151
|
finish_race(player);
|
|
@@ -157,7 +157,7 @@ fn reach_checkpoint(player: selector, cp: int) {
|
|
|
157
157
|
scoreboard_set(player, "pk_checkpoint", cp);
|
|
158
158
|
|
|
159
159
|
title(player, "");
|
|
160
|
-
subtitle(player, "§
|
|
160
|
+
subtitle(player, "§aCheckpoint " + cp + " / " + CHECKPOINT_COUNT);
|
|
161
161
|
|
|
162
162
|
happy(player);
|
|
163
163
|
playsound("minecraft:entity.experience_orb.pickup", "player", player);
|
|
@@ -189,45 +189,45 @@ fn finish_race(player: selector) {
|
|
|
189
189
|
let seconds: int = time / 20;
|
|
190
190
|
let ms: int = (time % 20) * 5;
|
|
191
191
|
|
|
192
|
-
//
|
|
192
|
+
// Check if it's a new record
|
|
193
193
|
if (best == 0) {
|
|
194
|
-
//
|
|
194
|
+
// First completion
|
|
195
195
|
scoreboard_set(player, "pk_best", time);
|
|
196
|
-
title(player, "§
|
|
197
|
-
subtitle(player, "§e" + seconds + "." + ms + "
|
|
198
|
-
announce("§b[
|
|
196
|
+
title(player, "§6Finished!");
|
|
197
|
+
subtitle(player, "§e" + seconds + "." + ms + "s §7(first record)");
|
|
198
|
+
announce("§b[Parkour] §fA player finished! Time: §e" + seconds + "." + ms + "s");
|
|
199
199
|
} else {
|
|
200
200
|
if (time < best) {
|
|
201
|
-
//
|
|
201
|
+
// New record!
|
|
202
202
|
scoreboard_set(player, "pk_best", time);
|
|
203
|
-
title(player, "§
|
|
204
|
-
subtitle(player, "§e" + seconds + "." + ms + "
|
|
205
|
-
announce("§b[
|
|
203
|
+
title(player, "§6New Record!");
|
|
204
|
+
subtitle(player, "§e" + seconds + "." + ms + "s");
|
|
205
|
+
announce("§b[Parkour] §6New Record! §fTime: §e" + seconds + "." + ms + "s");
|
|
206
206
|
totem_effect(player);
|
|
207
207
|
} else {
|
|
208
|
-
title(player, "§
|
|
209
|
-
subtitle(player, "§e" + seconds + "." + ms + "
|
|
208
|
+
title(player, "§aFinished!");
|
|
209
|
+
subtitle(player, "§e" + seconds + "." + ms + "s");
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
//
|
|
213
|
+
// Effects
|
|
214
214
|
sparkles(player);
|
|
215
215
|
playsound("minecraft:ui.toast.challenge_complete", "player", player);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
// =====
|
|
218
|
+
// ===== Quit Race =====
|
|
219
219
|
fn quit_race(player: selector) {
|
|
220
220
|
let running: int = scoreboard_get(player, "pk_running");
|
|
221
221
|
if (running == 1) {
|
|
222
222
|
scoreboard_set(player, "pk_running", 0);
|
|
223
|
-
tell(player, "§
|
|
223
|
+
tell(player, "§cRace abandoned");
|
|
224
224
|
tp(player, START_X, START_Y, START_Z);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
// =====
|
|
228
|
+
// ===== View Leaderboard =====
|
|
229
229
|
fn show_leaderboard() {
|
|
230
|
-
announce("§b=====
|
|
231
|
-
//
|
|
230
|
+
announce("§b===== Parkour Leaderboard =====");
|
|
231
|
+
// Display top 5 (requires scoreboard sorting)
|
|
232
232
|
scoreboard_display("sidebar", "pk_best");
|
|
233
233
|
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface CompileOptions {
|
|
|
35
35
|
typeCheck?: boolean
|
|
36
36
|
filePath?: string
|
|
37
37
|
dce?: boolean
|
|
38
|
+
mangle?: boolean
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
export interface CompileResult {
|
|
@@ -45,6 +46,7 @@ export interface CompileResult {
|
|
|
45
46
|
typeErrors?: DiagnosticError[]
|
|
46
47
|
warnings?: Warning[]
|
|
47
48
|
stats?: OptimizationStats
|
|
49
|
+
sourceMap?: Record<string, string>
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -59,6 +61,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
59
61
|
const shouldOptimize = options.optimize ?? true
|
|
60
62
|
const shouldTypeCheck = options.typeCheck ?? true
|
|
61
63
|
const shouldRunDce = options.dce ?? shouldOptimize
|
|
64
|
+
const mangle = options.mangle ?? false
|
|
62
65
|
const filePath = options.filePath
|
|
63
66
|
const preprocessed = preprocessSourceWithMetadata(source, { filePath })
|
|
64
67
|
const preprocessedSource = preprocessed.source
|
|
@@ -83,7 +86,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
83
86
|
const ir = lowering.lower(ast)
|
|
84
87
|
|
|
85
88
|
let optimizedIR: IRModule = ir
|
|
86
|
-
let generated = generateDatapackWithStats(ir, { optimizeCommands: shouldOptimize })
|
|
89
|
+
let generated = generateDatapackWithStats(ir, { optimizeCommands: shouldOptimize, mangle })
|
|
87
90
|
let optimizationStats: OptimizationStats | undefined
|
|
88
91
|
|
|
89
92
|
if (shouldOptimize) {
|
|
@@ -105,10 +108,10 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
105
108
|
const copyPropagatedIR: IRModule = { ...ir, functions: copyPropagatedFunctions }
|
|
106
109
|
optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions }
|
|
107
110
|
|
|
108
|
-
const baselineGenerated = generateDatapackWithStats(ir, { optimizeCommands: false })
|
|
109
|
-
const beforeDceGenerated = generateDatapackWithStats(copyPropagatedIR, { optimizeCommands: false })
|
|
110
|
-
const afterDceGenerated = generateDatapackWithStats(optimizedIR, { optimizeCommands: false })
|
|
111
|
-
generated = generateDatapackWithStats(optimizedIR, { optimizeCommands: true })
|
|
111
|
+
const baselineGenerated = generateDatapackWithStats(ir, { optimizeCommands: false, mangle })
|
|
112
|
+
const beforeDceGenerated = generateDatapackWithStats(copyPropagatedIR, { optimizeCommands: false, mangle })
|
|
113
|
+
const afterDceGenerated = generateDatapackWithStats(optimizedIR, { optimizeCommands: false, mangle })
|
|
114
|
+
generated = generateDatapackWithStats(optimizedIR, { optimizeCommands: true, mangle })
|
|
112
115
|
|
|
113
116
|
stats.deadCodeRemoved =
|
|
114
117
|
countMcfunctionCommands(beforeDceGenerated.files) - countMcfunctionCommands(afterDceGenerated.files)
|
|
@@ -124,7 +127,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
124
127
|
optimizationStats = stats
|
|
125
128
|
} else {
|
|
126
129
|
optimizedIR = ir
|
|
127
|
-
generated = generateDatapackWithStats(ir, { optimizeCommands: false })
|
|
130
|
+
generated = generateDatapackWithStats(ir, { optimizeCommands: false, mangle })
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
return {
|
|
@@ -135,6 +138,7 @@ export function compile(source: string, options: CompileOptions = {}): CompileRe
|
|
|
135
138
|
typeErrors,
|
|
136
139
|
warnings: [...dceResult.warnings, ...lowering.warnings],
|
|
137
140
|
stats: optimizationStats,
|
|
141
|
+
sourceMap: generated.sourceMap,
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
package/src/lowering/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
import type { GlobalVar } from '../ir/types'
|
|
18
18
|
import * as path from 'path'
|
|
19
19
|
import { EVENT_TYPES, getEventParamSpecs, isEventTypeName } from '../events/types'
|
|
20
|
+
import { getBaseSelectorType, areCompatibleTypes, getConcreteSubtypes } from '../types/entity-hierarchy'
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
// Macro-aware builtins (MC 1.20.2+)
|
|
@@ -134,11 +135,21 @@ const ENTITY_TO_MC_TYPE: Partial<Record<EntityTypeName, string>> = {
|
|
|
134
135
|
Creeper: 'minecraft:creeper',
|
|
135
136
|
Spider: 'minecraft:spider',
|
|
136
137
|
Enderman: 'minecraft:enderman',
|
|
138
|
+
Blaze: 'minecraft:blaze',
|
|
139
|
+
Witch: 'minecraft:witch',
|
|
140
|
+
Slime: 'minecraft:slime',
|
|
141
|
+
ZombieVillager: 'minecraft:zombie_villager',
|
|
142
|
+
Husk: 'minecraft:husk',
|
|
143
|
+
Drowned: 'minecraft:drowned',
|
|
144
|
+
Stray: 'minecraft:stray',
|
|
145
|
+
WitherSkeleton: 'minecraft:wither_skeleton',
|
|
146
|
+
CaveSpider: 'minecraft:cave_spider',
|
|
137
147
|
Pig: 'minecraft:pig',
|
|
138
148
|
Cow: 'minecraft:cow',
|
|
139
149
|
Sheep: 'minecraft:sheep',
|
|
140
150
|
Chicken: 'minecraft:chicken',
|
|
141
151
|
Villager: 'minecraft:villager',
|
|
152
|
+
WanderingTrader: 'minecraft:wandering_trader',
|
|
142
153
|
ArmorStand: 'minecraft:armor_stand',
|
|
143
154
|
Item: 'minecraft:item',
|
|
144
155
|
Arrow: 'minecraft:arrow',
|
|
@@ -211,6 +222,15 @@ export class Lowering {
|
|
|
211
222
|
private intervalCounter: number = 0
|
|
212
223
|
readonly warnings: Warning[] = []
|
|
213
224
|
|
|
225
|
+
// Entity type context stack for W_IMPOSSIBLE_AS warnings
|
|
226
|
+
private entityContextStack: string[] = []
|
|
227
|
+
|
|
228
|
+
private currentEntityContext(): string {
|
|
229
|
+
return this.entityContextStack.length > 0
|
|
230
|
+
? this.entityContextStack[this.entityContextStack.length - 1]
|
|
231
|
+
: 'Entity'
|
|
232
|
+
}
|
|
233
|
+
|
|
214
234
|
// Builder state for current function
|
|
215
235
|
private builder!: LoweringBuilder
|
|
216
236
|
private varMap: Map<string, string> = new Map()
|
|
@@ -1056,17 +1076,31 @@ export class Lowering {
|
|
|
1056
1076
|
}
|
|
1057
1077
|
|
|
1058
1078
|
const mcType = ENTITY_TO_MC_TYPE[cond.entityType]
|
|
1079
|
+
const thenFnName = `${this.currentFn}/then_${this.foreachCounter++}`
|
|
1080
|
+
|
|
1059
1081
|
if (!mcType) {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1082
|
+
// Abstract type — check all concrete subtypes
|
|
1083
|
+
const subtypes = getConcreteSubtypes(cond.entityType)
|
|
1084
|
+
if (subtypes.length === 0) {
|
|
1085
|
+
throw new DiagnosticError(
|
|
1086
|
+
'LoweringError',
|
|
1087
|
+
`Cannot lower entity type check for '${cond.entityType}'`,
|
|
1088
|
+
cond.span ?? stmt.span ?? { line: 0, col: 0 }
|
|
1089
|
+
)
|
|
1090
|
+
}
|
|
1091
|
+
// Use a temp scoreboard variable to OR multiple type checks
|
|
1092
|
+
this.builder.emitRaw(`scoreboard players set __is_result rs:temp 0`)
|
|
1093
|
+
for (const subtype of subtypes) {
|
|
1094
|
+
if (subtype.mcId) {
|
|
1095
|
+
this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, subtype.mcId)} run scoreboard players set __is_result rs:temp 1`)
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
this.builder.emitRaw(`execute if score __is_result rs:temp matches 1 run function ${this.namespace}:${thenFnName}`)
|
|
1099
|
+
} else {
|
|
1100
|
+
// Concrete type — single check
|
|
1101
|
+
this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, mcType)} run function ${this.namespace}:${thenFnName}`)
|
|
1065
1102
|
}
|
|
1066
1103
|
|
|
1067
|
-
const thenFnName = `${this.currentFn}/then_${this.foreachCounter++}`
|
|
1068
|
-
this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, mcType)} run function ${this.namespace}:${thenFnName}`)
|
|
1069
|
-
|
|
1070
1104
|
const savedBuilder = this.builder
|
|
1071
1105
|
const savedVarMap = new Map(this.varMap)
|
|
1072
1106
|
const savedBlockPosVars = new Map(this.blockPosVars)
|
|
@@ -1243,12 +1277,22 @@ export class Lowering {
|
|
|
1243
1277
|
// In foreach body, the binding maps to @s
|
|
1244
1278
|
this.varMap.set(stmt.binding, '@s')
|
|
1245
1279
|
|
|
1280
|
+
// Track entity context for type narrowing
|
|
1281
|
+
const selectorEntityType = getBaseSelectorType(selector)
|
|
1282
|
+
if (selectorEntityType) {
|
|
1283
|
+
this.entityContextStack.push(selectorEntityType)
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1246
1286
|
this.builder.startBlock('entry')
|
|
1247
1287
|
this.lowerBlock(stmt.body)
|
|
1248
1288
|
if (!this.builder.isBlockSealed()) {
|
|
1249
1289
|
this.builder.emitReturn()
|
|
1250
1290
|
}
|
|
1251
1291
|
|
|
1292
|
+
if (selectorEntityType) {
|
|
1293
|
+
this.entityContextStack.pop()
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1252
1296
|
const subFn = this.builder.build(subFnName, [], false)
|
|
1253
1297
|
this.functions.push(subFn)
|
|
1254
1298
|
|
|
@@ -1397,6 +1441,18 @@ export class Lowering {
|
|
|
1397
1441
|
const selector = this.selectorToString(stmt.selector)
|
|
1398
1442
|
const subFnName = `${this.currentFn}/as_${this.foreachCounter++}`
|
|
1399
1443
|
|
|
1444
|
+
// Check for impossible type assertions (W_IMPOSSIBLE_AS)
|
|
1445
|
+
const innerType = getBaseSelectorType(selector)
|
|
1446
|
+
const outerType = this.currentEntityContext()
|
|
1447
|
+
if (innerType && outerType !== 'Entity' && innerType !== 'Entity' && !areCompatibleTypes(outerType, innerType)) {
|
|
1448
|
+
this.warnings.push({
|
|
1449
|
+
message: `Impossible type assertion: @s is ${outerType} but as-block targets ${innerType}`,
|
|
1450
|
+
code: 'W_IMPOSSIBLE_AS',
|
|
1451
|
+
line: stmt.span?.line,
|
|
1452
|
+
col: stmt.span?.col,
|
|
1453
|
+
})
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1400
1456
|
this.builder.emitRaw(`execute as ${selector} run function ${this.namespace}:${subFnName}`)
|
|
1401
1457
|
|
|
1402
1458
|
// Create sub-function
|
|
@@ -1408,12 +1464,21 @@ export class Lowering {
|
|
|
1408
1464
|
this.varMap = new Map(savedVarMap)
|
|
1409
1465
|
this.blockPosVars = new Map(savedBlockPosVars)
|
|
1410
1466
|
|
|
1467
|
+
// Track entity context inside as-block
|
|
1468
|
+
if (innerType) {
|
|
1469
|
+
this.entityContextStack.push(innerType)
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1411
1472
|
this.builder.startBlock('entry')
|
|
1412
1473
|
this.lowerBlock(stmt.body)
|
|
1413
1474
|
if (!this.builder.isBlockSealed()) {
|
|
1414
1475
|
this.builder.emitReturn()
|
|
1415
1476
|
}
|
|
1416
1477
|
|
|
1478
|
+
if (innerType) {
|
|
1479
|
+
this.entityContextStack.pop()
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1417
1482
|
const subFn = this.builder.build(subFnName, [], false)
|
|
1418
1483
|
this.functions.push(subFn)
|
|
1419
1484
|
|
package/src/parser/index.ts
CHANGED
|
@@ -41,11 +41,21 @@ const ENTITY_TYPE_NAMES = new Set<EntityTypeName>([
|
|
|
41
41
|
'Creeper',
|
|
42
42
|
'Spider',
|
|
43
43
|
'Enderman',
|
|
44
|
+
'Blaze',
|
|
45
|
+
'Witch',
|
|
46
|
+
'Slime',
|
|
47
|
+
'ZombieVillager',
|
|
48
|
+
'Husk',
|
|
49
|
+
'Drowned',
|
|
50
|
+
'Stray',
|
|
51
|
+
'WitherSkeleton',
|
|
52
|
+
'CaveSpider',
|
|
44
53
|
'Pig',
|
|
45
54
|
'Cow',
|
|
46
55
|
'Sheep',
|
|
47
56
|
'Chicken',
|
|
48
57
|
'Villager',
|
|
58
|
+
'WanderingTrader',
|
|
49
59
|
'ArmorStand',
|
|
50
60
|
'Item',
|
|
51
61
|
'Arrow',
|
|
@@ -446,7 +456,16 @@ export class Parser {
|
|
|
446
456
|
type = { kind: 'named', name: token.kind }
|
|
447
457
|
} else if (token.kind === 'ident') {
|
|
448
458
|
this.advance()
|
|
449
|
-
|
|
459
|
+
if (token.value === 'selector' && this.check('<')) {
|
|
460
|
+
this.advance() // consume <
|
|
461
|
+
const entityType = this.expect('ident').value
|
|
462
|
+
this.expect('>')
|
|
463
|
+
type = { kind: 'selector', entityType }
|
|
464
|
+
} else if (token.value === 'selector') {
|
|
465
|
+
type = { kind: 'selector' }
|
|
466
|
+
} else {
|
|
467
|
+
type = { kind: 'struct', name: token.value }
|
|
468
|
+
}
|
|
450
469
|
} else {
|
|
451
470
|
this.error(`Expected type, got '${token.kind}'`)
|
|
452
471
|
}
|
package/src/typechecker/index.ts
CHANGED
|
@@ -31,11 +31,21 @@ const ENTITY_HIERARCHY: Record<EntityTypeName, EntityTypeName | null> = {
|
|
|
31
31
|
'Creeper': 'HostileMob',
|
|
32
32
|
'Spider': 'HostileMob',
|
|
33
33
|
'Enderman': 'HostileMob',
|
|
34
|
+
'Blaze': 'HostileMob',
|
|
35
|
+
'Witch': 'HostileMob',
|
|
36
|
+
'Slime': 'HostileMob',
|
|
37
|
+
'ZombieVillager': 'HostileMob',
|
|
38
|
+
'Husk': 'HostileMob',
|
|
39
|
+
'Drowned': 'HostileMob',
|
|
40
|
+
'Stray': 'HostileMob',
|
|
41
|
+
'WitherSkeleton': 'HostileMob',
|
|
42
|
+
'CaveSpider': 'HostileMob',
|
|
34
43
|
'Pig': 'PassiveMob',
|
|
35
44
|
'Cow': 'PassiveMob',
|
|
36
45
|
'Sheep': 'PassiveMob',
|
|
37
46
|
'Chicken': 'PassiveMob',
|
|
38
47
|
'Villager': 'PassiveMob',
|
|
48
|
+
'WanderingTrader': 'PassiveMob',
|
|
39
49
|
'ArmorStand': 'entity',
|
|
40
50
|
'Item': 'entity',
|
|
41
51
|
'Arrow': 'entity',
|
|
@@ -53,6 +63,24 @@ const MC_TYPE_TO_ENTITY: Record<string, EntityTypeName> = {
|
|
|
53
63
|
'minecraft:spider': 'Spider',
|
|
54
64
|
'enderman': 'Enderman',
|
|
55
65
|
'minecraft:enderman': 'Enderman',
|
|
66
|
+
'blaze': 'Blaze',
|
|
67
|
+
'minecraft:blaze': 'Blaze',
|
|
68
|
+
'witch': 'Witch',
|
|
69
|
+
'minecraft:witch': 'Witch',
|
|
70
|
+
'slime': 'Slime',
|
|
71
|
+
'minecraft:slime': 'Slime',
|
|
72
|
+
'zombie_villager': 'ZombieVillager',
|
|
73
|
+
'minecraft:zombie_villager': 'ZombieVillager',
|
|
74
|
+
'husk': 'Husk',
|
|
75
|
+
'minecraft:husk': 'Husk',
|
|
76
|
+
'drowned': 'Drowned',
|
|
77
|
+
'minecraft:drowned': 'Drowned',
|
|
78
|
+
'stray': 'Stray',
|
|
79
|
+
'minecraft:stray': 'Stray',
|
|
80
|
+
'wither_skeleton': 'WitherSkeleton',
|
|
81
|
+
'minecraft:wither_skeleton': 'WitherSkeleton',
|
|
82
|
+
'cave_spider': 'CaveSpider',
|
|
83
|
+
'minecraft:cave_spider': 'CaveSpider',
|
|
56
84
|
'pig': 'Pig',
|
|
57
85
|
'minecraft:pig': 'Pig',
|
|
58
86
|
'cow': 'Cow',
|
|
@@ -63,6 +91,8 @@ const MC_TYPE_TO_ENTITY: Record<string, EntityTypeName> = {
|
|
|
63
91
|
'minecraft:chicken': 'Chicken',
|
|
64
92
|
'villager': 'Villager',
|
|
65
93
|
'minecraft:villager': 'Villager',
|
|
94
|
+
'wandering_trader': 'WanderingTrader',
|
|
95
|
+
'minecraft:wandering_trader': 'WanderingTrader',
|
|
66
96
|
'armor_stand': 'ArmorStand',
|
|
67
97
|
'minecraft:armor_stand': 'ArmorStand',
|
|
68
98
|
'item': 'Item',
|