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.

main
Zed A. Shaw 10 months ago
parent babc190525
commit d113dba42f
  1. 18
      combat.cpp
  2. 16
      combat.hpp
  3. 8
      components.hpp
  4. 3
      dinkyecs.hpp
  5. 11
      gui.cpp
  6. 1
      gui.hpp
  7. 3
      meson.build
  8. 3
      point.hpp
  9. 63
      save.cpp
  10. 21
      save.hpp
  11. 2
      systems.cpp
  12. 2
      systems.hpp
  13. 45
      tests/config.cpp
  14. 80
      tests/save.cpp
  15. 1
      tser.hpp

@ -1,14 +1,16 @@
#include "combat.hpp"
#include "rand.hpp"
int Combat::attack(Combat &target) {
int attack = Random::uniform<int>(0,1);
int my_dmg = 0;
namespace components {
int Combat::attack(Combat &target) {
int attack = Random::uniform<int>(0,1);
int my_dmg = 0;
if(attack) {
my_dmg = Random::uniform<int>(1, damage);
target.hp -= my_dmg;
}
if(attack) {
my_dmg = Random::uniform<int>(1, damage);
target.hp -= my_dmg;
}
return my_dmg;
return my_dmg;
}
}

@ -1,11 +1,11 @@
#pragma once
#include "components.hpp"
namespace components {
struct Combat {
int hp;
int damage;
bool dead;
struct Combat {
int hp;
int damage;
bool dead;
int attack(Combat &target);
};
int attack(Combat &target);
};
}

@ -3,27 +3,33 @@
#include "map.hpp"
#include "combat.hpp"
#include <deque>
#include "tser.hpp"
namespace Components {
namespace components {
struct Player {
DinkyECS::Entity entity;
DEFINE_SERIALIZABLE(Player, entity);
};
struct Position {
Point location;
DEFINE_SERIALIZABLE(Position, location);
};
struct Motion {
int dx;
int dy;
DEFINE_SERIALIZABLE(Motion, dx, dy);
};
struct Treasure {
int amount;
DEFINE_SERIALIZABLE(Treasure, amount);
};
struct Tile {
std::string chr = "!";
DEFINE_SERIALIZABLE(Tile, chr);
};
struct MapConfig {

@ -7,6 +7,7 @@
#include <any>
#include <tuple>
#include <queue>
#include "tser.hpp"
namespace DinkyECS {
@ -116,4 +117,6 @@ namespace DinkyECS {
return !queue.empty();
}
};
}

@ -24,7 +24,7 @@ using std::string;
using namespace fmt;
using namespace std::chrono_literals;
using namespace ftxui;
using namespace Components;
using namespace components;
GUI::GUI(DinkyECS::World &world, Map& game_map) :
@ -56,6 +56,13 @@ void GUI::resize_map(int new_size) {
}
}
void GUI::save_world() {
tser::BinaryArchive archive;
archive.save($world);
std::string_view archive_view = archive.get_buffer();
$log.log(format("Game saved! {} bytes.", archive_view.size()));
}
void GUI::create_renderer() {
Terminal::SetColorSupport(Terminal::Color::TrueColor);
auto player = $world.get_the<Player>();
@ -151,6 +158,8 @@ bool GUI::handle_ui_events() {
resize_map(map_font_size + 10);
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) {
resize_map(map_font_size - 10);
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
save_world();
}
}
}

@ -58,6 +58,7 @@ public:
void handle_world_events();
void draw_screen(bool clear=true, float map_off_x=0.0f, float map_off_y=0.0f);
void run_systems();
void save_world();
int main();
};

@ -21,6 +21,7 @@ runtests = executable('runtests', [
'collider.cpp',
'ansi_parser.cpp',
'config.cpp',
'save.cpp',
'tests/fsm.cpp',
'tests/dbc.cpp',
'tests/map.cpp',
@ -29,6 +30,7 @@ runtests = executable('runtests', [
'tests/dinkyecs.cpp',
'tests/ansi_parser.cpp',
'tests/config.cpp',
'tests/save.cpp',
],
dependencies: dependencies)
@ -45,6 +47,7 @@ roguish = executable('roguish', [
'ansi_parser.cpp',
'render.cpp',
'config.cpp',
'save.cpp',
],
dependencies: dependencies)

@ -1,4 +1,5 @@
#pragma once
#include "tser.hpp"
struct Point {
size_t x = 0;
@ -7,6 +8,8 @@ struct Point {
bool operator==(const Point& other) const {
return other.x == x && other.y == y;
}
DEFINE_SERIALIZABLE(Point, x, y);
};
typedef std::vector<Point> PointList;

@ -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);
}

@ -10,7 +10,7 @@
using std::string;
using namespace fmt;
using namespace Components;
using namespace components;
using ftxui::Color;
void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) {

@ -4,7 +4,7 @@
#include "components.hpp"
#include <ftxui/dom/canvas.hpp>
using namespace Components;
using namespace components;
namespace System {
void motion(DinkyECS::World &world, Map &game_map);

@ -6,6 +6,7 @@
#include "dinkyecs.hpp"
#include "components.hpp"
using namespace fmt;
using std::string;
@ -61,47 +62,3 @@ TEST_CASE("can go into a world", "[config]") {
Config &cfg = world.get_the<Config>();
REQUIRE(cfg["types"]["NUMBER"] == 1234);
}
#include <optional>
#include <iostream>
#include "tser.hpp"
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);
}

@ -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);
}

@ -8,6 +8,7 @@
#include <string_view>
#include <type_traits>
#include <tuple>
#include <locale>
#include <codecvt>
namespace tser{