return kv[ty]
return map(self._components[ty].__getitem__, positions)
+ itr = range(len(dst._entities) - len(components), len(dst._entities))
dst._extend(cb)
- self._discard_sort(positions)
+ self._discard(positions)
+
+ for k in comps_ty:
+ lst = dst._components[k]
+ hooks = self._world._components_cache[k][1]
+ hooks._insert((dst._entities[i], lst[i]) for i in itr)
def remove_components[T: Components](
self, ty: type[T], entities: Iterable["Entity"]
) -> None:
- dst, _ = self._compute_remove(ty)
+ dst, comps_ty = self._compute_remove(ty)
positions = [self._positions[e] for e in entities]
def cb(ty: ComponentID | None) -> Iterable[Any]:
return entities
return map(self._components[ty].__getitem__, positions)
+ removed_ents = [self._entities[i] for i in positions]
+ removed_comps = [
+ [lst[i] for i in positions]
+ for lst in map(self._components.__getitem__, comps_ty)
+ ]
dst._extend(cb)
- self._discard_sort(positions)
+ self._discard(positions)
+ for k, comp in zip(comps_ty, removed_comps, strict=True):
+ hooks = self._world._components_cache[k][1]
+ hooks._remove(zip(removed_ents, comp, strict=True))
- def remove(self, entities: Iterable["Entity"]) -> None:
- self._discard_sort([self._positions[e] for e in entities])
+ def remove(self, entities: list["Entity"]) -> None:
+ positions = [self._positions[e] for e in entities]
+ comps = [
+ (
+ self._world._components_cache[comp][1],
+ [lst[i] for i in positions],
+ )
+ for comp, lst in self._components.items()
+ ]
+ self._discard(positions)
+ for ent in entities:
+ del self._world._entities[ent]
+ for hook, comp in comps:
+ hook._remove(zip(entities, comp))
def _extend(
self,
for entity in cb(None):
self._positions[entity] = len(self._entities)
self._entities.append(entity)
+ self._world._entities[entity] = self
for ty, comp in self._components.items():
itr = cb(ty)
comp.extend(itr)
for comp in self._components.values()
), "Callaback's return lists weren't all of equal size"
- def _discard_sort(self, itr: list[int]) -> None:
- itr.sort(reverse=True)
+ def _discard(self, itr: Iterable[int]) -> None:
+ sort = sorted(itr, reverse=True)
def remove_idx[T](
lst: list[T], cb: Callable[[T, int], None] = lambda _1, _2: None
) -> None:
- for i in itr:
+ for i in sort:
shifted = lst.pop()
if len(lst) == i:
continue
cb(shifted, i)
lst[i] = shifted
- for i in itr:
+ for i in sort:
self._positions.__delitem__(self._entities[i])
remove_idx(self._entities, self._positions.__setitem__)
for comp in self._components.values():
type Plugin = RecursiveTuple[Callable[["World"], None]]
-type RawComponentHook[T] = Callable[[list[tuple["Entity", T]]], None]
+type RawComponentArg[T] = Iterable[tuple["Entity", T]]
+type RawComponentHook[T] = Callable[[RawComponentArg], None]
type ComponentHook[T] = System[RawComponentHook[T]]
@override
def __contains__(self, entity: Entity) -> bool:
- arch: Archetype = self._world._entities[entity]
+ arch: Archetype | None = self._world._entities.get(entity)
+ if arch is None:
+ return False
if (arch_idx := self._archetypes_idx.get(arch)) is None:
return False
components_idx = arch._positions[entity]
self._queue.append(sys)
+def _system_failed_va[T, *P = *tuple[()]](*_: *P) -> T:
+ raise ValueError("Failed to fetch arguments for system")
+
+
def _system_failed[T]() -> T:
raise ValueError("Failed to fetch arguments for system")
def component_id(self, comp: type) -> ComponentID:
if (res := self._components_lookup.get(comp)) is None:
- print(f"added comp {comp}")
res = ComponentID(len(self._components_cache))
self._components_cache.append((comp, ComponentHooks()))
self._components_lookup[comp] = res
)
return res
- def spawn[T](self, ty: type[T], comp: T) -> Entity:
+ def spawn[T: Components](self, ty: type[T], comp: T) -> Entity:
return self.spawn_many(ty, [comp])[0]
- def spawn_many[T](self, ty: type[T], comps: list[T]) -> list[Entity]:
+ def spawn_many[T: Components](
+ self, ty: type[T], comps: list[T]
+ ) -> list[Entity]:
ents = self.spawn_many_empty(len(comps))
self._base_archetype.add_components(
cast(type[Components], ty),
)
return ents
- def add_system_raw[T](
- self,
- system: Callable[..., T],
- default: Callable[[], T] = _system_failed,
- ) -> RawSystem[T]:
+ def despawn(self, ent: Entity) -> None:
+ self.despawn_many([ent])
+
+ def despawn_many(self, ents: Iterable[Entity]) -> None:
+ archs: dict[Archetype, list[Entity]] = {}
+ for ent in ents:
+ arch = self._entities[ent]
+ if arch not in archs:
+ archs[arch] = []
+ archs[arch].append(ent)
+ for arch, lst in archs.items():
+ arch.remove(lst)
+
+ def insert[T: Components](self, ty: type[T], ent: Entity, comp: T) -> None:
+ self.insert_many(ty, [(ent, comp)])
+
+ def insert_many[T: Components](
+ self, ty: type[T], comps: Iterable[tuple[Entity, T]]
+ ) -> None:
+ archs: dict[Archetype, list[tuple[Entity, T]]] = {}
+ for ent, comp in comps:
+ arch = self._entities[ent]
+ if arch not in archs:
+ archs[arch] = []
+ archs[arch].append((ent, comp))
+
+ for arch, lst in archs.items():
+ arch.add_components(ty, lst)
+
+ def remove[T: Components](self, ty: type[T], ent: Entity) -> None:
+ self.remove_many(ty, [ent])
+
+ def remove_many[T: Components](
+ self, ty: type[T], comps: Iterable[Entity]
+ ) -> None:
+ archs: dict[Archetype, list[Entity]] = {}
+ for ent in comps:
+ arch = self._entities[ent]
+ if arch not in archs:
+ archs[arch] = []
+ archs[arch].append(ent)
+
+ for arch, lst in archs.items():
+ arch.remove_components(ty, lst)
+
+ def _sys_extractors[T](
+ self, system: System[T]
+ ) -> tuple[Callable[[], FromWorld | None], ...]:
sig = inspect.signature(system, eval_str=True)
if err := next(
filter(
None,
):
raise TypeError(f"System {system}'s {err[0]} parameter is invalid")
- extractors = tuple(
+ return tuple(
cast(type[FromWorld], param.annotation).extract_from_world(
param.annotation, self
)
for param in sig.parameters.values()
)
+ def add_system[T, *P](
+ self,
+ system: System[Callable[[*P], T]],
+ default: Callable[[*P], T] = _system_failed_va,
+ ) -> Callable[[*P], T]:
+ extractors = self._sys_extractors(system)
+
+ def inner(*cb_args: *P) -> T:
+ args = []
+ for extract in extractors:
+ curr = extract()
+ if curr is None:
+ return default()
+ args.append(curr)
+ res = system(*args)(*cb_args)
+ for arg in args:
+ arg.cleanup()
+ return res
+
+ return inner
+
+ def add_raw_system[T](
+ self,
+ system: Callable[..., T],
+ default: Callable[[], T] = _system_failed,
+ ) -> RawSystem[T]:
+ extractors = self._sys_extractors(system)
+
def inner() -> T:
args = []
for extract in extractors:
) -> "World":
comp_id = self.component_id(resolve_type_aliases(ty))
hooks = self._components_cache[comp_id][1]
- sys = self.add_system_raw(hook)
+ sys = self.add_raw_system(hook)
match cycle:
case "insert":
hooks.add_insert(lambda e: sys()(e))
return self
def run_main(self) -> None:
- self.run(MainSchedule, self.add_system_raw(should_exit, lambda: False))
+ self.run(MainSchedule, self.add_raw_system(should_exit, lambda: False))
_order_groups: int = 0
else (
e
if isinstance(e, SystemSet)
- else world.add_system_raw(e, default=lambda: None)
+ else world.add_raw_system(e, default=lambda: None)
)
for e in self._systems
)