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])