Release/v0.6.0 #12
8 changed files with 3 additions and 263 deletions
|
|
@ -1,88 +0,0 @@
|
||||||
from typing import final
|
|
||||||
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from snakia.core.ecs import Component
|
|
||||||
from snakia.core.ecs.system import System
|
|
||||||
from snakia.core.engine import Engine
|
|
||||||
from snakia.core.es import Event
|
|
||||||
from snakia.core.loader import Meta, Plugin, PluginProcessor
|
|
||||||
from snakia.types import Version
|
|
||||||
|
|
||||||
|
|
||||||
class HealthComponent(Component):
|
|
||||||
max_value: int = Field(default=100, ge=0)
|
|
||||||
value: int = Field(default=100, ge=0)
|
|
||||||
|
|
||||||
|
|
||||||
class DamageComponent(Component):
|
|
||||||
damage: int = Field(ge=0)
|
|
||||||
ticks: int = Field(default=1, ge=0)
|
|
||||||
|
|
||||||
|
|
||||||
class HealComponent(Component):
|
|
||||||
heal: int = Field(ge=0)
|
|
||||||
ticks: int = Field(default=1, ge=0)
|
|
||||||
|
|
||||||
|
|
||||||
class DeathEvent(Event):
|
|
||||||
entity: int = Field()
|
|
||||||
|
|
||||||
|
|
||||||
class HealthProcessor(PluginProcessor):
|
|
||||||
def process(self, system: System) -> None:
|
|
||||||
for entity, (heal, health) in system.get_components(
|
|
||||||
HealComponent, HealthComponent
|
|
||||||
):
|
|
||||||
health.value += heal.heal
|
|
||||||
heal.ticks -= 1
|
|
||||||
if heal.ticks <= 0:
|
|
||||||
system.remove_component(entity, HealComponent)
|
|
||||||
for entity, (damage, health) in system.get_components(
|
|
||||||
DamageComponent, HealthComponent
|
|
||||||
):
|
|
||||||
health.value -= damage.damage
|
|
||||||
damage.ticks -= 1
|
|
||||||
if damage.ticks <= 0:
|
|
||||||
system.remove_component(entity, DamageComponent)
|
|
||||||
if health.value <= 0:
|
|
||||||
system.remove_component(entity, HealthComponent)
|
|
||||||
self.plugin.dispatcher.publish(DeathEvent(entity=entity))
|
|
||||||
|
|
||||||
|
|
||||||
@final
|
|
||||||
class HealthPlugin(
|
|
||||||
Plugin,
|
|
||||||
meta=Meta(
|
|
||||||
name="health",
|
|
||||||
author="snakia",
|
|
||||||
version=Version.from_args(1, 0, 0),
|
|
||||||
subscribers=(),
|
|
||||||
processors=(HealthProcessor,),
|
|
||||||
),
|
|
||||||
):
|
|
||||||
def on_load(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_unload(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
engine = Engine()
|
|
||||||
engine.loader.register(HealthPlugin)
|
|
||||||
engine.loader.load_all()
|
|
||||||
|
|
||||||
@engine.dispatcher.on(DeathEvent)
|
|
||||||
def on_death(event: DeathEvent) -> None:
|
|
||||||
print(f"Entity: {event.entity} is death!")
|
|
||||||
|
|
||||||
player = engine.system.create_entity()
|
|
||||||
engine.system.add_component(player, HealthComponent())
|
|
||||||
engine.system.add_component(player, DamageComponent(damage=10, ticks=10))
|
|
||||||
|
|
||||||
engine.start()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -3,14 +3,12 @@ from typing import Final
|
||||||
|
|
||||||
from .ecs import System
|
from .ecs import System
|
||||||
from .es import Dispatcher
|
from .es import Dispatcher
|
||||||
from .loader.loader import Loader
|
|
||||||
|
|
||||||
|
|
||||||
class Engine:
|
class Engine:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"system",
|
"system",
|
||||||
"dispatcher",
|
"dispatcher",
|
||||||
"loader",
|
|
||||||
"__system_thread",
|
"__system_thread",
|
||||||
"__dispatcher_thread",
|
"__dispatcher_thread",
|
||||||
)
|
)
|
||||||
|
|
@ -18,12 +16,13 @@ class Engine:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.system: Final = System()
|
self.system: Final = System()
|
||||||
self.dispatcher: Final = Dispatcher()
|
self.dispatcher: Final = Dispatcher()
|
||||||
self.loader: Final = Loader(self)
|
|
||||||
self.__system_thread: threading.Thread | None = None
|
self.__system_thread: threading.Thread | None = None
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
from .loadable import Loadable
|
|
||||||
from .meta import Meta
|
|
||||||
from .plugin import Plugin
|
|
||||||
from .plugin_processor import PluginProcessor
|
|
||||||
|
|
||||||
__all__ = ["Loadable", "Meta", "Plugin", "PluginProcessor"]
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from snakia.core.engine import Engine
|
|
||||||
|
|
||||||
|
|
||||||
class Loadable(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
def __init__(self, engine: Engine) -> None: ...
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def load(self) -> None: ...
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def unload(self) -> None: ...
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Callable, Final
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from snakia.core.engine import Engine
|
|
||||||
from snakia.core.loader import Loadable
|
|
||||||
|
|
||||||
|
|
||||||
class Loader:
|
|
||||||
__slots__ = ("__engine", "__loadables")
|
|
||||||
|
|
||||||
def __init__(self, engine: Engine) -> None:
|
|
||||||
self.__engine: Final = engine
|
|
||||||
self.__loadables: Final[list[Loadable]] = []
|
|
||||||
|
|
||||||
def register(self, loadable: Callable[[Engine], Loadable]) -> None:
|
|
||||||
self.__loadables.append(loadable(self.__engine))
|
|
||||||
|
|
||||||
def load_all(self) -> None:
|
|
||||||
for loadable in self.__loadables:
|
|
||||||
loadable.load()
|
|
||||||
|
|
||||||
def unload_all(self) -> None:
|
|
||||||
for loadable in reversed(self.__loadables):
|
|
||||||
loadable.unload()
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
|
||||||
|
|
||||||
from snakia.core.es import Event, Subscriber
|
|
||||||
from snakia.types import Version
|
|
||||||
|
|
||||||
from .plugin_processor import PluginProcessor
|
|
||||||
|
|
||||||
|
|
||||||
class Meta(BaseModel):
|
|
||||||
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
|
|
||||||
|
|
||||||
name: str = Field(
|
|
||||||
default="unknown",
|
|
||||||
min_length=4,
|
|
||||||
max_length=32,
|
|
||||||
pattern="^[a-z0-9_]{4,32}$",
|
|
||||||
)
|
|
||||||
author: str = Field(
|
|
||||||
default="unknown",
|
|
||||||
min_length=4,
|
|
||||||
max_length=32,
|
|
||||||
pattern="^[a-z0-9_]{4,32}$",
|
|
||||||
)
|
|
||||||
version: Version = Field(default_factory=lambda: Version(major=1, minor=0, patch=0))
|
|
||||||
|
|
||||||
subscribers: tuple[tuple[type[Event], Subscriber[Event]], ...] = Field(
|
|
||||||
default_factory=tuple
|
|
||||||
)
|
|
||||||
processors: tuple[type[PluginProcessor], ...] = Field(default_factory=tuple)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self) -> str:
|
|
||||||
return f"{self.author}.{self.name}"
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from abc import abstractmethod
|
|
||||||
from typing import TYPE_CHECKING, ClassVar, Final, final
|
|
||||||
|
|
||||||
from snakia.core.ecs import System
|
|
||||||
from snakia.core.es import Dispatcher
|
|
||||||
|
|
||||||
from .loadable import Loadable
|
|
||||||
from .meta import Meta
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from snakia.core.engine import Engine
|
|
||||||
|
|
||||||
|
|
||||||
class Plugin(Loadable):
|
|
||||||
__meta: ClassVar[Meta]
|
|
||||||
|
|
||||||
@final
|
|
||||||
def __init__(self, engine: Engine) -> None:
|
|
||||||
self.__engine: Final = engine
|
|
||||||
|
|
||||||
@final
|
|
||||||
@property
|
|
||||||
def meta(self) -> Meta:
|
|
||||||
"""The plugin's metadata."""
|
|
||||||
return self.__meta
|
|
||||||
|
|
||||||
@final
|
|
||||||
@property
|
|
||||||
def dispatcher(self) -> Dispatcher:
|
|
||||||
return self.__engine.dispatcher
|
|
||||||
|
|
||||||
@final
|
|
||||||
@property
|
|
||||||
def system(self) -> System:
|
|
||||||
return self.__engine.system
|
|
||||||
|
|
||||||
@final
|
|
||||||
def load(self) -> None:
|
|
||||||
for processor in self.meta.processors:
|
|
||||||
self.__engine.system.add_processor(processor(self))
|
|
||||||
for event_type, subscriber in self.meta.subscribers:
|
|
||||||
self.__engine.dispatcher.subscribe(event_type, subscriber)
|
|
||||||
self.on_load()
|
|
||||||
|
|
||||||
@final
|
|
||||||
def unload(self) -> None:
|
|
||||||
for processor in self.meta.processors:
|
|
||||||
self.__engine.system.remove_processor(processor)
|
|
||||||
for event_type, subscriber in self.meta.subscribers:
|
|
||||||
self.__engine.dispatcher.unsubscribe(event_type, subscriber)
|
|
||||||
self.on_unload()
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def on_load(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def on_unload(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
|
|
||||||
@final
|
|
||||||
def __init_subclass__(cls, meta: Meta) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def __init_subclass__(cls, meta: Meta) -> None:
|
|
||||||
cls.meta = meta
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Final, final
|
|
||||||
|
|
||||||
from snakia.core.ecs import Processor
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .plugin import Plugin
|
|
||||||
|
|
||||||
|
|
||||||
class PluginProcessor(Processor):
|
|
||||||
@final
|
|
||||||
def __init__(self, plugin: Plugin) -> None:
|
|
||||||
self.plugin: Final = plugin
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue