From 55b504792e64978220b7e5f0bf069522ab4afb3b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 28 Dec 2023 21:27:21 +0200 Subject: [PATCH] Implemented basic version of login window and persistent connection on client --- Mods/vcmi/config/vcmi/english.json | 5 ++ client/CMakeLists.txt | 2 + client/CServerHandler.cpp | 7 ++ client/CServerHandler.h | 4 + client/globalLobby/GlobalLobbyClient.cpp | 78 +++++++++++++++---- client/globalLobby/GlobalLobbyClient.h | 19 +++-- client/globalLobby/GlobalLobbyLoginWindow.cpp | 76 ++++++++++++++++++ client/globalLobby/GlobalLobbyLoginWindow.h | 41 ++++++++++ client/globalLobby/GlobalLobbyWidget.cpp | 1 + client/globalLobby/GlobalLobbyWindow.cpp | 23 +++--- client/globalLobby/GlobalLobbyWindow.h | 6 +- client/lobby/CSelectionBase.cpp | 2 +- client/lobby/OptionsTab.cpp | 2 +- client/mainmenu/CHighScoreScreen.cpp | 4 +- client/mainmenu/CMainMenu.cpp | 7 +- client/widgets/TextControls.cpp | 4 +- client/widgets/TextControls.h | 2 +- client/windows/CSpellWindow.cpp | 2 +- client/windows/GUIClasses.cpp | 4 +- config/widgets/lobbyWindow.json | 2 + lib/network/NetworkClient.cpp | 9 +++ lib/network/NetworkClient.h | 2 + lobby/LobbyServer.cpp | 35 +++++---- 23 files changed, 278 insertions(+), 59 deletions(-) create mode 100644 client/globalLobby/GlobalLobbyLoginWindow.cpp create mode 100644 client/globalLobby/GlobalLobbyLoginWindow.h diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 81215b65c..0cc354008 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -71,6 +71,11 @@ "vcmi.lobby.noPreview" : "no preview", "vcmi.lobby.noUnderground" : "no underground", "vcmi.lobby.sortDate" : "Sorts maps by change date", + + "vcmi.lobby.login.title" : "VCMI Lobby", + "vcmi.lobby.login.username" : "Username:", + "vcmi.lobby.login.connecting" : "Connecting...", +// "vcmi.lobby.login.connectionFailed" : "Connection failed: %s", "vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.", "vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.", diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5d39afeb7..7d00fee8c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -96,6 +96,7 @@ set(client_SRCS renderSDL/SDL_Extensions.cpp globalLobby/GlobalLobbyClient.cpp + globalLobby/GlobalLobbyLoginWindow.cpp globalLobby/GlobalLobbyWidget.cpp globalLobby/GlobalLobbyWindow.cpp @@ -275,6 +276,7 @@ set(client_HEADERS renderSDL/SDL_PixelAccess.h globalLobby/GlobalLobbyClient.h + globalLobby/GlobalLobbyLoginWindow.h globalLobby/GlobalLobbyWidget.h globalLobby/GlobalLobbyWindow.h diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 0719af7fa..165c6f902 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -16,6 +16,7 @@ #include "gui/CGuiHandler.h" #include "gui/WindowHandler.h" +#include "globalLobby/GlobalLobbyClient.h" #include "lobby/CSelectionBase.h" #include "lobby/CLobbyScreen.h" #include "windows/InfoWindows.h" @@ -139,6 +140,7 @@ CServerHandler::CServerHandler() : state(EClientState::NONE) , networkClient(std::make_unique(*this)) , applier(std::make_unique>()) + , lobbyClient(std::make_unique()) , client(nullptr) , loadMode(0) , campaignStateToSend(nullptr) @@ -175,6 +177,11 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std:: myNames.push_back(settings["general"]["playerName"].String()); } +GlobalLobbyClient & CServerHandler::getGlobalLobby() +{ + return *lobbyClient; +} + void CServerHandler::startLocalServerAndConnect() { if(threadRunLocalServer) diff --git a/client/CServerHandler.h b/client/CServerHandler.h index fd3dd1145..bca2f4608 100644 --- a/client/CServerHandler.h +++ b/client/CServerHandler.h @@ -35,6 +35,7 @@ VCMI_LIB_NAMESPACE_END class CClient; class CBaseForLobbyApply; +class GlobalLobbyClient; class HighScoreCalculation; class HighScoreParameter; @@ -86,6 +87,7 @@ class CServerHandler : public IServerAPI, public LobbyInfo, public INetworkClien friend class ApplyOnLobbyHandlerNetPackVisitor; std::unique_ptr networkClient; + std::unique_ptr lobbyClient; std::unique_ptr> applier; std::shared_ptr mapToStart; std::vector myNames; @@ -138,6 +140,8 @@ public: void startLocalServerAndConnect(); void connectToServer(const std::string & addr, const ui16 port); + GlobalLobbyClient & getGlobalLobby(); + // Helpers for lobby state access std::set getHumanColors(); PlayerColor myFirstColor() const; diff --git a/client/globalLobby/GlobalLobbyClient.cpp b/client/globalLobby/GlobalLobbyClient.cpp index eb2d558fa..fd3ba1ac8 100644 --- a/client/globalLobby/GlobalLobbyClient.cpp +++ b/client/globalLobby/GlobalLobbyClient.cpp @@ -12,6 +12,7 @@ #include "GlobalLobbyClient.h" #include "GlobalLobbyWindow.h" +#include "GlobalLobbyLoginWindow.h" #include "../gui/CGuiHandler.h" #include "../gui/WindowHandler.h" @@ -21,12 +22,19 @@ #include "../../lib/CConfigHandler.h" #include "../../lib/network/NetworkClient.h" -GlobalLobbyClient::~GlobalLobbyClient() = default; +GlobalLobbyClient::~GlobalLobbyClient() +{ + networkClient->stop(); + networkThread->join(); +} -GlobalLobbyClient::GlobalLobbyClient(GlobalLobbyWindow * window) +GlobalLobbyClient::GlobalLobbyClient() : networkClient(std::make_unique(*this)) - , window(window) -{} +{ + networkThread = std::make_unique([this](){ + networkClient->run(); + }); +} static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0) { @@ -47,10 +55,22 @@ static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0) void GlobalLobbyClient::onPacketReceived(const std::shared_ptr &, const std::vector & message) { + boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); + // FIXME: find better approach const char * payloadBegin = reinterpret_cast(message.data()); JsonNode json(payloadBegin, message.size()); + if (json["type"].String() == "authentication") + { + auto loginWindowPtr = loginWindow.lock(); + + if (!loginWindowPtr || !GH.windows().topWindow()) + throw std::runtime_error("lobby connection finished without active login window!"); + + loginWindowPtr->onConnectionSuccess(); + } + if (json["type"].String() == "chatHistory") { for (auto const & entry : json["messages"].Vector()) @@ -59,7 +79,10 @@ void GlobalLobbyClient::onPacketReceived(const std::shared_ptronGameChatMessage(senderName, messageText, timeFormatted); + + auto lobbyWindowPtr = lobbyWindow.lock(); + if(lobbyWindowPtr) + lobbyWindowPtr->onGameChatMessage(senderName, messageText, timeFormatted); } } @@ -68,7 +91,9 @@ void GlobalLobbyClient::onPacketReceived(const std::shared_ptronGameChatMessage(senderName, messageText, timeFormatted); + auto lobbyWindowPtr = lobbyWindow.lock(); + if(lobbyWindowPtr) + lobbyWindowPtr->onGameChatMessage(senderName, messageText, timeFormatted); } } @@ -83,8 +108,13 @@ void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr()) + throw std::runtime_error("lobby connection failed without active login window!"); + + logGlobal->warn("Connection to game lobby failed! Reason: %s", errorMessage); + loginWindowPtr->onConnectionFailed(errorMessage); } void GlobalLobbyClient::onDisconnected(const std::shared_ptr &) @@ -111,17 +141,37 @@ void GlobalLobbyClient::sendMessage(const JsonNode & data) networkClient->sendPacket(payloadBuffer); } -void GlobalLobbyClient::start(const std::string & host, uint16_t port) +void GlobalLobbyClient::connect() { - networkClient->start(host, port); + networkClient->start("127.0.0.1", 30303); } -void GlobalLobbyClient::run() +bool GlobalLobbyClient::isConnected() { - networkClient->run(); + return networkClient->isConnected(); } -void GlobalLobbyClient::poll() +std::shared_ptr GlobalLobbyClient::createLoginWindow() { - networkClient->poll(); + auto loginWindowPtr = loginWindow.lock(); + if (loginWindowPtr) + return loginWindowPtr; + + auto loginWindowNew = std::make_shared(); + loginWindow = loginWindowNew; + + return loginWindowNew; } + +std::shared_ptr GlobalLobbyClient::createLobbyWindow() +{ + auto lobbyWindowPtr = lobbyWindow.lock(); + if (lobbyWindowPtr) + return lobbyWindowPtr; + + lobbyWindowPtr = std::make_shared(); + lobbyWindow = lobbyWindowPtr; + lobbyWindowLock = lobbyWindowPtr; + return lobbyWindowPtr; +} + diff --git a/client/globalLobby/GlobalLobbyClient.h b/client/globalLobby/GlobalLobbyClient.h index c018792ff..50e1020d8 100644 --- a/client/globalLobby/GlobalLobbyClient.h +++ b/client/globalLobby/GlobalLobbyClient.h @@ -15,12 +15,17 @@ VCMI_LIB_NAMESPACE_BEGIN class JsonNode; VCMI_LIB_NAMESPACE_END +class GlobalLobbyLoginWindow; class GlobalLobbyWindow; -class GlobalLobbyClient : public INetworkClientListener +class GlobalLobbyClient : public INetworkClientListener, boost::noncopyable { + std::unique_ptr networkThread; std::unique_ptr networkClient; - GlobalLobbyWindow * window; + + std::weak_ptr loginWindow; + std::weak_ptr lobbyWindow; + std::shared_ptr lobbyWindowLock; // helper strong reference to prevent window destruction on closing void onPacketReceived(const std::shared_ptr &, const std::vector & message) override; void onConnectionFailed(const std::string & errorMessage) override; @@ -29,12 +34,12 @@ class GlobalLobbyClient : public INetworkClientListener void onTimer() override; public: - explicit GlobalLobbyClient(GlobalLobbyWindow * window); + explicit GlobalLobbyClient(); ~GlobalLobbyClient(); void sendMessage(const JsonNode & data); - void start(const std::string & host, uint16_t port); - void run(); - void poll(); - + void connect(); + bool isConnected(); + std::shared_ptr createLoginWindow(); + std::shared_ptr createLobbyWindow(); }; diff --git a/client/globalLobby/GlobalLobbyLoginWindow.cpp b/client/globalLobby/GlobalLobbyLoginWindow.cpp new file mode 100644 index 000000000..6e4056d68 --- /dev/null +++ b/client/globalLobby/GlobalLobbyLoginWindow.cpp @@ -0,0 +1,76 @@ +/* + * GlobalLobbyLoginWindow.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "GlobalLobbyLoginWindow.h" + +#include "GlobalLobbyClient.h" +#include "GlobalLobbyWindow.h" + +#include "../gui/CGuiHandler.h" +#include "../gui/WindowHandler.h" +#include "../widgets/TextControls.h" +#include "../widgets/Images.h" +#include "../widgets/Buttons.h" +#include "../widgets/MiscWidgets.h" +#include "../CGameInfo.h" +#include "../CServerHandler.h" + +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/MetaString.h" + +GlobalLobbyLoginWindow::GlobalLobbyLoginWindow() + : CWindowObject(BORDERED) +{ + OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; + + pos.w = 200; + pos.h = 200; + + background = std::make_shared(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h)); + labelTitle = std::make_shared( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title")); + labelUsername = std::make_shared( 10, 45, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.lobby.login.username")); + backgroundUsername = std::make_shared(Rect(10, 70, 180, 20), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64)); + inputUsername = std::make_shared(Rect(15, 73, 176, 16), FONT_SMALL, nullptr, ETextAlignment::TOPLEFT, true); + buttonLogin = std::make_shared(Point(10, 160), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); }); + buttonClose = std::make_shared(Point(126, 160), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); }); + labelStatus = std::make_shared( "", Rect(15, 95, 175, 60), 1, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE); + + background->playerColored(PlayerColor(1)); + + center(); +} + +void GlobalLobbyLoginWindow::onClose() +{ + close(); + // TODO: abort ongoing connection attempt, if any +} + +void GlobalLobbyLoginWindow::onLogin() +{ + labelStatus->setText(CGI->generaltexth->translate("vcmi.lobby.login.connecting")); + CSH->getGlobalLobby().connect(); +} + +void GlobalLobbyLoginWindow::onConnectionSuccess() +{ + close(); + GH.windows().pushWindow(CSH->getGlobalLobby().createLobbyWindow()); +} + +void GlobalLobbyLoginWindow::onConnectionFailed(const std::string & reason) +{ + MetaString formatter; + formatter.appendTextID("vcmi.lobby.login.error"); + formatter.replaceRawString(reason); + + labelStatus->setText(formatter.toString()); +} diff --git a/client/globalLobby/GlobalLobbyLoginWindow.h b/client/globalLobby/GlobalLobbyLoginWindow.h new file mode 100644 index 000000000..31bcd5015 --- /dev/null +++ b/client/globalLobby/GlobalLobbyLoginWindow.h @@ -0,0 +1,41 @@ +/* + * GlobalLobbyLoginWindow.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "../windows/CWindowObject.h" + +class CLabel; +class CTextBox; +class CTextInput; +class FilledTexturePlayerColored; +class TransparentFilledRectangle; +class CButton; + +class GlobalLobbyLoginWindow : public CWindowObject +{ + std::shared_ptr background; + std::shared_ptr labelTitle; + std::shared_ptr labelUsername; + std::shared_ptr labelStatus; + std::shared_ptr backgroundUsername; + std::shared_ptr inputUsername; + + std::shared_ptr buttonLogin; + std::shared_ptr buttonClose; + + void onClose(); + void onLogin(); + +public: + GlobalLobbyLoginWindow(); + + void onConnectionSuccess(); + void onConnectionFailed(const std::string & reason); +}; diff --git a/client/globalLobby/GlobalLobbyWidget.cpp b/client/globalLobby/GlobalLobbyWidget.cpp index 6491d1a24..e5b462721 100644 --- a/client/globalLobby/GlobalLobbyWidget.cpp +++ b/client/globalLobby/GlobalLobbyWidget.cpp @@ -21,6 +21,7 @@ GlobalLobbyWidget::GlobalLobbyWidget(GlobalLobbyWindow * window) { addCallback("closeWindow", [](int) { GH.windows().popWindows(1); }); addCallback("sendMessage", [this](int) { this->window->doSendChatMessage(); }); + addCallback("createGameRoom", [this](int) { this->window->doCreateGameRoom(); }); const JsonNode config(JsonPath::builtin("config/widgets/lobbyWindow.json")); build(config); diff --git a/client/globalLobby/GlobalLobbyWindow.cpp b/client/globalLobby/GlobalLobbyWindow.cpp index 8ea9dad64..fa8b7bd1d 100644 --- a/client/globalLobby/GlobalLobbyWindow.cpp +++ b/client/globalLobby/GlobalLobbyWindow.cpp @@ -16,6 +16,7 @@ #include "../gui/CGuiHandler.h" #include "../widgets/TextControls.h" +#include "../CServerHandler.h" #include "../../lib/MetaString.h" #include "../../lib/CConfigHandler.h" @@ -27,17 +28,8 @@ GlobalLobbyWindow::GlobalLobbyWindow(): widget = std::make_shared(this); pos = widget->pos; center(); - connection = std::make_shared(this); - connection->start("127.0.0.1", 30303); widget->getAccountNameLabel()->setText(settings["general"]["playerName"].String()); - - addUsedEvents(TIME); -} - -void GlobalLobbyWindow::tick(uint32_t msPassed) -{ - connection->poll(); } void GlobalLobbyWindow::doSendChatMessage() @@ -48,11 +40,22 @@ void GlobalLobbyWindow::doSendChatMessage() toSend["type"].String() = "sendChatMessage"; toSend["messageText"].String() = messageText; - connection->sendMessage(toSend); + CSH->getGlobalLobby().sendMessage(toSend); widget->getMessageInput()->setText(""); } +void GlobalLobbyWindow::doCreateGameRoom() +{ + // TODO: + // start local server and supply our UUID / client credentials to it + // server logs into lobby ( uuid = client, mode = server ). This creates 'room' in mode 'empty' + // server starts accepting connections from players (including host) + // client connects to local server + // client sends createGameRoom query to lobby with own / server UUID and mode 'direct' (non-proxy) + // client requests to change room status to private or public +} + void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when) { MetaString chatMessageFormatted; diff --git a/client/globalLobby/GlobalLobbyWindow.h b/client/globalLobby/GlobalLobbyWindow.h index 9856d96cb..5958ab495 100644 --- a/client/globalLobby/GlobalLobbyWindow.h +++ b/client/globalLobby/GlobalLobbyWindow.h @@ -12,21 +12,19 @@ #include "../windows/CWindowObject.h" class GlobalLobbyWidget; -class GlobalLobbyClient; class GlobalLobbyWindow : public CWindowObject { std::string chatHistory; std::shared_ptr widget; - std::shared_ptr connection; - - void tick(uint32_t msPassed); public: GlobalLobbyWindow(); void doSendChatMessage(); + void doCreateGameRoom(); void onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when); + }; diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 6ead7387c..56e814663 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -335,7 +335,7 @@ CChatBox::CChatBox(const Rect & rect) Rect textInputArea(1, rect.h - height, rect.w - 1, height); Rect chatHistoryArea(3, 1, rect.w - 3, rect.h - height - 1); inputBackground = std::make_shared(textInputArea, ColorRGBA(0,0,0,192)); - inputBox = std::make_shared(textInputArea, EFonts::FONT_SMALL, 0); + inputBox = std::make_shared(textInputArea, EFonts::FONT_SMALL, nullptr, ETextAlignment::TOPLEFT, true); inputBox->removeUsedEvents(KEYBOARD); chatHistory = std::make_shared("", chatHistoryArea, 1); diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index f67d3d447..99f6851f7 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -892,7 +892,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con labelPlayerName = std::make_shared(55, 10, EFonts::FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, name, 95); else { - labelPlayerNameEdit = std::make_shared(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, nullptr, false); + labelPlayerNameEdit = std::make_shared(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, nullptr, ETextAlignment::CENTER, false); labelPlayerNameEdit->setText(name); } labelWhoCanPlay = std::make_shared(Rect(6, 23, 45, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]); diff --git a/client/mainmenu/CHighScoreScreen.cpp b/client/mainmenu/CHighScoreScreen.cpp index 07b119950..4abcc712b 100644 --- a/client/mainmenu/CHighScoreScreen.cpp +++ b/client/mainmenu/CHighScoreScreen.cpp @@ -372,7 +372,7 @@ CHighScoreInput::CHighScoreInput(std::string playerName, std::function(Point(26, 142), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CHighScoreInput::okay, this), EShortcut::GLOBAL_ACCEPT); buttonCancel = std::make_shared(Point(142, 142), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[561], std::bind(&CHighScoreInput::abort, this), EShortcut::GLOBAL_CANCEL); statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(7, 186, 218, 18), 7, 186)); - textInput = std::make_shared(Rect(18, 104, 200, 25), FONT_SMALL, 0); + textInput = std::make_shared(Rect(18, 104, 200, 25), FONT_SMALL, nullptr, ETextAlignment::CENTER, true); textInput->setText(playerName); } @@ -384,4 +384,4 @@ void CHighScoreInput::okay() void CHighScoreInput::abort() { ready(""); -} \ No newline at end of file +} diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index 6938666cf..c7e3fdcb5 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -24,6 +24,8 @@ #include "../gui/Shortcut.h" #include "../gui/WindowHandler.h" #include "../render/Canvas.h" +#include "../globalLobby/GlobalLobbyLoginWindow.h" +#include "../globalLobby/GlobalLobbyClient.h" #include "../globalLobby/GlobalLobbyWindow.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" @@ -467,7 +469,10 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType) void CMultiMode::openLobby() { close(); - GH.windows().createAndPushWindow(); + if (CSH->getGlobalLobby().isConnected()) + GH.windows().pushWindow(CSH->getGlobalLobby().createLobbyWindow()); + else + GH.windows().pushWindow(CSH->getGlobalLobby().createLoginWindow()); } void CMultiMode::hostTCP() diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index 0e9fad026..d58576eba 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -551,8 +551,8 @@ Point CGStatusBar::getBorderSize() return Point(); } -CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList & CB, bool giveFocusToInput) - : CLabel(Pos.x, Pos.y, font, ETextAlignment::CENTER), +CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList & CB, ETextAlignment alignment, bool giveFocusToInput) + : CLabel(Pos.x, Pos.y, font, alignment), cb(CB), CFocusable(std::make_shared(this)) { diff --git a/client/widgets/TextControls.h b/client/widgets/TextControls.h index 7c6b5266b..264158ced 100644 --- a/client/widgets/TextControls.h +++ b/client/widgets/TextControls.h @@ -228,7 +228,7 @@ public: void setText(const std::string & nText, bool callCb); void setHelpText(const std::string &); - CTextInput(const Rect & Pos, EFonts font, const CFunctionList & CB, bool giveFocusToInput = true); + CTextInput(const Rect & Pos, EFonts font, const CFunctionList & CB, ETextAlignment alignment, bool giveFocusToInput); CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName, const CFunctionList & CB); CTextInput(const Rect & Pos, std::shared_ptr srf); diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index fbbc8f5e3..1fd66293a 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -136,7 +136,7 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m searchBoxRectangle = std::make_shared(r.resize(1), rectangleColor, borderColor); searchBoxDescription = std::make_shared(r.center().x, r.center().y, FONT_SMALL, ETextAlignment::CENTER, grayedColor, CGI->generaltexth->translate("vcmi.spellBook.search")); - searchBox = std::make_shared(r, FONT_SMALL, std::bind(&CSpellWindow::searchInput, this)); + searchBox = std::make_shared(r, FONT_SMALL, std::bind(&CSpellWindow::searchInput, this), ETextAlignment::CENTER, true); } processSpells(); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 94c11a178..509f70413 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -324,8 +324,8 @@ CSplitWindow::CSplitWindow(const CCreature * creature, std::function(Rect(20, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, true)); - rightInput = std::make_shared(Rect(176, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, false)); + leftInput = std::make_shared(Rect(20, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, true), ETextAlignment::CENTER, true); + rightInput = std::make_shared(Rect(176, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, false), ETextAlignment::CENTER, true); //add filters to allow only number input leftInput->filters += std::bind(&CTextInput::numberFilter, _1, _2, leftMin, leftMax); diff --git a/config/widgets/lobbyWindow.json b/config/widgets/lobbyWindow.json index 9c98d5e0e..2b81f3dbe 100644 --- a/config/widgets/lobbyWindow.json +++ b/config/widgets/lobbyWindow.json @@ -158,6 +158,7 @@ "position": {"x": 10, "y": 520}, "image": "settingsWindow/button190", "help": "core.help.288", + "callback": "createGameRoom", "items": [ { @@ -175,6 +176,7 @@ "position": {"x": 10, "y": 555}, "image": "settingsWindow/button190", "help": "core.help.288", + "callback": "createGameRoom", "items": [ { diff --git a/lib/network/NetworkClient.cpp b/lib/network/NetworkClient.cpp index 44624e0d9..25462cf01 100644 --- a/lib/network/NetworkClient.cpp +++ b/lib/network/NetworkClient.cpp @@ -22,6 +22,9 @@ NetworkClient::NetworkClient(INetworkClientListener & listener) void NetworkClient::start(const std::string & host, uint16_t port) { + if (isConnected()) + throw std::runtime_error("Attempting to connect while already connected!"); + boost::asio::ip::tcp::resolver resolver(*io); auto endpoints = resolver.resolve(host, std::to_string(port)); @@ -58,6 +61,11 @@ void NetworkClient::stop() io->stop(); } +bool NetworkClient::isConnected() const +{ + return connection != nullptr; +} + void NetworkClient::setTimer(std::chrono::milliseconds duration) { auto timer = std::make_shared(*io, duration); @@ -74,6 +82,7 @@ void NetworkClient::sendPacket(const std::vector & message) void NetworkClient::onDisconnected(const std::shared_ptr & connection) { + this->connection.reset(); listener.onDisconnected(connection); } diff --git a/lib/network/NetworkClient.h b/lib/network/NetworkClient.h index ef45af7c6..cc244aa50 100644 --- a/lib/network/NetworkClient.h +++ b/lib/network/NetworkClient.h @@ -33,6 +33,8 @@ public: NetworkClient(INetworkClientListener & listener); virtual ~NetworkClient() = default; + bool isConnected() const; + void setTimer(std::chrono::milliseconds duration); void sendPacket(const std::vector & message); diff --git a/lobby/LobbyServer.cpp b/lobby/LobbyServer.cpp index 3ad7393f5..67627b447 100644 --- a/lobby/LobbyServer.cpp +++ b/lobby/LobbyServer.cpp @@ -173,23 +173,32 @@ void LobbyServer::receiveAuthentication(const std::shared_ptr activeAccounts[connection].accountName = accountName; - auto history = database->getRecentMessageHistory(); - - JsonNode reply; - reply["type"].String() = "chatHistory"; - - for (auto const & message : boost::adaptors::reverse(history)) { - JsonNode jsonEntry; + JsonNode reply; + reply["type"].String() = "authentication"; - jsonEntry["messageText"].String() = message.messageText; - jsonEntry["senderName"].String() = message.sender; - jsonEntry["ageSeconds"].Integer() = message.messageAgeSeconds; - - reply["messages"].Vector().push_back(jsonEntry); + sendMessage(connection, reply); } - sendMessage(connection, reply); + auto history = database->getRecentMessageHistory(); + + { + JsonNode reply; + reply["type"].String() = "chatHistory"; + + for (auto const & message : boost::adaptors::reverse(history)) + { + JsonNode jsonEntry; + + jsonEntry["messageText"].String() = message.messageText; + jsonEntry["senderName"].String() = message.sender; + jsonEntry["ageSeconds"].Integer() = message.messageAgeSeconds; + + reply["messages"].Vector().push_back(jsonEntry); + } + + sendMessage(connection, reply); + } } void LobbyServer::receiveJoinGameRoom(const std::shared_ptr & connection, const JsonNode & json)