moonscratch 0.1.1 → 0.1.2

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.
Files changed (149) hide show
  1. package/dist/chunk-DQk6qfdC.mjs +18 -0
  2. package/dist/index.d.mts +1173 -0
  3. package/dist/index.mjs +27135 -0
  4. package/package.json +6 -1
  5. package/.agents/skills/moonbit-agent-guide/LICENSE +0 -202
  6. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +0 -1126
  7. package/.agents/skills/moonbit-agent-guide/SKILL.md +0 -1126
  8. package/.agents/skills/moonbit-agent-guide/ide.md +0 -116
  9. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +0 -106
  10. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +0 -422
  11. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +0 -422
  12. package/.agents/skills/moonbit-practice/SKILL.md +0 -258
  13. package/.agents/skills/moonbit-practice/assets/ci.yaml +0 -25
  14. package/.agents/skills/moonbit-practice/reference/agents.md +0 -1469
  15. package/.agents/skills/moonbit-practice/reference/configuration.md +0 -228
  16. package/.agents/skills/moonbit-practice/reference/ffi.md +0 -229
  17. package/.agents/skills/moonbit-practice/reference/ide.md +0 -189
  18. package/.agents/skills/moonbit-practice/reference/performance.md +0 -217
  19. package/.agents/skills/moonbit-practice/reference/refactor.md +0 -154
  20. package/.agents/skills/moonbit-practice/reference/stdlib.md +0 -351
  21. package/.agents/skills/moonbit-practice/reference/testing.md +0 -228
  22. package/.agents/skills/moonbit-refactoring/LICENSE +0 -21
  23. package/.agents/skills/moonbit-refactoring/SKILL.md +0 -323
  24. package/.githooks/README.md +0 -23
  25. package/.githooks/pre-commit +0 -3
  26. package/.github/workflows/copilot-setup-steps.yml +0 -40
  27. package/AGENTS.md +0 -91
  28. package/PLAN.md +0 -64
  29. package/TODO.md +0 -120
  30. package/benchmarks/calc.bench.ts +0 -144
  31. package/benchmarks/draw.bench.ts +0 -215
  32. package/benchmarks/load.bench.ts +0 -28
  33. package/benchmarks/render.bench.ts +0 -53
  34. package/benchmarks/run.bench.ts +0 -8
  35. package/benchmarks/types.d.ts +0 -15
  36. package/docs/scratch-vm-specs/eventloop.md +0 -103
  37. package/docs/scratch-vm-specs/moonscratch-time-separation.md +0 -50
  38. package/index.html +0 -91
  39. package/js/AGENTS.md +0 -5
  40. package/js/a.ts +0 -52
  41. package/js/assets/AGENTS.md +0 -5
  42. package/js/assets/base64.test.ts +0 -14
  43. package/js/assets/base64.ts +0 -21
  44. package/js/assets/build-asset.test.ts +0 -26
  45. package/js/assets/build-asset.ts +0 -28
  46. package/js/assets/create.test.ts +0 -142
  47. package/js/assets/create.ts +0 -122
  48. package/js/assets/index.test.ts +0 -15
  49. package/js/assets/index.ts +0 -2
  50. package/js/assets/types.ts +0 -26
  51. package/js/assets/validation.test.ts +0 -34
  52. package/js/assets/validation.ts +0 -25
  53. package/js/assets.test.ts +0 -14
  54. package/js/assets.ts +0 -1
  55. package/js/index.test.ts +0 -26
  56. package/js/index.ts +0 -3
  57. package/js/render/index.test.ts +0 -65
  58. package/js/render/index.ts +0 -13
  59. package/js/render/sharp.ts +0 -87
  60. package/js/render/svg.ts +0 -68
  61. package/js/render/types.ts +0 -35
  62. package/js/render/utils.ts +0 -108
  63. package/js/render/webgl.ts +0 -274
  64. package/js/sharp-optional.d.ts +0 -16
  65. package/js/test/helpers.ts +0 -116
  66. package/js/test/hikkaku-sample.test.ts +0 -37
  67. package/js/test/rubik-components.input-motion.test.ts +0 -60
  68. package/js/test/rubik-components.lists.test.ts +0 -49
  69. package/js/test/rubik-components.operators.test.ts +0 -104
  70. package/js/test/rubik-components.pen.test.ts +0 -112
  71. package/js/test/rubik-components.procedures-loops.test.ts +0 -72
  72. package/js/test/rubik-components.variables-branches.test.ts +0 -57
  73. package/js/test/rubik-components.visibility-entry.test.ts +0 -31
  74. package/js/test/test-projects.ts +0 -598
  75. package/js/test/variable.ts +0 -200
  76. package/js/test/warp.test.ts +0 -59
  77. package/js/vm/AGENTS.md +0 -6
  78. package/js/vm/README.md +0 -183
  79. package/js/vm/bindings.test.ts +0 -13
  80. package/js/vm/bindings.ts +0 -5
  81. package/js/vm/compare-operators.test.ts +0 -145
  82. package/js/vm/constants.test.ts +0 -11
  83. package/js/vm/constants.ts +0 -4
  84. package/js/vm/effect-guards.test.ts +0 -68
  85. package/js/vm/effect-guards.ts +0 -44
  86. package/js/vm/factory.test.ts +0 -486
  87. package/js/vm/factory.ts +0 -615
  88. package/js/vm/headless-vm.test.ts +0 -131
  89. package/js/vm/headless-vm.ts +0 -342
  90. package/js/vm/index.test.ts +0 -28
  91. package/js/vm/index.ts +0 -5
  92. package/js/vm/internal-types.ts +0 -32
  93. package/js/vm/json.test.ts +0 -40
  94. package/js/vm/json.ts +0 -273
  95. package/js/vm/normalize.test.ts +0 -48
  96. package/js/vm/normalize.ts +0 -65
  97. package/js/vm/options.test.ts +0 -30
  98. package/js/vm/options.ts +0 -55
  99. package/js/vm/pen-transparency.test.ts +0 -115
  100. package/js/vm/program-wasm.ts +0 -322
  101. package/js/vm/scheduler-render.test.ts +0 -401
  102. package/js/vm/scratch-assets.test.ts +0 -136
  103. package/js/vm/scratch-assets.ts +0 -202
  104. package/js/vm/types.ts +0 -358
  105. package/js/vm/value-guards.test.ts +0 -25
  106. package/js/vm/value-guards.ts +0 -18
  107. package/moon.mod.json +0 -10
  108. package/scripts/preinstall.ts +0 -4
  109. package/src/AGENTS.md +0 -6
  110. package/src/api.mbt +0 -161
  111. package/src/api_aot_commands.mbt +0 -184
  112. package/src/api_effects_json.mbt +0 -72
  113. package/src/api_options.mbt +0 -60
  114. package/src/api_program_wasm.mbt +0 -1647
  115. package/src/api_program_wat.mbt +0 -2206
  116. package/src/api_snapshot_json.mbt +0 -44
  117. package/src/cmd/AGENTS.md +0 -5
  118. package/src/cmd/main/AGENTS.md +0 -5
  119. package/src/cmd/main/main.mbt +0 -29
  120. package/src/cmd/main/moon.pkg +0 -7
  121. package/src/cmd/main/pkg.generated.mbti +0 -13
  122. package/src/json_helpers.mbt +0 -176
  123. package/src/moon.pkg +0 -65
  124. package/src/moonscratch.mbt +0 -3
  125. package/src/moonscratch_wbtest.mbt +0 -40
  126. package/src/parser_sb3.mbt +0 -890
  127. package/src/pkg.generated.mbti +0 -479
  128. package/src/runtime_eval.mbt +0 -2844
  129. package/src/runtime_exec.mbt +0 -3850
  130. package/src/runtime_render.mbt +0 -2550
  131. package/src/runtime_state.mbt +0 -870
  132. package/src/test/AGENTS.md +0 -3
  133. package/src/test/projects/AGENTS.md +0 -6
  134. package/src/test/projects/moon.pkg +0 -4
  135. package/src/test/projects/moonscratch_compat_test.mbt +0 -642
  136. package/src/test/projects/moonscratch_core_test.mbt +0 -1332
  137. package/src/test/projects/moonscratch_runtime_test.mbt +0 -1087
  138. package/src/test/projects/pkg.generated.mbti +0 -13
  139. package/src/test/projects/test_support.mbt +0 -35
  140. package/src/types_effects.mbt +0 -20
  141. package/src/types_error.mbt +0 -4
  142. package/src/types_options.mbt +0 -31
  143. package/src/types_runtime_structs.mbt +0 -254
  144. package/src/types_vm.mbt +0 -109
  145. package/tsconfig.json +0 -29
  146. package/viewer/index.ts +0 -399
  147. package/viewer/vite.d.ts +0 -1
  148. package/viewer/worker.ts +0 -161
  149. package/vite.config.ts +0 -61
@@ -1,870 +0,0 @@
1
- ///|
2
- fn precompiled_backdrop_cache_key(width : Int, height : Int) -> String {
3
- "\{width}x\{height}"
4
- }
5
-
6
- ///|
7
- fn precompiled_initial_opaque_backdrop_pixels(
8
- precompiled : PrecompiledProject,
9
- width : Int,
10
- height : Int,
11
- ) -> Array[Byte] {
12
- if width <= 0 || height <= 0 {
13
- return []
14
- }
15
- let key = precompiled_backdrop_cache_key(width, height)
16
- match precompiled.initial_opaque_backdrops.get(key) {
17
- Some(pixels) => pixels
18
- None => {
19
- let pixels = if precompiled.stage_index >= 0 &&
20
- precompiled.stage_index < precompiled.targets.length() {
21
- let stage = precompiled.targets[precompiled.stage_index]
22
- match render_current_costume(stage) {
23
- Some(costume) =>
24
- render_build_opaque_stage_pixels(costume, width, height)
25
- None => render_build_opaque_white_pixels(width, height)
26
- }
27
- } else {
28
- render_build_opaque_white_pixels(width, height)
29
- }
30
- precompiled.initial_opaque_backdrops[key] = pixels
31
- pixels
32
- }
33
- }
34
- }
35
-
36
- ///|
37
- fn target_has_renderable_costume(target : TargetState) -> Bool {
38
- if target.size <= 0.0 {
39
- return false
40
- }
41
- match render_current_costume(target) {
42
- Some(costume) =>
43
- costume.width > 0 &&
44
- costume.height > 0 &&
45
- costume.bitmap_resolution > 0 &&
46
- costume.pixels.length() >= costume.width * costume.height * 4
47
- None => false
48
- }
49
- }
50
-
51
- ///|
52
- fn has_initial_renderable_sprite(
53
- targets : Array[TargetState],
54
- stage_index : Int,
55
- ) -> Bool {
56
- for i, target in targets {
57
- if i == stage_index {
58
- continue
59
- }
60
- if target.deleted || target.is_stage || !target.visible {
61
- continue
62
- }
63
- if target_has_renderable_costume(target) {
64
- return true
65
- }
66
- }
67
- false
68
- }
69
-
70
- ///|
71
- fn vm_new_internal(precompiled : PrecompiledProject, options : VmOptions) -> Vm {
72
- let targets = instantiate_targets(precompiled.targets)
73
- let pen_width = if options.pen_width > 0 { options.pen_width } else { 480 }
74
- let pen_height = if options.pen_height > 0 { options.pen_height } else { 360 }
75
- let pen_pixels = []
76
- let pen_vectors = []
77
-
78
- let stage = if precompiled.stage_index >= 0 &&
79
- precompiled.stage_index < targets.length() {
80
- Some(targets[precompiled.stage_index])
81
- } else {
82
- None
83
- }
84
- let backdrop_cache_pixels = precompiled_initial_opaque_backdrop_pixels(
85
- precompiled, pen_width, pen_height,
86
- )
87
- let backdrop_cache_valid = backdrop_cache_pixels.length() ==
88
- pen_width * pen_height * 4
89
- let backdrop_cache_stage_index = match stage {
90
- Some(_) => precompiled.stage_index
91
- None => -1
92
- }
93
- let backdrop_cache_stage_costume_index = match stage {
94
- Some(stage) => render_stage_costume_index_for_cache(stage)
95
- None => -1
96
- }
97
- let (
98
- backdrop_cache_effect_color,
99
- backdrop_cache_effect_fisheye,
100
- backdrop_cache_effect_whirl,
101
- backdrop_cache_effect_pixelate,
102
- backdrop_cache_effect_mosaic,
103
- backdrop_cache_effect_brightness,
104
- backdrop_cache_effect_ghost,
105
- ) = match stage {
106
- Some(stage) =>
107
- (
108
- stage.looks_effect_color,
109
- stage.looks_effect_fisheye,
110
- stage.looks_effect_whirl,
111
- stage.looks_effect_pixelate,
112
- stage.looks_effect_mosaic,
113
- stage.looks_effect_brightness,
114
- stage.looks_effect_ghost,
115
- )
116
- None => (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
117
- }
118
- let stage_uniforms = match stage {
119
- Some(stage) => render_effect_uniforms(stage)
120
- None => render_no_effect_uniforms()
121
- }
122
- let stage_has_effects = stage_uniforms.enable_mosaic ||
123
- stage_uniforms.enable_pixelate ||
124
- stage_uniforms.enable_whirl ||
125
- stage_uniforms.enable_fisheye ||
126
- stage_uniforms.enable_color ||
127
- stage_uniforms.enable_brightness ||
128
- stage_uniforms.enable_ghost
129
- let can_seed_render_cache = backdrop_cache_valid &&
130
- !stage_has_effects &&
131
- !has_initial_renderable_sprite(targets, precompiled.stage_index)
132
-
133
- {
134
- targets,
135
- stage_index: precompiled.stage_index,
136
- assets: precompiled.assets,
137
- options,
138
- pen_width,
139
- pen_height,
140
- pen_pixels,
141
- pen_vectors,
142
- render_revision: 0,
143
- render_cache_valid: can_seed_render_cache,
144
- render_cache_revision: if can_seed_render_cache {
145
- 0
146
- } else {
147
- -1
148
- },
149
- render_cache_pixels: if can_seed_render_cache {
150
- backdrop_cache_pixels
151
- } else {
152
- []
153
- },
154
- pen_bounds_valid: false,
155
- pen_min_x: 0,
156
- pen_min_y: 0,
157
- pen_max_x: 0,
158
- pen_max_y: 0,
159
- backdrop_cache_valid,
160
- backdrop_cache_pixels,
161
- backdrop_cache_width: if backdrop_cache_valid {
162
- pen_width
163
- } else {
164
- 0
165
- },
166
- backdrop_cache_height: if backdrop_cache_valid {
167
- pen_height
168
- } else {
169
- 0
170
- },
171
- backdrop_cache_stage_index,
172
- backdrop_cache_stage_costume_index,
173
- backdrop_cache_effect_color,
174
- backdrop_cache_effect_fisheye,
175
- backdrop_cache_effect_whirl,
176
- backdrop_cache_effect_pixelate,
177
- backdrop_cache_effect_mosaic,
178
- backdrop_cache_effect_brightness,
179
- backdrop_cache_effect_ghost,
180
- threads: [],
181
- next_thread_id: 1,
182
- run_id: 0,
183
- now_ms: 0,
184
- running: false,
185
- answer: "",
186
- effects: [],
187
- io_state: {},
188
- io_prev_state: {},
189
- waiting_children: {},
190
- rng_state: options.seed,
191
- control_counter: 0,
192
- hot_op_counts: {},
193
- next_clone_id: 1,
194
- hat_predicates: {},
195
- timer_start_ms: 0,
196
- music_tempo: 60.0,
197
- tts_language: "en",
198
- procedure_frames: {},
199
- pending_translate_requests: {},
200
- redraw_requested: false,
201
- redraw_requested_while_warp: false,
202
- current_thread_id: None,
203
- mathop_sin_cache_valid: false,
204
- mathop_sin_cache_input: 0.0,
205
- mathop_sin_cache_output: 0.0,
206
- mathop_cos_cache_valid: false,
207
- mathop_cos_cache_input: 0.0,
208
- mathop_cos_cache_output: 0.0,
209
- aot_commands: [],
210
- aot_pending: false,
211
- aot_full_green_flag_starts: {},
212
- aot_use_full_exec: false,
213
- aot_wasm_only: false,
214
- frame_override: None,
215
- }
216
- }
217
-
218
- ///|
219
- fn push_effect(vm : Vm, effect : HostEffect) -> Unit {
220
- vm.effects.push(effect)
221
- }
222
-
223
- ///|
224
- fn take_effects(vm : Vm) -> Array[HostEffect] {
225
- let out = vm.effects.copy()
226
- vm.effects.clear()
227
- out
228
- }
229
-
230
- ///|
231
- fn next_random_unit(vm : Vm) -> Double {
232
- // 32-bit LCG for deterministic stepping.
233
- vm.rng_state = vm.rng_state * 1664525 + 1013904223
234
- let unsigned = vm.rng_state.abs()
235
- let x = unsigned % 1000000
236
- Double::from_int(x) / 1000000.0
237
- }
238
-
239
- ///|
240
- fn current_step_budget(vm : Vm) -> Int {
241
- if vm.options.turbo {
242
- 1000
243
- } else {
244
- 200
245
- }
246
- }
247
-
248
- ///|
249
- fn resolve_variable_ref(
250
- vm : Vm,
251
- target_index : Int,
252
- variable_id : String?,
253
- variable_name : String?,
254
- ) -> (Int, String, Int?)? {
255
- let target = vm.targets[target_index]
256
- let stage = vm.targets[vm.stage_index]
257
-
258
- match variable_id {
259
- Some(id) => resolve_variable_ref_by_id(vm, target_index, id)
260
- None =>
261
- match variable_name {
262
- Some(name) =>
263
- match target.variable_names.get(name) {
264
- Some(id) =>
265
- Some((target_index, id, target.variable_slot_by_name.get(name)))
266
- None =>
267
- match stage.variable_names.get(name) {
268
- Some(id) =>
269
- Some(
270
- (vm.stage_index, id, stage.variable_slot_by_name.get(name)),
271
- )
272
- None => None
273
- }
274
- }
275
- None => None
276
- }
277
- }
278
- }
279
-
280
- ///|
281
- fn resolve_variable_ref_by_id(
282
- vm : Vm,
283
- target_index : Int,
284
- variable_id : String,
285
- ) -> (Int, String, Int?)? {
286
- let target = vm.targets[target_index]
287
- let stage = vm.targets[vm.stage_index]
288
-
289
- match target.variable_slot_by_id.get(variable_id) {
290
- Some(slot) => return Some((target_index, variable_id, Some(slot)))
291
- None => ()
292
- }
293
- if target.variables.contains(variable_id) {
294
- return Some((target_index, variable_id, None))
295
- }
296
- match stage.variable_slot_by_id.get(variable_id) {
297
- Some(slot) => return Some((vm.stage_index, variable_id, Some(slot)))
298
- None => ()
299
- }
300
- if stage.variables.contains(variable_id) {
301
- return Some((vm.stage_index, variable_id, None))
302
- }
303
- None
304
- }
305
-
306
- ///|
307
- fn read_variable(
308
- vm : Vm,
309
- target_index : Int,
310
- variable_id : String?,
311
- variable_name : String?,
312
- ) -> Json {
313
- match resolve_variable_ref(vm, target_index, variable_id, variable_name) {
314
- Some((owner_index, id, slot)) =>
315
- read_variable_by_ref(vm, owner_index, id, slot)
316
- None => Json::null()
317
- }
318
- }
319
-
320
- ///|
321
- fn read_variable_by_ref(
322
- vm : Vm,
323
- owner_index : Int,
324
- variable_id : String,
325
- slot : Int?,
326
- ) -> Json {
327
- match slot {
328
- Some(slot) =>
329
- if slot >= 0 && slot < vm.targets[owner_index].variable_values.length() {
330
- vm.targets[owner_index].variable_values[slot]
331
- } else {
332
- vm.targets[owner_index].variables.get_or_default(
333
- variable_id,
334
- Json::null(),
335
- )
336
- }
337
- None =>
338
- vm.targets[owner_index].variables.get_or_default(
339
- variable_id,
340
- Json::null(),
341
- )
342
- }
343
- }
344
-
345
- ///|
346
- fn write_variable_by_ref(
347
- vm : Vm,
348
- owner_index : Int,
349
- variable_id : String,
350
- slot : Int?,
351
- value : Json,
352
- ) -> Unit {
353
- vm.targets[owner_index].variables[variable_id] = value
354
- match slot {
355
- Some(slot) =>
356
- if slot >= 0 && slot < vm.targets[owner_index].variable_values.length() {
357
- vm.targets[owner_index].variable_values[slot] = value
358
- vm.targets[owner_index].variable_numeric_flags[slot] = json_is_numeric_comparable(
359
- value,
360
- )
361
- }
362
- None => ()
363
- }
364
- }
365
-
366
- ///|
367
- fn write_variable(
368
- vm : Vm,
369
- target_index : Int,
370
- variable_id : String?,
371
- variable_name : String?,
372
- value : Json,
373
- ) -> Unit {
374
- match resolve_variable_ref(vm, target_index, variable_id, variable_name) {
375
- Some((owner_index, id, slot)) =>
376
- write_variable_by_ref(vm, owner_index, id, slot, value)
377
- None =>
378
- match variable_name {
379
- Some(name) => {
380
- let stage_index = vm.stage_index
381
- let new_id = "stage_var_\{name}"
382
- let new_slot = vm.targets[stage_index].variable_values.length()
383
- vm.targets[stage_index].variables[new_id] = value
384
- vm.targets[stage_index].variable_names[name] = new_id
385
- vm.targets[stage_index].variable_slot_by_id[new_id] = new_slot
386
- vm.targets[stage_index].variable_slot_by_name[name] = new_slot
387
- vm.targets[stage_index].variable_id_by_slot.push(new_id)
388
- vm.targets[stage_index].variable_values.push(value)
389
- vm.targets[stage_index].variable_numeric_flags.push(
390
- json_is_numeric_comparable(value),
391
- )
392
- }
393
- None => ()
394
- }
395
- }
396
- }
397
-
398
- ///|
399
- fn resolve_list_ref(
400
- vm : Vm,
401
- target_index : Int,
402
- list_id : String?,
403
- list_name : String?,
404
- ) -> (Int, String, Int?)? {
405
- let target = vm.targets[target_index]
406
- let stage = vm.targets[vm.stage_index]
407
-
408
- match list_id {
409
- Some(id) => {
410
- match target.list_slot_by_id.get(id) {
411
- Some(slot) => return Some((target_index, id, Some(slot)))
412
- None => ()
413
- }
414
- if target.lists.contains(id) {
415
- return Some((target_index, id, None))
416
- }
417
- match stage.list_slot_by_id.get(id) {
418
- Some(slot) => return Some((vm.stage_index, id, Some(slot)))
419
- None => ()
420
- }
421
- if stage.lists.contains(id) {
422
- return Some((vm.stage_index, id, None))
423
- }
424
- None
425
- }
426
- None =>
427
- match list_name {
428
- Some(name) =>
429
- match target.list_names.get(name) {
430
- Some(id) =>
431
- Some((target_index, id, target.list_slot_by_name.get(name)))
432
- None =>
433
- match stage.list_names.get(name) {
434
- Some(id) =>
435
- Some((vm.stage_index, id, stage.list_slot_by_name.get(name)))
436
- None => None
437
- }
438
- }
439
- None => None
440
- }
441
- }
442
- }
443
-
444
- ///|
445
- fn read_list(
446
- vm : Vm,
447
- target_index : Int,
448
- list_id : String?,
449
- list_name : String?,
450
- ) -> Array[Json] {
451
- match resolve_list_ref(vm, target_index, list_id, list_name) {
452
- Some((owner_index, id, slot)) => read_list_by_ref(vm, owner_index, id, slot)
453
- None => []
454
- }
455
- }
456
-
457
- ///|
458
- fn read_list_by_ref(
459
- vm : Vm,
460
- owner_index : Int,
461
- list_id : String,
462
- slot : Int?,
463
- ) -> Array[Json] {
464
- match slot {
465
- Some(slot) =>
466
- if slot >= 0 && slot < vm.targets[owner_index].list_values.length() {
467
- vm.targets[owner_index].list_values[slot]
468
- } else {
469
- vm.targets[owner_index].lists.get_or_default(list_id, [])
470
- }
471
- None => vm.targets[owner_index].lists.get_or_default(list_id, [])
472
- }
473
- }
474
-
475
- ///|
476
- fn write_list_by_ref(
477
- vm : Vm,
478
- owner_index : Int,
479
- list_id : String,
480
- slot : Int?,
481
- value : Array[Json],
482
- ) -> Unit {
483
- vm.targets[owner_index].lists[list_id] = value
484
- match slot {
485
- Some(slot) =>
486
- if slot >= 0 && slot < vm.targets[owner_index].list_values.length() {
487
- vm.targets[owner_index].list_values[slot] = value
488
- }
489
- None => ()
490
- }
491
- }
492
-
493
- ///|
494
- fn with_list_mut(
495
- vm : Vm,
496
- target_index : Int,
497
- list_id : String?,
498
- list_name : String?,
499
- ) -> (Int, String, Int?)? {
500
- match resolve_list_ref(vm, target_index, list_id, list_name) {
501
- Some(pair) => Some(pair)
502
- None =>
503
- match list_name {
504
- Some(name) => {
505
- let stage_index = vm.stage_index
506
- let created = "stage_list_\{name}"
507
- let new_slot = vm.targets[stage_index].list_values.length()
508
- vm.targets[stage_index].lists[created] = []
509
- vm.targets[stage_index].list_names[name] = created
510
- vm.targets[stage_index].list_slot_by_id[created] = new_slot
511
- vm.targets[stage_index].list_slot_by_name[name] = new_slot
512
- vm.targets[stage_index].list_id_by_slot.push(created)
513
- vm.targets[stage_index].list_values.push([])
514
- Some((stage_index, created, Some(new_slot)))
515
- }
516
- None => None
517
- }
518
- }
519
- }
520
-
521
- ///|
522
- fn copy_json_map(source : Map[String, Json]) -> Map[String, Json] {
523
- let out = {}
524
- source.each((key, value) => out[key] = value)
525
- out
526
- }
527
-
528
- ///|
529
- fn copy_string_map(source : Map[String, String]) -> Map[String, String] {
530
- let out = {}
531
- source.each((key, value) => out[key] = value)
532
- out
533
- }
534
-
535
- ///|
536
- fn copy_int_map(source : Map[String, Int]) -> Map[String, Int] {
537
- let out = {}
538
- source.each((key, value) => out[key] = value)
539
- out
540
- }
541
-
542
- ///|
543
- fn copy_double_map(source : Map[String, Double]) -> Map[String, Double] {
544
- let out = {}
545
- source.each((key, value) => out[key] = value)
546
- out
547
- }
548
-
549
- ///|
550
- fn copy_string_array(source : Array[String]) -> Array[String] {
551
- source.copy()
552
- }
553
-
554
- ///|
555
- fn copy_json_array(source : Array[Json]) -> Array[Json] {
556
- source.copy()
557
- }
558
-
559
- ///|
560
- fn copy_nested_json_array(source : Array[Array[Json]]) -> Array[Array[Json]] {
561
- source.map(fn(values) { values.copy() })
562
- }
563
-
564
- ///|
565
- fn copy_list_map(source : Map[String, Array[Json]]) -> Map[String, Array[Json]] {
566
- let out = {}
567
- source.each((key, value) => out[key] = value.copy())
568
- out
569
- }
570
-
571
- ///|
572
- fn copy_block_map(
573
- source : Map[String, ScratchBlock],
574
- ) -> Map[String, ScratchBlock] {
575
- let out = {}
576
- source.each((key, value) => out[key] = value)
577
- out
578
- }
579
-
580
- ///|
581
- fn copy_procedure_map(
582
- source : Map[String, ProcedureSpec],
583
- ) -> Map[String, ProcedureSpec] {
584
- let out = {}
585
- source.each((key, value) => {
586
- out[key] = {
587
- start_block: value.start_block,
588
- param_names: value.param_names.copy(),
589
- param_ids: value.param_ids.copy(),
590
- param_defaults: value.param_defaults.copy(),
591
- warp_mode: value.warp_mode,
592
- }
593
- })
594
- out
595
- }
596
-
597
- ///|
598
- fn copy_target_state_with_meta(
599
- source : TargetState,
600
- id : String,
601
- is_original : Bool,
602
- deleted : Bool,
603
- ) -> TargetState {
604
- {
605
- id,
606
- name: source.name,
607
- is_stage: source.is_stage,
608
- is_original,
609
- deleted,
610
- x: source.x,
611
- y: source.y,
612
- direction: source.direction,
613
- size: source.size,
614
- volume: source.volume,
615
- music_instrument: source.music_instrument,
616
- tts_voice: source.tts_voice,
617
- visible: source.visible,
618
- current_costume: source.current_costume,
619
- costume_names: copy_string_array(source.costume_names),
620
- costumes: source.costumes.copy(),
621
- pen_down: source.pen_down,
622
- pen_color: source.pen_color,
623
- pen_saturation: source.pen_saturation,
624
- pen_brightness: source.pen_brightness,
625
- pen_transparency: source.pen_transparency,
626
- pen_size: source.pen_size,
627
- pen_legacy_shade: source.pen_legacy_shade,
628
- looks_effect_color: source.looks_effect_color,
629
- looks_effect_fisheye: source.looks_effect_fisheye,
630
- looks_effect_whirl: source.looks_effect_whirl,
631
- looks_effect_pixelate: source.looks_effect_pixelate,
632
- looks_effect_mosaic: source.looks_effect_mosaic,
633
- looks_effect_brightness: source.looks_effect_brightness,
634
- looks_effect_ghost: source.looks_effect_ghost,
635
- variables: copy_json_map(source.variables),
636
- variable_names: copy_string_map(source.variable_names),
637
- variable_slot_by_id: copy_int_map(source.variable_slot_by_id),
638
- variable_slot_by_name: copy_int_map(source.variable_slot_by_name),
639
- variable_id_by_slot: source.variable_id_by_slot.copy(),
640
- variable_values: copy_json_array(source.variable_values),
641
- variable_numeric_flags: source.variable_numeric_flags.copy(),
642
- lists: copy_list_map(source.lists),
643
- list_names: copy_string_map(source.list_names),
644
- list_slot_by_id: copy_int_map(source.list_slot_by_id),
645
- list_slot_by_name: copy_int_map(source.list_slot_by_name),
646
- list_id_by_slot: source.list_id_by_slot.copy(),
647
- list_values: copy_nested_json_array(source.list_values),
648
- blocks: copy_block_map(source.blocks),
649
- const_number_block_values: copy_double_map(source.const_number_block_values),
650
- procedures: copy_procedure_map(source.procedures),
651
- block_pc_by_id: copy_int_map(source.block_pc_by_id),
652
- blocks_by_pc: source.blocks_by_pc.copy(),
653
- block_fast_meta_by_pc: source.block_fast_meta_by_pc.copy(),
654
- numeric_program_cache: {},
655
- numeric_program_compile_failed: {},
656
- bool_program_cache: {},
657
- bool_program_compile_failed: {},
658
- top_level_hats: source.top_level_hats.copy(),
659
- green_flag_starts: source.green_flag_starts.copy(),
660
- stage_clicked_starts: source.stage_clicked_starts.copy(),
661
- sprite_clicked_starts: source.sprite_clicked_starts.copy(),
662
- clone_start_starts: source.clone_start_starts.copy(),
663
- key_pressed_hats: source.key_pressed_hats.copy(),
664
- broadcast_hats: source.broadcast_hats.copy(),
665
- backdrop_hats: source.backdrop_hats.copy(),
666
- predicate_hats: source.predicate_hats.copy(),
667
- }
668
- }
669
-
670
- ///|
671
- fn instantiate_targets(
672
- source_targets : Array[TargetState],
673
- ) -> Array[TargetState] {
674
- let targets = []
675
- for source in source_targets {
676
- targets.push(
677
- copy_target_state_with_meta(
678
- source,
679
- source.id,
680
- source.is_original,
681
- source.deleted,
682
- ),
683
- )
684
- }
685
- targets
686
- }
687
-
688
- ///|
689
- fn active_clone_count(vm : Vm) -> Int {
690
- let mut count = 0
691
- for target in vm.targets {
692
- if !target.is_stage && !target.is_original && !target.deleted {
693
- count += 1
694
- }
695
- }
696
- count
697
- }
698
-
699
- ///|
700
- fn find_reusable_clone_slot(vm : Vm) -> Int? {
701
- for i, target in vm.targets {
702
- if !target.is_stage && !target.is_original && target.deleted {
703
- return Some(i)
704
- }
705
- }
706
- None
707
- }
708
-
709
- ///|
710
- fn clone_target_state(source : TargetState, clone_id : Int) -> TargetState {
711
- copy_target_state_with_meta(
712
- source,
713
- "\{source.id}#clone_\{clone_id}",
714
- false,
715
- false,
716
- )
717
- }
718
-
719
- ///|
720
- fn spawn_clone_target(vm : Vm, source_index : Int) -> Int? {
721
- if source_index < 0 || source_index >= vm.targets.length() {
722
- return None
723
- }
724
- let source = vm.targets[source_index]
725
- if source.is_stage || source.deleted {
726
- return None
727
- }
728
- if active_clone_count(vm) >= vm.options.max_clones {
729
- return None
730
- }
731
-
732
- let clone_id = vm.next_clone_id
733
- vm.next_clone_id += 1
734
- let clone_target = clone_target_state(source, clone_id)
735
-
736
- match find_reusable_clone_slot(vm) {
737
- Some(slot) => {
738
- vm.targets[slot] = clone_target
739
- Some(slot)
740
- }
741
- None => {
742
- vm.targets.push(clone_target)
743
- Some(vm.targets.length() - 1)
744
- }
745
- }
746
- }
747
-
748
- ///|
749
- fn dispose_clone_target(
750
- vm : Vm,
751
- target_index : Int,
752
- current_thread_id : Int,
753
- ) -> Unit {
754
- if target_index < 0 || target_index >= vm.targets.length() {
755
- return
756
- }
757
- let target = vm.targets[target_index]
758
- if target.is_stage || target.is_original || target.deleted {
759
- return
760
- }
761
- vm.targets[target_index].deleted = true
762
- vm.targets[target_index].visible = false
763
- for i, thread in vm.threads {
764
- if thread.target_index == target_index && thread.id != current_thread_id {
765
- vm.threads[i].done = true
766
- }
767
- }
768
- }
769
-
770
- ///|
771
- fn reset_targets_for_green_flag(vm : Vm) -> Unit {
772
- for i, target in vm.targets {
773
- if target.is_original {
774
- vm.targets[i].deleted = false
775
- } else {
776
- vm.targets[i].deleted = true
777
- }
778
- }
779
- }
780
-
781
- ///|
782
- fn spawn_thread(
783
- vm : Vm,
784
- target_index : Int,
785
- start_block : String,
786
- parent_waiter : Int?,
787
- ) -> Unit {
788
- if target_index < 0 || target_index >= vm.targets.length() {
789
- return
790
- }
791
- let start_pc = match
792
- vm.targets[target_index].block_pc_by_id.get(start_block) {
793
- Some(pc) => pc
794
- None => return
795
- }
796
- ignore(spawn_thread_pc(vm, target_index, start_pc, parent_waiter))
797
- }
798
-
799
- ///|
800
- fn spawn_thread_pc(
801
- vm : Vm,
802
- target_index : Int,
803
- start_pc : Int,
804
- parent_waiter : Int?,
805
- ) -> Bool {
806
- if target_index < 0 || target_index >= vm.targets.length() {
807
- return false
808
- }
809
- if start_pc < 0 || start_pc >= vm.targets[target_index].blocks_by_pc.length() {
810
- return false
811
- }
812
- let thread_id = vm.next_thread_id
813
- vm.next_thread_id += 1
814
- let thread : Thread = {
815
- id: thread_id,
816
- target_index,
817
- pc: Some(start_pc),
818
- wait_until_ms: None,
819
- wait_for_input: None,
820
- done: false,
821
- warp_mode: false,
822
- warp_started_ms: 0,
823
- stack: [],
824
- loop_counters: {},
825
- parent_waiter,
826
- }
827
- vm.threads.push(thread)
828
- true
829
- }
830
-
831
- ///|
832
- fn clear_threads(vm : Vm) -> Unit {
833
- vm.threads.clear()
834
- vm.waiting_children.clear()
835
- vm.procedure_frames.clear()
836
- vm.frame_override = None
837
- vm.current_thread_id = None
838
- vm.redraw_requested_while_warp = false
839
- vm.running = false
840
- }
841
-
842
- ///|
843
- fn decrement_waiting_child(vm : Vm, waiter_id : Int) -> Unit {
844
- let current = vm.waiting_children.get_or_default(waiter_id, 0)
845
- if current <= 1 {
846
- vm.waiting_children.remove(waiter_id)
847
- } else {
848
- vm.waiting_children[waiter_id] = current - 1
849
- }
850
- }
851
-
852
- ///|
853
- fn cleanup_done_threads(vm : Vm) -> Unit {
854
- let survivors = []
855
- for thread in vm.threads {
856
- if thread.done {
857
- match thread.parent_waiter {
858
- Some(waiter) => decrement_waiting_child(vm, waiter)
859
- None => ()
860
- }
861
- vm.procedure_frames.remove(thread.id)
862
- } else {
863
- survivors.push(thread)
864
- }
865
- }
866
- vm.threads = survivors
867
- if vm.threads.is_empty() {
868
- vm.running = false
869
- }
870
- }