sonolus.py 0.9.2__py3-none-any.whl → 0.10.0__py3-none-any.whl
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.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/visitor.py +46 -21
- sonolus/build/cli.py +10 -8
- sonolus/build/collection.py +7 -3
- sonolus/build/compile.py +12 -11
- sonolus/build/dev_server.py +143 -32
- sonolus/build/engine.py +26 -24
- sonolus/build/project.py +8 -3
- sonolus/script/archetype.py +8 -8
- sonolus/script/bucket.py +1 -1
- sonolus/script/debug.py +18 -5
- sonolus/script/internal/context.py +55 -27
- sonolus/script/options.py +2 -2
- sonolus/script/project.py +2 -5
- sonolus/script/runtime.py +37 -37
- sonolus/script/stream.py +3 -3
- {sonolus_py-0.9.2.dist-info → sonolus_py-0.10.0.dist-info}/METADATA +1 -1
- {sonolus_py-0.9.2.dist-info → sonolus_py-0.10.0.dist-info}/RECORD +20 -20
- {sonolus_py-0.9.2.dist-info → sonolus_py-0.10.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.9.2.dist-info → sonolus_py-0.10.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.9.2.dist-info → sonolus_py-0.10.0.dist-info}/licenses/LICENSE +0 -0
sonolus/build/engine.py
CHANGED
|
@@ -24,7 +24,7 @@ from sonolus.script.internal.callbacks import (
|
|
|
24
24
|
update_callback,
|
|
25
25
|
update_spawn_callback,
|
|
26
26
|
)
|
|
27
|
-
from sonolus.script.internal.context import ReadOnlyMemory
|
|
27
|
+
from sonolus.script.internal.context import ProjectContextState, ReadOnlyMemory
|
|
28
28
|
from sonolus.script.options import Options
|
|
29
29
|
from sonolus.script.particle import Particles
|
|
30
30
|
from sonolus.script.project import BuildConfig
|
|
@@ -69,12 +69,14 @@ def package_engine(
|
|
|
69
69
|
engine: EngineData,
|
|
70
70
|
config: BuildConfig | None = None,
|
|
71
71
|
cache: CompileCache | None = None,
|
|
72
|
+
project_state: ProjectContextState | None = None,
|
|
72
73
|
):
|
|
73
74
|
if cache is None:
|
|
74
75
|
cache = CompileCache()
|
|
75
76
|
|
|
76
77
|
config = config or BuildConfig()
|
|
77
|
-
|
|
78
|
+
if project_state is None:
|
|
79
|
+
project_state = ProjectContextState()
|
|
78
80
|
configuration = build_engine_configuration(engine.options, engine.ui)
|
|
79
81
|
if no_gil():
|
|
80
82
|
# process_cpu_count is available in Python 3.13+
|
|
@@ -99,7 +101,7 @@ def package_engine(
|
|
|
99
101
|
effects=play_mode.effects,
|
|
100
102
|
particles=play_mode.particles,
|
|
101
103
|
buckets=play_mode.buckets,
|
|
102
|
-
|
|
104
|
+
project_state=project_state,
|
|
103
105
|
config=config,
|
|
104
106
|
thread_pool=thread_pool,
|
|
105
107
|
cache=cache,
|
|
@@ -111,7 +113,7 @@ def package_engine(
|
|
|
111
113
|
effects=watch_mode.effects,
|
|
112
114
|
particles=watch_mode.particles,
|
|
113
115
|
buckets=watch_mode.buckets,
|
|
114
|
-
|
|
116
|
+
project_state=project_state,
|
|
115
117
|
update_spawn=watch_mode.update_spawn,
|
|
116
118
|
config=config,
|
|
117
119
|
thread_pool=thread_pool,
|
|
@@ -121,7 +123,7 @@ def package_engine(
|
|
|
121
123
|
build_preview_mode,
|
|
122
124
|
archetypes=preview_mode.archetypes,
|
|
123
125
|
skin=preview_mode.skin,
|
|
124
|
-
|
|
126
|
+
project_state=project_state,
|
|
125
127
|
config=config,
|
|
126
128
|
thread_pool=thread_pool,
|
|
127
129
|
cache=cache,
|
|
@@ -136,7 +138,7 @@ def package_engine(
|
|
|
136
138
|
preprocess=tutorial_mode.preprocess,
|
|
137
139
|
navigate=tutorial_mode.navigate,
|
|
138
140
|
update=tutorial_mode.update,
|
|
139
|
-
|
|
141
|
+
project_state=project_state,
|
|
140
142
|
config=config,
|
|
141
143
|
thread_pool=thread_pool,
|
|
142
144
|
cache=cache,
|
|
@@ -154,7 +156,7 @@ def package_engine(
|
|
|
154
156
|
effects=play_mode.effects,
|
|
155
157
|
particles=play_mode.particles,
|
|
156
158
|
buckets=play_mode.buckets,
|
|
157
|
-
|
|
159
|
+
project_state=project_state,
|
|
158
160
|
config=config,
|
|
159
161
|
thread_pool=None,
|
|
160
162
|
cache=cache,
|
|
@@ -165,7 +167,7 @@ def package_engine(
|
|
|
165
167
|
effects=watch_mode.effects,
|
|
166
168
|
particles=watch_mode.particles,
|
|
167
169
|
buckets=watch_mode.buckets,
|
|
168
|
-
|
|
170
|
+
project_state=project_state,
|
|
169
171
|
update_spawn=watch_mode.update_spawn,
|
|
170
172
|
config=config,
|
|
171
173
|
thread_pool=None,
|
|
@@ -174,7 +176,7 @@ def package_engine(
|
|
|
174
176
|
preview_data = build_preview_mode(
|
|
175
177
|
archetypes=preview_mode.archetypes,
|
|
176
178
|
skin=preview_mode.skin,
|
|
177
|
-
|
|
179
|
+
project_state=project_state,
|
|
178
180
|
config=config,
|
|
179
181
|
thread_pool=None,
|
|
180
182
|
cache=cache,
|
|
@@ -188,7 +190,7 @@ def package_engine(
|
|
|
188
190
|
preprocess=tutorial_mode.preprocess,
|
|
189
191
|
navigate=tutorial_mode.navigate,
|
|
190
192
|
update=tutorial_mode.update,
|
|
191
|
-
|
|
193
|
+
project_state=project_state,
|
|
192
194
|
config=config,
|
|
193
195
|
thread_pool=None,
|
|
194
196
|
cache=cache,
|
|
@@ -200,7 +202,7 @@ def package_engine(
|
|
|
200
202
|
watch_data=package_data(watch_data),
|
|
201
203
|
preview_data=package_data(preview_data),
|
|
202
204
|
tutorial_data=package_data(tutorial_data),
|
|
203
|
-
rom=package_rom(rom),
|
|
205
|
+
rom=package_rom(project_state.rom),
|
|
204
206
|
)
|
|
205
207
|
|
|
206
208
|
|
|
@@ -209,7 +211,7 @@ def validate_engine(
|
|
|
209
211
|
config: BuildConfig | None = None,
|
|
210
212
|
):
|
|
211
213
|
config = config or BuildConfig()
|
|
212
|
-
|
|
214
|
+
project_state = ProjectContextState()
|
|
213
215
|
|
|
214
216
|
play_mode = engine.play if config.build_play else empty_play_mode()
|
|
215
217
|
watch_mode = engine.watch if config.build_watch else empty_watch_mode()
|
|
@@ -222,7 +224,7 @@ def validate_engine(
|
|
|
222
224
|
effects=play_mode.effects,
|
|
223
225
|
particles=play_mode.particles,
|
|
224
226
|
buckets=play_mode.buckets,
|
|
225
|
-
|
|
227
|
+
project_state=project_state,
|
|
226
228
|
config=config,
|
|
227
229
|
thread_pool=None,
|
|
228
230
|
validate_only=True,
|
|
@@ -233,7 +235,7 @@ def validate_engine(
|
|
|
233
235
|
effects=watch_mode.effects,
|
|
234
236
|
particles=watch_mode.particles,
|
|
235
237
|
buckets=watch_mode.buckets,
|
|
236
|
-
|
|
238
|
+
project_state=project_state,
|
|
237
239
|
update_spawn=watch_mode.update_spawn,
|
|
238
240
|
config=config,
|
|
239
241
|
thread_pool=None,
|
|
@@ -242,7 +244,7 @@ def validate_engine(
|
|
|
242
244
|
build_preview_mode(
|
|
243
245
|
archetypes=preview_mode.archetypes,
|
|
244
246
|
skin=preview_mode.skin,
|
|
245
|
-
|
|
247
|
+
project_state=project_state,
|
|
246
248
|
config=config,
|
|
247
249
|
thread_pool=None,
|
|
248
250
|
validate_only=True,
|
|
@@ -256,7 +258,7 @@ def validate_engine(
|
|
|
256
258
|
preprocess=tutorial_mode.preprocess,
|
|
257
259
|
navigate=tutorial_mode.navigate,
|
|
258
260
|
update=tutorial_mode.update,
|
|
259
|
-
|
|
261
|
+
project_state=project_state,
|
|
260
262
|
config=config,
|
|
261
263
|
thread_pool=None,
|
|
262
264
|
validate_only=True,
|
|
@@ -279,7 +281,7 @@ def build_play_mode(
|
|
|
279
281
|
effects: Effects,
|
|
280
282
|
particles: Particles,
|
|
281
283
|
buckets: Buckets,
|
|
282
|
-
|
|
284
|
+
project_state: ProjectContextState,
|
|
283
285
|
config: BuildConfig,
|
|
284
286
|
thread_pool: Executor | None = None,
|
|
285
287
|
validate_only: bool = False,
|
|
@@ -288,7 +290,7 @@ def build_play_mode(
|
|
|
288
290
|
return {
|
|
289
291
|
**compile_mode(
|
|
290
292
|
mode=Mode.PLAY,
|
|
291
|
-
|
|
293
|
+
project_state=project_state,
|
|
292
294
|
archetypes=archetypes,
|
|
293
295
|
global_callbacks=None,
|
|
294
296
|
passes=config.passes,
|
|
@@ -309,7 +311,7 @@ def build_watch_mode(
|
|
|
309
311
|
effects: Effects,
|
|
310
312
|
particles: Particles,
|
|
311
313
|
buckets: Buckets,
|
|
312
|
-
|
|
314
|
+
project_state: ProjectContextState,
|
|
313
315
|
update_spawn: Callable[[], float],
|
|
314
316
|
config: BuildConfig,
|
|
315
317
|
thread_pool: Executor | None = None,
|
|
@@ -319,7 +321,7 @@ def build_watch_mode(
|
|
|
319
321
|
return {
|
|
320
322
|
**compile_mode(
|
|
321
323
|
mode=Mode.WATCH,
|
|
322
|
-
|
|
324
|
+
project_state=project_state,
|
|
323
325
|
archetypes=archetypes,
|
|
324
326
|
global_callbacks=[(update_spawn_callback, update_spawn)],
|
|
325
327
|
passes=config.passes,
|
|
@@ -337,7 +339,7 @@ def build_watch_mode(
|
|
|
337
339
|
def build_preview_mode(
|
|
338
340
|
archetypes: list[type[_BaseArchetype]],
|
|
339
341
|
skin: Skin,
|
|
340
|
-
|
|
342
|
+
project_state: ProjectContextState,
|
|
341
343
|
config: BuildConfig,
|
|
342
344
|
thread_pool: Executor | None = None,
|
|
343
345
|
validate_only: bool = False,
|
|
@@ -346,7 +348,7 @@ def build_preview_mode(
|
|
|
346
348
|
return {
|
|
347
349
|
**compile_mode(
|
|
348
350
|
mode=Mode.PREVIEW,
|
|
349
|
-
|
|
351
|
+
project_state=project_state,
|
|
350
352
|
archetypes=archetypes,
|
|
351
353
|
global_callbacks=None,
|
|
352
354
|
passes=config.passes,
|
|
@@ -367,7 +369,7 @@ def build_tutorial_mode(
|
|
|
367
369
|
preprocess: Callable[[], None],
|
|
368
370
|
navigate: Callable[[], None],
|
|
369
371
|
update: Callable[[], None],
|
|
370
|
-
|
|
372
|
+
project_state: ProjectContextState,
|
|
371
373
|
config: BuildConfig,
|
|
372
374
|
thread_pool: Executor | None = None,
|
|
373
375
|
validate_only: bool = False,
|
|
@@ -376,7 +378,7 @@ def build_tutorial_mode(
|
|
|
376
378
|
return {
|
|
377
379
|
**compile_mode(
|
|
378
380
|
mode=Mode.TUTORIAL,
|
|
379
|
-
|
|
381
|
+
project_state=project_state,
|
|
380
382
|
archetypes=[],
|
|
381
383
|
global_callbacks=[
|
|
382
384
|
(preprocess_callback, preprocess),
|
sonolus/build/project.py
CHANGED
|
@@ -7,6 +7,7 @@ from sonolus.build.compile import CompileCache
|
|
|
7
7
|
from sonolus.build.engine import package_engine, unpackage_data
|
|
8
8
|
from sonolus.build.level import package_level_data
|
|
9
9
|
from sonolus.script.engine import Engine
|
|
10
|
+
from sonolus.script.internal.context import ProjectContextState
|
|
10
11
|
from sonolus.script.level import ExternalLevelData, ExternalLevelDataDict, Level, LevelData, parse_external_level_data
|
|
11
12
|
from sonolus.script.project import BuildConfig, Project, ProjectSchema
|
|
12
13
|
|
|
@@ -21,7 +22,10 @@ BLANK_AUDIO = (
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def build_project_to_collection(
|
|
24
|
-
project: Project,
|
|
25
|
+
project: Project,
|
|
26
|
+
config: BuildConfig | None,
|
|
27
|
+
cache: CompileCache | None = None,
|
|
28
|
+
project_state: ProjectContextState | None = None,
|
|
25
29
|
) -> Collection:
|
|
26
30
|
collection = load_resources_files_to_collection(project.resources)
|
|
27
31
|
for src_engine, converter in project.converters.items():
|
|
@@ -34,7 +38,7 @@ def build_project_to_collection(
|
|
|
34
38
|
if config.override_resource_level_engines:
|
|
35
39
|
for level in collection.categories.get("levels", {}).values():
|
|
36
40
|
level["item"]["engine"] = project.engine.name
|
|
37
|
-
add_engine_to_collection(collection, project, project.engine, config, cache=cache)
|
|
41
|
+
add_engine_to_collection(collection, project, project.engine, config, cache=cache, project_state=project_state)
|
|
38
42
|
for level in project.levels:
|
|
39
43
|
add_level_to_collection(collection, project, level)
|
|
40
44
|
collection.name = f"{project.engine.name}"
|
|
@@ -72,8 +76,9 @@ def add_engine_to_collection(
|
|
|
72
76
|
engine: Engine,
|
|
73
77
|
config: BuildConfig | None,
|
|
74
78
|
cache: CompileCache | None = None,
|
|
79
|
+
project_state: ProjectContextState | None = None,
|
|
75
80
|
):
|
|
76
|
-
packaged_engine = package_engine(engine.data, config, cache=cache)
|
|
81
|
+
packaged_engine = package_engine(engine.data, config, cache=cache, project_state=project_state)
|
|
77
82
|
item = {
|
|
78
83
|
"name": engine.name,
|
|
79
84
|
"version": engine.version,
|
sonolus/script/archetype.py
CHANGED
|
@@ -207,7 +207,7 @@ class _IsScoredDescriptor(SonolusDescriptor):
|
|
|
207
207
|
if instance is None:
|
|
208
208
|
return self.value
|
|
209
209
|
elif ctx():
|
|
210
|
-
return ctx().
|
|
210
|
+
return ctx().mode_state.is_scored_by_archetype_id[instance.id]
|
|
211
211
|
else:
|
|
212
212
|
return self.value
|
|
213
213
|
|
|
@@ -220,7 +220,7 @@ class _IdDescriptor(SonolusDescriptor):
|
|
|
220
220
|
if not ctx():
|
|
221
221
|
raise RuntimeError("Archetype id is only available during compilation")
|
|
222
222
|
if instance is None:
|
|
223
|
-
result = ctx().
|
|
223
|
+
result = ctx().mode_state.archetypes.get(owner)
|
|
224
224
|
if result is None:
|
|
225
225
|
raise RuntimeError("Archetype is not registered")
|
|
226
226
|
return result
|
|
@@ -237,7 +237,7 @@ class _KeyDescriptor(SonolusDescriptor):
|
|
|
237
237
|
|
|
238
238
|
def __get__(self, instance, owner):
|
|
239
239
|
if instance is not None and ctx():
|
|
240
|
-
return ctx().
|
|
240
|
+
return ctx().mode_state.keys_by_archetype_id[instance.id]
|
|
241
241
|
else:
|
|
242
242
|
return self.value
|
|
243
243
|
|
|
@@ -249,8 +249,8 @@ class _ArchetypeLifeDescriptor(SonolusDescriptor):
|
|
|
249
249
|
def __get__(self, instance, owner):
|
|
250
250
|
if not ctx():
|
|
251
251
|
raise RuntimeError("Archetype life is only available during compilation")
|
|
252
|
-
if ctx().
|
|
253
|
-
raise RuntimeError(f"Archetype life is not available in mode '{ctx().
|
|
252
|
+
if ctx().mode_state.mode not in {Mode.PLAY, Mode.WATCH}:
|
|
253
|
+
raise RuntimeError(f"Archetype life is not available in mode '{ctx().mode_state.mode.value}'")
|
|
254
254
|
if instance is not None:
|
|
255
255
|
return _deref(ctx().blocks.ArchetypeLife, instance.id * ArchetypeLife._size_(), ArchetypeLife)
|
|
256
256
|
else:
|
|
@@ -1115,7 +1115,7 @@ def get_archetype_by_name(name: str) -> AnyArchetype:
|
|
|
1115
1115
|
name = name._as_py_() # type: ignore
|
|
1116
1116
|
if not isinstance(name, str):
|
|
1117
1117
|
raise TypeError(f"Invalid name: '{name}'")
|
|
1118
|
-
archetypes_by_name = ctx().
|
|
1118
|
+
archetypes_by_name = ctx().mode_state.archetypes_by_name
|
|
1119
1119
|
if name not in archetypes_by_name:
|
|
1120
1120
|
raise KeyError(f"Unknown archetype: '{name}'")
|
|
1121
1121
|
return archetypes_by_name[name] # type: ignore
|
|
@@ -1129,7 +1129,7 @@ def entity_info_at(index: int) -> PlayEntityInfo | WatchEntityInfo | PreviewEnti
|
|
|
1129
1129
|
"""
|
|
1130
1130
|
if not ctx():
|
|
1131
1131
|
raise RuntimeError("Calling entity_info_at is only allowed within a callback")
|
|
1132
|
-
match ctx().
|
|
1132
|
+
match ctx().mode_state.mode:
|
|
1133
1133
|
case Mode.PLAY:
|
|
1134
1134
|
return _deref(ctx().blocks.EntityInfoArray, index * PlayEntityInfo._size_(), PlayEntityInfo)
|
|
1135
1135
|
case Mode.WATCH:
|
|
@@ -1137,7 +1137,7 @@ def entity_info_at(index: int) -> PlayEntityInfo | WatchEntityInfo | PreviewEnti
|
|
|
1137
1137
|
case Mode.PREVIEW:
|
|
1138
1138
|
return _deref(ctx().blocks.EntityInfoArray, index * PreviewEntityInfo._size_(), PreviewEntityInfo)
|
|
1139
1139
|
case _:
|
|
1140
|
-
raise RuntimeError(f"Entity info is not available in mode '{ctx().
|
|
1140
|
+
raise RuntimeError(f"Entity info is not available in mode '{ctx().mode_state.mode}'")
|
|
1141
1141
|
|
|
1142
1142
|
|
|
1143
1143
|
class PlayEntityInfo(Record):
|
sonolus/script/bucket.py
CHANGED
|
@@ -156,7 +156,7 @@ class Bucket(Record):
|
|
|
156
156
|
"""The judgment window of the bucket."""
|
|
157
157
|
if not ctx():
|
|
158
158
|
raise RuntimeError("Bucket window access outside of compilation")
|
|
159
|
-
match ctx().
|
|
159
|
+
match ctx().mode_state.mode:
|
|
160
160
|
case Mode.PLAY:
|
|
161
161
|
return _deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
|
|
162
162
|
case Mode.WATCH:
|
sonolus/script/debug.py
CHANGED
|
@@ -7,7 +7,7 @@ from sonolus.backend.ops import Op
|
|
|
7
7
|
from sonolus.backend.optimize.flow import cfg_to_mermaid
|
|
8
8
|
from sonolus.backend.optimize.passes import CompilerPass, OptimizerConfig, run_passes
|
|
9
9
|
from sonolus.backend.optimize.simplify import RenumberVars
|
|
10
|
-
from sonolus.script.internal.context import
|
|
10
|
+
from sonolus.script.internal.context import ModeContextState, ProjectContextState, ctx, set_ctx
|
|
11
11
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
12
12
|
from sonolus.script.internal.native import native_function
|
|
13
13
|
from sonolus.script.internal.simulation_context import SimulationContext
|
|
@@ -27,7 +27,7 @@ def error(message: str | None = None) -> Never: # type: ignore
|
|
|
27
27
|
if not isinstance(message, str):
|
|
28
28
|
raise ValueError("Expected a string")
|
|
29
29
|
if ctx():
|
|
30
|
-
debug_log(ctx().
|
|
30
|
+
debug_log(ctx().map_debug_message(message))
|
|
31
31
|
debug_pause()
|
|
32
32
|
terminate()
|
|
33
33
|
else:
|
|
@@ -68,6 +68,19 @@ def debug_pause():
|
|
|
68
68
|
input("[DEBUG] Paused")
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
@meta_fn
|
|
72
|
+
def notify(message: str):
|
|
73
|
+
"""Log a code that can be decoded by the dev server and pause the game if in debug mode."""
|
|
74
|
+
message = validate_value(message)._as_py_() # type: ignore
|
|
75
|
+
if not isinstance(message, str):
|
|
76
|
+
raise ValueError("Expected a string")
|
|
77
|
+
if ctx():
|
|
78
|
+
debug_log(ctx().map_debug_message(message))
|
|
79
|
+
debug_pause()
|
|
80
|
+
else:
|
|
81
|
+
print(f"[NOTIFY] {message}")
|
|
82
|
+
|
|
83
|
+
|
|
71
84
|
@meta_fn
|
|
72
85
|
def assert_true(value: int | float | bool, message: str | None = None):
|
|
73
86
|
if not ctx():
|
|
@@ -156,13 +169,13 @@ def visualize_cfg(
|
|
|
156
169
|
RenumberVars(),
|
|
157
170
|
]
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
project_state = ProjectContextState()
|
|
173
|
+
mode_state = ModeContextState(
|
|
160
174
|
mode,
|
|
161
175
|
{a: i for i, a in enumerate(archetypes)} if archetypes is not None else None,
|
|
162
|
-
ReadOnlyMemory(),
|
|
163
176
|
)
|
|
164
177
|
|
|
165
|
-
cfg = callback_to_cfg(
|
|
178
|
+
cfg = callback_to_cfg(project_state, mode_state, fn, callback, archetype=archetype) # type: ignore
|
|
166
179
|
cfg = run_passes(cfg, passes, OptimizerConfig(mode=mode))
|
|
167
180
|
return cfg_to_mermaid(cfg)
|
|
168
181
|
|
|
@@ -39,19 +39,35 @@ _disabled_debug_config = DebugConfig(
|
|
|
39
39
|
debug_var = ContextVar("debug_var", default=_disabled_debug_config)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
class
|
|
42
|
+
class ProjectContextState:
|
|
43
|
+
rom: ReadOnlyMemory
|
|
44
|
+
const_mappings: dict[Any, int]
|
|
45
|
+
debug_str_mappings: dict[str, int]
|
|
46
|
+
lock: Lock
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
rom: ReadOnlyMemory | None = None,
|
|
51
|
+
const_mappings: dict[Any, int] | None = None,
|
|
52
|
+
debug_str_mappings: dict[str, int] | None = None,
|
|
53
|
+
):
|
|
54
|
+
self.rom = ReadOnlyMemory() if rom is None else rom
|
|
55
|
+
self.const_mappings = {} if const_mappings is None else const_mappings
|
|
56
|
+
self.debug_str_mappings = {} if debug_str_mappings is None else debug_str_mappings
|
|
57
|
+
self.lock = Lock()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ModeContextState:
|
|
43
61
|
archetypes: dict[type, int]
|
|
44
62
|
archetypes_by_name: dict[str, type]
|
|
45
63
|
keys_by_archetype_id: Sequence[int]
|
|
46
64
|
is_scored_by_archetype_id: Sequence[bool]
|
|
47
|
-
rom: ReadOnlyMemory
|
|
48
|
-
const_mappings: dict[Any, int]
|
|
49
65
|
environment_mappings: dict[_GlobalInfo, int]
|
|
50
66
|
environment_offsets: dict[Block, int]
|
|
51
67
|
mode: Mode
|
|
52
68
|
lock: Lock
|
|
53
69
|
|
|
54
|
-
def __init__(self, mode: Mode, archetypes: dict[type, int] | None = None
|
|
70
|
+
def __init__(self, mode: Mode, archetypes: dict[type, int] | None = None):
|
|
55
71
|
from sonolus.script.array import Array
|
|
56
72
|
|
|
57
73
|
self.archetypes = archetypes or {}
|
|
@@ -65,8 +81,6 @@ class GlobalContextState:
|
|
|
65
81
|
if archetypes
|
|
66
82
|
else Array[bool, Literal[0]]()
|
|
67
83
|
)
|
|
68
|
-
self.rom = ReadOnlyMemory() if rom is None else rom
|
|
69
|
-
self.const_mappings = {}
|
|
70
84
|
self.environment_mappings = {}
|
|
71
85
|
self.environment_offsets = {}
|
|
72
86
|
self.mode = mode
|
|
@@ -76,16 +90,19 @@ class GlobalContextState:
|
|
|
76
90
|
class CallbackContextState:
|
|
77
91
|
callback: str
|
|
78
92
|
used_names: dict[str, int]
|
|
93
|
+
debug_stack: list[str]
|
|
79
94
|
no_eval: bool
|
|
80
95
|
|
|
81
96
|
def __init__(self, callback: str, no_eval: bool = False):
|
|
82
97
|
self.callback = callback
|
|
83
98
|
self.used_names = {}
|
|
99
|
+
self.debug_stack = []
|
|
84
100
|
self.no_eval = no_eval
|
|
85
101
|
|
|
86
102
|
|
|
87
103
|
class Context:
|
|
88
|
-
|
|
104
|
+
project_state: ProjectContextState
|
|
105
|
+
mode_state: ModeContextState
|
|
89
106
|
callback_state: CallbackContextState
|
|
90
107
|
statements: list[IRStmt]
|
|
91
108
|
test: IRExpr
|
|
@@ -96,13 +113,15 @@ class Context:
|
|
|
96
113
|
|
|
97
114
|
def __init__(
|
|
98
115
|
self,
|
|
99
|
-
|
|
116
|
+
project_state: ProjectContextState,
|
|
117
|
+
mode_state: ModeContextState,
|
|
100
118
|
callback_state: CallbackContextState,
|
|
101
119
|
scope: Scope | None = None,
|
|
102
120
|
live: bool = True,
|
|
103
121
|
foldable_constants: dict[TempBlock, list[float | None]] | None = None,
|
|
104
122
|
):
|
|
105
|
-
self.
|
|
123
|
+
self.project_state = project_state
|
|
124
|
+
self.mode_state = mode_state
|
|
106
125
|
self.callback_state = callback_state
|
|
107
126
|
self.statements = []
|
|
108
127
|
self.test = IRConst(0)
|
|
@@ -114,11 +133,11 @@ class Context:
|
|
|
114
133
|
|
|
115
134
|
@property
|
|
116
135
|
def rom(self) -> ReadOnlyMemory:
|
|
117
|
-
return self.
|
|
136
|
+
return self.project_state.rom
|
|
118
137
|
|
|
119
138
|
@property
|
|
120
139
|
def blocks(self) -> type[Block]:
|
|
121
|
-
return self.
|
|
140
|
+
return self.mode_state.mode.blocks
|
|
122
141
|
|
|
123
142
|
@property
|
|
124
143
|
def callback(self) -> str:
|
|
@@ -182,7 +201,8 @@ class Context:
|
|
|
182
201
|
|
|
183
202
|
def copy_with_scope(self, scope: Scope) -> Context:
|
|
184
203
|
return Context(
|
|
185
|
-
|
|
204
|
+
project_state=self.project_state,
|
|
205
|
+
mode_state=self.mode_state,
|
|
186
206
|
callback_state=self.callback_state,
|
|
187
207
|
scope=scope,
|
|
188
208
|
live=self.live,
|
|
@@ -267,25 +287,33 @@ class Context:
|
|
|
267
287
|
)
|
|
268
288
|
|
|
269
289
|
def map_constant(self, value: Any) -> int:
|
|
270
|
-
with self.
|
|
271
|
-
const_mappings = self.
|
|
290
|
+
with self.project_state.lock:
|
|
291
|
+
const_mappings = self.project_state.const_mappings
|
|
272
292
|
if value not in const_mappings:
|
|
273
293
|
const_mappings[value] = len(const_mappings)
|
|
274
294
|
return const_mappings[value]
|
|
275
295
|
|
|
296
|
+
def map_debug_message(self, message: str) -> int:
|
|
297
|
+
with self.project_state.lock:
|
|
298
|
+
message_with_trace = "\n".join([*self.callback_state.debug_stack, message])
|
|
299
|
+
debug_str_mappings = self.project_state.debug_str_mappings
|
|
300
|
+
if message_with_trace not in debug_str_mappings:
|
|
301
|
+
debug_str_mappings[message_with_trace] = len(debug_str_mappings) + 1
|
|
302
|
+
return debug_str_mappings[message_with_trace]
|
|
303
|
+
|
|
276
304
|
def get_global_base(self, value: _GlobalInfo | _GlobalPlaceholder) -> BlockPlace:
|
|
277
|
-
with self.
|
|
278
|
-
block = value.blocks.get(self.
|
|
305
|
+
with self.mode_state.lock:
|
|
306
|
+
block = value.blocks.get(self.mode_state.mode)
|
|
279
307
|
if block is None:
|
|
280
|
-
raise RuntimeError(f"Global {value} is not available in '{self.
|
|
281
|
-
if value not in self.
|
|
308
|
+
raise RuntimeError(f"Global {value} is not available in '{self.mode_state.mode.name}' mode")
|
|
309
|
+
if value not in self.mode_state.environment_mappings:
|
|
282
310
|
if value.offset is None:
|
|
283
|
-
offset = self.
|
|
284
|
-
self.
|
|
285
|
-
self.
|
|
311
|
+
offset = self.mode_state.environment_offsets.get(block, 0)
|
|
312
|
+
self.mode_state.environment_mappings[value] = offset
|
|
313
|
+
self.mode_state.environment_offsets[block] = offset + value.size
|
|
286
314
|
else:
|
|
287
|
-
self.
|
|
288
|
-
return BlockPlace(block, self.
|
|
315
|
+
self.mode_state.environment_mappings[value] = value.offset
|
|
316
|
+
return BlockPlace(block, self.mode_state.environment_mappings[value])
|
|
289
317
|
|
|
290
318
|
@classmethod
|
|
291
319
|
def meet(cls, contexts: list[Context]) -> Context:
|
|
@@ -303,10 +331,10 @@ class Context:
|
|
|
303
331
|
return target
|
|
304
332
|
|
|
305
333
|
def register_archetype(self, type_: type) -> int:
|
|
306
|
-
with self.
|
|
307
|
-
if type_ not in self.
|
|
308
|
-
self.
|
|
309
|
-
return self.
|
|
334
|
+
with self.mode_state.lock:
|
|
335
|
+
if type_ not in self.mode_state.archetypes:
|
|
336
|
+
self.mode_state.archetypes[type_] = len(self.mode_state.archetypes)
|
|
337
|
+
return self.mode_state.archetypes[type_]
|
|
310
338
|
|
|
311
339
|
|
|
312
340
|
def ctx() -> Context | Any: # Using Any to silence type checker warnings if it's None
|
sonolus/script/options.py
CHANGED
|
@@ -191,7 +191,7 @@ class _OptionField(SonolusDescriptor):
|
|
|
191
191
|
if sim_ctx():
|
|
192
192
|
return sim_ctx().get_or_put_value((instance, self), lambda: copy(self.info.default))
|
|
193
193
|
if ctx():
|
|
194
|
-
match ctx().
|
|
194
|
+
match ctx().mode_state.mode:
|
|
195
195
|
case Mode.PLAY:
|
|
196
196
|
block = ctx().blocks.LevelOption
|
|
197
197
|
case Mode.WATCH:
|
|
@@ -212,7 +212,7 @@ class _OptionField(SonolusDescriptor):
|
|
|
212
212
|
if sim_ctx():
|
|
213
213
|
return sim_ctx().set_or_put_value((instance, self), lambda: copy(self.info.default), value)
|
|
214
214
|
if ctx() and debug_config().unchecked_writes:
|
|
215
|
-
match ctx().
|
|
215
|
+
match ctx().mode_state.mode:
|
|
216
216
|
case Mode.PLAY:
|
|
217
217
|
block = ctx().blocks.LevelOption
|
|
218
218
|
case Mode.WATCH:
|
sonolus/script/project.py
CHANGED
|
@@ -8,7 +8,6 @@ from typing import ClassVar, TypedDict
|
|
|
8
8
|
|
|
9
9
|
from sonolus.backend.optimize import optimize
|
|
10
10
|
from sonolus.backend.optimize.passes import CompilerPass
|
|
11
|
-
from sonolus.build.compile import CompileCache
|
|
12
11
|
from sonolus.script.archetype import ArchetypeSchema
|
|
13
12
|
from sonolus.script.engine import Engine
|
|
14
13
|
from sonolus.script.level import ExternalLevelData, Level, LevelData
|
|
@@ -64,13 +63,11 @@ class Project:
|
|
|
64
63
|
port: The port of the development server.
|
|
65
64
|
config: The build configuration.
|
|
66
65
|
"""
|
|
67
|
-
from sonolus.build.cli import
|
|
66
|
+
from sonolus.build.cli import run_server
|
|
68
67
|
|
|
69
68
|
if config is None:
|
|
70
69
|
config = BuildConfig()
|
|
71
70
|
|
|
72
|
-
cache = CompileCache()
|
|
73
|
-
build_collection(self, Path(build_dir), config, cache=cache)
|
|
74
71
|
run_server(
|
|
75
72
|
Path(build_dir) / "site",
|
|
76
73
|
port=port,
|
|
@@ -78,7 +75,7 @@ class Project:
|
|
|
78
75
|
core_module_names=None,
|
|
79
76
|
build_dir=Path(build_dir),
|
|
80
77
|
config=config,
|
|
81
|
-
|
|
78
|
+
project=self,
|
|
82
79
|
)
|
|
83
80
|
|
|
84
81
|
def build(self, build_dir: PathLike, config: BuildConfig | None = None):
|