diff --git a/code/application/game/world.cc b/code/application/game/world.cc index f35ce23da..378b513b1 100644 --- a/code/application/game/world.cc +++ b/code/application/game/world.cc @@ -1,8 +1,7 @@ //------------------------------------------------------------------------------ // @file entitypool.cc -// @copyright (C) 2021 Individual contributors, see AUTHORS file +// @copyright (C) 2021-2024 Individual contributors, see AUTHORS file //------------------------------------------------------------------------------ - #include "world.h" #include "gameserver.h" #include "api.h" @@ -63,13 +62,8 @@ World::World(WorldHash hash, WorldId id) Game::GetComponentId(), Game::GetComponentId(), Game::GetComponentId(), - Game::GetComponentId() - }; - MemDb::TableCreateInfo info = { - .name = "Empty", - .attributeIds = attributes, - .numAttributes = 4 - }; + Game::GetComponentId()}; + MemDb::TableCreateInfo info = {.name = "Empty", .attributeIds = attributes, .numAttributes = 4}; this->defaultTableId = this->db->CreateTable(info); // clang-format off @@ -143,7 +137,7 @@ World::PreloadLevel(Util::String const& path) ComponentId cid = componentIds[c]; components[componentIndex++] = cid; } - MemDb::TableId const tableId = this->CreateEntityTable({.name="", .components=components}); + MemDb::TableId const tableId = this->CreateEntityTable({.name = "", .components = components}); entityGroup.dstTable = tableId; entityGroup.numRows = table->num_rows(); @@ -192,7 +186,7 @@ World::PreloadLevel(Util::String const& path) while (it < entityGroup.columns + offset + bytesInColumn) { static_assert(sizeof(Util::StringAtom) == sizeof(uint64_t)); - + Util::StringAtom* asStringAtom = reinterpret_cast(it); uint64_t asInt = *reinterpret_cast(it); @@ -282,8 +276,7 @@ World::ExportLevel(Util::String const& path) { feature = ComponentFieldFeature::ComponentFieldFeature_StringAtom; } - else if (Util::String::StrCmp(fieldTypename, "Game::Entity") == 0 || - Util::String::StrCmp(fieldTypename, "entity") == 0) + else if (Util::String::StrCmp(fieldTypename, "Game::Entity") == 0 || Util::String::StrCmp(fieldTypename, "entity") == 0) { feature = ComponentFieldFeature::ComponentFieldFeature_EntityId; } @@ -331,8 +324,7 @@ World::ExportLevel(Util::String const& path) // do the same for entity references and other reference types if (Util::String::StrCmp(fieldTypename, "Resources::ResourceName") == 0 || Util::String::StrCmp(fieldTypename, "string") == 0 || - Util::String::StrCmp(fieldTypename, "Util::StringAtom") == 0 - ) + Util::String::StrCmp(fieldTypename, "Util::StringAtom") == 0) { // This is a stringatom field, so we to serialize the // string into the string table, and replace the @@ -434,7 +426,6 @@ World::Start() { // "Prefilter" the processors with the new table (insert the table in the cache that accepts it) this->pipeline.CacheTable(this->defaultTableId, this->db->GetTable(this->defaultTableId).GetSignature()); - this->pipeline.Begin(); } @@ -502,15 +493,6 @@ World::PrefilterProcessors() this->cacheValid = true; } -//------------------------------------------------------------------------------ -/** -*/ -bool -World::Prefiltered() const -{ - return this->cacheValid; -} - //------------------------------------------------------------------------------ /** */ @@ -548,7 +530,12 @@ World::ManageEntities() if (db.isvalid()) { - db->ForEachTable([this](MemDb::TableId tid) { this->Defragment(tid); }); + db->ForEachTable( + [this](MemDb::TableId tid) + { + this->Defragment(tid); + } + ); } } @@ -774,7 +761,7 @@ World::ClearDecayBuffers() /** */ bool -World::IsValid(Entity e) +World::IsValid(Entity e) const { return (uint32_t)this->worldId == e.world && this->pool.IsValid(e); } @@ -783,7 +770,7 @@ World::IsValid(Entity e) /** */ bool -World::HasInstance(Entity e) +World::HasInstance(Entity e) const { n_assert(this->IsValid(e)); return this->entityMap[e.index].instance != MemDb::InvalidRow; @@ -793,7 +780,7 @@ World::HasInstance(Entity e) /** */ EntityMapping -World::GetEntityMapping(Game::Entity entity) +World::GetEntityMapping(Game::Entity entity) const { n_assert(this->IsValid(entity)); n_assert(this->HasInstance(entity)); @@ -805,7 +792,7 @@ World::GetEntityMapping(Game::Entity entity) TODO: This is not thread safe! */ bool -World::HasComponent(Game::Entity const entity, ComponentId const component) +World::HasComponent(Game::Entity const entity, ComponentId const component) const { EntityMapping mapping = this->GetEntityMapping(entity); return this->db->GetTable(mapping.table).HasAttribute(component); @@ -921,7 +908,7 @@ World::AddStagedComponentsToEntity(Entity entity, AddStagedComponentCommand* cmd MemDb::Table const& tbl = this->db->GetTable(mapping.table); Util::Array const& cols = tbl.GetAttributes(); info.components.SetSize(cols.Size() + numCmds); - + for (i = 0; i < cols.Size(); ++i) { info.components[i] = cols[i]; @@ -931,7 +918,7 @@ World::AddStagedComponentsToEntity(Entity entity, AddStagedComponentCommand* cmd { info.components.SetSize(numCmds); } - + SizeT end = i + numCmds; IndexT cmdIndex = 0; for (; i < end; ++i, ++cmdIndex) @@ -1046,7 +1033,7 @@ World::GetColumnData(MemDb::TableId const tid, uint16_t partitionId, MemDb::Colu /** */ MemDb::RowId -World::GetInstance(Entity entity) +World::GetInstance(Entity entity) const { return this->GetEntityMapping(entity).instance; } @@ -1075,8 +1062,7 @@ World::CreateEntityTable(EntityTableCreateInfo const& info) if (info.components[Game::Entity::Traits::fixed_column_index] != GetComponentId() && info.components[Game::Position::Traits::fixed_column_index] != GetComponentId() && info.components[Game::Orientation::Traits::fixed_column_index] != GetComponentId() && - info.components[Game::Scale::Traits::fixed_column_index] != GetComponentId() - ) + info.components[Game::Scale::Traits::fixed_column_index] != GetComponentId()) { // always have owner and transform as first columns components[Game::Entity::Traits::fixed_column_index] = GetComponentId(); @@ -1106,7 +1092,7 @@ World::CreateEntityTable(EntityTableCreateInfo const& info) // "Prefilter" the processors with the new table (insert the table in the cache that accepts it) this->pipeline.CacheTable(categoryId, this->db->GetTable(categoryId).GetSignature()); - + return categoryId; } @@ -1199,10 +1185,22 @@ World::AllocateInstance(Entity entity, BlueprintId blueprint) #if _DEBUG // make sure the first column in always owner - n_assert(this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == Game::Entity::Traits::fixed_column_index); - n_assert(this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == Game::Position::Traits::fixed_column_index); - n_assert(this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == Game::Orientation::Traits::fixed_column_index); - n_assert(this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == Game::Scale::Traits::fixed_column_index); + n_assert( + this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == + Game::Entity::Traits::fixed_column_index + ); + n_assert( + this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == + Game::Position::Traits::fixed_column_index + ); + n_assert( + this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == + Game::Orientation::Traits::fixed_column_index + ); + n_assert( + this->db->GetTable(mapping.table).GetAttributeIndex(Game::GetComponentId()) == + Game::Scale::Traits::fixed_column_index + ); #endif // Set the owner of this instance @@ -1249,7 +1247,7 @@ World::AllocateInstance(Entity entity, TemplateId templateId, bool performInitia //------------------------------------------------------------------------------ /** */ -void +void World::FinalizeAllocate(Entity entity) { n_assert(this->IsValid(entity)); @@ -1303,9 +1301,8 @@ World::Migrate(Entity entity, MemDb::TableId newTableId) n_assert(this->IsValid(entity)); n_assert(this->HasInstance(entity)); EntityMapping mapping = this->GetEntityMapping(entity); - MemDb::RowId newInstance = MemDb::Table::MigrateInstance( - this->db->GetTable(mapping.table), mapping.instance, this->db->GetTable(newTableId), false - ); + MemDb::RowId newInstance = + MemDb::Table::MigrateInstance(this->db->GetTable(mapping.table), mapping.instance, this->db->GetTable(newTableId), false); this->entityMap[entity.index] = {newTableId, newInstance}; return newInstance; @@ -1361,8 +1358,8 @@ World::Migrate( void World::MoveInstance(MemDb::Table::Partition* partition, MemDb::RowId from, MemDb::RowId to) { - Game::Entity fromEntity = ((Game::Entity*)partition->columns[0])[from.index]; - Game::Entity toEntity = ((Game::Entity*)partition->columns[0])[to.index]; + Game::Entity fromEntity = ((Game::Entity*)partition->columns[Game::Entity::Traits::fixed_column_index])[from.index]; + Game::Entity toEntity = ((Game::Entity*)partition->columns[Game::Entity::Traits::fixed_column_index])[to.index]; if (!this->IsValid(fromEntity)) { // we need to add these instances new index to the to the freeids list, since it's been deleted. @@ -1401,7 +1398,8 @@ World::Defragment(MemDb::TableId tableId) // defragment the table. Any instances that has been deleted will be swap'n'popped, // which means we need to update the entity mapping. // The move callback is signaled BEFORE the swap has happened. - UNUSED(SizeT) numErased = this->db->GetTable(tableId).Defragment( + UNUSED(SizeT) + numErased = this->db->GetTable(tableId).Defragment( [this](MemDb::Table::Partition* partition, MemDb::RowId from, MemDb::RowId to) { this->MoveInstance(partition, from, to); @@ -1465,7 +1463,6 @@ World::ReinitializeComponent(Game::Entity entity, Game::ComponentId component, v } } - //------------------------------------------------------------------------------ /** */ @@ -1482,7 +1479,8 @@ World::RenderDebug() if (ImGui::IsItemHovered()) { //ImGui::SetTooltip("Processors are executed _after_ feature units for each event."); - ImGui::SetTooltip("The pipeline that makes up the frame.\nThis consists of multiple frame events, possibly bundled in frame batches."); + ImGui::SetTooltip("The pipeline that makes up the frame.\nThis consists of multiple frame events, possibly bundled " + "in frame batches."); } auto const events = this->pipeline.GetFrameEvents(); @@ -1512,14 +1510,14 @@ World::RenderDebug() ImGui::SameLine(); ImGui::Text(" | Async: %s", processor->async ? "true" : "false"); ImGui::SameLine(); - ImGui::TextColored({0.5,0.5,0.9f,1.0f}, " | Filter : %i", processor->filter); + ImGui::TextColored({0.5, 0.5, 0.9f, 1.0f}, " | Filter : %i", processor->filter); if (ImGui::IsItemHovered()) { ImGui::Indent(); auto const compsInc = Game::ComponentsInFilter(processor->filter); if (compsInc.Size() > 0) { - ImGui::TextColored({0.1f, 0.8f, 0.1f, 1.0f},"Includes entities with components:"); + ImGui::TextColored({0.1f, 0.8f, 0.1f, 1.0f}, "Includes entities with components:"); ImGui::BeginChild(1, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * compsInc.Size())); { for (auto c : compsInc) @@ -1606,7 +1604,13 @@ World::RenderDebug() ImGui::BeginGroup(); ImGui::Text("[%i] ", entityIndex); ImGui::SameLine(); - ImGui::TextColored({1, 0.3f, 0, 1}, "tid:%i, partition: %i, index: %i", entity.table, entity.instance.partition, entity.instance.index); + ImGui::TextColored( + {1, 0.3f, 0, 1}, + "tid:%i, partition: %i, index: %i", + entity.table, + entity.instance.partition, + entity.instance.index + ); if (entity.table != MemDb::TableId::Invalid()) { ImGui::TextDisabled("- %s", this->db->GetTable(entity.table).name.Value()); @@ -1682,10 +1686,7 @@ World::Override(World* src, World* dst) { Game::Entity& entity = entities[i]; entity.world = dst->worldId; - MemDb::RowId row = { - .partition = view.partitionId, - .index = i - }; + MemDb::RowId row = {.partition = view.partitionId, .index = i}; dst->InitializeAllComponents(entity, view.tableId, row); } } diff --git a/code/application/game/world.h b/code/application/game/world.h index 03634f9ee..fab6d9103 100644 --- a/code/application/game/world.h +++ b/code/application/game/world.h @@ -20,8 +20,14 @@ #include "frameevent.h" #include "util/fourcc.h" -namespace MemDb { class Database; } -namespace Util { class Blob; } +namespace MemDb +{ +class Database; +} +namespace Util +{ +class Blob; +} namespace Game { @@ -78,7 +84,9 @@ class World World(WorldHash hash, WorldId id); ~World(); + /// Returns the world hash for this world. WorldHash GetWorldHash() const; + /// Returns the world ID for this world. This corresponds to Game::Entity::world. WorldId GetWorldId() const; /// Create a new empty entity @@ -87,71 +95,75 @@ class World Entity CreateEntity(EntityCreateInfo const& info); /// Delete entity void DeleteEntity(Entity entity); + /// Check if an entity ID is still valid. - bool IsValid(Entity entity); + bool IsValid(Entity entity) const; /// Check if an entity has an instance. It might be valid, but not have received an instance just after it has been created. - bool HasInstance(Entity entity); + bool HasInstance(Entity entity) const; + /// Returns the entity mapping of an entity - EntityMapping GetEntityMapping(Entity entity); - /// Check if entity has a specific component. (SLOW!) - bool HasComponent(Entity entity, ComponentId component); + EntityMapping GetEntityMapping(Entity entity) const; + + /// Create a component. This queues the component in a command buffer to be added later + template + TYPE* AddComponent(Entity entity); + /// Queues a component to be added to the entity in a command buffer. + void* AddComponent(Entity entity, ComponentId component); + /// Check if entity has a specific component. template - bool HasComponent(Entity entity); - /// Get instance of entity - MemDb::RowId GetInstance(Entity entity); + bool HasComponent(Entity entity) const; + /// Check if entity has a specific component. (SLOW!) + bool HasComponent(Entity entity, ComponentId component) const; + /// Remove a component from an entity template void RemoveComponent(Entity entity); /// Remove a component from an entity void RemoveComponent(Entity entity, ComponentId component); + /// Set the value of an entitys component template void SetComponent(Entity entity, TYPE const& value); /// Get an entitys component template TYPE GetComponent(Entity entity); - /// Create a component. This queues the component in a command buffer to be added later - template - TYPE* AddComponent(Entity entity); - /// Add a component to an entity. This queues the component in a command buffer to be added later. - void* AddComponent(Entity entity, ComponentId component); - /// Get a decay buffer for the given component - ComponentDecayBuffer const GetDecayBuffer(ComponentId component); - /// Set the value of a component by providing a pointer and type size - void SetComponentValue(Entity entity, ComponentId component, void* value, uint64_t size); - - /// Set the value of a component by providing a pointer and type size, then reinitialize the component - void ReinitializeComponent(Entity entity, ComponentId component, void* value, uint64_t size); - + /// Mark an entity as modified in its table. + void MarkAsModified(Game::Entity entity); + /// Query the entity database using specified filter set. This does NOT wait for resources to be available. Dataset Query(Filter filter); /// Query a subset of tables using a specified filter set. Modifies the tables array so that it only contains valid tables. /// This does NOT wait for resources to be available. Dataset Query(Filter filter, Util::Array& tids); - /// Get the entity database. Be careful when directly modifying the database, as some information is only kept track of via the World. - Ptr GetDatabase(); - /// Create a table in the entity database that has a specific set of components - MemDb::TableId CreateEntityTable(EntityTableCreateInfo const& info); - - /// Get the frame pipeline - FramePipeline& GetFramePipeline(); - - /// Mark an entity as modified in its table. - void MarkAsModified(Game::Entity entity); + /// Get a decay buffer for the given component + ComponentDecayBuffer const GetDecayBuffer(ComponentId component); /// preload a level that can be instantiated PackedLevel* PreloadLevel(Util::String const& path); /// unload a preloaded level void UnloadLevel(PackedLevel* level); - /// Export the world as a level void ExportLevel(Util::String const& path); + /// Get the frame pipeline + FramePipeline& GetFramePipeline(); + + /// Get the entity database. Be careful when directly modifying the database, as some information is only kept track of via the World. + Ptr GetDatabase(); + // -- Internal methods -- Use with caution! -- + /// Get instance of entity + MemDb::RowId GetInstance(Entity entity) const; + /// Set the value of a component by providing a pointer and type size + void SetComponentValue(Entity entity, ComponentId component, void* value, uint64_t size); + /// Set the value of a component by providing a pointer and type size, then reinitialize the component + void ReinitializeComponent(Entity entity, ComponentId component, void* value, uint64_t size); + /// Create a table in the entity database that has a specific set of components + MemDb::TableId CreateEntityTable(EntityTableCreateInfo const& info); /// copies and overrides dst with src. This is extremely destructive - make sure you understand the implications! static void Override(World* src, World* dst); /// Allocate an entity id. Use this with caution! @@ -186,27 +198,30 @@ class World friend class GameServer; friend class BlueprintManager; friend class PackedLevel; - + struct AllocateInstanceCommand { - Game::Entity entity; - TemplateId tid; + Game::Entity entity = Game::Entity::Invalid(); + TemplateId tid = TemplateId::Invalid(); }; + struct DeallocInstanceCommand { - Game::Entity entity; + Game::Entity entity = Game::Entity::Invalid(); }; + struct AddStagedComponentCommand { - Game::Entity entity; - ComponentId componentId; - SizeT dataSize; - void* data; + Game::Entity entity = Game::Entity::Invalid(); + ComponentId componentId = ComponentId::Invalid(); + SizeT dataSize = 0; + void* data = nullptr; }; + struct RemoveComponentCommand { - Entity entity; - ComponentId componentId; + Entity entity = Game::Entity::Invalid(); + ComponentId componentId = ComponentId::Invalid(); }; // These functions are called from game server @@ -219,20 +234,17 @@ class World void ManageEntities(); void Reset(); void PrefilterProcessors(); - /// Check if the database is fully prefiltered. - bool Prefiltered() const; /// Clears all decay buffers. This is called by the game server automatically. void ClearDecayBuffers(); void ExecuteRemoveComponentCommands(); - void AddStagedComponentsToEntity(Entity entity, AddStagedComponentCommand* cmds, SizeT numCmds); - void RemoveComponentsFromEntity(Entity entity, RemoveComponentCommand* cmds, SizeT numCmds); - /// Get total number of instances in an entity table SizeT GetNumInstances(MemDb::TableId tid); - + + /// Migrate an entity from it's current table to a different table MemDb::RowId Migrate(Entity entity, MemDb::TableId newTable); + /// Migrate an array of entities within the same table to a different table void Migrate( Util::Array const& entities, MemDb::TableId fromTable, @@ -240,13 +252,20 @@ class World Util::FixedArray& newInstances ); + /// Copies the component to the decay table void DecayComponent(ComponentId component, MemDb::TableId tableId, MemDb::ColumnIndex column, MemDb::RowId instance); + /// Move a instance/row within a partition void MoveInstance(MemDb::Table::Partition* partition, MemDb::RowId from, MemDb::RowId to); /// Run OnInit on all components. Use with caution, since they can only be initialized once and the function doesn't check for this. void InitializeAllComponents(Entity entity, MemDb::TableId tableId, MemDb::RowId row); + /// Adds all components in cmds to entity + void AddStagedComponentsToEntity(Entity entity, AddStagedComponentCommand* cmds, SizeT numCmds); + /// Removes all components in cmds from entity + void RemoveComponentsFromEntity(Entity entity, RemoveComponentCommand* cmds, SizeT numCmds); + /// used to allocate entity ids for this world EntityPool pool; /// Number of entities alive @@ -261,26 +280,23 @@ class World WorldId worldId; /// maps from blueprint to a table that has the same signature Util::HashTable blueprintToTableMap; - /// + /// Stores all deferred allocation commands Util::Queue allocQueue; - /// + /// Stores all deferred deallocation commands Util::Queue deallocQueue; - /// + /// Stores all deferred add staged component commands Util::Array addStagedQueue; - /// + /// Stores all deferred remove component commands Util::Array removeComponentQueue; - - /// allocator for staged components + /// Allocator for staged components Memory::ArenaAllocator<4096_KB> componentStageAllocator; - - /// set to true if the caches for the frame pipeline is valid + /// Set to true if the caches for the frame pipeline are valid bool cacheValid = false; - - /// the frame pipeline for this world + /// The frame pipeline for this world FramePipeline pipeline; - + /// The default table that empty entities are instantiated into MemDb::TableId defaultTableId; - + /// Contains all the component decay buffers. Lookup directly via ComponentId Util::FixedArray componentDecayTable; }; @@ -336,7 +352,8 @@ World::GetComponent(Entity entity) { #if NEBULA_DEBUG n_assert2( - !this->pipeline.IsRunningAsync(), "Getting components from entities while executing an async processor is currently not supported!" + !this->pipeline.IsRunningAsync(), + "Getting components from entities while executing an async processor is currently not supported!" ); #endif @@ -376,7 +393,7 @@ World::RemoveComponent(Entity entity) */ template inline bool -World::HasComponent(Game::Entity entity) +World::HasComponent(Game::Entity entity) const { return this->HasComponent(entity, Game::GetComponentId()); } @@ -395,7 +412,9 @@ World::AddComponent(Entity entity) just introducing a simple mutex. */ #if NEBULA_DEBUG - n_assert2(!this->pipeline.IsRunningAsync(), "Adding component to entities while in an async processor is currently not supported!"); + n_assert2( + !this->pipeline.IsRunningAsync(), "Adding component to entities while in an async processor is currently not supported!" + ); #endif Game::ComponentId id = Game::GetComponentId(); #if NEBULA_DEBUG @@ -427,13 +446,18 @@ World::AddComponent(Entity entity) return data; } -template<> Game::Position World::GetComponent(Entity entity); -template<> Game::Orientation World::GetComponent(Entity entity); -template<> Game::Scale World::GetComponent(Entity entity); - -template<> void World::SetComponent(Entity entity, Game::Position const&); -template<> void World::SetComponent(Entity entity, Game::Orientation const&); -template<> void World::SetComponent(Entity entity, Game::Scale const&); - +template <> +Game::Position World::GetComponent(Entity entity); +template <> +Game::Orientation World::GetComponent(Entity entity); +template <> +Game::Scale World::GetComponent(Entity entity); + +template <> +void World::SetComponent(Entity entity, Game::Position const&); +template <> +void World::SetComponent(Entity entity, Game::Orientation const&); +template <> +void World::SetComponent(Entity entity, Game::Scale const&); } // namespace Game