sonolus.py 0.1.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/__init__.py +0 -0
- sonolus/backend/__init__.py +0 -0
- sonolus/backend/allocate.py +51 -0
- sonolus/backend/blocks.py +756 -0
- sonolus/backend/excepthook.py +37 -0
- sonolus/backend/finalize.py +69 -0
- sonolus/backend/flow.py +92 -0
- sonolus/backend/interpret.py +333 -0
- sonolus/backend/ir.py +89 -0
- sonolus/backend/mode.py +24 -0
- sonolus/backend/node.py +40 -0
- sonolus/backend/ops.py +197 -0
- sonolus/backend/optimize.py +9 -0
- sonolus/backend/passes.py +6 -0
- sonolus/backend/place.py +90 -0
- sonolus/backend/simplify.py +30 -0
- sonolus/backend/utils.py +48 -0
- sonolus/backend/visitor.py +880 -0
- sonolus/build/__init__.py +0 -0
- sonolus/build/cli.py +170 -0
- sonolus/build/collection.py +293 -0
- sonolus/build/compile.py +90 -0
- sonolus/build/defaults.py +32 -0
- sonolus/build/engine.py +149 -0
- sonolus/build/level.py +23 -0
- sonolus/build/node.py +43 -0
- sonolus/build/project.py +94 -0
- sonolus/py.typed +0 -0
- sonolus/script/__init__.py +0 -0
- sonolus/script/archetype.py +651 -0
- sonolus/script/array.py +241 -0
- sonolus/script/bucket.py +192 -0
- sonolus/script/callbacks.py +105 -0
- sonolus/script/comptime.py +146 -0
- sonolus/script/containers.py +247 -0
- sonolus/script/debug.py +70 -0
- sonolus/script/effect.py +132 -0
- sonolus/script/engine.py +101 -0
- sonolus/script/globals.py +234 -0
- sonolus/script/graphics.py +141 -0
- sonolus/script/icon.py +73 -0
- sonolus/script/internal/__init__.py +5 -0
- sonolus/script/internal/builtin_impls.py +144 -0
- sonolus/script/internal/context.py +365 -0
- sonolus/script/internal/descriptor.py +17 -0
- sonolus/script/internal/error.py +15 -0
- sonolus/script/internal/generic.py +197 -0
- sonolus/script/internal/impl.py +69 -0
- sonolus/script/internal/introspection.py +14 -0
- sonolus/script/internal/native.py +38 -0
- sonolus/script/internal/value.py +144 -0
- sonolus/script/interval.py +98 -0
- sonolus/script/iterator.py +211 -0
- sonolus/script/level.py +52 -0
- sonolus/script/math.py +92 -0
- sonolus/script/num.py +382 -0
- sonolus/script/options.py +194 -0
- sonolus/script/particle.py +158 -0
- sonolus/script/pointer.py +30 -0
- sonolus/script/project.py +17 -0
- sonolus/script/range.py +58 -0
- sonolus/script/record.py +293 -0
- sonolus/script/runtime.py +526 -0
- sonolus/script/sprite.py +332 -0
- sonolus/script/text.py +404 -0
- sonolus/script/timing.py +42 -0
- sonolus/script/transform.py +118 -0
- sonolus/script/ui.py +160 -0
- sonolus/script/values.py +43 -0
- sonolus/script/vec.py +48 -0
- sonolus_py-0.1.0.dist-info/METADATA +10 -0
- sonolus_py-0.1.0.dist-info/RECORD +75 -0
- sonolus_py-0.1.0.dist-info/WHEEL +4 -0
- sonolus_py-0.1.0.dist-info/entry_points.txt +2 -0
- sonolus_py-0.1.0.dist-info/licenses/LICENSE +21 -0
sonolus/build/engine.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import gzip
|
|
2
|
+
import json
|
|
3
|
+
import struct
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from sonolus.backend.mode import Mode
|
|
9
|
+
from sonolus.build.compile import compile_mode
|
|
10
|
+
from sonolus.build.defaults import EMPTY_ENGINE_PREVIEW_DATA, EMPTY_ENGINE_TUTORIAL_DATA
|
|
11
|
+
from sonolus.script.archetype import BaseArchetype
|
|
12
|
+
from sonolus.script.bucket import Buckets
|
|
13
|
+
from sonolus.script.callbacks import update_spawn_callback
|
|
14
|
+
from sonolus.script.effect import Effects
|
|
15
|
+
from sonolus.script.engine import EngineData
|
|
16
|
+
from sonolus.script.internal.context import ReadOnlyMemory
|
|
17
|
+
from sonolus.script.options import Options
|
|
18
|
+
from sonolus.script.particle import Particles
|
|
19
|
+
from sonolus.script.sprite import Skin
|
|
20
|
+
from sonolus.script.ui import UiConfig
|
|
21
|
+
|
|
22
|
+
type JsonValue = None | bool | int | float | str | list[JsonValue] | dict[str, JsonValue]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class PackagedEngine:
|
|
27
|
+
configuration: bytes
|
|
28
|
+
play_data: bytes
|
|
29
|
+
watch_data: bytes
|
|
30
|
+
preview_data: bytes
|
|
31
|
+
tutorial_data: bytes
|
|
32
|
+
rom: bytes
|
|
33
|
+
|
|
34
|
+
def write(self, path: Path):
|
|
35
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
(path / "EngineConfiguration").write_bytes(self.configuration)
|
|
37
|
+
(path / "EnginePlayData").write_bytes(self.play_data)
|
|
38
|
+
(path / "EngineWatchData").write_bytes(self.watch_data)
|
|
39
|
+
(path / "EnginePreviewData").write_bytes(self.preview_data)
|
|
40
|
+
(path / "EngineTutorialData").write_bytes(self.tutorial_data)
|
|
41
|
+
(path / "EngineRom").write_bytes(self.rom)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def package_engine(engine: EngineData):
|
|
45
|
+
rom = ReadOnlyMemory()
|
|
46
|
+
configuration = build_engine_configuration(engine.options, engine.ui)
|
|
47
|
+
play_data = build_play_mode(
|
|
48
|
+
archetypes=engine.play.archetypes,
|
|
49
|
+
skin=engine.play.skin,
|
|
50
|
+
effects=engine.play.effects,
|
|
51
|
+
particles=engine.play.particles,
|
|
52
|
+
buckets=engine.play.buckets,
|
|
53
|
+
rom=rom,
|
|
54
|
+
)
|
|
55
|
+
watch_data = build_watch_mode(
|
|
56
|
+
archetypes=engine.watch.archetypes,
|
|
57
|
+
skin=engine.watch.skin,
|
|
58
|
+
effects=engine.watch.effects,
|
|
59
|
+
particles=engine.watch.particles,
|
|
60
|
+
buckets=engine.watch.buckets,
|
|
61
|
+
rom=rom,
|
|
62
|
+
update_spawn=engine.watch.update_spawn,
|
|
63
|
+
)
|
|
64
|
+
return PackagedEngine(
|
|
65
|
+
configuration=package_output(configuration),
|
|
66
|
+
play_data=package_output(play_data),
|
|
67
|
+
watch_data=package_output(watch_data),
|
|
68
|
+
preview_data=package_output(EMPTY_ENGINE_PREVIEW_DATA),
|
|
69
|
+
tutorial_data=package_output(EMPTY_ENGINE_TUTORIAL_DATA),
|
|
70
|
+
rom=package_rom(rom),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def build_engine_configuration(
|
|
75
|
+
options: Options,
|
|
76
|
+
ui: UiConfig,
|
|
77
|
+
) -> JsonValue:
|
|
78
|
+
return {
|
|
79
|
+
"options": [option.to_dict() for option in options._options_],
|
|
80
|
+
"ui": ui.to_dict(),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def build_play_mode(
|
|
85
|
+
archetypes: list[type[BaseArchetype]],
|
|
86
|
+
skin: Skin,
|
|
87
|
+
effects: Effects,
|
|
88
|
+
particles: Particles,
|
|
89
|
+
buckets: Buckets,
|
|
90
|
+
rom: ReadOnlyMemory,
|
|
91
|
+
):
|
|
92
|
+
return {
|
|
93
|
+
**compile_mode(mode=Mode.Play, rom=rom, archetypes=archetypes, global_callbacks=None),
|
|
94
|
+
"skin": build_skin(skin),
|
|
95
|
+
"effect": build_effects(effects),
|
|
96
|
+
"particle": build_particles(particles),
|
|
97
|
+
"buckets": build_buckets(buckets),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_watch_mode(
|
|
102
|
+
archetypes: list[type[BaseArchetype]],
|
|
103
|
+
skin: Skin,
|
|
104
|
+
effects: Effects,
|
|
105
|
+
particles: Particles,
|
|
106
|
+
buckets: Buckets,
|
|
107
|
+
rom: ReadOnlyMemory,
|
|
108
|
+
update_spawn: Callable[[], float],
|
|
109
|
+
):
|
|
110
|
+
return {
|
|
111
|
+
**compile_mode(
|
|
112
|
+
mode=Mode.Watch, rom=rom, archetypes=archetypes, global_callbacks=[(update_spawn_callback, update_spawn)]
|
|
113
|
+
),
|
|
114
|
+
"skin": build_skin(skin),
|
|
115
|
+
"effect": build_effects(effects),
|
|
116
|
+
"particle": build_particles(particles),
|
|
117
|
+
"buckets": build_buckets(buckets),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def build_skin(skin: Skin) -> JsonValue:
|
|
122
|
+
return {"sprites": [{"name": name, "id": i} for i, name in enumerate(skin._sprites_)]}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def build_effects(effects: Effects) -> JsonValue:
|
|
126
|
+
return {"clips": [{"name": name, "id": i} for i, name in enumerate(effects._effects_)]}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def build_particles(particles: Particles) -> JsonValue:
|
|
130
|
+
return {"effects": [{"name": name, "id": i} for i, name in enumerate(particles._particles_)]}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def build_buckets(buckets: Buckets) -> JsonValue:
|
|
134
|
+
return [bucket.to_dict() for bucket in buckets._buckets_]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def package_rom(rom: ReadOnlyMemory) -> bytes:
|
|
138
|
+
values = rom.values or [0]
|
|
139
|
+
output = bytearray()
|
|
140
|
+
|
|
141
|
+
for value in values:
|
|
142
|
+
output.extend(struct.pack("<f", value))
|
|
143
|
+
|
|
144
|
+
return gzip.compress(bytes(output))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def package_output(value: JsonValue) -> bytes:
|
|
148
|
+
json_data = json.dumps(value, separators=(",", ":")).encode("utf-8")
|
|
149
|
+
return gzip.compress(json_data)
|
sonolus/build/level.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from sonolus.build.engine import package_output
|
|
2
|
+
from sonolus.script.level import LevelData
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def package_level_data(
|
|
6
|
+
level_data: LevelData,
|
|
7
|
+
):
|
|
8
|
+
return package_output(build_level_data(level_data))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_level_data(
|
|
12
|
+
level_data: LevelData,
|
|
13
|
+
):
|
|
14
|
+
return {
|
|
15
|
+
"bgmOffset": level_data.bgm_offset,
|
|
16
|
+
"entities": [
|
|
17
|
+
{
|
|
18
|
+
"archetype": entity.name,
|
|
19
|
+
"data": entity._level_data_entries(),
|
|
20
|
+
}
|
|
21
|
+
for entity in level_data.entities
|
|
22
|
+
],
|
|
23
|
+
}
|
sonolus/build/node.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import TypedDict
|
|
2
|
+
|
|
3
|
+
from sonolus.backend.node import ConstantNode, EngineNode, FunctionNode
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ValueOutputNode(TypedDict):
|
|
7
|
+
value: float
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FunctionOutputNode(TypedDict):
|
|
11
|
+
func: str
|
|
12
|
+
args: list[int]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OutputNodeGenerator:
|
|
16
|
+
nodes: list[ValueOutputNode | FunctionOutputNode]
|
|
17
|
+
indexes: dict[EngineNode, int]
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.nodes = []
|
|
21
|
+
self.indexes = {}
|
|
22
|
+
|
|
23
|
+
def add(self, node: EngineNode):
|
|
24
|
+
if node in self.indexes:
|
|
25
|
+
return self.indexes[node]
|
|
26
|
+
|
|
27
|
+
match node:
|
|
28
|
+
case ConstantNode(value):
|
|
29
|
+
index = len(self.nodes)
|
|
30
|
+
self.nodes.append({"value": value})
|
|
31
|
+
self.indexes[node] = index
|
|
32
|
+
return index
|
|
33
|
+
case FunctionNode(func, args):
|
|
34
|
+
arg_indexes = [self.add(arg) for arg in args]
|
|
35
|
+
index = len(self.nodes)
|
|
36
|
+
self.nodes.append({"func": func.value, "args": arg_indexes})
|
|
37
|
+
self.indexes[node] = index
|
|
38
|
+
return index
|
|
39
|
+
case _:
|
|
40
|
+
raise ValueError("Invalid node")
|
|
41
|
+
|
|
42
|
+
def get(self):
|
|
43
|
+
return self.nodes
|
sonolus/build/project.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from sonolus.build.collection import Asset, Collection, Srl
|
|
4
|
+
from sonolus.build.engine import package_engine
|
|
5
|
+
from sonolus.build.level import package_level_data
|
|
6
|
+
from sonolus.script.engine import Engine
|
|
7
|
+
from sonolus.script.level import Level
|
|
8
|
+
from sonolus.script.project import Project
|
|
9
|
+
|
|
10
|
+
BLANK_PNG = (
|
|
11
|
+
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x007n\xf9$"
|
|
12
|
+
b"\x00\x00\x00\nIDATx\x01c`\x00\x00\x00\x02\x00\x01su\x01\x18\x00\x00\x00\x00IEND\xaeB`\x82"
|
|
13
|
+
)
|
|
14
|
+
BLANK_AUDIO = (
|
|
15
|
+
b"RIFF$\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00D\xac\x00\x00\x88X\x01\x00\x02"
|
|
16
|
+
b"\x00\x10\x00data\x00\x00\x00\x00"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_project_to_collection(project: Project):
|
|
21
|
+
collection = load_resources_files_to_collection(project.resources)
|
|
22
|
+
add_engine_to_collection(collection, project, project.engine)
|
|
23
|
+
for level in project.levels:
|
|
24
|
+
add_level_to_collection(collection, project, level)
|
|
25
|
+
collection.name = f"{project.engine.name}"
|
|
26
|
+
return collection
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def add_engine_to_collection(collection: Collection, project: Project, engine: Engine):
|
|
30
|
+
packaged_engine = package_engine(engine.data)
|
|
31
|
+
item = {
|
|
32
|
+
"name": engine.name,
|
|
33
|
+
"version": engine.version,
|
|
34
|
+
"title": engine.title,
|
|
35
|
+
"subtitle": engine.subtitle,
|
|
36
|
+
"author": engine.author,
|
|
37
|
+
"tags": [],
|
|
38
|
+
"skin": collection.get_item("skins", engine.skin) if engine.skin else collection.get_default_item("skins"),
|
|
39
|
+
"background": collection.get_item("backgrounds", engine.background)
|
|
40
|
+
if engine.background
|
|
41
|
+
else collection.get_default_item("backgrounds"),
|
|
42
|
+
"effect": collection.get_item("effects", engine.effect)
|
|
43
|
+
if engine.effect
|
|
44
|
+
else collection.get_default_item("effects"),
|
|
45
|
+
"particle": collection.get_item("particles", engine.particle)
|
|
46
|
+
if engine.particle
|
|
47
|
+
else collection.get_default_item("particles"),
|
|
48
|
+
"thumbnail": load_resource(collection, engine.thumbnail, project.resources, BLANK_PNG),
|
|
49
|
+
"playData": collection.add_asset(packaged_engine.play_data),
|
|
50
|
+
"watchData": collection.add_asset(packaged_engine.watch_data),
|
|
51
|
+
"previewData": collection.add_asset(packaged_engine.preview_data),
|
|
52
|
+
"tutorialData": collection.add_asset(packaged_engine.tutorial_data),
|
|
53
|
+
"rom": collection.add_asset(packaged_engine.rom),
|
|
54
|
+
"configuration": collection.add_asset(packaged_engine.configuration),
|
|
55
|
+
}
|
|
56
|
+
collection.add_item("engines", engine.name, item)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def add_level_to_collection(collection: Collection, project: Project, level: Level):
|
|
60
|
+
packaged_level_data = package_level_data(level.data)
|
|
61
|
+
item = {
|
|
62
|
+
"name": level.name,
|
|
63
|
+
"version": level.version,
|
|
64
|
+
"rating": level.rating,
|
|
65
|
+
"title": level.title,
|
|
66
|
+
"artists": level.artists,
|
|
67
|
+
"author": level.author,
|
|
68
|
+
"tags": [],
|
|
69
|
+
"engine": collection.get_item("engines", project.engine.name),
|
|
70
|
+
"useSkin": {"useDefault": True},
|
|
71
|
+
"useBackground": {"useDefault": True},
|
|
72
|
+
"useEffect": {"useDefault": True},
|
|
73
|
+
"useParticle": {"useDefault": True},
|
|
74
|
+
"cover": load_resource(collection, level.cover, project.resources, BLANK_PNG),
|
|
75
|
+
"bgm": load_resource(collection, level.bgm, project.resources, BLANK_AUDIO),
|
|
76
|
+
"data": collection.add_asset(packaged_level_data),
|
|
77
|
+
}
|
|
78
|
+
collection.add_item("levels", level.name, item)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def load_resource(collection: Collection, asset: Asset | None, base_path: Path, default: bytes) -> Srl:
|
|
82
|
+
if asset is None:
|
|
83
|
+
return collection.add_asset(default)
|
|
84
|
+
if isinstance(asset, str) and not asset.startswith(("http://", "https://")):
|
|
85
|
+
return collection.add_asset(base_path / asset)
|
|
86
|
+
return collection.add_asset(asset)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_resources_files_to_collection(base_path: Path) -> Collection:
|
|
90
|
+
collection = Collection()
|
|
91
|
+
for path in base_path.rglob("*.scp"):
|
|
92
|
+
collection.load_from_scp(path)
|
|
93
|
+
collection.load_from_source(base_path)
|
|
94
|
+
return collection
|
sonolus/py.typed
ADDED
|
File without changes
|
|
File without changes
|