Architecture & Internals#
This section provides detailed flowcharts showing how PyECS components work internally.
Core Components#
World Operations#
create_entity#
flowchart TD Start([create_entity called]) --> CallEntityManager[Call entity_manager.create_entity] CallEntityManager --> CheckResult{Result is tuple?} CheckResult -->|Yes| ExtractEntity[Extract entity from tuple index 1] CheckResult -->|No| ReturnFailure[Return result as FAILURE] ExtractEntity --> CreateEmptyMask[Create empty frozenset mask] CreateEmptyMask --> CheckArchetypeExists{Empty mask in archetypes?} CheckArchetypeExists -->|No| ImportArchetype[Import Archetype class] CheckArchetypeExists -->|Yes| SetMapping[Set entity_to_archetype mapping] ImportArchetype --> CreateArchetype[Create new Archetype instance] CreateArchetype --> AddToArchetypes[Add to component_storage.archetypes] AddToArchetypes --> SetMapping SetMapping --> ReturnEntity[Return entity] ReturnFailure --> End1([End]) ReturnEntity --> End2([End])
create_entity_or_raise#
flowchart TD Start([create_entity_or_raise called]) --> CallSafe[Call create_entity internally] CallSafe --> CheckResult{Result == StatusCodes.FAILURE?} CheckResult -->|No| ReturnEntity[Return entity] CheckResult -->|Yes| RaiseException[Raise OperationFailedError] ReturnEntity --> Success([Success: Entity returned]) RaiseException --> Error([Error: Exception raised])
get_component#
flowchart TD Start([get_component called with entity and component_type]) --> CheckAlive{entity_manager.is_alive?} CheckAlive -->|Yes| CallGetComponent[Call component_storage.get_component] CheckAlive -->|No| ReturnFailure[Return FAILURE] CallGetComponent --> ReturnResult[Return component or FAILURE] ReturnFailure --> End1([End]) ReturnResult --> End2([End])
get_component_or_raise#
flowchart TD Start([get_component_or_raise called with entity and component_type]) --> CallSafe[Call get_component internally] CallSafe --> CheckResult{Result == StatusCodes.FAILURE?} CheckResult -->|No| ReturnComponent[Return component] CheckResult -->|Yes| RaiseException[Raise ComponentNotFoundError] ReturnComponent --> Success([Success: Component returned]) RaiseException --> Error([Error: Exception raised])
get_components#
flowchart TD Start([get_components called with entity and component_types]) --> CheckSingle{len component_types == 1?} CheckSingle -->|Yes| WarnDeprecated[warn_deprecated: Use get_component for single component] CheckSingle -->|No| CheckAlive{entity_manager.is_alive?} WarnDeprecated --> CallSingle[Call get_component with single type] CallSingle --> CheckSingleResult{Result == FAILURE?} CheckSingleResult -->|Yes| ReturnFailure1[Return FAILURE] CheckSingleResult -->|No| WrapTuple[Wrap in tuple] WrapTuple --> End1([End]) ReturnFailure1 --> End2([End]) CheckAlive -->|No| ReturnFailure2[Return FAILURE] CheckAlive -->|Yes| InitResult[Initialize empty result list] InitResult --> ForEachType{For each component_type} ForEachType --> GetComponent[Call component_storage.get_component] GetComponent --> CheckResult{component == FAILURE?} CheckResult -->|Yes| ReturnFailure3[Return FAILURE] CheckResult -->|No| AppendResult[Append component to result] AppendResult --> MoreTypes{More types?} MoreTypes -->|Yes| ForEachType MoreTypes -->|No| ReturnTuple[Return tuple of results] ReturnFailure2 --> End3([End]) ReturnFailure3 --> End4([End]) ReturnTuple --> End5([End])
get_components_or_raise#
flowchart TD Start([get_components_or_raise called with entity and component_types]) --> CallSafe[Call get_components internally] CallSafe --> CheckResult{Result == StatusCodes.FAILURE?} CheckResult -->|No| ReturnTuple[Return tuple of components] CheckResult -->|Yes| RaiseException[Raise ComponentNotFoundError] ReturnTuple --> Success([Success: Components tuple returned]) RaiseException --> Error([Error: Exception raised])
add_component#
flowchart TD Start([add_component called with entity and component]) --> CheckAlive{entity_manager.is_alive?} CheckAlive -->|Yes| CallAddComponent[Call component_storage.add_component] CheckAlive -->|No| End1([End]) CallAddComponent --> End2([End])
remove_component#
flowchart TD Start([remove_component called with entity and component_type]) --> CheckAlive{entity_manager.is_alive?} CheckAlive -->|Yes| CallRemoveComponent[Call component_storage.remove_component] CheckAlive -->|No| End1([End]) CallRemoveComponent --> End2([End])
destroy_entity#
flowchart TD Start([destroy_entity called with entity]) --> CallEntityManager[Call entity_manager.destroy_entity] CallEntityManager --> CheckResult{Result == ENTITY_DESTROYED?} CheckResult -->|Yes| RemoveComponents[Call component_storage.remove_entity] CheckResult -->|No| End1([End]) RemoveComponents --> End2([End])
add_system#
flowchart TD Start([add_system called with system]) --> RegisterSystem[Call system_manager.register_system] RegisterSystem --> CheckResult{Result is tuple?} CheckResult -->|Yes| InitSystem[Call system.init with self] CheckResult -->|No| End1([End]) InitSystem --> End2([End])
remove_system#
flowchart TD Start([remove_system called with system]) --> CleanupSystem[Call system.cleanup with self] CleanupSystem --> RemoveFromManager[Call system_manager.remove_system] RemoveFromManager --> End([End])
update#
flowchart TD Start([update called with dt]) --> UpdateAllSystems[Call system_manager.update_all with self and dt] UpdateAllSystems --> End([End])
Query System#
Query Construction#
with_components#
flowchart TD Start([with_components called with types]) --> UpdateSet[Update _with set with types] UpdateSet --> ReturnSelf[Return self] ReturnSelf --> End([End])
without_components#
flowchart TD Start([without_components called with types]) --> UpdateSet[Update _without set with types] UpdateSet --> ReturnSelf[Return self] ReturnSelf --> End([End])
Query Execution#
flowchart TD Start([execute called with storage_or_world]) --> CheckType{Is ComponentStorage?} CheckType -->|Yes| WarnDeprecated[warn_deprecated: Passing ComponentStorage directly is deprecated] CheckType -->|No| GetStorage[Get component_storage from world] WarnDeprecated --> SetStorage1[storage = storage_or_world] GetStorage --> SetStorage2[storage = storage_or_world.component_storage] SetStorage1 --> InitList[Initialize empty matching list] SetStorage2 --> InitList InitList --> LoopArchetypes[Loop through storage.archetypes items] LoopArchetypes --> GetMaskAndArch[Get mask and archetype] GetMaskAndArch --> CheckWith{_with subset of mask?} CheckWith -->|No| NextArchetype[Continue to next archetype] CheckWith -->|Yes| CheckWithout{_without intersects mask?} CheckWithout -->|Yes| NextArchetype CheckWithout -->|No| ExtendMatching[Extend matching with archetype.entities] NextArchetype --> MoreArchetypes{More archetypes?} ExtendMatching --> MoreArchetypes MoreArchetypes -->|Yes| LoopArchetypes MoreArchetypes -->|No| ReturnMatching[Return matching list] ReturnMatching --> End([End])
Storage Layer#
ComponentStorage Operations#
add_component#
flowchart TD Start([add_component called with entity and component]) --> CheckEntity{Entity in entity_to_archetype?} CheckEntity -->|No| ReturnFailure[Return FAILURE] CheckEntity -->|Yes| GetMask[Get current archetype mask] GetMask --> GetArchetype[Get current archetype from mask] GetArchetype --> CheckComponentType{Component type in mask?} CheckComponentType -->|No| CreateNewMask[Create new mask with component type] CheckComponentType -->|Yes| GetEntityIndex[Get entity_index from archetype.entity_indices - O1 lookup] CreateNewMask --> GetComponents[Get all entity components] GetComponents --> AppendComponent[Append new component to list] AppendComponent --> MoveEntity[Call move_entity_to_archetype with new mask] MoveEntity --> ReturnAdded[Return COMPONENT_ADDED] GetEntityIndex --> UpdateComponent[Update component at entity_index in component array] UpdateComponent --> ReturnUpdated[Return COMPONENT_UPDATED] ReturnFailure --> End1([End]) ReturnAdded --> End2([End]) ReturnUpdated --> End3([End])
remove_component#
flowchart TD Start([remove_component called with entity and component_type]) --> CheckEntity{Entity in entity_to_archetype?} CheckEntity -->|No| ReturnFailure1[Return FAILURE] CheckEntity -->|Yes| GetMask[Get current archetype mask] GetMask --> GetArchetype[Get current archetype] GetArchetype --> CheckHasComponent{Component type in mask?} CheckHasComponent -->|No| ReturnFailure2[Return FAILURE] CheckHasComponent -->|Yes| CreateNewMask[Create new mask without component type] CreateNewMask --> CheckMaskEmpty{New mask empty?} CheckMaskEmpty -->|No| GetRemainingComponents[Get all components except removed type] CheckMaskEmpty -->|Yes| RemoveEntity[Call self.remove_entity] GetRemainingComponents --> LoopComponents[Loop through mask types] LoopComponents --> CheckNotRemoved{Type != removed type?} CheckNotRemoved -->|Yes| GetComponent[Get component from archetype] CheckNotRemoved -->|No| SkipComponent[Skip this component] GetComponent --> AppendToList[Append to components list] AppendToList --> LoopComponents SkipComponent --> LoopComponents LoopComponents --> MoveEntity[Call move_entity_to_archetype with new mask] MoveEntity --> ReturnRemoved1[Return COMPONENT_REMOVED] RemoveEntity --> ReturnRemoved2[Return COMPONENT_REMOVED] ReturnFailure1 --> End1([End]) ReturnFailure2 --> End2([End]) ReturnRemoved1 --> End3([End]) ReturnRemoved2 --> End4([End])
get_component#
flowchart TD Start([get_component called with entity and component_type]) --> CheckEntity{Entity in entity_to_archetype?} CheckEntity -->|No| ReturnFailure1[Return FAILURE] CheckEntity -->|Yes| GetMask[Get entity archetype mask] GetMask --> GetArchetype[Get archetype from mask] GetArchetype --> CheckHasComponent{Component type in mask?} CheckHasComponent -->|No| ReturnFailure2[Return FAILURE] CheckHasComponent -->|Yes| CallArchetypeGet[Call archetype.get_component] CallArchetypeGet --> ReturnComponent[Return component instance] ReturnFailure1 --> End1([End]) ReturnFailure2 --> End2([End]) ReturnComponent --> End3([End])
get_entity_components#
flowchart TD Start([get_entity_components called with entity]) --> GetMask[Get entity mask from entity_to_archetype] GetMask --> GetArchetype[Get archetype from mask] GetArchetype --> InitList[Initialize empty components list] InitList --> LoopTypes[Loop through component types in mask] LoopTypes --> GetComponent[Call archetype.get_component for type] GetComponent --> AppendComponent[Append component to list] AppendComponent --> LoopTypes LoopTypes --> ReturnList[Return components list] ReturnList --> End([End])
has_component#
flowchart TD Start([has_component called with entity and component_type]) --> CheckEntity{Entity in entity_to_archetype?} CheckEntity -->|No| ReturnFalse[Return False] CheckEntity -->|Yes| GetMask[Get entity archetype mask] GetMask --> CheckInMask{Component type in mask?} CheckInMask -->|No| ReturnFalse2[Return False] CheckInMask -->|Yes| ReturnTrue[Return True] ReturnFalse --> End1([End]) ReturnFalse2 --> End2([End]) ReturnTrue --> End3([End])
move_entity_to_archetype#
flowchart TD Start([move_entity_to_archetype called]) --> CheckEntityExists{Entity in entity_to_archetype?} CheckEntityExists -->|Yes| GetCurrentMask[Get current mask] CheckEntityExists -->|No| CheckComponentsParam{Components parameter provided?} GetCurrentMask --> GetCurrentArchetype[Get current archetype] GetCurrentArchetype --> CheckComponentsProvided{Components parameter provided?} CheckComponentsProvided -->|No| GetEntityComponents[Call get_entity_components] CheckComponentsProvided -->|Yes| UseProvidedComponents[Use provided components] GetEntityComponents --> RemoveFromCurrent[Remove entity from current archetype] UseProvidedComponents --> RemoveFromCurrent CheckComponentsParam -->|No| SetEmptyComponents[Set components = empty list] CheckComponentsParam -->|Yes| UseProvided[Use provided components] SetEmptyComponents --> CheckArchetypeExists{New mask in archetypes?} UseProvided --> CheckArchetypeExists RemoveFromCurrent --> CheckArchetypeExists CheckArchetypeExists -->|No| CreateArchetype[Create new Archetype] CheckArchetypeExists -->|Yes| GetTargetArchetype[Get target archetype] CreateArchetype --> AddToArchetypes[Add to archetypes dict] AddToArchetypes --> GetTargetArchetype GetTargetArchetype --> AddEntity[Call target_archetype.add_entity] AddEntity --> UpdateMapping[Update entity_to_archetype mapping] UpdateMapping --> ReturnSuccess[Return SUCCESS] ReturnSuccess --> End([End])
remove_entity#
flowchart TD Start([remove_entity called with entity]) --> CheckEntity{Entity in entity_to_archetype?} CheckEntity -->|No| ReturnFailure[Return FAILURE] CheckEntity -->|Yes| GetMask[Get entity archetype mask] GetMask --> GetArchetype[Get archetype from mask] GetArchetype --> RemoveFromArchetype[Call archetype.remove_entity] RemoveFromArchetype --> DeleteMapping[Delete from entity_to_archetype] DeleteMapping --> ReturnSuccess[Return SUCCESS] ReturnFailure --> End1([End]) ReturnSuccess --> End2([End])
Archetype Operations#
add_entity#
flowchart TD Start([add_entity called with entity and components list]) --> CheckExists{Entity in entity_indices dict?} CheckExists -->|Yes| ReturnFailure[Return FAILURE] CheckExists -->|No| CalcIndex["Calculate entity_index as len(entities)"] CalcIndex --> AppendEntity[Append entity to entities list] AppendEntity --> AddToDict["Add entity->entity_index to entity_indices dict"] AddToDict --> LoopComponents[Loop through components] LoopComponents --> GetCompType[Get component.__class__] GetCompType --> CheckTypeExists{Component type in self.components?} CheckTypeExists -->|No| CreateList[Create list with None * entity_index] CheckTypeExists -->|Yes| AppendToList[Append component to type list] CreateList --> AddToDict[Add list to components dict] AddToDict --> AppendToList AppendToList --> MoreComponents{More components?} MoreComponents -->|Yes| LoopComponents MoreComponents -->|No| ReturnSuccess[Return SUCCESS] ReturnFailure --> End1([End]) ReturnSuccess --> End2([End])
remove_entity#
flowchart TD Start([remove_entity called with entity]) --> CheckExists{Entity in entity_indices dict?} CheckExists -->|No| ReturnFailure[Return FAILURE] CheckExists -->|Yes| GetIndex[Get entity_index from dict - O1 lookup] GetIndex --> CheckLast{Is entity_index == last_index?} CheckLast -->|No| SwapWithLast[Swap entity with last entity in lists] SwapWithLast --> UpdateLastEntityIndex[Update last entity's index in dict] UpdateLastEntityIndex --> SwapComponents[Swap components in all component lists] SwapComponents --> RemoveLast CheckLast -->|Yes| RemoveLast[Pop last entity from entities list] RemoveLast --> RemoveFromDict[Delete entity from entity_indices dict] RemoveFromDict --> PopComponents[Pop last element from all component lists] PopComponents --> ReturnSuccess[Return SUCCESS] ReturnFailure --> End1([End]) ReturnSuccess --> End2([End])
get_component#
flowchart TD Start([get_component called with entity and component_type]) --> CheckEntity{Entity in entity_indices dict?} CheckEntity -->|No| ReturnFailure1[Return FAILURE] CheckEntity -->|Yes| GetIndex[Get entity_index from entity_indices dict - O1 lookup] GetIndex --> CheckTypeExists{Component type in components dict?} CheckTypeExists -->|No| ReturnFailure2[Return FAILURE] CheckTypeExists -->|Yes| GetFromList[Get component from type list at entity_index] GetFromList --> ReturnComponent[Return component instance] ReturnFailure1 --> End1([End]) ReturnFailure2 --> End2([End]) ReturnComponent --> End3([End])
iter_components#
flowchart TD Start([iter_components called with component_type]) --> CheckTypeExists{Component type in components dict?} CheckTypeExists -->|Yes| YieldComponents[Yield from components list for type] CheckTypeExists -->|No| YieldNothing[Yield nothing] YieldComponents --> End1([End]) YieldNothing --> End2([End])
iter_entities#
flowchart TD Start([iter_entities called]) --> CreateIterator[Create iterator from entities list] CreateIterator --> ReturnIterator[Return iterator] ReturnIterator --> End([End])
Entity Management#
EntityManager Operations#
create_entity#
flowchart TD Start([create_entity called]) --> Lock[Acquire thread lock] Lock --> GenerateUUID[Generate unique UUID4] GenerateUUID --> AddToSet[Add UUID to alive_entities set] AddToSet --> CreateTuple[Create tuple with ENTITY_CREATED status and UUID] CreateTuple --> ReleaseLock[Release thread lock] ReleaseLock --> ReturnSuccess[Return tuple] ReturnSuccess --> End([End])
destroy_entity#
flowchart TD Start([destroy_entity called with entity UUID]) --> Lock[Acquire thread lock] Lock --> CheckExists{Entity in alive_entities?} CheckExists -->|No| ReturnFailure[Return FAILURE status] CheckExists -->|Yes| RemoveEntity[Remove entity from alive_entities set] RemoveEntity --> ReturnDestroyed[Return ENTITY_DESTROYED status] ReturnFailure --> ReleaseLock1[Release thread lock] ReturnDestroyed --> ReleaseLock2[Release thread lock] ReleaseLock1 --> End1([End]) ReleaseLock2 --> End2([End])
is_alive#
flowchart TD Start([is_alive called with entity]) --> Lock[Acquire thread lock] Lock --> CheckInSet{Entity in alive_entities?} CheckInSet -->|Yes| ReturnTrue[Return True] CheckInSet -->|No| ReturnFalse[Return False] ReturnTrue --> ReleaseLock1[Release thread lock] ReturnFalse --> ReleaseLock2[Release thread lock] ReleaseLock1 --> End1([End]) ReleaseLock2 --> End2([End])
System Management#
SystemManager Operations#
register_system#
flowchart TD Start([register_system called with system]) --> CheckExists{System in system_to_id?} CheckExists -->|Yes| ReturnFailure[Return FAILURE] CheckExists -->|No| GenerateUUID[Call _unique_id to generate UUID] GenerateUUID --> AppendSystem[Append system to systems list] AppendSystem --> MapSystemToId[Add to system_to_id mapping] MapSystemToId --> MapIdToSystem[Add to id_to_system mapping] MapIdToSystem --> CreateTuple[Create tuple with SYSTEM_REGISTERED and system] CreateTuple --> ReturnTuple[Return tuple] ReturnFailure --> End1([End]) ReturnTuple --> End2([End])
remove_system#
flowchart TD Start([remove_system called with system]) --> CheckExists{System in system_to_id?} CheckExists -->|No| ReturnFailure[Return FAILURE] CheckExists -->|Yes| GetSystemId[Get system_id from system_to_id] GetSystemId --> CallUnregister[Call unregister_system with system_id] CallUnregister --> ReturnResult[Return unregister result] ReturnFailure --> End1([End]) ReturnResult --> End2([End])
unregister_system#
flowchart TD Start([unregister_system called with id]) --> CheckIdExists{ID in id_to_system?} CheckIdExists -->|No| ReturnFailure[Return FAILURE] CheckIdExists -->|Yes| GetSystem[Get system from id_to_system] GetSystem --> RemoveFromList[Remove system from systems list] RemoveFromList --> DeleteSystemToId[Delete from system_to_id mapping] DeleteSystemToId --> DeleteIdToSystem[Delete from id_to_system mapping] DeleteIdToSystem --> ReturnUnregistered[Return SYSTEM_UNREGISTERED] ReturnFailure --> End1([End]) ReturnUnregistered --> End2([End])
update_all#
flowchart TD Start([update_all called with world and dt]) --> LoopSystems[Loop through systems list] LoopSystems --> CallUpdate[Call system.update with world and dt] CallUpdate --> MoreSystems{More systems?} MoreSystems -->|Yes| LoopSystems MoreSystems -->|No| End([End])
Exception Handling#
PyECS provides both safe (status code) and unsafe (exception-based) APIs for error handling.
Exception Hierarchy#
classDiagram class PyECSError { <<abstract>> Base exception for all PyECS errors } class ComponentNotFoundError { Raised when a component is not found on an entity } class EntityNotFoundError { Raised when an entity does not exist or is not alive } class OperationFailedError { Raised when a general operation fails } PyECSError <|-- ComponentNotFoundError PyECSError <|-- EntityNotFoundError PyECSError <|-- OperationFailedError
Unsafe Decorator Flow#
flowchart TD Start([Class decorated with @auto_unsafe]) --> ScanMethods{Scan all class methods} ScanMethods --> ForEachMethod{For each method} ForEachMethod --> CheckAnnotations{Has return annotation with StatusCodes.FAILURE?} CheckAnnotations -->|No| NextMethod[Skip to next method] CheckAnnotations -->|Yes| DetermineException{Check method name} DetermineException -->|Contains 'component'| SetComponentError[Exception = ComponentNotFoundError] DetermineException -->|Contains 'entity'| SetEntityError[Exception = EntityNotFoundError] DetermineException -->|Otherwise| SetOperationError[Exception = OperationFailedError] SetComponentError --> GenerateUnsafe[Call generate_unsafe decorator] SetEntityError --> GenerateUnsafe SetOperationError --> GenerateUnsafe GenerateUnsafe --> CreateWrapper[Create unsafe_wrapper function] CreateWrapper --> SetAttribute[Set _unsafe_version attribute on method] SetAttribute --> NextMethod NextMethod --> MoreMethods{More methods?} MoreMethods -->|Yes| ForEachMethod MoreMethods -->|No| ProcessUnsafe[Call process_unsafe_methods] ProcessUnsafe --> AddToClass[Add all _or_raise methods to class] AddToClass --> End([Return decorated class])
Unsafe Method Execution#
flowchart TD Start([User calls method_or_raise]) --> CallOriginal[Call original method] CallOriginal --> CheckResult{Result == StatusCodes.FAILURE?} CheckResult -->|No| ReturnValue[Return result value] CheckResult -->|Yes| RaiseException[Raise configured exception] RaiseException --> ExceptionType{Exception Type} ExceptionType -->|ComponentNotFoundError| CompError([Component not found on entity]) ExceptionType -->|EntityNotFoundError| EntError([Entity does not exist]) ExceptionType -->|OperationFailedError| OpError([Operation failed]) ReturnValue --> Success([Success: Value returned])