1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Implemented basic version of login window and persistent connection on

client
This commit is contained in:
Ivan Savenko 2023-12-28 21:27:21 +02:00
parent 78833a1015
commit 55b504792e
23 changed files with 278 additions and 59 deletions

View File

@ -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.",

View File

@ -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

View File

@ -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<NetworkClient>(*this))
, applier(std::make_unique<CApplier<CBaseForLobbyApply>>())
, lobbyClient(std::make_unique<GlobalLobbyClient>())
, 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)

View File

@ -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> networkClient;
std::unique_ptr<GlobalLobbyClient> lobbyClient;
std::unique_ptr<CApplier<CBaseForLobbyApply>> applier;
std::shared_ptr<CMapInfo> mapToStart;
std::vector<std::string> 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<PlayerColor> getHumanColors();
PlayerColor myFirstColor() const;

View File

@ -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<NetworkClient>(*this))
, window(window)
{}
{
networkThread = std::make_unique<boost::thread>([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<NetworkConnection> &, const std::vector<uint8_t> & message)
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
// FIXME: find better approach
const char * payloadBegin = reinterpret_cast<const char*>(message.data());
JsonNode json(payloadBegin, message.size());
if (json["type"].String() == "authentication")
{
auto loginWindowPtr = loginWindow.lock();
if (!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
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_ptr<NetworkConnection
std::string messageText = entry["messageText"].String();
int ageSeconds = entry["ageSeconds"].Integer();
std::string timeFormatted = getCurrentTimeFormatted(-ageSeconds);
window->onGameChatMessage(senderName, messageText, timeFormatted);
auto lobbyWindowPtr = lobbyWindow.lock();
if(lobbyWindowPtr)
lobbyWindowPtr->onGameChatMessage(senderName, messageText, timeFormatted);
}
}
@ -68,7 +91,9 @@ void GlobalLobbyClient::onPacketReceived(const std::shared_ptr<NetworkConnection
std::string senderName = json["senderName"].String();
std::string messageText = json["messageText"].String();
std::string timeFormatted = getCurrentTimeFormatted();
window->onGameChatMessage(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<NetworkCon
void GlobalLobbyClient::onConnectionFailed(const std::string & errorMessage)
{
GH.windows().popWindows(1);
CInfoWindow::showInfoDialog("Failed to connect to game lobby!\n" + errorMessage, {});
auto loginWindowPtr = loginWindow.lock();
if (!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
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<NetworkConnection> &)
@ -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<GlobalLobbyLoginWindow> GlobalLobbyClient::createLoginWindow()
{
networkClient->poll();
auto loginWindowPtr = loginWindow.lock();
if (loginWindowPtr)
return loginWindowPtr;
auto loginWindowNew = std::make_shared<GlobalLobbyLoginWindow>();
loginWindow = loginWindowNew;
return loginWindowNew;
}
std::shared_ptr<GlobalLobbyWindow> GlobalLobbyClient::createLobbyWindow()
{
auto lobbyWindowPtr = lobbyWindow.lock();
if (lobbyWindowPtr)
return lobbyWindowPtr;
lobbyWindowPtr = std::make_shared<GlobalLobbyWindow>();
lobbyWindow = lobbyWindowPtr;
lobbyWindowLock = lobbyWindowPtr;
return lobbyWindowPtr;
}

View File

@ -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<boost::thread> networkThread;
std::unique_ptr<NetworkClient> networkClient;
GlobalLobbyWindow * window;
std::weak_ptr<GlobalLobbyLoginWindow> loginWindow;
std::weak_ptr<GlobalLobbyWindow> lobbyWindow;
std::shared_ptr<GlobalLobbyWindow> lobbyWindowLock; // helper strong reference to prevent window destruction on closing
void onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & 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<GlobalLobbyLoginWindow> createLoginWindow();
std::shared_ptr<GlobalLobbyWindow> createLobbyWindow();
};

View File

@ -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<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title"));
labelUsername = std::make_shared<CLabel>( 10, 45, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.lobby.login.username"));
backgroundUsername = std::make_shared<TransparentFilledRectangle>(Rect(10, 70, 180, 20), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
inputUsername = std::make_shared<CTextInput>(Rect(15, 73, 176, 16), FONT_SMALL, nullptr, ETextAlignment::TOPLEFT, true);
buttonLogin = std::make_shared<CButton>(Point(10, 160), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); });
buttonClose = std::make_shared<CButton>(Point(126, 160), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
labelStatus = std::make_shared<CTextBox>( "", 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());
}

View File

@ -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<FilledTexturePlayerColored> background;
std::shared_ptr<CLabel> labelTitle;
std::shared_ptr<CLabel> labelUsername;
std::shared_ptr<CTextBox> labelStatus;
std::shared_ptr<TransparentFilledRectangle> backgroundUsername;
std::shared_ptr<CTextInput> inputUsername;
std::shared_ptr<CButton> buttonLogin;
std::shared_ptr<CButton> buttonClose;
void onClose();
void onLogin();
public:
GlobalLobbyLoginWindow();
void onConnectionSuccess();
void onConnectionFailed(const std::string & reason);
};

View File

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

View File

@ -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<GlobalLobbyWidget>(this);
pos = widget->pos;
center();
connection = std::make_shared<GlobalLobbyClient>(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;

View File

@ -12,21 +12,19 @@
#include "../windows/CWindowObject.h"
class GlobalLobbyWidget;
class GlobalLobbyClient;
class GlobalLobbyWindow : public CWindowObject
{
std::string chatHistory;
std::shared_ptr<GlobalLobbyWidget> widget;
std::shared_ptr<GlobalLobbyClient> 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);
};

View File

@ -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<TransparentFilledRectangle>(textInputArea, ColorRGBA(0,0,0,192));
inputBox = std::make_shared<CTextInput>(textInputArea, EFonts::FONT_SMALL, 0);
inputBox = std::make_shared<CTextInput>(textInputArea, EFonts::FONT_SMALL, nullptr, ETextAlignment::TOPLEFT, true);
inputBox->removeUsedEvents(KEYBOARD);
chatHistory = std::make_shared<CTextBox>("", chatHistoryArea, 1);

View File

@ -892,7 +892,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
labelPlayerName = std::make_shared<CLabel>(55, 10, EFonts::FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, name, 95);
else
{
labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, nullptr, false);
labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, nullptr, ETextAlignment::CENTER, false);
labelPlayerNameEdit->setText(name);
}
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);

View File

@ -372,7 +372,7 @@ CHighScoreInput::CHighScoreInput(std::string playerName, std::function<void(std:
buttonOk = std::make_shared<CButton>(Point(26, 142), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CHighScoreInput::okay, this), EShortcut::GLOBAL_ACCEPT);
buttonCancel = std::make_shared<CButton>(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<CPicture>(background->getSurface(), Rect(7, 186, 218, 18), 7, 186));
textInput = std::make_shared<CTextInput>(Rect(18, 104, 200, 25), FONT_SMALL, 0);
textInput = std::make_shared<CTextInput>(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("");
}
}

View File

@ -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<GlobalLobbyWindow>();
if (CSH->getGlobalLobby().isConnected())
GH.windows().pushWindow(CSH->getGlobalLobby().createLobbyWindow());
else
GH.windows().pushWindow(CSH->getGlobalLobby().createLoginWindow());
}
void CMultiMode::hostTCP()

View File

@ -551,8 +551,8 @@ Point CGStatusBar::getBorderSize()
return Point();
}
CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, bool giveFocusToInput)
: CLabel(Pos.x, Pos.y, font, ETextAlignment::CENTER),
CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, ETextAlignment alignment, bool giveFocusToInput)
: CLabel(Pos.x, Pos.y, font, alignment),
cb(CB),
CFocusable(std::make_shared<CKeyboardFocusListener>(this))
{

View File

@ -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<void(const std::string &)> & CB, bool giveFocusToInput = true);
CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, ETextAlignment alignment, bool giveFocusToInput);
CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName, const CFunctionList<void(const std::string &)> & CB);
CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf);

View File

@ -136,7 +136,7 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
searchBoxRectangle = std::make_shared<TransparentFilledRectangle>(r.resize(1), rectangleColor, borderColor);
searchBoxDescription = std::make_shared<CLabel>(r.center().x, r.center().y, FONT_SMALL, ETextAlignment::CENTER, grayedColor, CGI->generaltexth->translate("vcmi.spellBook.search"));
searchBox = std::make_shared<CTextInput>(r, FONT_SMALL, std::bind(&CSpellWindow::searchInput, this));
searchBox = std::make_shared<CTextInput>(r, FONT_SMALL, std::bind(&CSpellWindow::searchInput, this), ETextAlignment::CENTER, true);
}
processSpells();

View File

@ -324,8 +324,8 @@ CSplitWindow::CSplitWindow(const CCreature * creature, std::function<void(int, i
int sliderPosition = total - leftMin - rightMin;
leftInput = std::make_shared<CTextInput>(Rect(20, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, true));
rightInput = std::make_shared<CTextInput>(Rect(176, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, false));
leftInput = std::make_shared<CTextInput>(Rect(20, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, true), ETextAlignment::CENTER, true);
rightInput = std::make_shared<CTextInput>(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);

View File

@ -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":
[
{

View File

@ -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<NetworkTimer>(*io, duration);
@ -74,6 +82,7 @@ void NetworkClient::sendPacket(const std::vector<uint8_t> & message)
void NetworkClient::onDisconnected(const std::shared_ptr<NetworkConnection> & connection)
{
this->connection.reset();
listener.onDisconnected(connection);
}

View File

@ -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<uint8_t> & message);

View File

@ -173,23 +173,32 @@ void LobbyServer::receiveAuthentication(const std::shared_ptr<NetworkConnection>
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<NetworkConnection> & connection, const JsonNode & json)