Switched back to using ids instead of names, added uuids to entities and started work on serialization

This commit is contained in:
Dreaded_X 2019-02-05 22:23:38 +00:00
parent 92f8fdd510
commit a6e5deb502
8 changed files with 259 additions and 40 deletions

3
.gitmodules vendored
View File

@ -4,3 +4,6 @@
[submodule "vendor/lua"] [submodule "vendor/lua"]
path = vendor/lua path = vendor/lua
url = https://github.com/lua/lua url = https://github.com/lua/lua
[submodule "vendor/stduuid"]
path = vendor/stduuid
url = https://github.com/mariusbancila/stduuid

View File

@ -6,25 +6,25 @@ namespace ecs::lua {
ecs.new_usertype<Entity>("Entity", ecs.new_usertype<Entity>("Entity",
"add_component", [] (Entity* thiz, std::string name, Component* component) { "add_component", [] (Entity* thiz, std::string name, Component* component) {
return thiz->add_component(name, component); return thiz->add_component(ComponentID::get_id({name})[0], component);
}, },
"has_components", [] (Entity* thiz, sol::variadic_args args) { "has_components", [] (Entity* thiz, sol::variadic_args args) {
std::vector<std::string> names; std::vector<std::string> names;
for (std::string name : args) { for (std::string name : args) {
names.push_back(name); names.push_back(name);
} }
return thiz->has_components(names); return thiz->has_components(ComponentID::get_id(names));
}, },
"get_component", [&lua] (Entity* thiz, std::string name) -> sol::object { "get_component", [&lua] (Entity* thiz, std::string name) -> sol::object {
// Convert to the correct component type // Convert to the correct component type
auto f1 = lua["_internal_to_" + name]; auto f1 = lua["_internal_to_" + name];
if (f1.valid()) { if (f1.valid()) {
return f1(thiz->get_component(name)); return f1(thiz->get_component(ComponentID::get_id({name})[0]));
} }
// If the type of the component unknown we assume it a lua object and convert it to the wrapper // If the type of the component unknown we assume it a lua object and convert it to the wrapper
auto f2 = lua["_internal_to_LuaWrapper"]; auto f2 = lua["_internal_to_LuaWrapper"];
return f2(thiz->get_component(name)); return f2(thiz->get_component(ComponentID::get_id({name})[0]));
} }
); );
@ -39,7 +39,7 @@ namespace ecs::lua {
for (std::string name : args) { for (std::string name : args) {
names.push_back(name); names.push_back(name);
} }
return thiz->view(names); return thiz->view(ComponentID::get_id(names));
} }
); );

View File

@ -0,0 +1,147 @@
#include <iostream>
#include "ecs.h"
#include "iohelper/memstream.h"
#include "iohelper/write.h"
#include "iohelper/read.h"
#include <fstream>
struct Position : ecs::Component {
Position(float x, float y) : _x(x), _y(y) {}
float _x;
float _y;
};
struct Velocity : ecs::Component{
Velocity(float vx, float vy) : _vx(vx), _vy(vy) {}
float _vx;
float _vy;
};
std::unordered_map<size_t, std::pair<std::function<std::vector<uint8_t>(ecs::Component*)>, std::function<ecs::Component*(std::vector<uint8_t>)>>> _functions;
// @todo Add deserializer stuff
template <typename T>
void register_component(std::function<std::vector<uint8_t>(ecs::Component*)> serializer, std::function<ecs::Component*(std::vector<uint8_t>)> deserializer) {
_functions.insert({ecs::ComponentID::id<T>, {serializer, deserializer}});
}
enum class Marker : uint8_t {
ENTITY,
COMPONENT,
END
};
int main(int argc, const char* argv) {
ecs::Manager manager;
register_component<Position>([] (ecs::Component* component) {
Position* pos = (Position*)component;
std::vector<uint8_t> data;
iohelper::omemstream stream(data);
iohelper::write<float>(stream, pos->_x);
iohelper::write<float>(stream, pos->_y);
return data;
}, [] (std::vector<uint8_t> data) {
iohelper::imemstream stream(data);
float x = iohelper::read<float>(stream);
float y = iohelper::read<float>(stream);
return new Position(x, y);
});
register_component<Velocity>([] (ecs::Component* component) {
Velocity* vel = (Velocity*)component;
std::vector<uint8_t> data;
iohelper::omemstream stream(data);
iohelper::write<float>(stream, vel->_vx);
iohelper::write<float>(stream, vel->_vy);
return data;
}, [] (std::vector<uint8_t> data) {
iohelper::imemstream stream(data);
float vx = iohelper::read<float>(stream);
float vy = iohelper::read<float>(stream);
return new Velocity(vx, vy);
});
if (argc == 1) {
// Create entities and store them
for (int i = 0; i < 10; ++i) {
// We can create entities
ecs::Entity* entity = manager.create_entity();
// Then we can add components to them
entity->add_component<Position>(0.0f, 0.0f);
if (i % 2 == 0) {
entity->add_component<Velocity>(0.1f, 0.2f);
}
}
manager.view<Position, Velocity>().for_each([] (ecs::Entity*, Position* pos, Velocity* vel) {
pos->_x += vel->_vx;
pos->_y += vel->_vy;
});
std::ofstream file("entities", std::ios::out | std::ios::trunc);
for (auto [uuid, entity] : manager.view<>()) {
iohelper::write<uint8_t>(file, (uint8_t)Marker::ENTITY);
// iohelper::write_length(file, uuid);
auto uuid_data = reinterpret_cast<const uint8_t*>(uuid.as_bytes().data());
iohelper::write_vector<uint8_t>(file, std::vector<uint8_t>(uuid_data, uuid_data + 16), false);
for (auto [id, component] : entity->get_components()) {
iohelper::write<uint8_t>(file, (uint8_t)Marker::COMPONENT);
iohelper::write_length(file, id);
auto data = _functions[id].first(component);
iohelper::write_vector<uint8_t>(file, data);
}
}
iohelper::write<uint8_t>(file, (uint8_t)Marker::END);
file.close();
} else {
// Load entities from disk as a test
std::ifstream file("entities", std::ios::in);
bool loop = true;
ecs::Entity* entity = nullptr;
while (loop) {
Marker marker = (Marker)iohelper::read<uint8_t>(file);
switch (marker) {
case Marker::ENTITY: {
// size_t uuid = iohelper::read_length(file);
auto uuid_vector = iohelper::read_vector<uint8_t>(file, 16);
entity = manager.create_entity(uuids::uuid(uuid_vector.begin(), uuid_vector.end()));
break;
}
case Marker::COMPONENT: {
size_t id = iohelper::read_length(file);
std::vector<uint8_t> data = iohelper::read_vector<uint8_t>(file);
entity->add_component(id, _functions[id].second(data));
break;
}
case Marker::END:
loop = false;
break;
}
}
if (false) {
auto ent = manager.get_entity(uuids::uuid::from_string("6d58fdb5-6d8c-4e6f-89d4-f7d7b184f463").value());
if (ent->has_components<Position>()) {
auto pos = ent->get_component<Position>();
pos->_x = 1.2f;
pos->_y = 3.4f;
}
}
}
manager.view<Position>().for_each([] (ecs::Entity* entity, Position* pos) {
std::cout << "uuid: " << entity->uuid << '\n';
std::cout << "x: " << pos->_x << '\n';
std::cout << "y: " << pos->_y << '\n';
});
}

View File

@ -10,6 +10,8 @@
#include <cxxabi.h> #include <cxxabi.h>
#endif #endif
#include "uuid.h"
namespace ecs { namespace ecs {
template <typename T> template <typename T>
std::string get_typename() { std::string get_typename() {
@ -49,6 +51,7 @@ namespace ecs {
class Entity { class Entity {
public: public:
Entity(uuids::uuid _uuid) : uuid(_uuid) {}
~Entity(); ~Entity();
template <typename T, typename... Args> template <typename T, typename... Args>
@ -63,8 +66,8 @@ namespace ecs {
} }
template <typename... Ts> template <typename... Ts>
bool has_components() { bool has_components() const {
auto ids = {ComponentID::id<Ts>...}; std::vector<size_t> ids = {ComponentID::id<Ts>...};
for (const auto& id : ids) { for (const auto& id : ids) {
if (_components.find(id) == _components.end()) { if (_components.find(id) == _components.end()) {
return false; return false;
@ -75,7 +78,7 @@ namespace ecs {
} }
template <typename T> template <typename T>
T* get_component() { T* get_component() const {
size_t id = ComponentID::id<T>; size_t id = ComponentID::id<T>;
auto it = _components.find(id); auto it = _components.find(id);
if (it == _components.end()) { if (it == _components.end()) {
@ -85,9 +88,15 @@ namespace ecs {
return (T*)it->second; return (T*)it->second;
} }
void add_component(std::string name, Component* component); const std::unordered_map<size_t, Component*> get_components() const {
bool has_components(std::vector<std::string> names); return _components;
Component* get_component(std::string name); }
void add_component(size_t id, Component* component);
bool has_components(std::vector<size_t> ids);
Component* get_component(size_t id);
const uuids::uuid uuid;
private: private:
std::unordered_map<size_t, Component*> _components; std::unordered_map<size_t, Component*> _components;
@ -96,16 +105,45 @@ namespace ecs {
template <typename... Ts> template <typename... Ts>
class View { class View {
public: public:
View(std::vector<Entity*> entities) : _entities(entities) {} View(std::unordered_map<uuids::uuid, Entity*> entities) : _entities(entities) {}
// @todo What should we do with the uuid
void for_each(std::function<void(Entity*, std::add_pointer_t<Ts>...)> function) { void for_each(std::function<void(Entity*, std::add_pointer_t<Ts>...)> function) {
for (auto entity : _entities) { for (auto [uuid, entity] : _entities) {
function(entity, entity->template get_component<Ts>()...); function(entity, entity->template get_component<Ts>()...);
} }
} }
size_t size() const {
return _entities.size();
}
auto begin() {
return _entities.begin();
}
const auto begin() const {
return _entities.begin();
}
const auto cbegin() const {
return _entities.cbegin();
}
auto end() {
return _entities.end();
}
const auto end() const {
return _entities.end();
}
const auto cend() const {
return _entities.cend();
}
private: private:
std::vector<Entity*> _entities; std::unordered_map<uuids::uuid, Entity*> _entities;
}; };
class Manager { class Manager {
@ -114,32 +152,42 @@ namespace ecs {
template <typename... Ts> template <typename... Ts>
View<Ts...> view() { View<Ts...> view() {
std::vector<Entity*> entities; std::unordered_map<uuids::uuid, Entity*> entities;
// @todo Fix this so it uses structed bindings, for some reason it does not work
for (auto entity : _entities) { for (auto entity : _entities) {
if (entity->has_components<Ts...>()) { if (entity.second->has_components<Ts...>()) {
entities.push_back(entity); entities.insert(entity);
} }
} }
return View<Ts...>(entities); return View<Ts...>(entities);
} }
View<> view(std::vector<std::string> names) { View<> view(std::vector<size_t> ids) {
std::vector<Entity*> entities; std::unordered_map<uuids::uuid, Entity*> entities;
for (auto entity : _entities) { for (auto [uuid, entity] : _entities) {
if (entity->has_components(names)) { if (entity->has_components(ids)) {
entities.push_back(entity); entities.insert({uuid, entity});
} }
} }
return View<>(entities); return View<>(entities);
} }
Entity* create_entity(uuids::uuid uuid);
Entity* create_entity(); Entity* create_entity();
Entity* get_entity(uuids::uuid uuid) {
auto it = _entities.find(uuid);
if (it == _entities.end()) {
throw std::runtime_error("Unable to find entity with uuid!");
}
return it->second;
}
private: private:
std::vector<Entity*> _entities; std::unordered_map<uuids::uuid, Entity*> _entities;
}; };
} }

View File

@ -36,8 +36,7 @@ namespace ecs {
_components.clear(); _components.clear();
} }
void Entity::add_component(std::string name, Component* component) { void Entity::add_component(size_t id, Component* component) {
size_t id = ComponentID::get_id({name})[0];
if (_components.find(id) != _components.end()) { if (_components.find(id) != _components.end()) {
throw std::runtime_error("Component already exists"); throw std::runtime_error("Component already exists");
} }
@ -45,8 +44,8 @@ namespace ecs {
_components[id] = component; _components[id] = component;
} }
bool Entity::has_components(std::vector<std::string> names) { bool Entity::has_components(std::vector<size_t> ids) {
for (const auto& id : ComponentID::get_id(names)) { for (const auto& id : ids) {
if (_components.find(id) == _components.end()) { if (_components.find(id) == _components.end()) {
return false; return false;
} }
@ -55,8 +54,8 @@ namespace ecs {
return true; return true;
} }
Component* Entity::get_component(std::string name) { Component* Entity::get_component(size_t id) {
auto it = _components.find(ComponentID::get_id({name})[0]); auto it = _components.find(id);
if (it == _components.end()) { if (it == _components.end()) {
throw std::runtime_error("Component does not exist"); throw std::runtime_error("Component does not exist");
} }
@ -66,17 +65,21 @@ namespace ecs {
Manager::~Manager() { Manager::~Manager() {
for (auto entity : _entities) { for (auto entity : _entities) {
delete entity; delete entity.second;
entity = nullptr; entity.second = nullptr;
} }
_entities.clear(); _entities.clear();
} }
Entity* Manager::create_entity() { Entity* Manager::create_entity() {
Entity* entity = new Entity; uuids::uuid uuid = uuids::uuid_system_generator{}();
_entities.push_back(entity); return create_entity(uuid);
}
Entity* Manager::create_entity(uuids::uuid uuid) {
Entity* entity = new Entity(uuid);
_entities.insert({uuid, entity});
return entity; return entity;
} }
} }

View File

@ -10,16 +10,27 @@ lib "sol2"
dependency "lua" dependency "lua"
lib "stduuid"
include "vendor/stduuid/include"
link "uuid"
lib "ecs" lib "ecs"
path "ecs" path "ecs"
dependency "sol2" dependency("sol2", "stduuid")
lib "ecs-lua" lib "ecs-lua"
path "ecs-lua" path "ecs-lua"
dependency "ecs" dependency "ecs"
subfile("../iohelper/flint.lua", "iohelper")
executable "ecs-serial"
path "ecs-serial"
dependency("ecs", "iohelper")
executable "test" executable "test"
path "test" path "test"

View File

@ -79,6 +79,11 @@ int main() {
print("v_y: " .. vel.vy) print("v_y: " .. vel.vy)
end) end)
-- @todo Implement this
-- for uuid,entity in pairs(view) do
-- print "TEST"
-- end
manager:view("Random"):for_each(function(ent) manager:view("Random"):for_each(function(ent)
wrapped = ent:get_component("Random").object wrapped = ent:get_component("Random").object
@ -123,16 +128,17 @@ int main() {
std::cout << "Y: " << pos->_y << '\n'; std::cout << "Y: " << pos->_y << '\n';
}); });
manager.view({"Random"}).for_each([](ecs::Entity* entity) { // These are really just an internal api that should not be used
sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component("Random"))->_object; for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) {
sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->_object;
random["a"] = 21; random["a"] = 21;
std::cout << random["a"].get<std::string>() << '\n'; std::cout << random["a"].get<std::string>() << '\n';
}); };
manager.view({"Random"}).for_each([](ecs::Entity* entity) { for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) {
sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component("Random"))->_object; sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->_object;
std::cout << random["a"].get<std::string>() << '\n'; std::cout << random["a"].get<std::string>() << '\n';
}); };
} }

1
vendor/stduuid vendored Submodule

@ -0,0 +1 @@
Subproject commit dee14f660da4d2889e60848b206c1d7b8d1fba99