sonolus.py 0.11.0__py3-none-any.whl → 0.12.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/finalize.py +30 -32
- sonolus/backend/interpret.py +3 -3
- sonolus/backend/node.py +6 -28
- sonolus/build/compile.py +17 -3
- sonolus/build/dev_server.py +2 -0
- sonolus/build/node.py +6 -8
- sonolus/script/archetype.py +145 -24
- sonolus/script/containers.py +6 -5
- sonolus/script/debug.py +9 -1
- sonolus/script/internal/context.py +46 -5
- sonolus/script/internal/introspection.py +8 -1
- {sonolus_py-0.11.0.dist-info → sonolus_py-0.12.0.dist-info}/METADATA +2 -2
- {sonolus_py-0.11.0.dist-info → sonolus_py-0.12.0.dist-info}/RECORD +16 -16
- {sonolus_py-0.11.0.dist-info → sonolus_py-0.12.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.11.0.dist-info → sonolus_py-0.12.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.11.0.dist-info → sonolus_py-0.12.0.dist-info}/licenses/LICENSE +0 -0
sonolus/backend/finalize.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from math import isfinite, isinf, isnan
|
|
2
2
|
|
|
3
3
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
4
|
-
from sonolus.backend.node import
|
|
4
|
+
from sonolus.backend.node import EngineNode, FunctionNode
|
|
5
5
|
from sonolus.backend.ops import Op
|
|
6
6
|
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_reverse_postorder
|
|
7
7
|
from sonolus.backend.place import BlockPlace
|
|
@@ -18,18 +18,18 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
18
18
|
}
|
|
19
19
|
match outgoing:
|
|
20
20
|
case {**other} if not other:
|
|
21
|
-
statements.append(
|
|
21
|
+
statements.append(len(block_indexes))
|
|
22
22
|
case {None: target, **other} if not other:
|
|
23
|
-
statements.append(
|
|
23
|
+
statements.append(block_indexes[target])
|
|
24
24
|
case {0: f_branch, None: t_branch, **other} if not other:
|
|
25
25
|
statements.append(
|
|
26
26
|
FunctionNode(
|
|
27
27
|
func=Op.If,
|
|
28
|
-
args=
|
|
28
|
+
args=(
|
|
29
29
|
ir_to_engine_node(block.test),
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
block_indexes[t_branch],
|
|
31
|
+
block_indexes[f_branch],
|
|
32
|
+
),
|
|
33
33
|
)
|
|
34
34
|
)
|
|
35
35
|
case {None: default_branch, **other} if len(other) == 1:
|
|
@@ -37,11 +37,11 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
37
37
|
statements.append(
|
|
38
38
|
FunctionNode(
|
|
39
39
|
func=Op.If,
|
|
40
|
-
args=
|
|
40
|
+
args=(
|
|
41
41
|
ir_to_engine_node(IRPureInstr(Op.Equal, args=[block.test, IRConst(cond)])),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
block_indexes[cond_branch],
|
|
43
|
+
block_indexes[default_branch],
|
|
44
|
+
),
|
|
45
45
|
)
|
|
46
46
|
)
|
|
47
47
|
case dict() as targets:
|
|
@@ -49,23 +49,23 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
49
49
|
default = len(block_indexes)
|
|
50
50
|
conds = [cond for cond in targets if cond is not None]
|
|
51
51
|
if min(conds) == 0 and max(conds) == len(conds) - 1 and all(int(cond) == cond for cond in conds):
|
|
52
|
-
args.extend(
|
|
52
|
+
args.extend(block_indexes[targets[cond]] for cond in range(len(conds)))
|
|
53
53
|
if None in targets:
|
|
54
54
|
default = block_indexes[targets[None]]
|
|
55
|
-
args.append(
|
|
56
|
-
statements.append(FunctionNode(Op.SwitchIntegerWithDefault, args))
|
|
55
|
+
args.append(default)
|
|
56
|
+
statements.append(FunctionNode(Op.SwitchIntegerWithDefault, tuple(args)))
|
|
57
57
|
else:
|
|
58
58
|
for cond, target in targets.items():
|
|
59
59
|
if cond is None:
|
|
60
60
|
default = block_indexes[target]
|
|
61
61
|
continue
|
|
62
|
-
args.append(
|
|
63
|
-
args.append(
|
|
64
|
-
args.append(
|
|
65
|
-
statements.append(FunctionNode(Op.SwitchWithDefault, args))
|
|
66
|
-
block_statements.append(FunctionNode(Op.Execute, statements))
|
|
67
|
-
block_statements.append(
|
|
68
|
-
result = FunctionNode(Op.Block,
|
|
62
|
+
args.append(cond)
|
|
63
|
+
args.append(block_indexes[target])
|
|
64
|
+
args.append(default)
|
|
65
|
+
statements.append(FunctionNode(Op.SwitchWithDefault, tuple(args)))
|
|
66
|
+
block_statements.append(FunctionNode(Op.Execute, tuple(statements)))
|
|
67
|
+
block_statements.append(0)
|
|
68
|
+
result = FunctionNode(Op.Block, (FunctionNode(Op.JumpLoop, tuple(block_statements)),))
|
|
69
69
|
for block in block_indexes:
|
|
70
70
|
# Clean up without relying on gc
|
|
71
71
|
del block.incoming
|
|
@@ -81,32 +81,30 @@ def ir_to_engine_node(stmt) -> EngineNode:
|
|
|
81
81
|
case int(value) | float(value) | IRConst(value=int(value) | float(value)):
|
|
82
82
|
value = float(value)
|
|
83
83
|
if value.is_integer():
|
|
84
|
-
return
|
|
84
|
+
return int(value)
|
|
85
85
|
elif isfinite(value):
|
|
86
|
-
return
|
|
86
|
+
return value
|
|
87
87
|
elif isinf(value):
|
|
88
88
|
# Read values from ROM
|
|
89
|
-
return FunctionNode(Op.Get, args=
|
|
89
|
+
return FunctionNode(Op.Get, args=(3000, 1 if value > 0 else 2))
|
|
90
90
|
elif isnan(value):
|
|
91
91
|
# Read value from ROM
|
|
92
|
-
return FunctionNode(Op.Get, args=
|
|
92
|
+
return FunctionNode(Op.Get, args=(3000, 0))
|
|
93
93
|
else:
|
|
94
94
|
raise ValueError(f"Invalid constant value: {value}")
|
|
95
95
|
case IRPureInstr(op=op, args=args) | IRInstr(op=op, args=args):
|
|
96
|
-
return FunctionNode(func=op, args=
|
|
96
|
+
return FunctionNode(func=op, args=tuple(ir_to_engine_node(arg) for arg in args))
|
|
97
97
|
case IRGet(place=place):
|
|
98
98
|
return ir_to_engine_node(place)
|
|
99
99
|
case BlockPlace() as place:
|
|
100
100
|
if place.offset == 0:
|
|
101
101
|
index = ir_to_engine_node(place.index)
|
|
102
102
|
elif place.index == 0:
|
|
103
|
-
index =
|
|
103
|
+
index = place.offset
|
|
104
104
|
else:
|
|
105
|
-
index = FunctionNode(
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
return FunctionNode(func=Op.Get, args=[ir_to_engine_node(place.block), index])
|
|
105
|
+
index = FunctionNode(func=Op.Add, args=(ir_to_engine_node(place.index), place.offset))
|
|
106
|
+
return FunctionNode(func=Op.Get, args=(ir_to_engine_node(place.block), index))
|
|
109
107
|
case IRSet(place=place, value=value):
|
|
110
|
-
return FunctionNode(func=Op.Set, args=
|
|
108
|
+
return FunctionNode(func=Op.Set, args=(*ir_to_engine_node(place).args, ir_to_engine_node(value)))
|
|
111
109
|
case _:
|
|
112
110
|
raise TypeError(f"Unsupported IR statement: {stmt}")
|
sonolus/backend/interpret.py
CHANGED
|
@@ -2,7 +2,7 @@ import math
|
|
|
2
2
|
import operator
|
|
3
3
|
import random
|
|
4
4
|
|
|
5
|
-
from sonolus.backend.node import
|
|
5
|
+
from sonolus.backend.node import EngineNode, FunctionNode
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
7
|
|
|
8
8
|
|
|
@@ -24,8 +24,8 @@ class Interpreter:
|
|
|
24
24
|
self.log = []
|
|
25
25
|
|
|
26
26
|
def run(self, node: EngineNode) -> float:
|
|
27
|
-
if isinstance(node,
|
|
28
|
-
return node
|
|
27
|
+
if not isinstance(node, FunctionNode):
|
|
28
|
+
return node
|
|
29
29
|
func = node.func
|
|
30
30
|
args = node.args
|
|
31
31
|
match func:
|
sonolus/backend/node.py
CHANGED
|
@@ -1,40 +1,18 @@
|
|
|
1
1
|
import textwrap
|
|
2
|
-
from
|
|
2
|
+
from typing import NamedTuple
|
|
3
3
|
|
|
4
4
|
from sonolus.backend.ops import Op
|
|
5
5
|
|
|
6
|
-
type EngineNode =
|
|
6
|
+
type EngineNode = int | float | FunctionNode
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
class ConstantNode:
|
|
11
|
-
value: float
|
|
12
|
-
_hash: int = field(init=False, repr=False)
|
|
13
|
-
|
|
14
|
-
def __post_init__(self):
|
|
15
|
-
self._hash = hash(self.value)
|
|
16
|
-
|
|
17
|
-
def __hash__(self):
|
|
18
|
-
return hash(self.value)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(slots=True)
|
|
22
|
-
class FunctionNode:
|
|
9
|
+
class FunctionNode(NamedTuple):
|
|
23
10
|
func: Op
|
|
24
|
-
args:
|
|
25
|
-
_hash: int = field(init=False, repr=False)
|
|
26
|
-
|
|
27
|
-
def __post_init__(self):
|
|
28
|
-
self._hash = hash((self.func, tuple(self.args)))
|
|
29
|
-
|
|
30
|
-
def __hash__(self):
|
|
31
|
-
return self._hash
|
|
11
|
+
args: tuple[EngineNode, ...]
|
|
32
12
|
|
|
33
13
|
|
|
34
14
|
def format_engine_node(node: EngineNode) -> str:
|
|
35
|
-
if isinstance(node,
|
|
36
|
-
return str(node.value)
|
|
37
|
-
elif isinstance(node, FunctionNode):
|
|
15
|
+
if isinstance(node, FunctionNode):
|
|
38
16
|
match len(node.args):
|
|
39
17
|
case 0:
|
|
40
18
|
return f"{node.func.name}()"
|
|
@@ -45,4 +23,4 @@ def format_engine_node(node: EngineNode) -> str:
|
|
|
45
23
|
textwrap.indent('\n'.join(format_engine_node(arg) for arg in node.args), ' ')
|
|
46
24
|
}\n)"
|
|
47
25
|
else:
|
|
48
|
-
|
|
26
|
+
return str(node)
|
sonolus/build/compile.py
CHANGED
|
@@ -32,9 +32,11 @@ class CompileCache:
|
|
|
32
32
|
self._cache = {}
|
|
33
33
|
self._lock = Lock()
|
|
34
34
|
self._event = Event()
|
|
35
|
+
self._accessed_hashes = set()
|
|
35
36
|
|
|
36
37
|
def get(self, entry_hash: int) -> EngineNode | None:
|
|
37
38
|
with self._lock:
|
|
39
|
+
self._accessed_hashes.add(entry_hash)
|
|
38
40
|
if entry_hash not in self._cache:
|
|
39
41
|
self._cache[entry_hash] = None # Mark as being compiled
|
|
40
42
|
return None
|
|
@@ -47,10 +49,21 @@ class CompileCache:
|
|
|
47
49
|
|
|
48
50
|
def set(self, entry_hash: int, node: EngineNode) -> None:
|
|
49
51
|
with self._lock:
|
|
52
|
+
self._accessed_hashes.add(entry_hash)
|
|
50
53
|
self._cache[entry_hash] = node
|
|
51
54
|
self._event.set()
|
|
52
55
|
self._event.clear()
|
|
53
56
|
|
|
57
|
+
def reset_accessed(self) -> None:
|
|
58
|
+
with self._lock:
|
|
59
|
+
self._accessed_hashes.clear()
|
|
60
|
+
|
|
61
|
+
def prune_unaccessed(self) -> None:
|
|
62
|
+
with self._lock:
|
|
63
|
+
unaccessed_hashes = set(self._cache.keys()) - self._accessed_hashes
|
|
64
|
+
for h in unaccessed_hashes:
|
|
65
|
+
del self._cache[h]
|
|
66
|
+
|
|
54
67
|
|
|
55
68
|
def compile_mode(
|
|
56
69
|
mode: Mode,
|
|
@@ -67,7 +80,7 @@ def compile_mode(
|
|
|
67
80
|
|
|
68
81
|
mode_state = ModeContextState(
|
|
69
82
|
mode,
|
|
70
|
-
|
|
83
|
+
archetypes,
|
|
71
84
|
)
|
|
72
85
|
nodes = OutputNodeGenerator()
|
|
73
86
|
results = {}
|
|
@@ -134,9 +147,10 @@ def compile_mode(
|
|
|
134
147
|
archetype_data["exports"] = [*archetype._exported_keys_]
|
|
135
148
|
|
|
136
149
|
callback_items = [
|
|
137
|
-
(cb_name, cb_info,
|
|
150
|
+
(cb_name, cb_info, archetype._callbacks_[cb_name])
|
|
138
151
|
for cb_name, cb_info in archetype._supported_callbacks_.items()
|
|
139
|
-
if
|
|
152
|
+
if cb_name in archetype._callbacks_
|
|
153
|
+
and archetype._callbacks_[cb_name] not in archetype._default_callbacks_
|
|
140
154
|
]
|
|
141
155
|
|
|
142
156
|
if thread_pool is not None:
|
sonolus/build/dev_server.py
CHANGED
|
@@ -119,6 +119,7 @@ class RebuildCommand:
|
|
|
119
119
|
print("Rebuilding...")
|
|
120
120
|
try:
|
|
121
121
|
start_time = perf_counter()
|
|
122
|
+
server_state.cache.reset_accessed()
|
|
122
123
|
server_state.project_state = ProjectContextState.from_build_config(server_state.config)
|
|
123
124
|
server_state.project = project_module.project
|
|
124
125
|
build_collection(
|
|
@@ -128,6 +129,7 @@ class RebuildCommand:
|
|
|
128
129
|
cache=server_state.cache,
|
|
129
130
|
project_state=server_state.project_state,
|
|
130
131
|
)
|
|
132
|
+
server_state.cache.prune_unaccessed()
|
|
131
133
|
end_time = perf_counter()
|
|
132
134
|
print(f"Rebuild completed in {end_time - start_time:.2f} seconds")
|
|
133
135
|
except CompilationError:
|
sonolus/build/node.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from threading import Lock
|
|
2
2
|
from typing import TypedDict
|
|
3
3
|
|
|
4
|
-
from sonolus.backend.node import
|
|
4
|
+
from sonolus.backend.node import EngineNode, FunctionNode
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ValueOutputNode(TypedDict):
|
|
@@ -32,19 +32,17 @@ class OutputNodeGenerator:
|
|
|
32
32
|
return self.indexes[node]
|
|
33
33
|
|
|
34
34
|
match node:
|
|
35
|
-
case ConstantNode(value):
|
|
36
|
-
index = len(self.nodes)
|
|
37
|
-
self.nodes.append({"value": value})
|
|
38
|
-
self.indexes[node] = index
|
|
39
|
-
return index
|
|
40
35
|
case FunctionNode(func, args):
|
|
41
36
|
arg_indexes = [self._add(arg) for arg in args]
|
|
42
37
|
index = len(self.nodes)
|
|
43
38
|
self.nodes.append({"func": func.value, "args": arg_indexes})
|
|
44
39
|
self.indexes[node] = index
|
|
45
40
|
return index
|
|
46
|
-
case
|
|
47
|
-
|
|
41
|
+
case constant:
|
|
42
|
+
index = len(self.nodes)
|
|
43
|
+
self.nodes.append({"value": constant})
|
|
44
|
+
self.indexes[node] = index
|
|
45
|
+
return index
|
|
48
46
|
|
|
49
47
|
def get(self):
|
|
50
48
|
return self.nodes
|
sonolus/script/archetype.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
4
|
from abc import abstractmethod
|
|
5
|
-
from collections.abc import Callable
|
|
5
|
+
from collections.abc import Callable, Sequence
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from enum import Enum, StrEnum
|
|
8
8
|
from types import FunctionType
|
|
@@ -11,7 +11,9 @@ from typing import Annotated, Any, ClassVar, Self, TypedDict, get_origin
|
|
|
11
11
|
from sonolus.backend.ir import IRConst, IRExpr, IRInstr, IRPureInstr, IRStmt
|
|
12
12
|
from sonolus.backend.mode import Mode
|
|
13
13
|
from sonolus.backend.ops import Op
|
|
14
|
+
from sonolus.backend.visitor import compile_and_call
|
|
14
15
|
from sonolus.script.bucket import Bucket, Judgment
|
|
16
|
+
from sonolus.script.debug import runtime_checks_enabled, static_error
|
|
15
17
|
from sonolus.script.internal.callbacks import PLAY_CALLBACKS, PREVIEW_CALLBACKS, WATCH_ARCHETYPE_CALLBACKS, CallbackInfo
|
|
16
18
|
from sonolus.script.internal.context import ctx
|
|
17
19
|
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
@@ -207,7 +209,7 @@ class _IsScoredDescriptor(SonolusDescriptor):
|
|
|
207
209
|
if instance is None:
|
|
208
210
|
return self.value
|
|
209
211
|
elif ctx():
|
|
210
|
-
return ctx().mode_state.is_scored_by_archetype_id
|
|
212
|
+
return ctx().mode_state.is_scored_by_archetype_id.get_unchecked(instance.id)
|
|
211
213
|
else:
|
|
212
214
|
return self.value
|
|
213
215
|
|
|
@@ -221,7 +223,7 @@ class _IdDescriptor(SonolusDescriptor):
|
|
|
221
223
|
raise RuntimeError("Archetype id is only available during compilation")
|
|
222
224
|
if instance is None:
|
|
223
225
|
result = ctx().mode_state.archetypes.get(owner)
|
|
224
|
-
if result is None:
|
|
226
|
+
if result is None or owner in ctx().mode_state.compile_time_only_archetypes:
|
|
225
227
|
raise RuntimeError("Archetype is not registered")
|
|
226
228
|
return result
|
|
227
229
|
else:
|
|
@@ -237,7 +239,7 @@ class _KeyDescriptor(SonolusDescriptor):
|
|
|
237
239
|
|
|
238
240
|
def __get__(self, instance, owner):
|
|
239
241
|
if instance is not None and ctx():
|
|
240
|
-
return ctx().mode_state.keys_by_archetype_id
|
|
242
|
+
return ctx().mode_state.keys_by_archetype_id.get_unchecked(instance.id)
|
|
241
243
|
else:
|
|
242
244
|
return self.value
|
|
243
245
|
|
|
@@ -427,7 +429,7 @@ class _BaseArchetype:
|
|
|
427
429
|
|
|
428
430
|
_imported_keys_: ClassVar[dict[str, int]]
|
|
429
431
|
_exported_keys_: ClassVar[dict[str, int]]
|
|
430
|
-
_callbacks_: ClassVar[
|
|
432
|
+
_callbacks_: ClassVar[dict[str, Callable]]
|
|
431
433
|
_data_constructor_signature_: ClassVar[inspect.Signature]
|
|
432
434
|
_spawn_signature_: ClassVar[inspect.Signature]
|
|
433
435
|
|
|
@@ -491,17 +493,67 @@ class _BaseArchetype:
|
|
|
491
493
|
|
|
492
494
|
@classmethod
|
|
493
495
|
@meta_fn
|
|
494
|
-
def
|
|
496
|
+
def _compile_time_id(cls):
|
|
497
|
+
if not ctx():
|
|
498
|
+
raise RuntimeError("Archetype id is only available during compilation")
|
|
499
|
+
result = ctx().mode_state.archetypes.get(cls)
|
|
500
|
+
if result is None:
|
|
501
|
+
raise RuntimeError("Archetype is not registered")
|
|
502
|
+
return result
|
|
503
|
+
|
|
504
|
+
@classmethod
|
|
505
|
+
@meta_fn
|
|
506
|
+
def at(cls, index: int, check: bool = True) -> Self:
|
|
507
|
+
"""Access the entity of this archetype at the given index.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
index: The index of the entity to reference.
|
|
511
|
+
check: If true, raises an error if the entity at the index is not of this archetype or of a subclass of
|
|
512
|
+
this archetype. If false, no validation is performed.
|
|
513
|
+
|
|
514
|
+
Returns:
|
|
515
|
+
The entity at the given index.
|
|
516
|
+
"""
|
|
517
|
+
if not ctx():
|
|
518
|
+
raise RuntimeError("Archetype.at is only available during compilation")
|
|
519
|
+
compile_and_call(cls._check_is_at, index, check=check)
|
|
495
520
|
result = cls._new()
|
|
496
521
|
result._data_ = _ArchetypeReferenceData(index=Num._accept_(index))
|
|
497
522
|
return result
|
|
498
523
|
|
|
499
524
|
@classmethod
|
|
525
|
+
def is_at(cls, index: int, strict: bool = False) -> bool:
|
|
526
|
+
"""Return whether the entity at the given index is of this archetype.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
index: The index of the entity to check.
|
|
530
|
+
strict: If true, only returns true if the entity is exactly of this archetype. If false, also returns true
|
|
531
|
+
if the entity is of a subclass of this archetype.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
Whether the entity at the given index is of this archetype.
|
|
535
|
+
"""
|
|
536
|
+
if strict:
|
|
537
|
+
return index >= 0 and cls._compile_time_id() == entity_info_at(index).archetype_id
|
|
538
|
+
else:
|
|
539
|
+
mro_ids = cls._get_mro_id_array(entity_info_at(index).archetype_id)
|
|
540
|
+
return index >= 0 and cls._compile_time_id() in mro_ids
|
|
541
|
+
|
|
542
|
+
@classmethod
|
|
543
|
+
def _check_is_at(cls, index: int, check: bool):
|
|
544
|
+
if not check or not runtime_checks_enabled():
|
|
545
|
+
return
|
|
546
|
+
assert index >= 0, "Entity index must be non-negative"
|
|
547
|
+
assert cls._compile_time_id() in cls._get_mro_id_array(entity_info_at(index).archetype_id), (
|
|
548
|
+
"Entity at index is not of the expected archetype"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
@staticmethod
|
|
500
552
|
@meta_fn
|
|
501
|
-
def
|
|
553
|
+
def _get_mro_id_array(archetype_id: int) -> Sequence[int]:
|
|
502
554
|
if not ctx():
|
|
503
|
-
raise RuntimeError("
|
|
504
|
-
return
|
|
555
|
+
raise RuntimeError("Archetype._get_mro_id_array is only available during compilation")
|
|
556
|
+
return ctx().get_archetype_mro_id_array(archetype_id)
|
|
505
557
|
|
|
506
558
|
@classmethod
|
|
507
559
|
@meta_fn
|
|
@@ -562,13 +614,16 @@ class _BaseArchetype:
|
|
|
562
614
|
return
|
|
563
615
|
if cls.name is None or cls.name in {getattr(mro_entry, "name", None) for mro_entry in cls.mro()[1:]}:
|
|
564
616
|
cls.name = cls.__name__.removeprefix(cls._removable_prefix)
|
|
565
|
-
cls._callbacks_ =
|
|
617
|
+
cls._callbacks_ = {}
|
|
566
618
|
for name in cls._supported_callbacks_:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
619
|
+
for mro_entry in cls.mro():
|
|
620
|
+
if name in mro_entry.__dict__:
|
|
621
|
+
cb = mro_entry.__dict__[name]
|
|
622
|
+
if cb not in cls._default_callbacks_:
|
|
623
|
+
cls._callbacks_[name] = cb
|
|
624
|
+
break
|
|
571
625
|
cls._field_init_done = False
|
|
626
|
+
cls._is_concrete_archetype_ = True
|
|
572
627
|
cls.id = _IdDescriptor()
|
|
573
628
|
cls._key_ = cls.key
|
|
574
629
|
cls.key = _KeyDescriptor(cls.key)
|
|
@@ -584,6 +639,14 @@ class _BaseArchetype:
|
|
|
584
639
|
for mro_entry in cls.mro()[1:]:
|
|
585
640
|
if hasattr(mro_entry, "_field_init_done"):
|
|
586
641
|
mro_entry._init_fields()
|
|
642
|
+
if sum(issubclass(base, _BaseArchetype) for base in cls.__bases__) > 1:
|
|
643
|
+
raise TypeError("Multiple inheritance of Archetypes is not supported")
|
|
644
|
+
mro_from_archetype_parents = set(
|
|
645
|
+
type("Dummy", tuple(base for base in cls.__bases__ if issubclass(base, _BaseArchetype)), {}).mro()
|
|
646
|
+
)
|
|
647
|
+
# Archetype parents would have already initialized relevant fields, so only consider the current class
|
|
648
|
+
# and mixins that were not already included via an archetype parent
|
|
649
|
+
mro_excluding_archetype_parents = [entry for entry in cls.mro() if entry not in mro_from_archetype_parents]
|
|
587
650
|
field_specifiers = get_field_specifiers(
|
|
588
651
|
cls,
|
|
589
652
|
skip={
|
|
@@ -599,7 +662,9 @@ class _BaseArchetype:
|
|
|
599
662
|
"_default_callbacks_",
|
|
600
663
|
"_callbacks_",
|
|
601
664
|
"_field_init_done",
|
|
665
|
+
"_is_concrete_archetype_",
|
|
602
666
|
},
|
|
667
|
+
included_classes=mro_excluding_archetype_parents,
|
|
603
668
|
).items()
|
|
604
669
|
if not hasattr(cls, "_imported_fields_"):
|
|
605
670
|
cls._imported_fields_ = {}
|
|
@@ -652,6 +717,13 @@ class _BaseArchetype:
|
|
|
652
717
|
f"Missing field annotation for '{name}', "
|
|
653
718
|
f"expected exactly one of imported, exported, entity_memory, or shared_memory"
|
|
654
719
|
)
|
|
720
|
+
if (
|
|
721
|
+
name in cls._imported_fields_
|
|
722
|
+
or name in cls._exported_fields_
|
|
723
|
+
or name in cls._memory_fields_
|
|
724
|
+
or name in cls._shared_memory_fields_
|
|
725
|
+
):
|
|
726
|
+
raise ValueError(f"Field '{name}' is already defined in a superclass")
|
|
655
727
|
field_type = validate_concrete_type(value.__args__[0])
|
|
656
728
|
match field_info.storage:
|
|
657
729
|
case _StorageType.IMPORTED:
|
|
@@ -770,6 +842,18 @@ class _BaseArchetype:
|
|
|
770
842
|
new_cls = type(name, (cls,), cls_dict)
|
|
771
843
|
return new_cls
|
|
772
844
|
|
|
845
|
+
@meta_fn
|
|
846
|
+
def _delegate(self, name: str, default: int | float | None = None):
|
|
847
|
+
name = validate_value(name)._as_py_()
|
|
848
|
+
if hasattr(super(), name):
|
|
849
|
+
fn = getattr(super(), name)
|
|
850
|
+
if ctx():
|
|
851
|
+
return compile_and_call(fn)
|
|
852
|
+
else:
|
|
853
|
+
return fn()
|
|
854
|
+
else:
|
|
855
|
+
return default
|
|
856
|
+
|
|
773
857
|
|
|
774
858
|
class PlayArchetype(_BaseArchetype):
|
|
775
859
|
"""Base class for play mode archetypes.
|
|
@@ -807,26 +891,28 @@ class PlayArchetype(_BaseArchetype):
|
|
|
807
891
|
|
|
808
892
|
Runs first when the level is loaded.
|
|
809
893
|
"""
|
|
894
|
+
self._delegate("preprocess")
|
|
810
895
|
|
|
811
896
|
def spawn_order(self) -> float:
|
|
812
897
|
"""Return the spawn order of the entity.
|
|
813
898
|
|
|
814
899
|
Runs when the level is loaded after [`preprocess`][sonolus.script.archetype.PlayArchetype.preprocess].
|
|
815
900
|
"""
|
|
816
|
-
return 0.0
|
|
901
|
+
return self._delegate("spawn_order", default=0.0)
|
|
817
902
|
|
|
818
903
|
def should_spawn(self) -> bool:
|
|
819
904
|
"""Return whether the entity should be spawned.
|
|
820
905
|
|
|
821
906
|
Runs each frame while the entity is the first entity in the spawn queue.
|
|
822
907
|
"""
|
|
823
|
-
return True
|
|
908
|
+
return self._delegate("should_spawn", default=True)
|
|
824
909
|
|
|
825
910
|
def initialize(self):
|
|
826
911
|
"""Initialize this entity.
|
|
827
912
|
|
|
828
913
|
Runs when this entity is spawned.
|
|
829
914
|
"""
|
|
915
|
+
self._delegate("initialize")
|
|
830
916
|
|
|
831
917
|
def update_sequential(self):
|
|
832
918
|
"""Perform non-parallel actions for this frame.
|
|
@@ -838,6 +924,7 @@ class PlayArchetype(_BaseArchetype):
|
|
|
838
924
|
[`update_parallel`][sonolus.script.archetype.PlayArchetype.update_parallel]
|
|
839
925
|
for better performance.
|
|
840
926
|
"""
|
|
927
|
+
self._delegate("update_sequential")
|
|
841
928
|
|
|
842
929
|
def update_parallel(self):
|
|
843
930
|
"""Perform parallel actions for this frame.
|
|
@@ -846,18 +933,21 @@ class PlayArchetype(_BaseArchetype):
|
|
|
846
933
|
|
|
847
934
|
This is where most gameplay logic should be placed.
|
|
848
935
|
"""
|
|
936
|
+
self._delegate("update_parallel")
|
|
849
937
|
|
|
850
938
|
def touch(self):
|
|
851
939
|
"""Handle user input.
|
|
852
940
|
|
|
853
941
|
Runs after [`update_sequential`][sonolus.script.archetype.PlayArchetype.update_sequential] each frame.
|
|
854
942
|
"""
|
|
943
|
+
self._delegate("touch")
|
|
855
944
|
|
|
856
945
|
def terminate(self):
|
|
857
946
|
"""Finalize before despawning.
|
|
858
947
|
|
|
859
948
|
Runs when the entity is despawned.
|
|
860
949
|
"""
|
|
950
|
+
self._delegate("terminate")
|
|
861
951
|
|
|
862
952
|
@property
|
|
863
953
|
@meta_fn
|
|
@@ -965,20 +1055,22 @@ class WatchArchetype(_BaseArchetype):
|
|
|
965
1055
|
|
|
966
1056
|
Runs first when the level is loaded.
|
|
967
1057
|
"""
|
|
1058
|
+
self._delegate("preprocess")
|
|
968
1059
|
|
|
969
1060
|
def spawn_time(self) -> float:
|
|
970
1061
|
"""Return the spawn time of the entity."""
|
|
971
|
-
return 0.0
|
|
1062
|
+
return self._delegate("spawn_time", default=0.0)
|
|
972
1063
|
|
|
973
1064
|
def despawn_time(self) -> float:
|
|
974
1065
|
"""Return the despawn time of the entity."""
|
|
975
|
-
return 0.0
|
|
1066
|
+
return self._delegate("despawn_time", default=0.0)
|
|
976
1067
|
|
|
977
1068
|
def initialize(self):
|
|
978
1069
|
"""Initialize this entity.
|
|
979
1070
|
|
|
980
1071
|
Runs when this entity is spawned.
|
|
981
1072
|
"""
|
|
1073
|
+
self._delegate("initialize")
|
|
982
1074
|
|
|
983
1075
|
def update_sequential(self):
|
|
984
1076
|
"""Perform non-parallel actions for this frame.
|
|
@@ -989,6 +1081,7 @@ class WatchArchetype(_BaseArchetype):
|
|
|
989
1081
|
Other logic should typically be placed in
|
|
990
1082
|
[`update_parallel`][sonolus.script.archetype.PlayArchetype.update_parallel] for better performance.
|
|
991
1083
|
"""
|
|
1084
|
+
self._delegate("update_sequential")
|
|
992
1085
|
|
|
993
1086
|
def update_parallel(self):
|
|
994
1087
|
"""Parallel update callback.
|
|
@@ -997,12 +1090,14 @@ class WatchArchetype(_BaseArchetype):
|
|
|
997
1090
|
|
|
998
1091
|
This is where most gameplay logic should be placed.
|
|
999
1092
|
"""
|
|
1093
|
+
self._delegate("update_parallel")
|
|
1000
1094
|
|
|
1001
1095
|
def terminate(self):
|
|
1002
1096
|
"""Finalize before despawning.
|
|
1003
1097
|
|
|
1004
1098
|
Runs when the entity is despawned.
|
|
1005
1099
|
"""
|
|
1100
|
+
self._delegate("terminate")
|
|
1006
1101
|
|
|
1007
1102
|
@property
|
|
1008
1103
|
@meta_fn
|
|
@@ -1073,12 +1168,14 @@ class PreviewArchetype(_BaseArchetype):
|
|
|
1073
1168
|
|
|
1074
1169
|
Runs first when the level is loaded.
|
|
1075
1170
|
"""
|
|
1171
|
+
self._delegate("preprocess")
|
|
1076
1172
|
|
|
1077
1173
|
def render(self):
|
|
1078
1174
|
"""Render the entity.
|
|
1079
1175
|
|
|
1080
1176
|
Runs after `preprocess`.
|
|
1081
1177
|
"""
|
|
1178
|
+
self._delegate("render")
|
|
1082
1179
|
|
|
1083
1180
|
@property
|
|
1084
1181
|
@meta_fn
|
|
@@ -1210,6 +1307,8 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1210
1307
|
|
|
1211
1308
|
Usage:
|
|
1212
1309
|
```python
|
|
1310
|
+
ref = EntityRef[MyArchetype](index=123)
|
|
1311
|
+
|
|
1213
1312
|
class MyArchetype(PlayArchetype):
|
|
1214
1313
|
ref_1: EntityRef[OtherArchetype] = imported()
|
|
1215
1314
|
ref_2: EntityRef[Any] = imported()
|
|
@@ -1239,11 +1338,25 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1239
1338
|
return super().__hash__()
|
|
1240
1339
|
|
|
1241
1340
|
@meta_fn
|
|
1242
|
-
def
|
|
1243
|
-
|
|
1341
|
+
def __bool__(self):
|
|
1342
|
+
if ctx():
|
|
1343
|
+
static_error("EntityRef cannot be used in a boolean context. Check index directly instead.")
|
|
1344
|
+
return True
|
|
1345
|
+
|
|
1346
|
+
@meta_fn
|
|
1347
|
+
def get(self, *, check: bool = True) -> A:
|
|
1348
|
+
"""Get the entity this reference points to.
|
|
1349
|
+
|
|
1350
|
+
Args:
|
|
1351
|
+
check: If true, raises an error if the referenced entity is not of this archetype or of a subclass of
|
|
1352
|
+
this archetype. If false, no validation is performed.
|
|
1353
|
+
|
|
1354
|
+
Returns:
|
|
1355
|
+
The entity this reference points to.
|
|
1356
|
+
"""
|
|
1244
1357
|
if ref := getattr(self, "_ref_", None):
|
|
1245
1358
|
return ref
|
|
1246
|
-
return self.archetype().at(self.index)
|
|
1359
|
+
return self.archetype().at(self.index, check=check)
|
|
1247
1360
|
|
|
1248
1361
|
@meta_fn
|
|
1249
1362
|
def get_as(self, archetype: type[_BaseArchetype]) -> _BaseArchetype:
|
|
@@ -1252,9 +1365,17 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1252
1365
|
raise TypeError("Using get_as in level data is not supported.")
|
|
1253
1366
|
return self.with_archetype(archetype).get()
|
|
1254
1367
|
|
|
1255
|
-
def archetype_matches(self) -> bool:
|
|
1256
|
-
"""Check if entity at the index is
|
|
1257
|
-
|
|
1368
|
+
def archetype_matches(self, strict: bool = False) -> bool:
|
|
1369
|
+
"""Check if entity at the index is of this archetype.
|
|
1370
|
+
|
|
1371
|
+
Args:
|
|
1372
|
+
strict: If true, only returns true if the entity is exactly of this archetype. If false, also returns true
|
|
1373
|
+
if the entity is of a subclass of this archetype.
|
|
1374
|
+
|
|
1375
|
+
Returns:
|
|
1376
|
+
Whether the entity at the given index is of this archetype.
|
|
1377
|
+
"""
|
|
1378
|
+
return self.archetype().is_at(self.index, strict=strict)
|
|
1258
1379
|
|
|
1259
1380
|
def _to_list_(self, level_refs: dict[Any, str] | None = None) -> list[DataValue | str]:
|
|
1260
1381
|
ref = getattr(self, "_ref_", None)
|
sonolus/script/containers.py
CHANGED
|
@@ -468,11 +468,8 @@ class FrozenNumSet[Size](Record):
|
|
|
468
468
|
return len(self._values)
|
|
469
469
|
|
|
470
470
|
def __contains__(self, value: Num) -> bool:
|
|
471
|
-
if len(self) <
|
|
472
|
-
|
|
473
|
-
if self._values.get_unchecked(i) == value:
|
|
474
|
-
return True
|
|
475
|
-
return False
|
|
471
|
+
if len(self) < 8:
|
|
472
|
+
return value in self._as_tuple()
|
|
476
473
|
else:
|
|
477
474
|
left = 0
|
|
478
475
|
right = len(self) - 1
|
|
@@ -490,6 +487,10 @@ class FrozenNumSet[Size](Record):
|
|
|
490
487
|
def __iter__(self) -> SonolusIterator[Num]:
|
|
491
488
|
return self._values.__iter__()
|
|
492
489
|
|
|
490
|
+
@meta_fn
|
|
491
|
+
def _as_tuple(self) -> tuple[Num, ...]:
|
|
492
|
+
return tuple(self._values.get_unchecked(i) for i in range(Num._accept_(len(self))._as_py_()))
|
|
493
|
+
|
|
493
494
|
|
|
494
495
|
class _ArrayMapEntry[K, V](Record):
|
|
495
496
|
key: K
|
sonolus/script/debug.py
CHANGED
|
@@ -102,6 +102,14 @@ def notify(message: str):
|
|
|
102
102
|
print(f"[NOTIFY] {message}")
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
@meta_fn
|
|
106
|
+
def runtime_checks_enabled() -> bool:
|
|
107
|
+
if ctx():
|
|
108
|
+
return ctx().project_state.runtime_checks != RuntimeChecks.NONE
|
|
109
|
+
else:
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
|
|
105
113
|
@meta_fn
|
|
106
114
|
def require(value: int | float | bool, message: str | None = None):
|
|
107
115
|
"""Require a condition to be true, or raise an error.
|
|
@@ -212,7 +220,7 @@ def visualize_cfg(
|
|
|
212
220
|
project_state = ProjectContextState()
|
|
213
221
|
mode_state = ModeContextState(
|
|
214
222
|
mode,
|
|
215
|
-
|
|
223
|
+
archetypes,
|
|
216
224
|
)
|
|
217
225
|
|
|
218
226
|
cfg = callback_to_cfg(project_state, mode_state, fn, callback, archetype=archetype) # type: ignore
|
|
@@ -94,25 +94,36 @@ class ProjectContextState:
|
|
|
94
94
|
|
|
95
95
|
class ModeContextState:
|
|
96
96
|
archetypes: dict[type, int]
|
|
97
|
+
compile_time_only_archetypes: set[type]
|
|
97
98
|
archetypes_by_name: dict[str, type]
|
|
98
99
|
keys_by_archetype_id: Sequence[int]
|
|
99
100
|
is_scored_by_archetype_id: Sequence[bool]
|
|
101
|
+
archetype_mro_id_array_rom_indexes: Sequence[int] | None = None
|
|
100
102
|
environment_mappings: dict[_GlobalInfo, int]
|
|
101
103
|
environment_offsets: dict[Block, int]
|
|
102
104
|
mode: Mode
|
|
103
105
|
lock: Lock
|
|
104
106
|
|
|
105
|
-
def __init__(self, mode: Mode, archetypes:
|
|
107
|
+
def __init__(self, mode: Mode, archetypes: list[type] | None = None):
|
|
106
108
|
from sonolus.script.array import Array
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
archetypes = [*archetypes] if archetypes is not None else []
|
|
111
|
+
seen_archetypes = {*archetypes}
|
|
112
|
+
compile_time_only_archetypes = set()
|
|
113
|
+
for type_ in [*archetypes]:
|
|
114
|
+
for entry in type_.mro():
|
|
115
|
+
if getattr(entry, "_is_concrete_archetype_", False) and entry not in seen_archetypes:
|
|
116
|
+
archetypes.append(entry)
|
|
117
|
+
seen_archetypes.add(entry)
|
|
118
|
+
compile_time_only_archetypes.add(entry)
|
|
119
|
+
self.archetypes = {type_: idx for idx, type_ in enumerate(archetypes)}
|
|
120
|
+
self.compile_time_only_archetypes = compile_time_only_archetypes
|
|
109
121
|
self.archetypes_by_name = {type_.name: type_ for type_, _ in self.archetypes.items()} # type: ignore
|
|
110
|
-
ordered_archetypes = sorted(self.archetypes, key=lambda a: self.archetypes[a])
|
|
111
122
|
self.keys_by_archetype_id = (
|
|
112
|
-
Array(*((getattr(a, "_key_", -1)) for a in
|
|
123
|
+
Array(*((getattr(a, "_key_", -1)) for a in archetypes)) if archetypes else Array[int, Literal[0]]()
|
|
113
124
|
)
|
|
114
125
|
self.is_scored_by_archetype_id = (
|
|
115
|
-
Array(*((getattr(a, "_is_scored_", False)) for a in
|
|
126
|
+
Array(*((getattr(a, "_is_scored_", False)) for a in archetypes))
|
|
116
127
|
if archetypes
|
|
117
128
|
else Array[bool, Literal[0]]()
|
|
118
129
|
)
|
|
@@ -121,6 +132,27 @@ class ModeContextState:
|
|
|
121
132
|
self.mode = mode
|
|
122
133
|
self.lock = Lock()
|
|
123
134
|
|
|
135
|
+
def _init_archetype_mro_info(self, rom: ReadOnlyMemory):
|
|
136
|
+
from sonolus.script.array import Array
|
|
137
|
+
from sonolus.script.num import Num
|
|
138
|
+
|
|
139
|
+
with self.lock:
|
|
140
|
+
if self.archetype_mro_id_array_rom_indexes is not None:
|
|
141
|
+
return
|
|
142
|
+
archetype_mro_id_values = []
|
|
143
|
+
archetype_mro_id_offsets = []
|
|
144
|
+
for type_ in self.archetypes:
|
|
145
|
+
mro_ids = [self.archetypes[entry] for entry in type_.mro() if entry in self.archetypes]
|
|
146
|
+
archetype_mro_id_offsets.append(len(archetype_mro_id_values))
|
|
147
|
+
archetype_mro_id_values.append(len(mro_ids))
|
|
148
|
+
archetype_mro_id_values.extend(mro_ids)
|
|
149
|
+
archetype_mro_id_array_place = rom[tuple(archetype_mro_id_values)]
|
|
150
|
+
|
|
151
|
+
archetype_mro_id_rom_indexes = Array[int, len(archetype_mro_id_offsets)]._with_value(
|
|
152
|
+
[Num._accept_(offset + archetype_mro_id_array_place.index) for offset in archetype_mro_id_offsets]
|
|
153
|
+
)
|
|
154
|
+
self.archetype_mro_id_array_rom_indexes = archetype_mro_id_rom_indexes
|
|
155
|
+
|
|
124
156
|
|
|
125
157
|
class CallbackContextState:
|
|
126
158
|
callback: str
|
|
@@ -371,6 +403,15 @@ class Context:
|
|
|
371
403
|
self.mode_state.archetypes[type_] = len(self.mode_state.archetypes)
|
|
372
404
|
return self.mode_state.archetypes[type_]
|
|
373
405
|
|
|
406
|
+
def get_archetype_mro_id_array(self, archetype_id: int) -> Sequence[int]:
|
|
407
|
+
from sonolus.script.containers import ArrayPointer
|
|
408
|
+
from sonolus.script.num import Num
|
|
409
|
+
from sonolus.script.pointer import _deref
|
|
410
|
+
|
|
411
|
+
self.mode_state._init_archetype_mro_info(self.rom)
|
|
412
|
+
rom_index = self.mode_state.archetype_mro_id_array_rom_indexes[archetype_id]
|
|
413
|
+
return ArrayPointer[int](_deref(self.blocks.EngineRom, rom_index, Num), self.blocks.EngineRom, rom_index + 1)
|
|
414
|
+
|
|
374
415
|
|
|
375
416
|
def ctx() -> Context | Any: # Using Any to silence type checker warnings if it's None
|
|
376
417
|
return context_var.get()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import inspect
|
|
2
|
+
from collections.abc import Sequence
|
|
2
3
|
from typing import Annotated
|
|
3
4
|
|
|
4
5
|
_missing = object()
|
|
@@ -11,9 +12,15 @@ def get_field_specifiers(
|
|
|
11
12
|
globals=None, # noqa: A002
|
|
12
13
|
locals=None, # noqa: A002
|
|
13
14
|
eval_str=True,
|
|
15
|
+
included_classes: Sequence[type] | None = None,
|
|
14
16
|
):
|
|
15
17
|
"""Like inspect.get_annotations, but also turns class attributes into Annotated."""
|
|
16
|
-
|
|
18
|
+
if included_classes is not None:
|
|
19
|
+
results = {}
|
|
20
|
+
for entry in reversed(included_classes):
|
|
21
|
+
results.update(inspect.get_annotations(entry, eval_str=eval_str))
|
|
22
|
+
else:
|
|
23
|
+
results = inspect.get_annotations(cls, globals=globals, locals=locals, eval_str=eval_str)
|
|
17
24
|
for key, value in results.items():
|
|
18
25
|
class_value = getattr(cls, key, _missing)
|
|
19
26
|
if class_value is not _missing and key not in skip:
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sonolus.py
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Sonolus engine development in Python
|
|
5
5
|
Project-URL: Documentation, https://sonolus.py.qwewqa.xyz/
|
|
6
6
|
Project-URL: Repository, https://github.com/qwewqa/sonolus.py
|
|
7
7
|
Project-URL: Issues, https://github.com/qwewqa/sonolus.py/issues
|
|
8
8
|
Project-URL: Changelog, https://sonolus.py.qwewqa.xyz/changelog/
|
|
9
9
|
License-File: LICENSE
|
|
10
|
-
Requires-Python: >=3.12
|
|
10
|
+
Requires-Python: >=3.12.5
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
|
|
13
13
|
# Sonolus.py
|
|
@@ -3,11 +3,11 @@ sonolus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
3
3
|
sonolus/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
sonolus/backend/blocks.py,sha256=3peyb9eYBy0s53xNVJ1KmK4IgoyVkkwG-lqDQ_VZTHc,18531
|
|
5
5
|
sonolus/backend/excepthook.py,sha256=ezsTi8hPXSUhqZ7-H0rmkWcndBQcZFAShF543zzaEPM,1912
|
|
6
|
-
sonolus/backend/finalize.py,sha256=
|
|
7
|
-
sonolus/backend/interpret.py,sha256=
|
|
6
|
+
sonolus/backend/finalize.py,sha256=MRp_ATPreSmBGn8N6iwesS50rFWH9FIiQI6_Ea8RVTM,5090
|
|
7
|
+
sonolus/backend/interpret.py,sha256=dnm0SJFfI4KIE6Ca2tKrLq10XwBC9EPSqN7OOYtA8Ws,14304
|
|
8
8
|
sonolus/backend/ir.py,sha256=eyNXorOQY4zgKOvN4kO1MdJF3sU8H0Qw5RTPqbEjJHY,3854
|
|
9
9
|
sonolus/backend/mode.py,sha256=NkcPZJm8dn83LX35uP24MtQOCnfRDFZ280dHeEEfauE,613
|
|
10
|
-
sonolus/backend/node.py,sha256=
|
|
10
|
+
sonolus/backend/node.py,sha256=w5y2GwSc2E9rQvGJNaMzvPW_FYjjfHw4QDUmKs1dAnc,714
|
|
11
11
|
sonolus/backend/ops.py,sha256=5weB_vIxbkwCSJuzYZyKUk7vVXsSIEDJYRlvE-2ke8A,10572
|
|
12
12
|
sonolus/backend/place.py,sha256=7qwV732hZ4WP-9GNN8FQSEKssPJZELip1wLXTWfop7Y,4717
|
|
13
13
|
sonolus/backend/utils.py,sha256=OwD1EPh8j-hsfkLzeKNzPQojT_3kklpJou0WTJNoCbc,2337
|
|
@@ -28,19 +28,19 @@ sonolus/backend/optimize/ssa.py,sha256=raQO0furQQRPYb8iIBKfNrJlj-_5wqtI4EWNfLZ8Q
|
|
|
28
28
|
sonolus/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
sonolus/build/cli.py,sha256=uLY7JG3PTAb2a1bbjulFlgUGDzsHXyRAfkGV0lbuMgQ,10538
|
|
30
30
|
sonolus/build/collection.py,sha256=6hniAzriPWBKUeGDkXabNXpbdHiHnqiK9shs6U1OExM,12748
|
|
31
|
-
sonolus/build/compile.py,sha256=
|
|
32
|
-
sonolus/build/dev_server.py,sha256=
|
|
31
|
+
sonolus/build/compile.py,sha256=m4v4wYWnI6ebot4-fJi9a7eoF0WIo3uPOPy0_NpLgK0,8855
|
|
32
|
+
sonolus/build/dev_server.py,sha256=yHD3KGqzebdqcEBOC5JDlNptzlH8Sq4i-DNnh_zXWCA,10705
|
|
33
33
|
sonolus/build/engine.py,sha256=jMymxbBXu-ekv71uU8TF2KbFaHs3yGjyJAztd1SoRDs,14808
|
|
34
34
|
sonolus/build/level.py,sha256=KLqUAtxIuIqrzeFURJA97rdqjA5pcvYSmwNZQhElaMQ,702
|
|
35
|
-
sonolus/build/node.py,sha256=
|
|
35
|
+
sonolus/build/node.py,sha256=Dhuz_-UlRd-EJC7-AP1NuyvrjHWNo7jGssniRh4dZhI,1239
|
|
36
36
|
sonolus/build/project.py,sha256=Uuz82QtTNFdklrVJ_i7EPp8hSjyOxLU1xAeOloa6G00,8579
|
|
37
37
|
sonolus/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
sonolus/script/archetype.py,sha256=
|
|
38
|
+
sonolus/script/archetype.py,sha256=qK1tcREt56qJPh1zw_cjZGz0UUQngLGXUDA1HmwNpY8,55083
|
|
39
39
|
sonolus/script/array.py,sha256=EbrNwl_WuJ0JjjkX0s_VJNXWqvYdm_ljTbyrDEMLGUY,13348
|
|
40
40
|
sonolus/script/array_like.py,sha256=E6S4TW2muXgcyVkhUASQVt7JSYUkpvdJPgHz6YiSHNo,14708
|
|
41
41
|
sonolus/script/bucket.py,sha256=LNePLmCwgXfKLmH4Z7ZcTFKWR32eq4AnnagI7jacrsU,7782
|
|
42
|
-
sonolus/script/containers.py,sha256=
|
|
43
|
-
sonolus/script/debug.py,sha256=
|
|
42
|
+
sonolus/script/containers.py,sha256=KKySXTh_ON9hut1ScMS31ewRNnVd4t7OwpIZg2bPhno,28676
|
|
43
|
+
sonolus/script/debug.py,sha256=sWMZ-c68GggD8eUZJUPRXxxDjtCt9j8X4-_FUgTvUNE,7856
|
|
44
44
|
sonolus/script/easing.py,sha256=2FUJI_nfp990P_armCcRqHm2329O985glJAhSC6tnxs,11379
|
|
45
45
|
sonolus/script/effect.py,sha256=SfJxSNF3RlPCRXnkt62ZlWhCXw3mmmRCsoMsvTErUP0,7960
|
|
46
46
|
sonolus/script/engine.py,sha256=etI9dJsQ7V9YZICVNZg54WqpLijPxG8eTPHiV-_EiG8,10687
|
|
@@ -72,13 +72,13 @@ sonolus/script/internal/__init__.py,sha256=T6rzLoiOUaiSQtaHMZ88SNO-ijSjSSv33TKtU
|
|
|
72
72
|
sonolus/script/internal/builtin_impls.py,sha256=1fo6UuWlaLoqpVwFSrFS5BabNeRCdS2T2mjsS4BPYcY,13603
|
|
73
73
|
sonolus/script/internal/callbacks.py,sha256=vWzJG8uiJoEtsNnbeZPqOHogCwoLpz2D1MnHY2wVV8s,2801
|
|
74
74
|
sonolus/script/internal/constant.py,sha256=3ycbGkDJVUwcrCZ96vLjAoAARgsvaqDM8rJ_YCrLrvo,4289
|
|
75
|
-
sonolus/script/internal/context.py,sha256=
|
|
75
|
+
sonolus/script/internal/context.py,sha256=dhWQS7fFPMhb0FJyw3y--0HlBtwuYANdIYiv7u_96jk,22023
|
|
76
76
|
sonolus/script/internal/descriptor.py,sha256=XRFey-EjiAm_--KsNl-8N0Mi_iyQwlPh68gDp0pKf3E,392
|
|
77
77
|
sonolus/script/internal/dict_impl.py,sha256=alu_wKGSk1kZajNf64qbe7t71shEzD4N5xNIATH8Swo,1885
|
|
78
78
|
sonolus/script/internal/error.py,sha256=ZNnsvQVQAnFKzcvsm6-sste2lo-tP5pPI8sD7XlAZWc,490
|
|
79
79
|
sonolus/script/internal/generic.py,sha256=_3d5Rn_tn214-77fPE67vdbdqt1PQF8-2WB_XDu5YRg,7551
|
|
80
80
|
sonolus/script/internal/impl.py,sha256=R88cl4nLcfF0UhA9qdYRBOsl4nMx8ucgz8l7_oRY-l8,3503
|
|
81
|
-
sonolus/script/internal/introspection.py,sha256=
|
|
81
|
+
sonolus/script/internal/introspection.py,sha256=q6c83G_jmuLSoYSJj8ln0XtuwNEdndE0PkLbNWOhGmk,1421
|
|
82
82
|
sonolus/script/internal/math_impls.py,sha256=ox2pBJ6ELRO0LdLn_RZxgHHs_PCgQOHIhmDkwmLxJaU,2975
|
|
83
83
|
sonolus/script/internal/native.py,sha256=zOuRtgI3XJ_ExyR_ZkvbDABVc_JIWaKl62lFEL_bMaw,2007
|
|
84
84
|
sonolus/script/internal/random.py,sha256=6Ku5edRcDUh7rtqEEYCJz0BQavw69RALsVHS25z50pI,1695
|
|
@@ -87,8 +87,8 @@ sonolus/script/internal/simulation_context.py,sha256=LGxLTvxbqBIhoe1R-SfwGajNIDw
|
|
|
87
87
|
sonolus/script/internal/transient.py,sha256=y2AWABqF1aoaP6H4_2u4MMpNioC4OsZQCtPyNI0txqo,1634
|
|
88
88
|
sonolus/script/internal/tuple_impl.py,sha256=WaI5HSF5h03ddXiSHEwzY9ttfsPUItaf86Y5VbZypek,3754
|
|
89
89
|
sonolus/script/internal/value.py,sha256=OngrCdmY_h6mV2Zgwqhuo4eYFad0kTk6263UAxctZcY,6963
|
|
90
|
-
sonolus_py-0.
|
|
91
|
-
sonolus_py-0.
|
|
92
|
-
sonolus_py-0.
|
|
93
|
-
sonolus_py-0.
|
|
94
|
-
sonolus_py-0.
|
|
90
|
+
sonolus_py-0.12.0.dist-info/METADATA,sha256=ncmBBjgZ1BHrxEb9xrtEIa_CiF8pK-lknGve69U3_LY,556
|
|
91
|
+
sonolus_py-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
92
|
+
sonolus_py-0.12.0.dist-info/entry_points.txt,sha256=oTYspY_b7SA8TptEMTDxh4-Aj-ZVPnYC9f1lqH6s9G4,54
|
|
93
|
+
sonolus_py-0.12.0.dist-info/licenses/LICENSE,sha256=JEKpqVhQYfEc7zg3Mj462sKbKYmO1K7WmvX1qvg9IJk,1067
|
|
94
|
+
sonolus_py-0.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|