<NME> EntityWorld.cpp
<BEF> /*******************************
Copyright (c) 2016-2022 Grégoire Angerand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**********************************/

#include "EntityWorld.h"


#include <y/utils/log.h>
#include <y/utils/format.h>

#include <yave/assets/AssetLoadingContext.h>


namespace yave {
namespace ecs {

static auto create_component_containers() {
    y_profile();

    core::Vector<std::unique_ptr<ComponentContainerBase>> containers;
    for(const auto* poly_base = ComponentContainerBase::_y_serde3_poly_base.first; poly_base; poly_base = poly_base->next) {
        if(poly_base->create) {
            std::unique_ptr<ComponentContainerBase> container = poly_base->create();
            y_debug_assert(container);

            const ComponentTypeIndex id = container->type_id();
            containers.set_min_size(id + 1);
            containers[id] = std::move(container);
        }
    }
    return containers;
}

EntityWorld::EntityWorld() : _containers(create_component_containers()) {
}

EntityWorld::~EntityWorld() {
    for(auto& system : _systems) {
        system->destroy(*this);
    }
    _containers.clear();
}

EntityWorld::EntityWorld(EntityWorld&& other) {
    swap(other);
}

EntityWorld& EntityWorld::operator=(EntityWorld&& other) {
    swap(other);
    return *this;
}

void EntityWorld::swap(EntityWorld& other) {
    if(this != &other) {
        std::swap(_containers, other._containers);
        std::swap(_tags, other._tags);
        std::swap(_entities, other._entities);
        std::swap(_required_components, other._required_components);
        std::swap(_systems, other._systems);
        std::swap(_world_components, other._world_components);
    }
    for(const ComponentTypeIndex c : _required_components) {
        unused(c);
	return container.get();
}

serde2::Result EntityWorld::serialize(WritableAssetArchive& writer) const {
	if(!writer(u64(_entities.size()))) {
		return core::Err();
	}

void EntityWorld::update(float dt) {
    y_profile();
    for(auto& system : _systems) {
        y_profile_dyn_zone(system->name().data());
        system->update(*this, dt);
        system->schedule_fixed_update(*this, dt);
    }
}

usize EntityWorld::entity_count() const {
    return _entities.size();
}

bool EntityWorld::exists(EntityId id) const {
    return _entities.contains(id);
}

serde2::Result EntityWorld::deserialize(ReadableAssetArchive& reader) {
	*this = EntityWorld();

	u64 entity_count = 0;
	if(!reader(entity_count)) {
		return core::Err();
        ComponentContainerBase* container = find_container(info.type_id);
        y_debug_assert(container);
        container->add(*this, id);
    }
		if(!reader(index)) {
			return core::Err();
		}
		_entities.create_with_index(EntityIndex(index)).unwrap();
	}
	y_debug_assert(_entities.size() == entity_count);

            comp->add_to(*this, id);
        }
    }
    return id;
}

void EntityWorld::remove_entity(EntityId id) {
    check_exists(id);
    for(auto& container : _containers) {
        if(container) {
            container->remove(id);
        }
    }
    for(auto& [tag, container] : _tags) {
        unused(tag);
        if(container.contains(id)) {
            container.erase(id);
        }
    }
    _entities.recycle(id);
}

EntityId EntityWorld::id_from_index(u32 index) const {
    return _entities.id_from_index(index);
}

EntityPrefab EntityWorld::create_prefab(EntityId id) const {
    check_exists(id);
    EntityPrefab prefab;
    for(auto& container : _containers) {
        if(container) {
            if(!container->contains(id)) {
                continue;
            }
            auto box = container->create_box(id);
            if(!box) {
                log_msg(fmt("% is not copyable and was excluded from prefab", container->runtime_info().type_name), Log::Warning);
            }
            prefab.add(std::move(box));
        }
    }
    return prefab;
}

core::Span<EntityId> EntityWorld::component_ids(ComponentTypeIndex type_id) const {
    return find_container(type_id)->ids();
}

core::Span<EntityId> EntityWorld::recently_added(ComponentTypeIndex type_id) const {
    return find_container(type_id)->recently_added();
}

core::Span<EntityId> EntityWorld::with_tag(const core::String& tag) const {
    const SparseIdSetBase* set = tag_set(tag);
    return set ? set->ids() : core::Span<EntityId>();
}

const SparseIdSet* EntityWorld::raw_tag_set(const core::String& tag) const {
    if(const auto it = _tags.find(tag); it != _tags.end()) {
        return &it->second;
    }
    return nullptr;
}

const SparseIdSetBase* EntityWorld::tag_set(const core::String& tag) const {
    if(tag.is_empty()) {
        return nullptr;
    }

    if(tag[0] == '@') {
        for(const auto& container : _containers) {
            if(container->runtime_info().clean_component_name() == tag.sub_str(1)) {
                return &container->id_set();
            }
        }
        return nullptr;
    }

    if(tag[0] == '!') {
        y_fatal("'!' tags can't have a set, use queries instead");
    }

    return raw_tag_set(tag);
}

void EntityWorld::add_tag(EntityId id, const core::String& tag) {
    check_exists(id);
    y_always_assert(!is_tag_implicit(tag), "Implicit tags can't be added directly");
    _tags[tag].insert(id);
}

void EntityWorld::remove_tag(EntityId id, const core::String& tag) {
    check_exists(id);
    y_always_assert(!is_tag_implicit(tag), "Implicit tags can't be removed directly");
    auto& tag_set = _tags[tag];
    if(tag_set.contains(id)) {
        tag_set.erase(id);
    }
}

void EntityWorld::clear_tag(const core::String& tag) {
    y_always_assert(!is_tag_implicit(tag), "Implicit tags can't be removed directly");
    _tags.erase(tag);
}

bool EntityWorld::has_tag(EntityId id, const core::String& tag) const {
    check_exists(id);
    const SparseIdSetBase* set = tag_set(tag);
    return set ? set->contains(id) : false;
}

bool EntityWorld::is_tag_implicit(std::string_view tag) {
    return !tag.empty() && (tag[0] == '@' || tag[0] == '!');
}

core::Span<ComponentTypeIndex> EntityWorld::required_components() const {
    return _required_components;
}

std::string_view EntityWorld::component_type_name(ComponentTypeIndex type_id) const {
    return find_container(type_id)->runtime_info().clean_component_name();
}

const ComponentContainerBase* EntityWorld::find_container(ComponentTypeIndex type_id) const {
    y_debug_assert(_containers.size() > type_id);
    y_debug_assert(_containers[type_id] != nullptr);
    return _containers[type_id].get();
}

ComponentContainerBase* EntityWorld::find_container(ComponentTypeIndex type_id) {
    y_debug_assert(_containers.size() > type_id);
    y_debug_assert(_containers[type_id] != nullptr);
    return _containers[type_id].get();
}

void EntityWorld::check_exists(EntityId id) const {
    y_always_assert(exists(id), "Entity doesn't exists");
}


void EntityWorld::post_deserialize() {
    auto patched = create_component_containers();
    for(auto& container : _containers) {
        if(container) {
            const ComponentTypeIndex id = container->type_id();
            patched.set_min_size(id + 1);
            patched[id] = std::move(container);
        }
    }
    _containers = std::move(patched);

    for(auto& system : _systems) {
        y_debug_assert(system);
        system->reset(*this);
    }


    for(const ComponentTypeIndex c : _required_components) {
        unused(c);
        y_always_assert(find_container(c), "Required component container not found");
    }
}

}
}


<MSG> Added World as an asset type

<DFF> @@ -82,7 +82,16 @@ ComponentContainerBase* EntityWorld::container(ComponentTypeIndex type) {
 	return container.get();
 }
 
+
+struct EntityWorldHeader {
+	y_serde2(serde2::check(fs::magic_number, AssetType::World, u32(1)))
+};
+
 serde2::Result EntityWorld::serialize(WritableAssetArchive& writer) const {
+	if(!writer(EntityWorldHeader())) {
+		return core::Err();
+	}
+
 	if(!writer(u64(_entities.size()))) {
 		return core::Err();
 	}
@@ -107,6 +116,13 @@ serde2::Result EntityWorld::serialize(WritableAssetArchive& writer) const {
 serde2::Result EntityWorld::deserialize(ReadableAssetArchive& reader) {
 	*this = EntityWorld();
 
+	{
+		EntityWorldHeader header;
+		if(!reader(header)) {
+			return core::Err();
+		}
+	}
+
 	u64 entity_count = 0;
 	if(!reader(entity_count)) {
 		return core::Err();
@@ -117,7 +133,9 @@ serde2::Result EntityWorld::deserialize(ReadableAssetArchive& reader) {
 		if(!reader(index)) {
 			return core::Err();
 		}
-		_entities.create_with_index(EntityIndex(index)).unwrap();
+		if(!_entities.create_with_index(EntityIndex(index))) {
+			return core::Err();
+		}
 	}
 	y_debug_assert(_entities.size() == entity_count);
 

Added World as an asset type