Release/v0.5.0 #8

Merged
rus07tam merged 20 commits from release/v0.5.0 into main 2025-11-24 16:28:03 +03:00
5 changed files with 63 additions and 17 deletions
Showing only changes of commit 8f9347381a - Show all commits

View file

@ -26,6 +26,15 @@ class System:
A system is a collection of entities and components that can be processed by processors. A system is a collection of entities and components that can be processed by processors.
""" """
__slots__ = (
"__processors",
"__components",
"__entitites",
"__entity_counter",
"__dead_entities",
"__is_running",
)
__processors: list[Processor] __processors: list[Processor]
__components: dict[type[Component], set[int]] __components: dict[type[Component], set[int]]
__entitites: dict[int, dict[type[Component], Component]] __entitites: dict[int, dict[type[Component], Component]]
@ -73,7 +82,9 @@ class System:
self.__processors.remove(processor) self.__processors.remove(processor)
@overload @overload
def get_components(self, c1: type[A], /) -> Iterable[tuple[int, tuple[A]]]: ... def get_components(
self, c1: type[A], /
) -> Iterable[tuple[int, tuple[A]]]: ...
@overload @overload
def get_components( def get_components(
@ -106,7 +117,10 @@ class System:
) -> Iterable[tuple[int, tuple[Component, ...]]]: ) -> Iterable[tuple[int, tuple[Component, ...]]]:
"""Returns all entities with the given components.""" """Returns all entities with the given components."""
entity_set = set.intersection( entity_set = set.intersection(
*(self.__components[component_type] for component_type in component_types) *(
self.__components[component_type]
for component_type in component_types
)
) )
for entity in entity_set: for entity in entity_set:
yield ( yield (
@ -167,7 +181,9 @@ class System:
), ),
) )
def get_component(self, component_type: type[C], /) -> Iterable[tuple[int, C]]: def get_component(
self, component_type: type[C], /
) -> Iterable[tuple[int, C]]:
"""Returns all entities with the given component.""" """Returns all entities with the given component."""
for entity in self.__components[component_type].copy(): for entity in self.__components[component_type].copy():
yield entity, cast(C, self.__entitites[entity][component_type]) yield entity, cast(C, self.__entitites[entity][component_type])
@ -198,16 +214,24 @@ class System:
self.__components[component_type].add(entity) self.__components[component_type].add(entity)
self.__entitites[entity][component_type] = component self.__entitites[entity][component_type] = component
def has_component(self, entity: int, component_type: type[Component]) -> bool: def has_component(
self, entity: int, component_type: type[Component]
) -> bool:
"""Returns True if the entity has the given component.""" """Returns True if the entity has the given component."""
return component_type in self.__entitites[entity] return component_type in self.__entitites[entity]
def has_components(self, entity: int, *component_types: type[Component]) -> bool: def has_components(
self, entity: int, *component_types: type[Component]
) -> bool:
"""Returns True if the entity has all the given components.""" """Returns True if the entity has all the given components."""
components_dict = self.__entitites[entity] components_dict = self.__entitites[entity]
return all(comp_type in components_dict for comp_type in component_types) return all(
comp_type in components_dict for comp_type in component_types
)
def remove_component(self, entity: int, component_type: type[C]) -> C | None: def remove_component(
self, entity: int, component_type: type[C]
) -> C | None:
"""Removes a component from an entity.""" """Removes a component from an entity."""
self.__components[component_type].discard(entity) self.__components[component_type].discard(entity)
if not self.__components[component_type]: if not self.__components[component_type]:
@ -239,7 +263,9 @@ class System:
def entity_exists(self, entity: int) -> bool: def entity_exists(self, entity: int) -> bool:
"""Returns True if the entity exists.""" """Returns True if the entity exists."""
return entity in self.__entitites and entity not in self.__dead_entities return (
entity in self.__entitites and entity not in self.__dead_entities
)
def start(self) -> None: def start(self) -> None:
"""Starts the system.""" """Starts the system."""

View file

@ -7,6 +7,14 @@ from .loader.loader import Loader
class Engine: class Engine:
__slots__ = (
"system",
"dispatcher",
"loader",
"__system_thread",
"__dispatcher_thread",
)
def __init__(self) -> None: def __init__(self) -> None:
self.system: Final = System() self.system: Final = System()
self.dispatcher: Final = Dispatcher() self.dispatcher: Final = Dispatcher()
@ -15,7 +23,9 @@ class Engine:
self.__dispatcher_thread: threading.Thread | None = None self.__dispatcher_thread: threading.Thread | None = None
def start(self) -> None: def start(self) -> None:
self.__system_thread = threading.Thread(target=self.system.start, daemon=False) self.__system_thread = threading.Thread(
target=self.system.start, daemon=False
)
self.__dispatcher_thread = threading.Thread( self.__dispatcher_thread = threading.Thread(
target=self.dispatcher.start, daemon=False target=self.dispatcher.start, daemon=False
) )

View file

@ -19,25 +19,29 @@ class Dispatcher:
Event dispatcher Event dispatcher
""" """
__running: bool __slots__ = ("__queue", "__subscribers", "__running")
def __init__(self) -> None: def __init__(self) -> None:
self.__queue: Final = queue.Queue[Event]() self.__queue: Final = queue.Queue[Event]()
self.__subscribers: Final[dict[type[Event], list[Subscriber[Event]]]] = ( self.__subscribers: Final[
defaultdict(list) dict[type[Event], list[Subscriber[Event]]]
) ] = defaultdict(list)
self.__running = False self.__running: bool = False
@property @property
def is_running(self) -> bool: def is_running(self) -> bool:
"""Returns True if the dispatcher is running.""" """Returns True if the dispatcher is running."""
return self.__running return self.__running
def subscribe(self, event_type: type[T], subscriber: Subscriber[T]) -> None: def subscribe(
self, event_type: type[T], subscriber: Subscriber[T]
) -> None:
"""Subscribe to an event type.""" """Subscribe to an event type."""
self.__subscribers[event_type].append(subscriber) # type: ignore self.__subscribers[event_type].append(subscriber) # type: ignore
def unsubscribe(self, event_type: type[T], subscriber: Subscriber[T]) -> None: def unsubscribe(
self, event_type: type[T], subscriber: Subscriber[T]
) -> None:
"""Unsubscribe from an event type.""" """Unsubscribe from an event type."""
for sub in self.__subscribers[event_type].copy(): for sub in self.__subscribers[event_type].copy():
if sub.handler != subscriber.handler: if sub.handler != subscriber.handler:
@ -93,7 +97,9 @@ class Dispatcher:
i = 0 i = 0
while i < len(subscribers): while i < len(subscribers):
subscriber = subscribers[i] subscriber = subscribers[i]
if subscriber.filters is not None and not subscriber.filters(event): if subscriber.filters is not None and not subscriber.filters(
event
):
continue continue
action = subscriber.handler(event) action = subscriber.handler(event)

View file

@ -15,6 +15,8 @@ class ExceptionHook(Protocol, Generic[T_contra]):
@final @final
class _ExceptionManager: class _ExceptionManager:
__slots__ = ("__hooks", "excepthook")
def __init__(self) -> None: def __init__(self) -> None:
self.__hooks: list[tuple[type[BaseException], ExceptionHook[Any]]] = [] self.__hooks: list[tuple[type[BaseException], ExceptionHook[Any]]] = []
sys.excepthook = self._excepthook sys.excepthook = self._excepthook

View file

@ -8,6 +8,8 @@ if TYPE_CHECKING:
class Loader: class Loader:
__slots__ = ("__engine", "__loadables")
def __init__(self, engine: Engine) -> None: def __init__(self, engine: Engine) -> None:
self.__engine: Final = engine self.__engine: Final = engine
self.__loadables: Final[list[Loadable]] = [] self.__loadables: Final[list[Loadable]] = []