From 18c2752c0a50131430ef3dca5b535674d04dc698 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 20 Jun 2020 03:33:55 +0200 Subject: [PATCH] [WorldSelectionState] Added. --- source/client/states/TitleScreenState.cpp | 11 +- source/client/states/TitleScreenState.hpp | 2 +- source/client/states/WorldSelectionState.cpp | 110 +++++++++++++++++++ source/client/states/WorldSelectionState.hpp | 56 ++++++++++ source/server/core/ServerApplication.cpp | 11 ++ source/server/core/ServerApplication.hpp | 3 + source/server/world/ServerChunk.hpp | 1 + source/server/world/WorldController.cpp | 16 ++- 8 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 source/client/states/WorldSelectionState.cpp create mode 100644 source/client/states/WorldSelectionState.hpp diff --git a/source/client/states/TitleScreenState.cpp b/source/client/states/TitleScreenState.cpp index d02e48e98..a3634f678 100644 --- a/source/client/states/TitleScreenState.cpp +++ b/source/client/states/TitleScreenState.cpp @@ -33,6 +33,7 @@ #include "ServerLoadingState.hpp" #include "SettingsMenuState.hpp" #include "TitleScreenState.hpp" +#include "WorldSelectionState.hpp" #include "ServerApplication.hpp" // For ServerOnlineEvent @@ -42,7 +43,7 @@ TitleScreenState::TitleScreenState(u16 port) : m_port(port) { m_menuWidget.setScale(Config::guiScale, Config::guiScale, 1); m_menuWidget.addButton("Singleplayer", [this] (TextButton &) { - startSingleplayer(true); + m_stateStack->push(this); }); m_menuWidget.addButton("Multiplayer", [this] (TextButton &) { @@ -91,7 +92,7 @@ void TitleScreenState::onEvent(const sf::Event &event) { void TitleScreenState::update() { } -void TitleScreenState::startSingleplayer(bool showLoadingState) { +void TitleScreenState::startSingleplayer(bool showLoadingState, const std::string &save) { auto &game = m_stateStack->push(); game.setSingleplayer(true); @@ -101,8 +102,12 @@ void TitleScreenState::startSingleplayer(bool showLoadingState) { if (m_thread.joinable()) m_thread.join(); - m_thread = std::thread([this] () { + m_thread = std::thread([this, save] () { ServerApplication app{*m_eventHandler}; + + if (!save.empty()) + app.setSaveFile(save); + app.setSingleplayer(true); app.setPort(sf::Socket::AnyPort); app.run(); diff --git a/source/client/states/TitleScreenState.hpp b/source/client/states/TitleScreenState.hpp index 0d99c2a07..16681651e 100644 --- a/source/client/states/TitleScreenState.hpp +++ b/source/client/states/TitleScreenState.hpp @@ -48,7 +48,7 @@ class TitleScreenState : public InterfaceState { void update() override; - void startSingleplayer(bool showLoadingState); + void startSingleplayer(bool showLoadingState, const std::string &save = ""); void startMultiplayer(const std::string &host); void setTexturePack(const std::string &texturePack) { m_texturePack = texturePack; } diff --git a/source/client/states/WorldSelectionState.cpp b/source/client/states/WorldSelectionState.cpp new file mode 100644 index 000000000..2ea430843 --- /dev/null +++ b/source/client/states/WorldSelectionState.cpp @@ -0,0 +1,110 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include +#include + +#include + +#include "Config.hpp" +#include "TitleScreenState.hpp" +#include "WorldSelectionState.hpp" + +namespace fs = ghc::filesystem; + +WorldSelectionState::WorldSelectionState(TitleScreenState *titleScreen) + : InterfaceState(titleScreen), m_titleScreen(titleScreen) +{ + m_menuWidget.setScale(Config::guiScale, Config::guiScale); + + m_menuWidget.addButton("New world", [this](TextButton &) { + m_stateStack->pop(); + m_titleScreen->startSingleplayer(true); + }); + + m_cancelButton.setScale(Config::guiScale, Config::guiScale); + m_cancelButton.setText("Cancel"); + m_cancelButton.setCallback([this](TextButton &) { + m_stateStack->pop(); + }); + + updateButtonPosition(); + loadSaveList(); +} + +void WorldSelectionState::onEvent(const sf::Event &event) { + InterfaceState::onEvent(event); + + if (event.type == sf::Event::Resized) { + updateButtonPosition(); + if (!m_stateStack->empty() && &m_stateStack->top() != this) + m_menuWidget.onEvent(event); + } + + if (!m_stateStack->empty() && &m_stateStack->top() == this) { + m_menuWidget.onEvent(event); + m_cancelButton.onEvent(event); + } +} + +void WorldSelectionState::update() { +} + +void WorldSelectionState::updateButtonPosition() { + m_cancelButton.setPosition(Config::screenWidth / 2.0f - m_cancelButton.getGlobalBounds().sizeX / 2.0f, Config::screenHeight * 0.85); +} + +void WorldSelectionState::loadSaveList() { + if (fs::is_directory("saves")) { + fs::path basePath = fs::current_path(); + fs::directory_iterator dir("saves/"); + for (const auto &entry : dir) { + if (entry.is_regular_file()) { + std::string filename = entry.path().filename(); + if (filename.substr(filename.find_last_of('.')) == ".dat") { + std::string saveFile = filename.substr(0, filename.find_last_of('.')); + m_menuWidget.addButton("- " + saveFile + " -", [&, saveFile](TextButton &) { + m_stateStack->pop(); + m_titleScreen->startSingleplayer(true, saveFile); + }); + } + } + } + } +} + +void WorldSelectionState::draw(gk::RenderTarget &target, gk::RenderStates states) const { + if (m_parent) + target.draw(*m_parent, states); + + if (&m_stateStack->top() == this) { + prepareDraw(target, states); + + target.draw(m_menuWidget, states); + target.draw(m_cancelButton, states); + } +} + diff --git a/source/client/states/WorldSelectionState.hpp b/source/client/states/WorldSelectionState.hpp new file mode 100644 index 000000000..cd32a75c0 --- /dev/null +++ b/source/client/states/WorldSelectionState.hpp @@ -0,0 +1,56 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef WORLDSELECTIONSTATE_HPP_ +#define WORLDSELECTIONSTATE_HPP_ + +#include "InterfaceState.hpp" +#include "MenuWidget.hpp" + +class TitleScreenState; + +class WorldSelectionState : public InterfaceState { + public: + WorldSelectionState(TitleScreenState *titleScreen); + + void onEvent(const sf::Event &event) override; + + void update() override; + + private: + void updateButtonPosition(); + void loadSaveList(); + + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + TitleScreenState *m_titleScreen = nullptr; + + MenuWidget m_menuWidget; + + TextButton m_cancelButton; +}; + +#endif // WORLDSELECTIONSTATE_HPP_ diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp index 029ff95d7..598eb174b 100644 --- a/source/server/core/ServerApplication.cpp +++ b/source/server/core/ServerApplication.cpp @@ -66,6 +66,7 @@ void ServerApplication::init() { BlockGeometry::initOrientation(); m_argumentParser.addArgument("port", {"-p", "--port", "Select the port to use.", "port"}); + m_argumentParser.addArgument("save", {"-s", "--save", "Select a save file to use.", "save"}); m_argumentParser.addArgument("working-dir", {"-w", "--working-dir", "Change the working direction to .", "dir"}); m_argumentParser.parse(); @@ -79,6 +80,9 @@ void ServerApplication::init() { if (m_argumentParser.getArgument("port").isFound) m_port = std::stoi(m_argumentParser.getArgument("port").parameter); + if (m_argumentParser.getArgument("save").isFound) + m_saveFile = m_argumentParser.getArgument("save").parameter; + ServerConfig::loadConfigFromFile("config/server.lua"); m_server.init(m_port); @@ -103,6 +107,9 @@ void ServerApplication::init() { if (m_eventHandler) m_eventHandler->emplaceEvent(true, m_server.port()); + + if (!m_saveFile.empty()) + m_worldController.load(m_saveFile); } int ServerApplication::run(bool isProtected) { @@ -135,6 +142,10 @@ int ServerApplication::run(bool isProtected) { gkInfo() << "Stopping server..."; + if (!m_saveFile.empty()) { + m_worldController.save(m_saveFile); + } + ServerConfig::saveConfigToFile("config/server.lua"); ServerConfig::options.clear(); diff --git a/source/server/core/ServerApplication.hpp b/source/server/core/ServerApplication.hpp index 42a706c99..a2ab83a0e 100644 --- a/source/server/core/ServerApplication.hpp +++ b/source/server/core/ServerApplication.hpp @@ -56,6 +56,7 @@ class ServerApplication { void setSingleplayer(bool isSingleplayer) { m_server.setSingleplayer(isSingleplayer); } void setPort(u16 port) { m_port = port; } + void setSaveFile(const std::string &saveFile) { m_saveFile = saveFile; } private: void update(); @@ -70,6 +71,8 @@ class ServerApplication { u16 m_port = 4242; + std::string m_saveFile; + WorldController m_worldController{m_registry, m_clock}; PlayerList m_players; diff --git a/source/server/world/ServerChunk.hpp b/source/server/world/ServerChunk.hpp index 4e242fbe4..22c184756 100644 --- a/source/server/world/ServerChunk.hpp +++ b/source/server/world/ServerChunk.hpp @@ -51,6 +51,7 @@ class ServerChunk : public Chunk { void setSent(bool isSent) { m_isSent = isSent; } bool hasBeenModified() const { return m_hasBeenModified; } + void setModified(bool hasBeenModified) { m_hasBeenModified = hasBeenModified; } private: std::atomic_bool m_isSent{false}; diff --git a/source/server/world/WorldController.cpp b/source/server/world/WorldController.cpp index 59ccb7204..9cd5fb8c5 100644 --- a/source/server/world/WorldController.cpp +++ b/source/server/world/WorldController.cpp @@ -24,12 +24,15 @@ * * ===================================================================================== */ -#include #include +#include + #include "Registry.hpp" #include "WorldController.hpp" +namespace fs = ghc::filesystem; + void WorldController::init(PlayerList &players) { for (const Dimension &dimension : m_registry.dimensions()) { m_worldList.emplace_back(players, dimension, m_clock); @@ -69,7 +72,7 @@ void WorldController::load(const std::string &name) { unsigned int chunkCount; save >> chunkCount; - gkInfo() << "Loading dimension" << world.dimension().id() << "| Chunk count:" << chunkCount; + // gkInfo() << "Loading dimension" << world.dimension().id() << "| Chunk count:" << chunkCount; for (unsigned int i = 0 ; i < chunkCount ; ++i) { int cx, cy, cz; @@ -92,17 +95,18 @@ void WorldController::load(const std::string &name) { chunk.setInitialized(true); chunk.setSent(false); + chunk.setModified(true); } } } - gkInfo() << "Loading done."; + // gkInfo() << "Loading done."; } void WorldController::save(const std::string &name) { gkInfo() << ("Saving '" + name + "'...").c_str(); - std::filesystem::create_directory("saves"); + fs::create_directory("saves"); std::ofstream file("saves/" + name + ".dat", std::ofstream::binary | std::ofstream::trunc); @@ -129,7 +133,7 @@ void WorldController::save(const std::string &name) { ++chunkCount; } - gkInfo() << "Saving dimension" << world.dimension().id() << "| Chunk count:" << chunkCount; + // gkInfo() << "Saving dimension" << world.dimension().id() << "| Chunk count:" << chunkCount; save << chunkCount; save.append(chunks.getData(), chunks.getDataSize()); @@ -137,5 +141,5 @@ void WorldController::save(const std::string &name) { file.write((const char *)save.getData(), save.getDataSize()); - gkInfo() << "Saving done."; + // gkInfo() << "Saving done."; }