From ef79c5b8bf5db5b6b335a2012b5381d182bdee4f Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 17 Mar 2020 23:54:24 +0100 Subject: [PATCH] [ChatCommandHandler] '/save' and '/load' commands added. --- README.md | 2 +- source/server/network/ChatCommandHandler.cpp | 31 ++++++- source/server/network/ChatCommandHandler.hpp | 7 +- .../server/network/ServerCommandHandler.hpp | 2 +- source/server/world/WorldController.cpp | 90 +++++++++++++++++++ source/server/world/WorldController.hpp | 3 + 6 files changed, 131 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fbcae9ce2..f92891b1c 100644 --- a/README.md +++ b/README.md @@ -104,11 +104,11 @@ This list is non exhaustive. - Block metadata - Player model display (currently without rotation nor animation) - Dimensions (like the Nether or the Ender in Minecraft) ([#80](https://github.com/Unarelith/OpenMiner/pull/80)) +- World loading/saving (using `/save ` and `/load ` commands, see [#26](https://github.com/Unarelith/OpenMiner/issues/26)) ### Missing features - Texture pack system ([#34](https://github.com/Unarelith/OpenMiner/issues/34)) -- World loading/saving ([#26](https://github.com/Unarelith/OpenMiner/issues/26)) - Fluid propagation ([#62](https://github.com/Unarelith/OpenMiner/issues/62)) - Day/night cycle with sun/moon display ([#73](https://github.com/Unarelith/OpenMiner/issues/73)) - Real worldgen (seed-based, cave tunnels) ([#79](https://github.com/Unarelith/OpenMiner/issues/79)) diff --git a/source/server/network/ChatCommandHandler.cpp b/source/server/network/ChatCommandHandler.cpp index 3d582b2ad..11555120e 100644 --- a/source/server/network/ChatCommandHandler.cpp +++ b/source/server/network/ChatCommandHandler.cpp @@ -30,6 +30,7 @@ #include "ChatCommandHandler.hpp" #include "ClientInfo.hpp" #include "ServerCommandHandler.hpp" +#include "WorldController.hpp" void ChatCommandHandler::parseCommand(const std::string &str, ClientInfo &client) const { std::stringstream sstream; @@ -42,7 +43,9 @@ void ChatCommandHandler::parseCommand(const std::string &str, ClientInfo &client } std::unordered_map commands = { - {"tp", &ChatCommandHandler::teleportationCommand} + {"tp", &ChatCommandHandler::teleportationCommand}, + {"save", &ChatCommandHandler::saveCommand}, + {"load", &ChatCommandHandler::loadCommand}, }; if (!command.empty()) { @@ -82,3 +85,29 @@ void ChatCommandHandler::teleportationCommand(const std::vector &co } } +void ChatCommandHandler::saveCommand(const std::vector &command, ClientInfo &client) const { + if (command.size() != 2) { + m_server.sendChatMessage(0, "Usage: /save ", &client); + } + else { + std::string name = command.at(1); + + m_worldController.save(name); + + m_server.sendChatMessage(0, "Saved '" + name + "'", &client); + } +} + +void ChatCommandHandler::loadCommand(const std::vector &command, ClientInfo &client) const { + if (command.size() != 2) { + m_server.sendChatMessage(0, "Usage: /load ", &client); + } + else { + std::string name = command.at(1); + + m_worldController.load(name); + + m_server.sendChatMessage(0, "Loaded '" + name + "'", &client); + } +} + diff --git a/source/server/network/ChatCommandHandler.hpp b/source/server/network/ChatCommandHandler.hpp index 46374f53f..ff41520b6 100644 --- a/source/server/network/ChatCommandHandler.hpp +++ b/source/server/network/ChatCommandHandler.hpp @@ -32,17 +32,22 @@ class ClientInfo; class ServerCommandHandler; +class WorldController; class ChatCommandHandler { public: - ChatCommandHandler(ServerCommandHandler &server) : m_server(server) {} + ChatCommandHandler(ServerCommandHandler &server, WorldController &worldController) + : m_server(server), m_worldController(worldController) {} void parseCommand(const std::string &str, ClientInfo &client) const; private: void teleportationCommand(const std::vector &command, ClientInfo &client) const; + void saveCommand(const std::vector &command, ClientInfo &client) const; + void loadCommand(const std::vector &command, ClientInfo &client) const; ServerCommandHandler &m_server; + WorldController &m_worldController; }; #endif // CHATCOMMANDHANDLER_HPP_ diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index ff3d44d08..aff2807e1 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -74,7 +74,7 @@ class ServerCommandHandler { gk::Vector3d m_spawnPosition{14.5, 14.5, 18.}; - ChatCommandHandler m_chatCommandHandler{*this}; + ChatCommandHandler m_chatCommandHandler{*this, m_worldController}; }; #endif // SERVERCOMMANDHANDLER_HPP_ diff --git a/source/server/world/WorldController.cpp b/source/server/world/WorldController.cpp index a4d9727d9..73170b2f5 100644 --- a/source/server/world/WorldController.cpp +++ b/source/server/world/WorldController.cpp @@ -24,6 +24,8 @@ * * ===================================================================================== */ +#include + #include "Registry.hpp" #include "WorldController.hpp" @@ -39,3 +41,91 @@ void WorldController::update() { it.update(); } +void WorldController::load(const std::string &name) { + // std::cout << "Loading '" + name + "'..." << std::endl; + + std::ifstream file(name + ".dat", std::ofstream::binary); + + if (file.is_open()) { + file.seekg(0, file.end); + int length = file.tellg(); + file.seekg(0, file.beg); + + char *buffer = new char[length]; + file.read(buffer, length); + + sf::Packet save; + save.append(buffer, length); + + delete[] buffer; + + for (auto &world : m_worldList) { + unsigned int chunkCount; + save >> chunkCount; + + // std::cout << "Dimension " << world.dimension().id() << " chunk count: " << chunkCount << std::endl; + + for (unsigned int i = 0 ; i < chunkCount ; ++i) { + int cx, cy, cz; + save >> cx >> cy >> cz; + + ServerChunk &chunk = world.createChunk(cx, cy, cz); + for (u8 z = 0 ; z < Chunk::height ; ++z) { + for (u8 y = 0 ; y < Chunk::depth ; ++y) { + for (u8 x = 0 ; x < Chunk::width ; ++x) { + u32 data; + u8 light; + save >> data >> light; + + chunk.setBlockRaw(x, y, z, data & 0xffff); + chunk.setData(x, y, z, data >> 16); + chunk.lightmap().setLightData(x, y, z, light); + } + } + } + + chunk.setInitialized(true); + chunk.setSent(false); + } + } + } + + // std::cout << "Loading done." << std::endl; +} + +void WorldController::save(const std::string &name) { + // std::cout << "Saving '" << name << "'..." << std::endl; + + std::ofstream file(name + ".dat", std::ofstream::binary | std::ofstream::trunc); + + sf::Packet save; + for (auto &world : m_worldList) { + sf::Packet chunks; + unsigned int chunkCount = 0; + for (auto &chunk : world.chunks()) { + if (!chunk.second->isInitialized()) continue; + + const gk::Vector3i &chunkpos = chunk.first; + const Chunk::DataArray &data = chunk.second->data(); + chunks << chunkpos.x << chunkpos.y << chunkpos.z; + + for (u8 z = 0 ; z < Chunk::height ; ++z) { + for (u8 y = 0 ; y < Chunk::depth ; ++y) { + for (u8 x = 0 ; x < Chunk::width ; ++x) { + chunks << u32(data[z][y][x]) + << u8(chunk.second->lightmap().getLightData(x, y, z)); + } + } + } + + ++chunkCount; + } + + // std::cout << "Saving dimension " << world.dimension().id() << ". Chunk count: " << chunkCount << std::endl; + + save << chunkCount; + save.append(chunks.getData(), chunks.getDataSize()); + } + + file.write((const char *)save.getData(), save.getDataSize()); +} diff --git a/source/server/world/WorldController.hpp b/source/server/world/WorldController.hpp index 027444863..e691b1f80 100644 --- a/source/server/world/WorldController.hpp +++ b/source/server/world/WorldController.hpp @@ -45,6 +45,9 @@ class WorldController { void update(); + void load(const std::string &name); + void save(const std::string &name); + ServerWorld &getWorld(u16 dimension) { return m_worldList.at(dimension); } void setServer(ServerCommandHandler &server) { m_server = &server; }