EnTT-(de)serialization with nlohmann::json
Again playing with EnTT (ECS) which has a builtin mechanism that helps you (de)serialize a registry. You have to implement an 'archive' that (de)serializes the data.
Here in the source-code I use nlohmann::json for this. In the end it was simpler than it seemed at the beginning.
The Input/Output-Archives:
Some sample structs:
#include <nlohmann/json.hpp>
#include <entt/entt.hpp>
#include <string>
struct Tower{
std::string name;
int type_id;
float range;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Tower, name, type_id, range)
};
struct Walker {
int type_id;
float speed;
entt::entity target;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Walker, type_id,speed,target)
};
struct Transform {
float x;
float y;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Transform, x,y)
};
And test it:
void test()
{
entt::registry reg;
auto e1 = reg.create();
Tower t{"Tower", 1895, 18.95f};
t.name = "hansi";
Tower tw1 = reg.emplace<Tower>(e1, std::string("tower1"), 1895, 18.95f);
reg.emplace<Transform>(e1, 1.0f, 1.0f);
auto e2 = reg.create();
Tower tw2 = reg.emplace<Tower>(e2, "tower2", 18, 0.95f);
reg.emplace<Transform>(e2, 5.0f, 5.0f);
auto w1 = reg.create();
Walker &w = reg.emplace<Walker>(w1, 1, 0.5f);
reg.emplace<Transform>(w1, 100.0f, 100.0f);
NJSONOutputArchive json_archive;
entt::basic_snapshot snapshot(reg);
snapshot.entities(json_archive)
.component<Tower, Walker, Transform>(json_archive);
json_archive.Close();
std::string json_output = json_archive.AsString();
printf("json:%s", json_output.c_str());
NJSONInputArchive json_in(json_output);
entt::registry reg2;
entt::basic_snapshot_loader loader(reg2);
loader.entities(json_in)
.component<Tower, Walker, Transform>(json_in);
auto _e1 = entt::entity(0);
auto _e2 = entt::entity(1);
auto [tower, transform] = reg2.get<Tower, Transform>(e1);
auto [tower2, transform2] = reg2.get<Tower, Transform>(e2);
}
This will result in following JSON:
[[3,0,1,2,1048575],
[2,0,{"name":"tower1","range":18.950000762939453,"type_id":1895},1,{"name":"tower2","range":0.949999988079071,"type_id":18}],
[1,2,{"speed":0.5,"target":0,"type_id":1}],
[3,0,{"x":1.0,"y":1.0},1,{"x":5.0,"y":5.0},2,{"x":100.0,"y":100.0}]]
Line 1(EntityIDs): [0]=amount of entities,[1-3]=entity-ids,[4]=destroyed(not sure what this is and why it is serialized)
Line 2(Tower): [0]=amount of components,[1,3]=entity-id,[2,4]=component data to be attached to the entity of [1,3]
Line 3(Walker):[0]=amount of components,[1]=entity-id.....
Line 4(Transform):....
Let's recall that how we called the snapshot:
snapshot.entities(json_archive)
.component<Tower, Walker, Transform>(json_archive);
You see that the component serialization was in exactly the order that we specified and it is important that the order is in the same in the snapshot_loader....