Switched back to using ids instead of names, added uuids to entities and started work on serialization
This commit is contained in:
parent
92f8fdd510
commit
a6e5deb502
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,3 +4,6 @@
|
|||
[submodule "vendor/lua"]
|
||||
path = vendor/lua
|
||||
url = https://github.com/lua/lua
|
||||
[submodule "vendor/stduuid"]
|
||||
path = vendor/stduuid
|
||||
url = https://github.com/mariusbancila/stduuid
|
||||
|
|
|
@ -6,25 +6,25 @@ namespace ecs::lua {
|
|||
|
||||
ecs.new_usertype<Entity>("Entity",
|
||||
"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) {
|
||||
std::vector<std::string> names;
|
||||
for (std::string name : args) {
|
||||
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 {
|
||||
// Convert to the correct component type
|
||||
auto f1 = lua["_internal_to_" + name];
|
||||
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
|
||||
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) {
|
||||
names.push_back(name);
|
||||
}
|
||||
return thiz->view(names);
|
||||
return thiz->view(ComponentID::get_id(names));
|
||||
}
|
||||
);
|
||||
|
||||
|
|
147
ecs-serial/src/ecs-serial.cpp
Normal file
147
ecs-serial/src/ecs-serial.cpp
Normal 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';
|
||||
});
|
||||
}
|
|
@ -10,6 +10,8 @@
|
|||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include "uuid.h"
|
||||
|
||||
namespace ecs {
|
||||
template <typename T>
|
||||
std::string get_typename() {
|
||||
|
@ -49,6 +51,7 @@ namespace ecs {
|
|||
|
||||
class Entity {
|
||||
public:
|
||||
Entity(uuids::uuid _uuid) : uuid(_uuid) {}
|
||||
~Entity();
|
||||
|
||||
template <typename T, typename... Args>
|
||||
|
@ -63,8 +66,8 @@ namespace ecs {
|
|||
}
|
||||
|
||||
template <typename... Ts>
|
||||
bool has_components() {
|
||||
auto ids = {ComponentID::id<Ts>...};
|
||||
bool has_components() const {
|
||||
std::vector<size_t> ids = {ComponentID::id<Ts>...};
|
||||
for (const auto& id : ids) {
|
||||
if (_components.find(id) == _components.end()) {
|
||||
return false;
|
||||
|
@ -75,7 +78,7 @@ namespace ecs {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T* get_component() {
|
||||
T* get_component() const {
|
||||
size_t id = ComponentID::id<T>;
|
||||
auto it = _components.find(id);
|
||||
if (it == _components.end()) {
|
||||
|
@ -85,9 +88,15 @@ namespace ecs {
|
|||
return (T*)it->second;
|
||||
}
|
||||
|
||||
void add_component(std::string name, Component* component);
|
||||
bool has_components(std::vector<std::string> names);
|
||||
Component* get_component(std::string name);
|
||||
const std::unordered_map<size_t, Component*> get_components() const {
|
||||
return _components;
|
||||
}
|
||||
|
||||
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:
|
||||
std::unordered_map<size_t, Component*> _components;
|
||||
|
@ -96,16 +105,45 @@ namespace ecs {
|
|||
template <typename... Ts>
|
||||
class View {
|
||||
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) {
|
||||
for (auto entity : _entities) {
|
||||
for (auto [uuid, entity] : _entities) {
|
||||
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:
|
||||
std::vector<Entity*> _entities;
|
||||
std::unordered_map<uuids::uuid, Entity*> _entities;
|
||||
};
|
||||
|
||||
class Manager {
|
||||
|
@ -114,32 +152,42 @@ namespace ecs {
|
|||
|
||||
template <typename... Ts>
|
||||
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) {
|
||||
if (entity->has_components<Ts...>()) {
|
||||
entities.push_back(entity);
|
||||
if (entity.second->has_components<Ts...>()) {
|
||||
entities.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return View<Ts...>(entities);
|
||||
}
|
||||
|
||||
View<> view(std::vector<std::string> names) {
|
||||
std::vector<Entity*> entities;
|
||||
View<> view(std::vector<size_t> ids) {
|
||||
std::unordered_map<uuids::uuid, Entity*> entities;
|
||||
|
||||
for (auto entity : _entities) {
|
||||
if (entity->has_components(names)) {
|
||||
entities.push_back(entity);
|
||||
for (auto [uuid, entity] : _entities) {
|
||||
if (entity->has_components(ids)) {
|
||||
entities.insert({uuid, entity});
|
||||
}
|
||||
}
|
||||
|
||||
return View<>(entities);
|
||||
}
|
||||
|
||||
Entity* create_entity(uuids::uuid uuid);
|
||||
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:
|
||||
std::vector<Entity*> _entities;
|
||||
std::unordered_map<uuids::uuid, Entity*> _entities;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,8 +36,7 @@ namespace ecs {
|
|||
_components.clear();
|
||||
}
|
||||
|
||||
void Entity::add_component(std::string name, Component* component) {
|
||||
size_t id = ComponentID::get_id({name})[0];
|
||||
void Entity::add_component(size_t id, Component* component) {
|
||||
if (_components.find(id) != _components.end()) {
|
||||
throw std::runtime_error("Component already exists");
|
||||
}
|
||||
|
@ -45,8 +44,8 @@ namespace ecs {
|
|||
_components[id] = component;
|
||||
}
|
||||
|
||||
bool Entity::has_components(std::vector<std::string> names) {
|
||||
for (const auto& id : ComponentID::get_id(names)) {
|
||||
bool Entity::has_components(std::vector<size_t> ids) {
|
||||
for (const auto& id : ids) {
|
||||
if (_components.find(id) == _components.end()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -55,8 +54,8 @@ namespace ecs {
|
|||
return true;
|
||||
}
|
||||
|
||||
Component* Entity::get_component(std::string name) {
|
||||
auto it = _components.find(ComponentID::get_id({name})[0]);
|
||||
Component* Entity::get_component(size_t id) {
|
||||
auto it = _components.find(id);
|
||||
if (it == _components.end()) {
|
||||
throw std::runtime_error("Component does not exist");
|
||||
}
|
||||
|
@ -66,17 +65,21 @@ namespace ecs {
|
|||
|
||||
Manager::~Manager() {
|
||||
for (auto entity : _entities) {
|
||||
delete entity;
|
||||
entity = nullptr;
|
||||
delete entity.second;
|
||||
entity.second = nullptr;
|
||||
}
|
||||
_entities.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
13
flint.lua
13
flint.lua
|
@ -10,16 +10,27 @@ lib "sol2"
|
|||
|
||||
dependency "lua"
|
||||
|
||||
lib "stduuid"
|
||||
include "vendor/stduuid/include"
|
||||
link "uuid"
|
||||
|
||||
lib "ecs"
|
||||
path "ecs"
|
||||
|
||||
dependency "sol2"
|
||||
dependency("sol2", "stduuid")
|
||||
|
||||
lib "ecs-lua"
|
||||
path "ecs-lua"
|
||||
|
||||
dependency "ecs"
|
||||
|
||||
subfile("../iohelper/flint.lua", "iohelper")
|
||||
|
||||
executable "ecs-serial"
|
||||
path "ecs-serial"
|
||||
|
||||
dependency("ecs", "iohelper")
|
||||
|
||||
executable "test"
|
||||
path "test"
|
||||
|
||||
|
|
|
@ -79,6 +79,11 @@ int main() {
|
|||
print("v_y: " .. vel.vy)
|
||||
end)
|
||||
|
||||
-- @todo Implement this
|
||||
-- for uuid,entity in pairs(view) do
|
||||
-- print "TEST"
|
||||
-- end
|
||||
|
||||
manager:view("Random"):for_each(function(ent)
|
||||
wrapped = ent:get_component("Random").object
|
||||
|
||||
|
@ -123,16 +128,17 @@ int main() {
|
|||
std::cout << "Y: " << pos->_y << '\n';
|
||||
});
|
||||
|
||||
manager.view({"Random"}).for_each([](ecs::Entity* entity) {
|
||||
sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component("Random"))->_object;
|
||||
// These are really just an internal api that should not be used
|
||||
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;
|
||||
std::cout << random["a"].get<std::string>() << '\n';
|
||||
});
|
||||
};
|
||||
|
||||
manager.view({"Random"}).for_each([](ecs::Entity* entity) {
|
||||
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;
|
||||
|
||||
std::cout << random["a"].get<std::string>() << '\n';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
1
vendor/stduuid
vendored
Submodule
1
vendor/stduuid
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit dee14f660da4d2889e60848b206c1d7b8d1fba99
|
Loading…
Reference in New Issue
Block a user