Browse Source

store entities contiguously in memory

There are still some things left to do:
* Implement despawn
* Maybe shrink SlotVector if there's a lot more free slots than occupied slots
* Implement iteration over entities
opengl-renderer-broken
Martin Dørum 4 years ago
parent
commit
4a8694dc27

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
/build
.tags
compile_commands.json
.clangd

+ 0
- 9
CMakeLists.txt View File

@@ -19,15 +19,6 @@ if(CMAKE_GENERATOR STREQUAL "Ninja")
add_compile_options(-fdiagnostics-color=always)
endif()

if(CMAKE_EXPORT_COMPILE_COMMANDS)
add_custom_target(compile_commands ALL
BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND
compdb -p ${CMAKE_CURRENT_BINARY_DIR} list
> ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json)
endif()

add_compile_options(-std=c++2a -Wall -Wextra -Wpedantic -Wno-unused-parameter)
if(CMAKE_BUILD_TYPE STREQUAL Sanitize OR CMAKE_BUILD_TYPE STREQUAL "")
message(STATUS "Build mode: Sanitize")

+ 3
- 4
core.mod/src/DefaultWorldGen.cc View File

@@ -74,9 +74,8 @@ void DefaultWorldGen::genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) {
}
}

Swan::BodyTrait::HasBody *DefaultWorldGen::spawnPlayer(const Swan::Context &ctx) {
Swan::EntityRef DefaultWorldGen::spawnPlayer(const Swan::Context &ctx) {
int x = 0;
return dynamic_cast<Swan::BodyTrait::HasBody *>(
ctx.plane.spawnEntity(std::make_unique<PlayerEntity>(
ctx, Swan::Vec2{ (float)x, (float)grassLevel(perlin_, x) - 4 })));
return ctx.plane.spawnEntity<PlayerEntity>(
ctx, Swan::Vec2{ (float)x, (float)grassLevel(perlin_, x) - 4 });
}

+ 1
- 1
core.mod/src/DefaultWorldGen.h View File

@@ -18,7 +18,7 @@ public:
void drawBackground(const Swan::Context &ctx, Swan::Win &win, Swan::Vec2 pos) override;
SDL_Color backgroundColor(Swan::Vec2 pos) override;
void genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) override;
Swan::BodyTrait::HasBody *spawnPlayer(const Swan::Context &ctx) override;
Swan::EntityRef spawnPlayer(const Swan::Context &ctx) override;

private:
Swan::Tile::ID genTile(Swan::TilePos pos);

+ 4
- 4
core.mod/src/main.cc View File

@@ -69,14 +69,14 @@ public:

void onTileBreak(const Swan::Context &ctx, Swan::TilePos pos, Swan::Tile &tile) {
if (tile.dropped_item_) {
ctx.plane.spawnEntity(std::make_unique<ItemStackEntity>(
ctx, pos, *tile.dropped_item_));
ctx.plane.spawnEntity<ItemStackEntity>(
ctx, pos, *tile.dropped_item_);
}
}

Swan::EventListener break_listener_;
};

extern "C" std::unique_ptr<Swan::Mod> mod_create(Swan::World &world) {
return std::make_unique<CoreMod>(world);
extern "C" Swan::Mod *mod_create(Swan::World &world) {
return new CoreMod(world);
}

+ 193
- 0
libswan/include/swan/Collection.h View File

@@ -0,0 +1,193 @@
#pragma once

#include <vector>
#include <string>
#include <typeindex>

#include "common.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<typename Func>
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<EntityCollection> (*const create)(std::string name);
};

template<typename Ent>
struct EntWrapper {
size_t generation;
Ent ent;
bool operator==(const EntWrapper &other) const {
return generation == other.generation;
}
};

template<typename Ent>
struct OptionalPolicy {
static void setEmpty(unsigned char *ptr) {
((EntWrapper<Ent> *)ptr)->generation = ~0ull;
}
static bool isEmpty(const unsigned char *ptr) {
return ((EntWrapper<Ent> *)ptr)->generation == ~0ull;
}
};

template<typename Ent>
using OptEnt = SmallOptional<EntWrapper<Ent>, OptionalPolicy<Ent>>;

virtual ~EntityCollection() = default;

template<typename Ent, typename... Args>
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 version) = 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, Win &win) = 0;

private:
virtual void *getEntityVector() = 0;
virtual size_t nextGeneration() = 0;
};

template<typename Ent>
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]->ent; }
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, Win &win) override;

private:
void *getEntityVector() override { return (void *)&entities_; }
size_t nextGeneration() override { return generation_++; }

const std::string name_;
SlotVector<EntityCollection::OptEnt<Ent>> entities_;
size_t generation_ = 0;
};

/*
* EntityRef
*/

template<typename Func>
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<typename Ent, typename... Args>
inline EntityRef EntityCollection::spawn(Args&&... args) {
auto entities = (SlotVector<OptEnt<Ent>> *)getEntityVector();

size_t generation = nextGeneration();
size_t idx = entities->emplace(EntWrapper<Ent>{
generation, std::move(Ent(std::forward<Args>(args)...)) });

return { this, idx, generation };
}

/*
* EntityCollectionImpl
*/

template<typename Ent>
inline Entity *EntityCollectionImpl<Ent>::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->ent;
}

template<typename Ent>
inline EntityRef EntityCollectionImpl<Ent>::spawn(const Context &ctx, const Entity::PackObject &obj) {
size_t generation = nextGeneration();
size_t idx = entities_.emplace(EntWrapper<Ent>{
generation, std::move(Ent(ctx, obj)) });

return { this, idx, generation };
}

template<typename Ent>
inline void EntityCollectionImpl<Ent>::update(const Context &ctx, float dt) {
for (auto &ent: entities_)
ent->ent.update(ctx, dt);
}

template<typename Ent>
inline void EntityCollectionImpl<Ent>::tick(const Context &ctx, float dt) {
for (auto &ent: entities_)
ent->ent.tick(ctx, dt);
}

template<typename Ent>
inline void EntityCollectionImpl<Ent>::draw(const Context &ctx, Win &win) {
for (auto &ent: entities_)
ent->ent.draw(ctx, win);
}

}

+ 4
- 1
libswan/include/swan/Entity.h View File

@@ -14,7 +14,7 @@ class World;
class WorldPlane;
class Game;

class Entity {
class Entity: NonCopyable {
public:
using PackObject = std::unordered_map<std::string_view, msgpack::object>;

@@ -23,6 +23,9 @@ public:
std::unique_ptr<Entity> (*create)(const Context &ctx, const PackObject &obj);
};

Entity() = default;
Entity(Entity &&) = default;

virtual ~Entity() = default;

virtual void draw(const Context &ctx, Win &win) {}

+ 3
- 3
libswan/include/swan/EventEmitter.h View File

@@ -20,12 +20,12 @@ public:
EventListener(EventEmitterInterface *emitter, size_t id):
emitter_(emitter), id_(id) {}

EventListener(EventListener &&other):
EventListener(EventListener &&other) noexcept:
emitter_(other.emitter_), id_(other.id_) {
other.emitter_ = nullptr;
}

EventListener &operator=(EventListener &&other) {
EventListener &operator=(EventListener &&other) noexcept {
emitter_ = other.emitter_;
id_ = other.id_;
other.emitter_ = nullptr;
@@ -69,7 +69,7 @@ public:
}

private:
SlotVector<Callback, SlotVectorDefaultSentinel<nullptr_t>> callbacks_;
SlotVector<Callback, SlotVectorDefaultSentinel<std::nullptr_t>> callbacks_;
};

}

+ 1
- 1
libswan/include/swan/Game.h View File

@@ -20,7 +20,7 @@ public:
mouse_pos_(0, 0) {}

std::optional<ModWrapper> loadMod(std::string path, World &world);
void createWorld(const std::string &worldgen, std::vector<std::string> mods);
void createWorld(const std::string &worldgen, const std::vector<std::string> &mods);

void onKeyDown(SDL_Keysym sym) {
pressed_keys_[sym.scancode] = true;

+ 12
- 9
libswan/include/swan/Mod.h View File

@@ -4,12 +4,14 @@
#include <string>
#include <vector>
#include <memory>
#include <type_traits>
#include <SDL2/SDL.h>

#include "Tile.h"
#include "Item.h"
#include "WorldGen.h"
#include "Entity.h"
#include "Collection.h"
#include "Resource.h"
#include "OS.h"
#include "util.h"
@@ -30,20 +32,21 @@ public:
void registerWorldGen(const std::string &name) {
worldgens_.push_back(WorldGen::Factory{
.name = name_ + "::" + name,
.create = [](World &world) {
return static_cast<std::unique_ptr<WorldGen>>(
std::make_unique<WG>(world));
.create = [](World &world) -> std::unique_ptr<WorldGen> {
return std::make_unique<WG>(world);
}
});
}

template<typename Ent>
void registerEntity(const std::string &name) {
entities_.push_back(Entity::Factory{
static_assert(
std::is_move_constructible_v<Ent>,
"Entities must be movable");
entities_.push_back(EntityCollection::Factory{
.name = name_ + "::" + name,
.create = [](const Context &ctx, const Entity::PackObject &obj) {
return static_cast<std::unique_ptr<Entity>>(
std::make_unique<Ent>(ctx, obj));
.create = [](std::string name) -> std::unique_ptr<EntityCollection> {
return std::make_unique<EntityCollectionImpl<Ent>>(std::move(name));
}
});
}
@@ -53,7 +56,7 @@ public:
std::vector<Tile::Builder> tiles_;
std::vector<Item::Builder> items_;
std::vector<WorldGen::Factory> worldgens_;
std::vector<Entity::Factory> entities_;
std::vector<EntityCollection::Factory> entities_;
};

class ModWrapper {
@@ -73,7 +76,7 @@ public:
Iter<std::unique_ptr<Tile>> buildTiles(const ResourceManager &resources);
Iter<std::unique_ptr<Item>> buildItems(const ResourceManager &resources);
Iter<WorldGen::Factory> getWorldGens();
Iter<Entity::Factory> getEntities();
Iter<EntityCollection::Factory> getEntities();

std::unique_ptr<Mod> mod_;
std::string path_;

+ 1
- 4
libswan/include/swan/SlotVector.h View File

@@ -47,10 +47,6 @@ public:
size_t operator-(const Iterator &other) const {
return idx_ - other.idx_;
}
void operator=(const Iterator &other) {
idx_ = other.idx_;
vec_ = other.vec_;
}

private:
SlotVector<T, Sentinel> *vec_;
@@ -59,6 +55,7 @@ public:

T &operator[](size_t idx) { return vec_[idx]; }
T &at(size_t idx) { return vec_.at(idx); }
size_t size() { return vec_.size(); }
bool empty() { return vec_.size() == free_.size(); }

Iterator begin() { Iterator it(this, 0); it.seek(); return it; }

+ 173
- 0
libswan/include/swan/SmallOptional.h View File

@@ -0,0 +1,173 @@
#pragma once

#include <new>
#include <stdlib.h>
#include <utility>
#include <iostream>

namespace Swan {

// The initial bytes policy assumes that no T object will ever start with
// 'size' 'empty_byte' bytes. This works for types where, for example,
// the first few bytes are a pointer.
template<typename T, size_t size = sizeof(T), unsigned char empty_byte = 0xFF>
struct SmallOptionalInitialBytesPolicy {
static_assert(sizeof(T) >= size);
static void setEmpty(unsigned char *ptr) {
for (size_t i = 0; i < size; ++i)
ptr[i] = empty_byte;
}

static bool isEmpty(const unsigned char *ptr) {
for (size_t i = 0; i < size; ++i)
if (ptr[i] != empty_byte)
return false;
return true;
}
};

// The value policy lets you define a particular T value which can be a sentinel
// and is considered different from valid T values according to T::operator==.
// For example, if T is int, -1 could be a sentinel if only positive values are expected.
template<typename T, T empty_value>
struct SmallOptionalValuePolicy {
static void setEmpty(unsigned char *ptr) {
*(T *)ptr = empty_value;
}

static bool isEmpty(const unsigned char *ptr) {
return *(T *)ptr == empty_value;
}
};

// This is probably UB but I don't care, it avoids wasting 8 bytes per entity
template<typename T, typename Policy = SmallOptionalInitialBytesPolicy<T>>
class SmallOptional {
public:
SmallOptional() {
Policy::setEmpty(data_);
}
SmallOptional(const T &other) {
new (data_) T(other);
}
SmallOptional(T &&other) noexcept {
new (data_) T(std::move(other));
}
SmallOptional(const SmallOptional<T, Policy> &other) {
if (other.hasValue()) {
new (data_) T(*other.get());
} else {
Policy::setEmpty(data_);
}
}
SmallOptional(SmallOptional<T, Policy> &&other) noexcept {
if (other.hasValue()) {
new (data_) T(std::move(*other));
} else {
Policy::setEmpty(data_);
}
}
~SmallOptional() {
if (hasValue()) {
get()->~T();
}
}

void reset() {
if (hasValue()) {
get()->~T();
Policy::setEmpty(data_);
}
}

template<typename... Args>
T &emplace(Args&&... args) {
if (hasValue()) {
get()->~T();
}

new (data_) T(std::forward<Args>(args)...);
return *get();
}

T *get() {
return std::launder<T>((T *)data_);
}
const T *get() const {
return std::launder<T>((T *)data_);
}

bool hasValue() const {
return !Policy::isEmpty(data_);
}

SmallOptional<T, Policy> &operator=(const T &other) {
if (hasValue()) {
*get() = other;
} else {
new (data_) T(other);
}
return *this;
}
SmallOptional<T, Policy> &operator=(const T &&other) noexcept {
if (hasValue()) {
*get() = std::move(other);
} else {
new (data_) T(std::move(other));
}
return *this;
}
SmallOptional<T, Policy> &operator=(const SmallOptional<T, Policy> &other) {
if (other.hasValue()) {
if (hasValue()) {
*get() = *other.get();
} else {
new (data_) T(*other.get());
}
} else {
reset();
}
return *this;
}
SmallOptional &operator=(SmallOptional<T, Policy> &&other) noexcept {
if (other.hasValue()) {
if (hasValue()) {
*get() = std::move(*other.get());
} else {
new (data_) T(std::move(*other.get()));
}
} else {
reset();
}
return *this;
}

bool operator==(const SmallOptional<T, Policy> &other) const {
bool a = hasValue(), b = other.hasValue();
if (!a && !b) return true;
if (!a || !b) return false;
return *get() == *other.get();
}

operator bool() const noexcept {
return !Policy::isEmpty(data_);
}

T *operator->() noexcept {
return get();
}
const T *operator->() const noexcept {
return get();
}
T &operator*() noexcept {
return *get();
}
const T &operator*() const noexcept {
return *get();
}

private:
alignas(alignof(T)) unsigned char data_[sizeof(T)];
};

}

+ 4
- 4
libswan/include/swan/World.h View File

@@ -12,6 +12,7 @@
#include "WorldPlane.h"
#include "WorldGen.h"
#include "Entity.h"
#include "Collection.h"
#include "Resource.h"
#include "Mod.h"
#include "EventEmitter.h"
@@ -54,10 +55,9 @@ public:
std::unordered_map<std::string, Tile::ID> tiles_map_;
std::unordered_map<std::string, std::unique_ptr<Item>> items_;

// The mods themselves retain ownership of world gens and entities,
// the world just has non-owning pointers to them
std::unordered_map<std::string, WorldGen::Factory> worldgens_;
std::unordered_map<std::string, Entity::Factory> ents_;
// Mods give us factories to create new world gens and new entity collections
std::unordered_map<std::string, WorldGen::Factory> worldgen_factories_;
std::vector<EntityCollection::Factory> ent_coll_factories_;

BodyTrait::HasBody *player_;
Game *game_;

+ 3
- 2
libswan/include/swan/WorldGen.h View File

@@ -6,6 +6,7 @@
#include "common.h"
#include "Chunk.h"
#include "Entity.h"
#include "Collection.h"
#include "traits/BodyTrait.h"
#include "Vector2.h"

@@ -19,7 +20,7 @@ class WorldGen {
public:
struct Factory {
const std::string name;
std::unique_ptr<WorldGen> (*create)(World &world);
std::unique_ptr<WorldGen> (*const create)(World &world);
};

virtual ~WorldGen() = default;
@@ -28,7 +29,7 @@ public:
virtual SDL_Color backgroundColor(Vec2 pos) = 0;

virtual void genChunk(WorldPlane &plane, Chunk &chunk) = 0;
virtual BodyTrait::HasBody *spawnPlayer(const Context &ctx) = 0;
virtual EntityRef spawnPlayer(const Context &ctx) = 0;
};

}

+ 24
- 8
libswan/include/swan/WorldPlane.h View File

@@ -6,6 +6,7 @@
#include <memory>
#include <map>
#include <set>
#include <typeindex>

#include "common.h"
#include "traits/BodyTrait.h"
@@ -14,6 +15,7 @@
#include "Tile.h"
#include "WorldGen.h"
#include "Entity.h"
#include "Collection.h"

namespace Swan {

@@ -24,11 +26,13 @@ class WorldPlane: NonCopyable {
public:
using ID = uint16_t;

WorldPlane(ID id, World *world, std::unique_ptr<WorldGen> gen):
id_(id), world_(world), gen_(std::move(gen)) {}
WorldPlane(
ID id, World *world, std::unique_ptr<WorldGen> gen,
std::vector<std::unique_ptr<EntityCollection>> &&colls);

Entity *spawnEntity(const std::string &name, const Entity::PackObject &params);
Entity *spawnEntity(std::unique_ptr<Entity> ent);
EntityRef spawnEntity(const std::string &name, const Entity::PackObject &params);
template<typename Ent, typename... Args>
EntityRef spawnEntity(Args&&... args);
void despawnEntity(Entity &ent);

Context getContext();
@@ -46,14 +50,17 @@ public:

template<typename T>
Iter<T *>getEntsOfType() {
return Iter<T *>([] { return std::nullopt; });
/* TODO
return mapFilter(entities_.begin(), entities_.end(), [](std::unique_ptr<Entity> &ent) -> std::optional<T *> {
if (T *e = dynamic_cast<T *>(ent.get()); e != nullptr)
return e;
return std::nullopt;
});
*/
}

BodyTrait::HasBody *spawnPlayer();
EntityRef spawnPlayer();
void breakTile(TilePos pos);

SDL_Color backgroundColor();
@@ -71,12 +78,21 @@ private:
std::map<std::pair<int, int>, Chunk> chunks_;
std::vector<Chunk *> active_chunks_;
std::vector<std::pair<ChunkPos, Chunk *>> tick_chunks_;
std::vector<std::unique_ptr<Entity>> entities_;
std::vector<std::unique_ptr<EntityCollection>> ent_colls_;
std::unordered_map<std::type_index, EntityCollection *> ent_colls_by_type_;
std::unordered_map<std::string, EntityCollection *> ent_colls_by_name_;

std::deque<Chunk *> chunk_init_list_;
std::vector<std::unique_ptr<Entity>> spawn_list_;
std::vector<Entity *> despawn_list_;
std::vector<TilePos> debug_boxes_;
};

/*
* WorldPlane
*/

template<typename Ent, typename... Args>
inline EntityRef WorldPlane::spawnEntity(Args&&... args) {
return ent_colls_by_type_.at(typeid(Ent))->spawn<Ent, Args...>(std::forward<Args>(args)...);
}

}

+ 0
- 1
libswan/include/swan/traits/BodyTrait.h View File

@@ -13,7 +13,6 @@ namespace BodyTrait {
class Body;
class HasBody {
public:
virtual ~HasBody() = default;
virtual Body &getBody() = 0;
};


+ 0
- 1
libswan/include/swan/traits/InventoryTrait.h View File

@@ -13,7 +13,6 @@ namespace InventoryTrait {
class Inventory;
class HasInventory {
public:
virtual ~HasInventory() = default;
virtual Inventory &getInventory() = 0;
};


+ 3
- 3
libswan/src/Game.cc View File

@@ -13,18 +13,18 @@ namespace Swan {

std::optional<ModWrapper> Game::loadMod(std::string path, World &world) {
OS::Dynlib dl(path + "/mod");
auto create = dl.get<std::unique_ptr<Mod> (*)(World &)>("mod_create");
auto create = dl.get<Mod *(*)(World &)>("mod_create");
if (create == NULL) {
warn << path << ": No 'mod_create' function!";
return std::nullopt;
}

std::unique_ptr<Mod> mod = create(world);
std::unique_ptr<Mod> mod(create(world));
return std::make_optional<ModWrapper>(
std::move(mod), std::move(path), std::move(dl));
}

void Game::createWorld(const std::string &worldgen, std::vector<std::string> modpaths) {
void Game::createWorld(const std::string &worldgen, const std::vector<std::string> &modpaths) {
world_.reset(new World(this, time(NULL)));

for (auto &modpath: modpaths) {

+ 2
- 2
libswan/src/Mod.cc View File

@@ -53,9 +53,9 @@ Iter<WorldGen::Factory> ModWrapper::getWorldGens() {
});
}

Iter<Entity::Factory> ModWrapper::getEntities() {
Iter<EntityCollection::Factory> ModWrapper::getEntities() {
return map(begin(mod_->entities_), end(mod_->entities_),
[](Entity::Factory &fact){
[](EntityCollection::Factory &fact){
return fact;
});
}

+ 0
- 1
libswan/src/Resource.cc View File

@@ -9,7 +9,6 @@

#include "log.h"
#include "common.h"
#include "Game.h"
#include "Win.h"

namespace Swan {

+ 17
- 13
libswan/src/World.cc View File

@@ -63,18 +63,15 @@ void World::addMod(ModWrapper &&mod) {
items_[i->name_] = std::move(i);
}

for (auto gen: mod.getWorldGens()) {
worldgens_.emplace(
for (auto fact: mod.getWorldGens()) {
worldgen_factories_.emplace(
std::piecewise_construct,
std::forward_as_tuple(gen.name),
std::forward_as_tuple(gen));
std::forward_as_tuple(fact.name),
std::forward_as_tuple(fact));
}

for (auto ent: mod.getEntities()) {
ents_.emplace(
std::piecewise_construct,
std::forward_as_tuple(ent.name),
std::forward_as_tuple(ent));
for (auto fact: mod.getEntities()) {
ent_coll_factories_.push_back(fact);
}

mods_.push_back(std::move(mod));
@@ -85,7 +82,8 @@ void World::setWorldGen(std::string gen) {
}

void World::spawnPlayer() {
player_ = planes_[current_plane_].spawnPlayer();
player_ = dynamic_cast<BodyTrait::HasBody *>(
planes_[current_plane_].spawnPlayer().get());
}

void World::setCurrentPlane(WorldPlane &plane) {
@@ -94,15 +92,21 @@ void World::setCurrentPlane(WorldPlane &plane) {

WorldPlane &World::addPlane(const std::string &gen) {
WorldPlane::ID id = planes_.size();
auto it = worldgens_.find(gen);
if (it == end(worldgens_)) {
auto it = worldgen_factories_.find(gen);
if (it == worldgen_factories_.end()) {
panic << "Tried to add plane with non-existant world gen " << gen << "!";
abort();
}

std::vector<std::unique_ptr<EntityCollection>> colls;
colls.reserve(ent_coll_factories_.size());
for (auto &fact: ent_coll_factories_) {
colls.emplace_back(fact.create(fact.name));
}

WorldGen::Factory &factory = it->second;
std::unique_ptr<WorldGen> g = factory.create(*this);
planes_.emplace_back(id, this, std::move(g));
planes_.emplace_back(id, this, std::move(g), std::move(colls));
return planes_[id];
}


+ 30
- 44
libswan/src/WorldPlane.cc View File

@@ -37,32 +37,24 @@ Context WorldPlane::getContext() {
};
}

Entity *WorldPlane::spawnEntity(const std::string &name, const Entity::PackObject &obj) {
if (world_->ents_.find(name) == world_->ents_.end()) {
panic << "Tried to spawn a non-existant entity " << name << "!";
abort();
WorldPlane::WorldPlane(
ID id, World *world, std::unique_ptr<WorldGen> gen,
std::vector<std::unique_ptr<EntityCollection>> &&colls):
id_(id), world_(world), gen_(std::move(gen)), ent_colls_(std::move(colls)) {

for (auto &coll: ent_colls_) {
ent_colls_by_type_[coll->type()] = coll.get();
ent_colls_by_name_[coll->name()] = coll.get();
}

return spawnEntity(world_->ents_[name].create(getContext(), obj));
}

Entity *WorldPlane::spawnEntity(std::unique_ptr<Entity> ent) {
Entity *ptr = ent.get();
if (auto has_body = dynamic_cast<BodyTrait::HasBody *>(ent.get()); has_body) {
BodyTrait::Body &body = has_body->getBody();
BodyTrait::Bounds bounds = body.getBounds();

body.move({ 0.5f - bounds.size.x / 2, 0 });
}

spawn_list_.push_back(std::move(ent));
info << "Spawned entity.";
return ptr;
EntityRef WorldPlane::spawnEntity(const std::string &name, const Entity::PackObject &obj) {
return ent_colls_by_name_.at(name)->spawn(getContext(), obj);
}

void WorldPlane::despawnEntity(Entity &ent) {
// TODO: this
info << "Despawned entity.";
despawn_list_.push_back(&ent);
}

bool WorldPlane::hasChunk(ChunkPos pos) {
@@ -128,7 +120,9 @@ Tile &WorldPlane::getTile(TilePos pos) {
}

Iter<Entity *> WorldPlane::getEntsInArea(Vec2 center, float radius) {
// TODO: Optimize this using fancy data structures.
return Iter<Entity *>([] { return std::nullopt; });
// TODO: this
/*
return mapFilter(entities_.begin(), entities_.end(), [=](std::unique_ptr<Entity> &ent)
-> std::optional<Entity *> {

@@ -147,9 +141,10 @@ Iter<Entity *> WorldPlane::getEntsInArea(Vec2 center, float radius) {

return ent.get();
});
*/
}

BodyTrait::HasBody *WorldPlane::spawnPlayer() {
EntityRef WorldPlane::spawnPlayer() {
return gen_->spawnPlayer(getContext());
}

@@ -171,9 +166,10 @@ SDL_Color WorldPlane::backgroundColor() {
}

void WorldPlane::draw(Win &win) {
auto ctx = getContext();
auto pbounds = world_->player_->getBody().getBounds();

gen_->drawBackground(getContext(), win, pbounds.pos);
gen_->drawBackground(ctx, win, pbounds.pos);

ChunkPos pcpos = ChunkPos(
(int)floor(pbounds.pos.x / CHUNK_WIDTH),
@@ -184,19 +180,19 @@ void WorldPlane::draw(Win &win) {
Chunk *chunk = chunk_init_list_.front();
info << "render chunk " << chunk->pos_;
chunk_init_list_.pop_front();
chunk->render(getContext(), win.renderer_);
chunk->render(ctx, win.renderer_);
}

for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; ++y) {
auto iter = chunks_.find(pcpos + ChunkPos(x, y));
if (iter != chunks_.end())
iter->second.draw(getContext(), win);
iter->second.draw(ctx, win);
}
}

for (auto &ent: entities_)
ent->draw(getContext(), win);
for (auto &coll: ent_colls_)
coll->draw(ctx, win);

if (debug_boxes_.size() > 0) {
for (auto &pos: debug_boxes_) {
@@ -206,35 +202,25 @@ void WorldPlane::draw(Win &win) {
}

void WorldPlane::update(float dt) {
auto ctx = getContext();
debug_boxes_.clear();

for (auto &ent: entities_)
ent->update(getContext(), dt);

for (auto &ent: spawn_list_)
entities_.push_back(std::move(ent));
spawn_list_.clear();

for (auto entptr: despawn_list_) {
for (auto it = entities_.begin(); it != entities_.end(); ++it) {
if (it->get() == entptr) {
entities_.erase(it);
break;
}
}
}
despawn_list_.clear();
for (auto &coll: ent_colls_)
coll->update(ctx, dt);
}

void WorldPlane::tick(float dt) {
auto ctx = getContext();

// Any chunk which has been in use since last tick should be kept alive
for (std::pair<ChunkPos, Chunk *> &ch: tick_chunks_)
ch.second->keepActive();
tick_chunks_.clear();

for (auto &ent: entities_)
ent->tick(getContext(), dt);
for (auto &coll: ent_colls_)
coll->tick(ctx, dt);

// Tick all chunks, figure out if any of them should be deleted or compressed
auto iter = active_chunks_.begin();
auto last = active_chunks_.end();
while (iter != last) {

+ 1
- 1
src/main.cc View File

@@ -111,7 +111,7 @@ int main(int argc, char **argv) {
// Create a world
Game game(win);
std::vector<std::string> mods{ "core.mod" };
game.createWorld("core::default", std::move(mods));
game.createWorld("core::default", mods);

PerfCounter pcounter;


Loading…
Cancel
Save