#pragma once #include #include #include #include #include "common.h" #include "log.h" #include "Entity.h" #include "SlotVector.h" #include "SmallOptional.h" namespace Swan { class EntityCollection; class EntityRef { public: EntityRef() = default; EntityRef(EntityCollection *coll, size_t index, size_t generation): coll_(coll), index_(index), generation_(generation) {} template EntityRef &then(Func func); bool hasValue(); Entity *get(); private: EntityCollection *coll_; size_t index_; size_t generation_; }; class EntityCollection { public: struct Factory { const std::string name; std::unique_ptr (*const create)(std::string name); }; template using OptEnt = SmallOptional< Ent, SmallOptionalInitialBytesPolicy>; template using SlotPolicy = SlotVectorDefaultSentinel; virtual ~EntityCollection() = default; template EntityRef spawn(Args&&... args); virtual const std::string &name() = 0; virtual std::type_index type() = 0; virtual size_t size() = 0; virtual Entity *get(size_t idx) = 0; virtual Entity *get(size_t idx, size_t generation) = 0; virtual EntityRef spawn(const Context &ctx, const Entity::PackObject &obj) = 0; virtual void update(const Context &ctx, float dt) = 0; virtual void tick(const Context &ctx, float dt) = 0; virtual void draw(const Context &ctx, Cygnet::Renderer &rnd) = 0; virtual void erase(size_t idx, size_t generation) = 0; private: virtual void *getEntityVector() = 0; virtual size_t nextGeneration() = 0; }; template class EntityCollectionImpl: public EntityCollection { public: EntityCollectionImpl(std::string name): name_(std::move(name)) {} size_t size() override { return entities_.size(); } Entity *get(size_t idx) override { return entities_[idx].get(); } Entity *get(size_t idx, size_t generation) override; const std::string &name() override { return name_; } std::type_index type() override { return typeid(Ent); } EntityRef spawn(const Context &ctx, const Entity::PackObject &obj) override; void update(const Context &ctx, float dt) override; void tick(const Context &ctx, float dt) override; void draw(const Context &ctx, Cygnet::Renderer &rnd) override; void erase(size_t idx, size_t generation) override; private: void *getEntityVector() override { return (void *)&entities_; } size_t nextGeneration() override { return generation_++; } const std::string name_; SlotVector, SlotPolicy> entities_; size_t generation_ = 0; }; /* * EntityRef */ template inline EntityRef &EntityRef::then(Func func) { Entity *ent = coll_->get(index_, generation_); if (ent != nullptr) func(ent); return *this; } inline Entity *EntityRef::get() { return coll_->get(index_, generation_); } inline bool EntityRef::hasValue() { return coll_->get(index_, generation_) != nullptr; } /* * EntityCollection */ template inline EntityRef EntityCollection::spawn(Args&&... args) { auto entities = (SlotVector, SlotPolicy> *)getEntityVector(); size_t generation = nextGeneration(); size_t idx = entities->emplace(); OptEnt &ent = (*entities)[idx]; ent.emplace(std::forward(args)...); ent->index_ = idx; ent->generation_ = generation; if constexpr (std::is_base_of_v) { BodyTrait::Body &body = ent->get(BodyTrait::Tag{}); body.pos -= body.size / 2; } return { this, idx, generation }; } /* * EntityCollectionImpl */ template inline Entity *EntityCollectionImpl::get(size_t idx, size_t generation) { if (idx >= entities_.size()) return nullptr; auto &e = entities_[idx]; // We don't even need to check if e.hasValue(), because if it doesn't, // its generation will be 0xffff... and the check will fail if (e->generation_ != generation) return nullptr; return e.get(); } template inline EntityRef EntityCollectionImpl::spawn(const Context &ctx, const Entity::PackObject &obj) { size_t generation = nextGeneration(); size_t idx = entities_.emplace(); OptEnt &ent = entities_[idx]; ent.emplace(ctx, obj); ent->index_ = idx; ent->generation_ = generation; return { this, idx, generation }; } template inline void EntityCollectionImpl::update(const Context &ctx, float dt) { ZoneScopedN(typeid(Ent).name()); for (auto &ent: entities_) { ZoneScopedN("update"); ent->update(ctx, dt); } } template inline void EntityCollectionImpl::tick(const Context &ctx, float dt) { ZoneScopedN(typeid(Ent).name()); for (auto &ent: entities_) { ZoneScopedN("tick"); ent->tick(ctx, dt); } } template inline void EntityCollectionImpl::draw(const Context &ctx, Cygnet::Renderer &rnd) { ZoneScopedN(typeid(Ent).name()); for (auto &ent: entities_) { ZoneScopedN("draw"); ent->draw(ctx, rnd); } } template inline void EntityCollectionImpl::erase(size_t idx, size_t generation) { ZoneScopedN(typeid(Ent).name()); OptEnt &ent = entities_[idx]; if (!ent.hasValue() || ent->generation_ != generation) { warn << "Erasing wrong entity " << typeid(Ent).name() << '[' << idx << "]!"; return; } entities_.erase(idx); } }