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