Almost working save sytem but the data I store is totally wrong. I need to also save the entity IDs being used and map them to the components.
parent
babc190525
commit
d113dba42f
@ -0,0 +1,63 @@ |
|||||||
|
#include "save.hpp" |
||||||
|
#include <fstream> |
||||||
|
#include "dbc.hpp" |
||||||
|
#include <fmt/core.h> |
||||||
|
|
||||||
|
using namespace components; |
||||||
|
|
||||||
|
|
||||||
|
template<typename CompT> |
||||||
|
inline void extract(DinkyECS::World &world, std::vector<CompT> &into) { |
||||||
|
auto from_world = world.entity_map_for<CompT>(); |
||||||
|
for(auto [entity, value] : from_world) { |
||||||
|
into.push_back(std::any_cast<CompT>(value)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void save::to_file(std::string path, DinkyECS::World &world) { |
||||||
|
SaveData save_data; |
||||||
|
tser::BinaryArchive archive; |
||||||
|
|
||||||
|
save_data.player = world.get_the<Player>(); |
||||||
|
extract<Position>(world, save_data.position); |
||||||
|
extract<Combat>(world, save_data.combat); |
||||||
|
extract<Motion>(world, save_data.motion); |
||||||
|
|
||||||
|
archive.save(save_data); |
||||||
|
std::string_view archive_view = archive.get_buffer(); |
||||||
|
|
||||||
|
std::ofstream out(path, std::ios::binary); |
||||||
|
out << archive_view; |
||||||
|
out.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void save::from_file(std::string path, DinkyECS::World &world_out) { |
||||||
|
tser::BinaryArchive archive(0); |
||||||
|
|
||||||
|
if(std::ifstream in_file{path, std::ios::binary | std::ios::ate}) { |
||||||
|
auto size = in_file.tellg(); |
||||||
|
std::string in_data(size, '\0'); |
||||||
|
in_file.seekg(0); |
||||||
|
|
||||||
|
if(in_file.read(&in_data[0], size)) { |
||||||
|
std::string_view in_view(in_data); |
||||||
|
archive.initialize(in_view); |
||||||
|
} else { |
||||||
|
dbc::sentinel(fmt::format("wrong size or error reading {}", path)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
dbc::sentinel(fmt::format("failed to load file {}", path)); |
||||||
|
} |
||||||
|
|
||||||
|
auto save_data = archive.load<SaveData>(); |
||||||
|
|
||||||
|
// BUG: need the entities!
|
||||||
|
world_out.set_the<Player>(save_data.player); |
||||||
|
|
||||||
|
for(auto position : save_data.position) { |
||||||
|
auto entity = world_out.entity(); |
||||||
|
// BUG: actually do need the entities
|
||||||
|
world_out.set<Position>(entity, position); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "components.hpp" |
||||||
|
#include "dinkyecs.hpp" |
||||||
|
#include "tser.hpp" |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace save { |
||||||
|
struct SaveData { |
||||||
|
components::Player player; |
||||||
|
std::vector<components::Position> position; |
||||||
|
std::vector<components::Motion> motion; |
||||||
|
std::vector<components::Combat> combat; |
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(SaveData, player, position, motion, combat); |
||||||
|
}; |
||||||
|
|
||||||
|
void to_file(std::string path, DinkyECS::World &world); |
||||||
|
void from_file(std::string path, DinkyECS::World &world_out); |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
#include <catch2/catch_test_macros.hpp> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <string> |
||||||
|
#include "dinkyecs.hpp" |
||||||
|
#include "components.hpp" |
||||||
|
#include "save.hpp" |
||||||
|
#include <optional> |
||||||
|
#include <iostream> |
||||||
|
#include "tser.hpp" |
||||||
|
|
||||||
|
using namespace fmt; |
||||||
|
using std::string; |
||||||
|
using namespace components; |
||||||
|
|
||||||
|
enum class Item : char { |
||||||
|
RADAR = 'R', |
||||||
|
TRAP = 'T', |
||||||
|
ORE = 'O' |
||||||
|
}; |
||||||
|
|
||||||
|
struct Pixel { |
||||||
|
int x = 0; |
||||||
|
int y = 0; |
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(Pixel, x, y); |
||||||
|
}; |
||||||
|
|
||||||
|
struct Robot { |
||||||
|
Pixel point; |
||||||
|
std::wstring name; |
||||||
|
std::optional<Item> item; |
||||||
|
|
||||||
|
DEFINE_SERIALIZABLE(Robot, point, name, item); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("test using tser for serialization", "[config]") { |
||||||
|
auto robot = Robot{ Pixel{3,4}, L"BIG NAME", Item::RADAR}; |
||||||
|
std::cout << robot << '\n'; |
||||||
|
|
||||||
|
tser::BinaryArchive archive; |
||||||
|
archive.save(robot); |
||||||
|
std::string_view archive_view = archive.get_buffer(); |
||||||
|
|
||||||
|
tser::BinaryArchive archive2(0); |
||||||
|
archive2.initialize(archive_view); |
||||||
|
auto loadedRobot = archive2.load<Robot>(); |
||||||
|
|
||||||
|
REQUIRE(loadedRobot.point.x == robot.point.x); |
||||||
|
REQUIRE(loadedRobot.point.y == robot.point.y); |
||||||
|
REQUIRE(loadedRobot.name == robot.name); |
||||||
|
REQUIRE(loadedRobot.item == robot.item); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_CASE("basic save a world", "[save]") { |
||||||
|
DinkyECS::World world; |
||||||
|
|
||||||
|
// configure a player as a fact of the world
|
||||||
|
Player player{world.entity()}; |
||||||
|
world.set_the<Player>(player); |
||||||
|
|
||||||
|
world.set<Position>(player.entity, {10,10}); |
||||||
|
world.set<Motion>(player.entity, {0, 0}); |
||||||
|
world.set<Combat>(player.entity, {100, 10}); |
||||||
|
|
||||||
|
save::to_file("./savetest.world", world); |
||||||
|
|
||||||
|
DinkyECS::World in_world; |
||||||
|
save::from_file("./savetest.world", in_world); |
||||||
|
|
||||||
|
Position &position1 = world.get<Position>(player.entity); |
||||||
|
Position &position2 = in_world.get<Position>(player.entity); |
||||||
|
|
||||||
|
// BUGGGGGGGG! This doesn't actually work, it's all fake
|
||||||
|
// The world uses an internal id to increment entities so
|
||||||
|
// by default player gets the first one, but all data after
|
||||||
|
// that is wrong.
|
||||||
|
REQUIRE(position1.location.x == position2.location.x); |
||||||
|
REQUIRE(position1.location.y == position2.location.y); |
||||||
|
} |
Loading…
Reference in new issue