Release/v0.5.0 #8
5 changed files with 63 additions and 17 deletions
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]] = []
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue