mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Added list of active accounts and rooms to UI. Added room creation logic
This commit is contained in:
parent
9e62eb28c5
commit
388ca6e776
@ -277,6 +277,7 @@ set(client_HEADERS
|
||||
renderSDL/SDL_PixelAccess.h
|
||||
|
||||
globalLobby/GlobalLobbyClient.h
|
||||
globalLobby/GlobalLobbyDefines.h
|
||||
globalLobby/GlobalLobbyLoginWindow.h
|
||||
globalLobby/GlobalLobbyServerSetup.h
|
||||
globalLobby/GlobalLobbyWidget.h
|
||||
|
@ -126,9 +126,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static const std::string NAME_AFFIX = "client";
|
||||
static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
|
||||
CServerHandler::~CServerHandler()
|
||||
{
|
||||
networkHandler->stop();
|
||||
@ -141,7 +138,8 @@ CServerHandler::CServerHandler()
|
||||
, applier(std::make_unique<CApplier<CBaseForLobbyApply>>())
|
||||
, lobbyClient(std::make_unique<GlobalLobbyClient>())
|
||||
, client(nullptr)
|
||||
, loadMode(0)
|
||||
, loadMode(ELoadMode::NONE)
|
||||
, screenType(ESelectionScreen::unknown)
|
||||
, campaignStateToSend(nullptr)
|
||||
, campaignServerRestartLock(false)
|
||||
{
|
||||
@ -158,7 +156,7 @@ void CServerHandler::threadRunNetwork()
|
||||
logGlobal->info("Ending network thread");
|
||||
}
|
||||
|
||||
void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names)
|
||||
void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen, const std::vector<std::string> & names)
|
||||
{
|
||||
hostClientId = -1;
|
||||
state = EClientState::NONE;
|
||||
@ -169,9 +167,10 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::
|
||||
playerNames.clear();
|
||||
si->difficulty = 1;
|
||||
si->mode = mode;
|
||||
screenType = screen;
|
||||
myNames.clear();
|
||||
if(names && !names->empty()) //if have custom set of player names - use it
|
||||
myNames = *names;
|
||||
if(!names.empty()) //if have custom set of player names - use it
|
||||
myNames = names;
|
||||
else
|
||||
myNames.push_back(settings["general"]["playerName"].String());
|
||||
}
|
||||
@ -617,7 +616,7 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const
|
||||
if(client)
|
||||
{
|
||||
lsg.initializedStartInfo = std::make_shared<StartInfo>(* const_cast<StartInfo *>(client->getStartInfo(true)));
|
||||
lsg.initializedStartInfo->mode = StartInfo::NEW_GAME;
|
||||
lsg.initializedStartInfo->mode = EStartMode::NEW_GAME;
|
||||
lsg.initializedStartInfo->seedToBeUsed = lsg.initializedStartInfo->seedPostInit = 0;
|
||||
* si = * lsg.initializedStartInfo;
|
||||
}
|
||||
@ -641,13 +640,13 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
|
||||
|
||||
switch(si->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
case EStartMode::NEW_GAME:
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
case EStartMode::CAMPAIGN:
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case StartInfo::LOAD_GAME:
|
||||
case EStartMode::LOAD_GAME:
|
||||
client->loadGame(gameState);
|
||||
break;
|
||||
default:
|
||||
@ -765,7 +764,7 @@ int CServerHandler::howManyPlayerInterfaces()
|
||||
return playerInts;
|
||||
}
|
||||
|
||||
ui8 CServerHandler::getLoadMode()
|
||||
ELoadMode CServerHandler::getLoadMode()
|
||||
{
|
||||
if(loadMode != ELoadMode::TUTORIAL && state == EClientState::GAMEPLAY)
|
||||
{
|
||||
@ -790,15 +789,13 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
if(save)
|
||||
{
|
||||
resetStateForLobby(StartInfo::LOAD_GAME);
|
||||
resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, {});
|
||||
mapInfo->saveInit(ResourcePath(filename, EResType::SAVEGAME));
|
||||
screenType = ESelectionScreen::loadGame;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetStateForLobby(StartInfo::NEW_GAME);
|
||||
resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, {});
|
||||
mapInfo->mapInit(filename);
|
||||
screenType = ESelectionScreen::newGame;
|
||||
}
|
||||
if(settings["session"]["donotstartserver"].Bool())
|
||||
connectToServer(getLocalHostname(), getLocalPort());
|
||||
|
@ -40,6 +40,9 @@ class GlobalLobbyClient;
|
||||
class HighScoreCalculation;
|
||||
class HighScoreParameter;
|
||||
|
||||
enum class ESelectionScreen : ui8;
|
||||
enum class ELoadMode : ui8;
|
||||
|
||||
// TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
|
||||
enum class EClientState : ui8
|
||||
{
|
||||
@ -128,8 +131,8 @@ public:
|
||||
// For starting non-custom campaign and continue to next mission
|
||||
std::shared_ptr<CampaignState> campaignStateToSend;
|
||||
|
||||
ui8 screenType; // To create lobby UI only after server is setup
|
||||
ui8 loadMode; // For saves filtering in SelectionTab
|
||||
ESelectionScreen screenType; // To create lobby UI only after server is setup
|
||||
ELoadMode loadMode; // For saves filtering in SelectionTab
|
||||
////////////////////
|
||||
|
||||
std::unique_ptr<CStopWatch> th;
|
||||
@ -143,7 +146,7 @@ public:
|
||||
CServerHandler();
|
||||
~CServerHandler();
|
||||
|
||||
void resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names = nullptr);
|
||||
void resetStateForLobby(EStartMode mode, ESelectionScreen screen, const std::vector<std::string> & names);
|
||||
void startLocalServerAndConnect(bool connectToLobby);
|
||||
void connectToServer(const std::string & addr, const ui16 port);
|
||||
|
||||
@ -196,7 +199,7 @@ public:
|
||||
|
||||
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
|
||||
int howManyPlayerInterfaces();
|
||||
ui8 getLoadMode();
|
||||
ELoadMode getLoadMode();
|
||||
|
||||
void visitForLobby(CPackForLobby & lobbyPack);
|
||||
void visitForClient(CPackForClient & clientPack);
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "lobby/SelectionTab.h"
|
||||
#include "lobby/CBonusSelection.h"
|
||||
#include "globalLobby/GlobalLobbyWindow.h"
|
||||
#include "globalLobby/GlobalLobbyServerSetup.h"
|
||||
#include "globalLobby/GlobalLobbyClient.h"
|
||||
|
||||
#include "CServerHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
@ -49,6 +51,18 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
|
||||
if (GH.windows().topWindow<CSimpleJoinScreen>())
|
||||
GH.windows().popWindows(1);
|
||||
|
||||
if (!GH.windows().findWindows<GlobalLobbyServerSetup>().empty())
|
||||
{
|
||||
// announce opened game room
|
||||
// TODO: find better approach?
|
||||
int roomType = settings["lobby"]["roomType"].Integer();
|
||||
|
||||
if (roomType != 0)
|
||||
handler.getGlobalLobby().sendOpenPrivateRoom();
|
||||
else
|
||||
handler.getGlobalLobby().sendOpenPublicRoom();
|
||||
}
|
||||
|
||||
while (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
|
||||
{
|
||||
// if global lobby is open, pop all dialogs on top of it as well as lobby itself
|
||||
@ -141,7 +155,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pac
|
||||
}
|
||||
|
||||
handler.state = EClientState::STARTING;
|
||||
if(handler.si->mode != StartInfo::LOAD_GAME || pack.clientId == handler.c->connectionID)
|
||||
if(handler.si->mode != EStartMode::LOAD_GAME || pack.clientId == handler.c->connectionID)
|
||||
{
|
||||
auto modeBackup = handler.si->mode;
|
||||
handler.si = pack.initializedStartInfo;
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/ShortcutHandler.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../globalLobby/GlobalLobbyClient.h"
|
||||
|
||||
#include <SDL_clipboard.h>
|
||||
#include <SDL_events.h>
|
||||
@ -31,6 +33,8 @@ InputSourceKeyboard::InputSourceKeyboard()
|
||||
|
||||
void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
assert(key.state == SDL_PRESSED);
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||
{
|
||||
if(key.keysym.sym == SDLK_v && isKeyboardCtrlDown())
|
||||
@ -51,7 +55,11 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
return; // ignore periodic event resends
|
||||
}
|
||||
|
||||
assert(key.state == SDL_PRESSED);
|
||||
|
||||
if(key.keysym.sym == SDLK_TAB && isKeyboardCtrlDown())
|
||||
{
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
}
|
||||
|
||||
if(key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
|
@ -59,6 +59,12 @@ void GlobalLobbyClient::onPacketReceived(const std::shared_ptr<INetworkConnectio
|
||||
if(json["type"].String() == "activeAccounts")
|
||||
return receiveActiveAccounts(json);
|
||||
|
||||
if(json["type"].String() == "activeGameRooms")
|
||||
return receiveActiveGameRooms(json);
|
||||
|
||||
if(json["type"].String() == "joinRoomSuccess")
|
||||
return receiveJoinRoomSuccess(json);
|
||||
|
||||
throw std::runtime_error("Received unexpected message from lobby server: " + json["type"].String());
|
||||
}
|
||||
|
||||
@ -140,11 +146,51 @@ void GlobalLobbyClient::receiveChatMessage(const JsonNode & json)
|
||||
|
||||
void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json)
|
||||
{
|
||||
//for (auto const & jsonEntry : json["messages"].Vector())
|
||||
//{
|
||||
// std::string accountID = jsonEntry["accountID"].String();
|
||||
// std::string displayName = jsonEntry["displayName"].String();
|
||||
//}
|
||||
activeAccounts.clear();
|
||||
|
||||
for (auto const & jsonEntry : json["accounts"].Vector())
|
||||
{
|
||||
GlobalLobbyAccount account;
|
||||
|
||||
account.accountID = jsonEntry["accountID"].String();
|
||||
account.displayName = jsonEntry["displayName"].String();
|
||||
account.status = jsonEntry["status"].String();
|
||||
|
||||
activeAccounts.push_back(account);
|
||||
}
|
||||
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onActiveAccounts(activeAccounts);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
|
||||
{
|
||||
activeRooms.clear();
|
||||
|
||||
for (auto const & jsonEntry : json["gameRooms"].Vector())
|
||||
{
|
||||
GlobalLobbyRoom room;
|
||||
|
||||
room.gameRoomID = jsonEntry["gameRoomID"].String();
|
||||
room.hostAccountID = jsonEntry["hostAccountID"].String();
|
||||
room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
|
||||
room.description = jsonEntry["description"].String();
|
||||
// room.status = jsonEntry["status"].String();
|
||||
room.playersCount = jsonEntry["playersCount"].Integer();
|
||||
room.playersLimit = jsonEntry["playersLimit"].Integer();
|
||||
|
||||
activeRooms.push_back(room);
|
||||
}
|
||||
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onActiveRooms(activeRooms);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json)
|
||||
{
|
||||
// TODO: store "gameRoomID" field and use it for future queries
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr<INetworkConnection> & connection)
|
||||
@ -211,6 +257,24 @@ void GlobalLobbyClient::sendMessage(const JsonNode & data)
|
||||
networkConnection->sendPacket(payloadBuffer);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendOpenPublicRoom()
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "openGameRoom";
|
||||
toSend["hostAccountID"] = settings["lobby"]["accountID"];
|
||||
toSend["roomType"].String() = "public";
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendOpenPrivateRoom()
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "openGameRoom";
|
||||
toSend["hostAccountID"] = settings["lobby"]["accountID"];
|
||||
toSend["roomType"].String() = "private";
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::connect()
|
||||
{
|
||||
std::string hostname = settings["lobby"]["hostname"].String();
|
||||
@ -246,3 +310,27 @@ std::shared_ptr<GlobalLobbyWindow> GlobalLobbyClient::createLobbyWindow()
|
||||
lobbyWindowLock = lobbyWindowPtr;
|
||||
return lobbyWindowPtr;
|
||||
}
|
||||
|
||||
const std::vector<GlobalLobbyAccount> & GlobalLobbyClient::getActiveAccounts() const
|
||||
{
|
||||
return activeAccounts;
|
||||
}
|
||||
|
||||
const std::vector<GlobalLobbyRoom> & GlobalLobbyClient::getActiveRooms() const
|
||||
{
|
||||
return activeRooms;
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::activateInterface()
|
||||
{
|
||||
if (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
|
||||
return;
|
||||
|
||||
if (!GH.windows().findWindows<GlobalLobbyLoginWindow>().empty())
|
||||
return;
|
||||
|
||||
if (isConnected())
|
||||
GH.windows().pushWindow(createLobbyWindow());
|
||||
else
|
||||
GH.windows().pushWindow(createLoginWindow());
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GlobalLobbyDefines.h"
|
||||
#include "../../lib/network/NetworkInterface.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -18,8 +19,11 @@ VCMI_LIB_NAMESPACE_END
|
||||
class GlobalLobbyLoginWindow;
|
||||
class GlobalLobbyWindow;
|
||||
|
||||
class GlobalLobbyClient : public INetworkClientListener, boost::noncopyable
|
||||
class GlobalLobbyClient final : public INetworkClientListener, boost::noncopyable
|
||||
{
|
||||
std::vector<GlobalLobbyAccount> activeAccounts;
|
||||
std::vector<GlobalLobbyRoom> activeRooms;
|
||||
|
||||
std::shared_ptr<INetworkConnection> networkConnection;
|
||||
|
||||
std::weak_ptr<GlobalLobbyLoginWindow> loginWindow;
|
||||
@ -40,14 +44,25 @@ class GlobalLobbyClient : public INetworkClientListener, boost::noncopyable
|
||||
void receiveChatHistory(const JsonNode & json);
|
||||
void receiveChatMessage(const JsonNode & json);
|
||||
void receiveActiveAccounts(const JsonNode & json);
|
||||
void receiveActiveGameRooms(const JsonNode & json);
|
||||
void receiveJoinRoomSuccess(const JsonNode & json);
|
||||
|
||||
std::shared_ptr<GlobalLobbyLoginWindow> createLoginWindow();
|
||||
std::shared_ptr<GlobalLobbyWindow> createLobbyWindow();
|
||||
|
||||
public:
|
||||
explicit GlobalLobbyClient();
|
||||
~GlobalLobbyClient();
|
||||
|
||||
const std::vector<GlobalLobbyAccount> & getActiveAccounts() const;
|
||||
const std::vector<GlobalLobbyRoom> & getActiveRooms() const;
|
||||
|
||||
/// Activate interface and pushes lobby UI as top window
|
||||
void activateInterface();
|
||||
void sendMessage(const JsonNode & data);
|
||||
void sendOpenPublicRoom();
|
||||
void sendOpenPrivateRoom();
|
||||
|
||||
void connect();
|
||||
bool isConnected();
|
||||
std::shared_ptr<GlobalLobbyLoginWindow> createLoginWindow();
|
||||
std::shared_ptr<GlobalLobbyWindow> createLobbyWindow();
|
||||
};
|
||||
|
28
client/globalLobby/GlobalLobbyDefines.h
Normal file
28
client/globalLobby/GlobalLobbyDefines.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* GlobalLobbyClient.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
|
||||
|
||||
struct GlobalLobbyAccount
|
||||
{
|
||||
std::string accountID;
|
||||
std::string displayName;
|
||||
std::string status;
|
||||
};
|
||||
|
||||
struct GlobalLobbyRoom
|
||||
{
|
||||
std::string gameRoomID;
|
||||
std::string hostAccountID;
|
||||
std::string hostAccountDisplayName;
|
||||
std::string description;
|
||||
// std::string status;
|
||||
int playersCount;
|
||||
int playersLimit;
|
||||
};
|
@ -74,7 +74,7 @@ void GlobalLobbyLoginWindow::onLogin()
|
||||
void GlobalLobbyLoginWindow::onConnectionSuccess()
|
||||
{
|
||||
close();
|
||||
GH.windows().pushWindow(CSH->getGlobalLobby().createLobbyWindow());
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onConnectionFailed(const std::string & reason)
|
||||
|
@ -125,17 +125,15 @@ void GlobalLobbyServerSetup::onGameModeChanged(int value)
|
||||
void GlobalLobbyServerSetup::onCreate()
|
||||
{
|
||||
if(toggleGameMode->getSelected() == 0)
|
||||
{
|
||||
CSH->resetStateForLobby(StartInfo::NEW_GAME, nullptr);
|
||||
CSH->screenType = ESelectionScreen::newGame;
|
||||
}
|
||||
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, {});
|
||||
else
|
||||
{
|
||||
CSH->resetStateForLobby(StartInfo::LOAD_GAME, nullptr);
|
||||
CSH->screenType = ESelectionScreen::loadGame;
|
||||
}
|
||||
CSH->resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, {});
|
||||
|
||||
CSH->loadMode = ELoadMode::MULTI;
|
||||
CSH->startLocalServerAndConnect(true);
|
||||
|
||||
buttonCreate->block(true);
|
||||
buttonClose->block(true);
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onClose()
|
||||
|
@ -10,12 +10,19 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyWidget.h"
|
||||
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../lib/MetaString.h"
|
||||
GlobalLobbyWidget::GlobalLobbyWidget(GlobalLobbyWindow * window)
|
||||
: window(window)
|
||||
{
|
||||
@ -23,10 +30,59 @@ GlobalLobbyWidget::GlobalLobbyWidget(GlobalLobbyWindow * window)
|
||||
addCallback("sendMessage", [this](int) { this->window->doSendChatMessage(); });
|
||||
addCallback("createGameRoom", [this](int) { this->window->doCreateGameRoom(); });
|
||||
|
||||
REGISTER_BUILDER("accountList", &GlobalLobbyWidget::buildAccountList);
|
||||
REGISTER_BUILDER("roomList", &GlobalLobbyWidget::buildRoomList);
|
||||
|
||||
const JsonNode config(JsonPath::builtin("config/widgets/lobbyWindow.json"));
|
||||
build(config);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> GlobalLobbyWidget::buildAccountList(const JsonNode & config) const
|
||||
{
|
||||
const auto & createCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
|
||||
{
|
||||
const auto & accounts = CSH->getGlobalLobby().getActiveAccounts();
|
||||
|
||||
if(index < accounts.size())
|
||||
return std::make_shared<GlobalLobbyAccountCard>(this->window, accounts[index]);
|
||||
return std::make_shared<CIntObject>();
|
||||
};
|
||||
|
||||
auto position = readPosition(config["position"]);
|
||||
auto itemOffset = readPosition(config["itemOffset"]);
|
||||
auto sliderPosition = readPosition(config["sliderPosition"]);
|
||||
auto sliderSize = readPosition(config["sliderSize"]);
|
||||
size_t visibleSize = 4; // FIXME: how many items can fit into UI?
|
||||
size_t totalSize = 4; //FIXME: how many items are there in total
|
||||
int sliderMode = 1 | 4; // present, vertical, blue
|
||||
int initialPos = 0;
|
||||
|
||||
return std::make_shared<CListBox>(createCallback, position, itemOffset, visibleSize, totalSize, initialPos, sliderMode, Rect(sliderPosition, sliderSize) );
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> GlobalLobbyWidget::buildRoomList(const JsonNode & config) const
|
||||
{
|
||||
const auto & createCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
|
||||
{
|
||||
const auto & rooms = CSH->getGlobalLobby().getActiveRooms();
|
||||
|
||||
if(index < rooms.size())
|
||||
return std::make_shared<GlobalLobbyRoomCard>(this->window, rooms[index]);
|
||||
return std::make_shared<CIntObject>();
|
||||
};
|
||||
|
||||
auto position = readPosition(config["position"]);
|
||||
auto itemOffset = readPosition(config["itemOffset"]);
|
||||
auto sliderPosition = readPosition(config["sliderPosition"]);
|
||||
auto sliderSize = readPosition(config["sliderSize"]);
|
||||
size_t visibleSize = 4; // FIXME: how many items can fit into UI?
|
||||
size_t totalSize = 4; //FIXME: how many items are there in total
|
||||
int sliderMode = 1 | 4; // present, vertical, blue
|
||||
int initialPos = 0;
|
||||
|
||||
return std::make_shared<CListBox>(createCallback, position, itemOffset, visibleSize, totalSize, initialPos, sliderMode, Rect(sliderPosition, sliderSize) );
|
||||
}
|
||||
|
||||
std::shared_ptr<CLabel> GlobalLobbyWidget::getAccountNameLabel()
|
||||
{
|
||||
return widget<CLabel>("accountNameLabel");
|
||||
@ -41,3 +97,54 @@ std::shared_ptr<CTextBox> GlobalLobbyWidget::getGameChat()
|
||||
{
|
||||
return widget<CTextBox>("gameChat");
|
||||
}
|
||||
|
||||
std::shared_ptr<CListBox> GlobalLobbyWidget::getAccountList()
|
||||
{
|
||||
return widget<CListBox>("accountList");
|
||||
}
|
||||
|
||||
std::shared_ptr<CListBox> GlobalLobbyWidget::getRoomList()
|
||||
{
|
||||
return widget<CListBox>("roomList");
|
||||
}
|
||||
|
||||
GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
const auto & onInviteClicked = [window, accountID=accountDescription.accountID]()
|
||||
{
|
||||
window->doInviteAccount(accountID);
|
||||
};
|
||||
|
||||
pos.w = 130;
|
||||
pos.h = 40;
|
||||
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||
labelName = std::make_shared<CLabel>( 5, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, accountDescription.displayName);
|
||||
labelStatus = std::make_shared<CLabel>( 5, 20, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, accountDescription.status);
|
||||
buttonInvite = std::make_shared<CButton>(Point(95, 8), AnimationPath::builtin("settingsWindow/button32"), CButton::tooltip(), onInviteClicked);
|
||||
}
|
||||
|
||||
GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
const auto & onJoinClicked = [window, roomID=roomDescription.gameRoomID]()
|
||||
{
|
||||
window->doJoinRoom(roomID);
|
||||
};
|
||||
|
||||
auto roomSizeText = MetaString::createFromRawString("%d/%d");
|
||||
roomSizeText.replaceNumber(roomDescription.playersCount);
|
||||
roomSizeText.replaceNumber(roomDescription.playersLimit);
|
||||
|
||||
pos.w = 230;
|
||||
pos.h = 40;
|
||||
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||
labelName = std::make_shared<CLabel>( 5, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, roomDescription.hostAccountDisplayName);
|
||||
labelStatus = std::make_shared<CLabel>( 5, 20, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, roomDescription.description);
|
||||
labelRoomSize = std::make_shared<CLabel>( 160, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, roomSizeText.toString());
|
||||
buttonJoin = std::make_shared<CButton>(Point(195, 8), AnimationPath::builtin("settingsWindow/button32"), CButton::tooltip(), onJoinClicked);
|
||||
}
|
||||
|
@ -12,15 +12,50 @@
|
||||
#include "../gui/InterfaceObjectConfigurable.h"
|
||||
|
||||
class GlobalLobbyWindow;
|
||||
struct GlobalLobbyAccount;
|
||||
struct GlobalLobbyRoom;
|
||||
class CListBox;
|
||||
|
||||
class GlobalLobbyWidget : public InterfaceObjectConfigurable
|
||||
{
|
||||
GlobalLobbyWindow * window;
|
||||
|
||||
std::shared_ptr<CIntObject> buildAccountList(const JsonNode &) const;
|
||||
std::shared_ptr<CIntObject> buildRoomList(const JsonNode &) const;
|
||||
|
||||
public:
|
||||
GlobalLobbyWidget(GlobalLobbyWindow * window);
|
||||
|
||||
std::shared_ptr<CLabel> getAccountNameLabel();
|
||||
std::shared_ptr<CTextInput> getMessageInput();
|
||||
std::shared_ptr<CTextBox> getGameChat();
|
||||
std::shared_ptr<CListBox> getAccountList();
|
||||
std::shared_ptr<CListBox> getRoomList();
|
||||
};
|
||||
|
||||
class GlobalLobbyAccountCard : public CIntObject
|
||||
{
|
||||
public:
|
||||
GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription);
|
||||
|
||||
GlobalLobbyWindow * window;
|
||||
|
||||
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
|
||||
std::shared_ptr<CLabel> labelName;
|
||||
std::shared_ptr<CLabel> labelStatus;
|
||||
std::shared_ptr<CButton> buttonInvite;
|
||||
};
|
||||
|
||||
class GlobalLobbyRoomCard : public CIntObject
|
||||
{
|
||||
public:
|
||||
GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription);
|
||||
|
||||
GlobalLobbyWindow * window;
|
||||
|
||||
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
|
||||
std::shared_ptr<CLabel> labelName;
|
||||
std::shared_ptr<CLabel> labelRoomSize;
|
||||
std::shared_ptr<CLabel> labelStatus;
|
||||
std::shared_ptr<CButton> buttonJoin;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
@ -59,6 +60,16 @@ void GlobalLobbyWindow::doCreateGameRoom()
|
||||
// client requests to change room status to private or public
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doInviteAccount(const std::string & accountID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doJoinRoom(const std::string & roomID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when)
|
||||
{
|
||||
MetaString chatMessageFormatted;
|
||||
@ -71,3 +82,13 @@ void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std:
|
||||
|
||||
widget->getGameChat()->setText(chatHistory);
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts)
|
||||
{
|
||||
widget->getAccountList()->reset();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms)
|
||||
{
|
||||
widget->getRoomList()->reset();
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
class GlobalLobbyWidget;
|
||||
struct GlobalLobbyAccount;
|
||||
struct GlobalLobbyRoom;
|
||||
|
||||
class GlobalLobbyWindow : public CWindowObject
|
||||
{
|
||||
@ -25,5 +27,10 @@ public:
|
||||
void doSendChatMessage();
|
||||
void doCreateGameRoom();
|
||||
|
||||
void doInviteAccount(const std::string & accountID);
|
||||
void doJoinRoom(const std::string & roomID);
|
||||
|
||||
void onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when);
|
||||
void onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts);
|
||||
void onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms);
|
||||
};
|
||||
|
@ -768,7 +768,7 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
mapInfo->saveInit(file);
|
||||
|
||||
// Filter out other game modes
|
||||
bool isCampaign = mapInfo->scenarioOptionsOfSave->mode == StartInfo::CAMPAIGN;
|
||||
bool isCampaign = mapInfo->scenarioOptionsOfSave->mode == EStartMode::CAMPAIGN;
|
||||
bool isMultiplayer = mapInfo->amountOfHumanPlayersInSave > 1;
|
||||
bool isTutorial = boost::to_upper_copy(mapInfo->scenarioOptionsOfSave->mapname) == "MAPS/TUTORIAL";
|
||||
switch(CSH->getLoadMode())
|
||||
|
@ -187,11 +187,11 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
|
||||
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
|
||||
{
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
|
||||
return []() { CMainMenu::openLobby(ESelectionScreen::newGame, true, {}, ELoadMode::NONE);};
|
||||
case 1:
|
||||
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::newGame); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::campaignList, true, nullptr, ELoadMode::NONE);
|
||||
return []() { CMainMenu::openLobby(ESelectionScreen::campaignList, true, {}, ELoadMode::NONE);};
|
||||
case 3:
|
||||
return std::bind(CMainMenu::startTutorial);
|
||||
}
|
||||
@ -202,13 +202,14 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
|
||||
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
|
||||
{
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::SINGLE);
|
||||
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::SINGLE);};
|
||||
case 1:
|
||||
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::loadGame); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::CAMPAIGN);
|
||||
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::CAMPAIGN);};
|
||||
case 3:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::TUTORIAL);
|
||||
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::TUTORIAL);};
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -358,10 +359,9 @@ void CMainMenu::update()
|
||||
GH.windows().simpleRedraw();
|
||||
}
|
||||
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode)
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode)
|
||||
{
|
||||
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? StartInfo::NEW_GAME : StartInfo::LOAD_GAME, names);
|
||||
CSH->screenType = screenType;
|
||||
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, names);
|
||||
CSH->loadMode = loadMode;
|
||||
|
||||
GH.windows().createAndPushWindow<CSimpleJoinScreen>(host);
|
||||
@ -376,8 +376,7 @@ void CMainMenu::openCampaignLobby(const std::string & campaignFileName, std::str
|
||||
|
||||
void CMainMenu::openCampaignLobby(std::shared_ptr<CampaignState> campaign)
|
||||
{
|
||||
CSH->resetStateForLobby(StartInfo::CAMPAIGN);
|
||||
CSH->screenType = ESelectionScreen::campaignList;
|
||||
CSH->resetStateForLobby(EStartMode::CAMPAIGN, ESelectionScreen::campaignList, {});
|
||||
CSH->campaignStateToSend = campaign;
|
||||
GH.windows().createAndPushWindow<CSimpleJoinScreen>();
|
||||
}
|
||||
@ -420,7 +419,7 @@ void CMainMenu::startTutorial()
|
||||
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->mapInit(tutorialMap.getName());
|
||||
CMainMenu::openLobby(ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
|
||||
CMainMenu::openLobby(ESelectionScreen::newGame, true, {}, ELoadMode::NONE);
|
||||
CSH->startMapAfterConnection(mapInfo);
|
||||
}
|
||||
|
||||
@ -470,10 +469,7 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType)
|
||||
void CMultiMode::openLobby()
|
||||
{
|
||||
close();
|
||||
if (CSH->getGlobalLobby().isConnected())
|
||||
GH.windows().pushWindow(CSH->getGlobalLobby().createLobbyWindow());
|
||||
else
|
||||
GH.windows().pushWindow(CSH->getGlobalLobby().createLoginWindow());
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
}
|
||||
|
||||
void CMultiMode::hostTCP()
|
||||
@ -547,7 +543,7 @@ void CMultiPlayers::enterSelectionScreen()
|
||||
Settings name = settings.write["general"]["playerName"];
|
||||
name->String() = names[0];
|
||||
|
||||
CMainMenu::openLobby(screenType, host, &names, loadMode);
|
||||
CMainMenu::openLobby(screenType, host, names, loadMode);
|
||||
}
|
||||
|
||||
CSimpleJoinScreen::CSimpleJoinScreen(bool host)
|
||||
|
@ -31,11 +31,11 @@ class CLabel;
|
||||
|
||||
|
||||
// TODO: Find new location for these enums
|
||||
enum ESelectionScreen : ui8 {
|
||||
enum class ESelectionScreen : ui8 {
|
||||
unknown = 0, newGame, loadGame, saveGame, scenarioInfo, campaignList
|
||||
};
|
||||
|
||||
enum ELoadMode : ui8
|
||||
enum class ELoadMode : ui8
|
||||
{
|
||||
NONE = 0, SINGLE, MULTI, CAMPAIGN, TUTORIAL
|
||||
};
|
||||
@ -150,7 +150,7 @@ public:
|
||||
void activate() override;
|
||||
void onScreenResize() override;
|
||||
void update() override;
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode);
|
||||
static void openCampaignLobby(const std::string & campaignFileName, std::string campaignSet = "");
|
||||
static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
|
||||
static void startTutorial();
|
||||
|
@ -24,7 +24,7 @@ class CTextBox;
|
||||
class IImage;
|
||||
class Canvas;
|
||||
class TransparentFilledRectangle;
|
||||
enum ESelectionScreen : ui8;
|
||||
enum class ESelectionScreen : ui8;
|
||||
|
||||
class CMapOverview;
|
||||
|
||||
|
@ -45,25 +45,23 @@
|
||||
"type": "labelTitleMain",
|
||||
"position": {"x": 15, "y": 10}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"type": "areaFilled",
|
||||
"rect": {"x": 5, "y": 50, "w": 250, "h": 150}
|
||||
"rect": {"x": 5, "y": 50, "w": 250, "h": 500}
|
||||
},
|
||||
{
|
||||
"type": "labelTitle",
|
||||
"position": {"x": 15, "y": 53},
|
||||
"text" : "Match Filter"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "areaFilled",
|
||||
"rect": {"x": 5, "y": 210, "w": 250, "h": 340}
|
||||
"text" : "Room List"
|
||||
},
|
||||
{
|
||||
"type": "labelTitle",
|
||||
"position": {"x": 15, "y": 213},
|
||||
"text" : "Match List"
|
||||
"type" : "roomList",
|
||||
"name" : "roomList",
|
||||
"position" : { "x" : 7, "y" : 69 },
|
||||
"itemOffset" : { "x" : 0, "y" : 40 },
|
||||
"sliderPosition" : { "x" : 230, "y" : 0 },
|
||||
"sliderSize" : { "x" : 450, "y" : 450 }
|
||||
},
|
||||
|
||||
{
|
||||
@ -112,9 +110,17 @@
|
||||
{
|
||||
"type": "labelTitle",
|
||||
"position": {"x": 880, "y": 53},
|
||||
"text" : "Player List"
|
||||
"text" : "Account List"
|
||||
},
|
||||
|
||||
{
|
||||
"type" : "accountList",
|
||||
"name" : "accountList",
|
||||
"position" : { "x" : 872, "y" : 69 },
|
||||
"itemOffset" : { "x" : 0, "y" : 40 },
|
||||
"sliderPosition" : { "x" : 130, "y" : 0 },
|
||||
"sliderSize" : { "x" : 520, "y" : 520 }
|
||||
},
|
||||
|
||||
{
|
||||
"type": "button",
|
||||
"position": {"x": 840, "y": 10},
|
||||
|
@ -111,7 +111,7 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
|
||||
if(i == si->playerInfos.cend() && !ignoreNoHuman)
|
||||
throw std::domain_error(VLC->generaltexth->translate("core.genrltxt.530"));
|
||||
|
||||
if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME)
|
||||
if(si->mapGenOptions && si->mode == EStartMode::NEW_GAME)
|
||||
{
|
||||
if(!si->mapGenOptions->checkOptions())
|
||||
throw std::domain_error(VLC->generaltexth->translate("core.genrltxt.751"));
|
||||
|
@ -98,12 +98,18 @@ struct DLL_LINKAGE PlayerSettings
|
||||
HeroTypeID getHeroValidated() const;
|
||||
};
|
||||
|
||||
enum class EStartMode : int32_t
|
||||
{
|
||||
NEW_GAME,
|
||||
LOAD_GAME,
|
||||
CAMPAIGN,
|
||||
INVALID = 255
|
||||
};
|
||||
|
||||
/// Struct which describes the difficulty, the turn time,.. of a heroes match.
|
||||
struct DLL_LINKAGE StartInfo
|
||||
{
|
||||
enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, INVALID = 255};
|
||||
|
||||
EMode mode;
|
||||
EStartMode mode;
|
||||
ui8 difficulty; //0=easy; 4=impossible
|
||||
|
||||
using TPlayerInfos = std::map<PlayerColor, PlayerSettings>;
|
||||
@ -152,7 +158,7 @@ struct DLL_LINKAGE StartInfo
|
||||
h & campState;
|
||||
}
|
||||
|
||||
StartInfo() : mode(INVALID), difficulty(1), seedToBeUsed(0), seedPostInit(0),
|
||||
StartInfo() : mode(EStartMode::INVALID), difficulty(1), seedToBeUsed(0), seedPostInit(0),
|
||||
mapfileChecksum(0), startTimeIso8601(vstd::getDateTimeISO8601Basic(std::time(nullptr))), fileURI("")
|
||||
{
|
||||
|
||||
|
@ -189,10 +189,10 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog
|
||||
|
||||
switch(scenarioOps->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
case EStartMode::NEW_GAME:
|
||||
initNewGame(mapService, allowSavingRandomMap, progressTracking);
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
case EStartMode::CAMPAIGN:
|
||||
initCampaign();
|
||||
break;
|
||||
default:
|
||||
@ -711,7 +711,7 @@ void CGameState::initFogOfWar()
|
||||
|
||||
void CGameState::initStartingBonus()
|
||||
{
|
||||
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
||||
if (scenarioOps->mode == EStartMode::CAMPAIGN)
|
||||
return;
|
||||
// These are the single scenario bonuses; predefined
|
||||
// campaign bonuses are spread out over other init* functions.
|
||||
|
@ -39,7 +39,7 @@ CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, const Ob
|
||||
CGameStateCampaign::CGameStateCampaign(CGameState * owner):
|
||||
gameState(owner)
|
||||
{
|
||||
assert(gameState->scenarioOps->mode == StartInfo::CAMPAIGN);
|
||||
assert(gameState->scenarioOps->mode == EStartMode::CAMPAIGN);
|
||||
assert(gameState->scenarioOps->campState != nullptr);
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,10 @@ void NetworkConnection::sendPacket(const std::vector<uint8_t> & message)
|
||||
ostream.write(reinterpret_cast<const char *>(&messageSize), messageHeaderSize);
|
||||
ostream.write(reinterpret_cast<const char *>(message.data()), message.size());
|
||||
|
||||
boost::asio::write(*socket, writeBuffer );
|
||||
boost::system::error_code ec;
|
||||
boost::asio::write(*socket, writeBuffer, ec );
|
||||
|
||||
// FIXME: handle error?
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -37,7 +37,7 @@ struct DLL_LINKAGE LobbyClientConnected : public CLobbyPackToPropagate
|
||||
// Set by client before sending pack to server
|
||||
std::string uuid;
|
||||
std::vector<std::string> names;
|
||||
StartInfo::EMode mode = StartInfo::INVALID;
|
||||
EStartMode mode = EStartMode::INVALID;
|
||||
// Changed by server before announcing pack
|
||||
int clientId = -1;
|
||||
int hostClientId = -1;
|
||||
|
@ -106,6 +106,11 @@ bool CConnection::isMyConnection(const std::shared_ptr<INetworkConnection> & oth
|
||||
return otherConnection != nullptr && networkConnection.lock() == otherConnection;
|
||||
}
|
||||
|
||||
std::shared_ptr<INetworkConnection> CConnection::getConnection()
|
||||
{
|
||||
return networkConnection.lock();
|
||||
}
|
||||
|
||||
void CConnection::disableStackSendingByID()
|
||||
{
|
||||
packReader->sendStackInstanceByIds = false;
|
||||
|
@ -41,6 +41,7 @@ class DLL_LINKAGE CConnection : boost::noncopyable
|
||||
|
||||
public:
|
||||
bool isMyConnection(const std::shared_ptr<INetworkConnection> & otherConnection) const;
|
||||
std::shared_ptr<INetworkConnection> getConnection();
|
||||
|
||||
std::string uuid;
|
||||
int connectionID;
|
||||
|
@ -96,7 +96,7 @@ void LobbyDatabase::prepareStatements()
|
||||
)";
|
||||
|
||||
static const std::string insertGameRoomText = R"(
|
||||
INSERT INTO gameRooms(roomID, hostAccountID, status, playerLimit) VALUES(?, ?, 'empty', 8);
|
||||
INSERT INTO gameRooms(roomID, hostAccountID, status, playerLimit) VALUES(?, ?, 0, 8);
|
||||
)";
|
||||
|
||||
static const std::string insertGameRoomPlayersText = R"(
|
||||
@ -119,6 +119,12 @@ void LobbyDatabase::prepareStatements()
|
||||
|
||||
// UPDATE
|
||||
|
||||
static const std::string setAccountOnlineText = R"(
|
||||
UPDATE accounts
|
||||
SET online = ?
|
||||
WHERE accountID = ?
|
||||
)";
|
||||
|
||||
static const std::string setGameRoomStatusText = R"(
|
||||
UPDATE gameRooms
|
||||
SET status = ?
|
||||
@ -145,7 +151,7 @@ void LobbyDatabase::prepareStatements()
|
||||
static const std::string getIdleGameRoomText = R"(
|
||||
SELECT roomID
|
||||
FROM gameRooms
|
||||
WHERE hostAccountID = ? AND status = 'idle'
|
||||
WHERE hostAccountID = ? AND status = 0
|
||||
LIMIT 1
|
||||
)";
|
||||
|
||||
@ -153,7 +159,7 @@ void LobbyDatabase::prepareStatements()
|
||||
SELECT grp.roomID
|
||||
FROM gameRoomPlayers grp
|
||||
LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
|
||||
WHERE accountID = ? AND status IN ('public', 'private', 'busy')
|
||||
WHERE accountID = ? AND status IN (1, 2)
|
||||
LIMIT 1
|
||||
)";
|
||||
|
||||
@ -163,6 +169,13 @@ void LobbyDatabase::prepareStatements()
|
||||
WHERE online = 1
|
||||
)";
|
||||
|
||||
static const std::string getActiveGameRoomsText = R"(
|
||||
SELECT roomID, hostAccountID, displayName, status, 0, playerLimit
|
||||
FROM gameRooms
|
||||
LEFT JOIN accounts ON hostAccountID = accountID
|
||||
WHERE status = 1
|
||||
)";
|
||||
|
||||
static const std::string getAccountDisplayNameText = R"(
|
||||
SELECT displayName
|
||||
FROM accounts
|
||||
@ -216,6 +229,7 @@ void LobbyDatabase::prepareStatements()
|
||||
deleteGameRoomPlayersStatement = database->prepare(deleteGameRoomPlayersText);
|
||||
deleteGameRoomInvitesStatement = database->prepare(deleteGameRoomInvitesText);
|
||||
|
||||
setAccountOnlineStatement = database->prepare(setAccountOnlineText);
|
||||
setGameRoomStatusStatement = database->prepare(setGameRoomStatusText);
|
||||
setGameRoomPlayerLimitStatement = database->prepare(setGameRoomPlayerLimitText);
|
||||
|
||||
@ -223,6 +237,7 @@ void LobbyDatabase::prepareStatements()
|
||||
getIdleGameRoomStatement = database->prepare(getIdleGameRoomText);
|
||||
getAccountGameRoomStatement = database->prepare(getAccountGameRoomText);
|
||||
getActiveAccountsStatement = database->prepare(getActiveAccountsText);
|
||||
getActiveGameRoomsStatement = database->prepare(getActiveGameRoomsText);
|
||||
getAccountDisplayNameStatement = database->prepare(getAccountDisplayNameText);
|
||||
|
||||
isAccountCookieValidStatement = database->prepare(isAccountCookieValidText);
|
||||
@ -285,6 +300,11 @@ std::vector<LobbyChatMessage> LobbyDatabase::getRecentMessageHistory()
|
||||
return result;
|
||||
}
|
||||
|
||||
void LobbyDatabase::setAccountOnline(const std::string & accountID, bool isOnline)
|
||||
{
|
||||
setAccountOnlineStatement->executeOnce(isOnline ? 1 : 0, accountID);
|
||||
}
|
||||
|
||||
void LobbyDatabase::setGameRoomStatus(const std::string & roomID, LobbyRoomState roomStatus)
|
||||
{
|
||||
setGameRoomStatusStatement->executeOnce(vstd::to_underlying(roomStatus), roomID);
|
||||
@ -404,7 +424,16 @@ bool LobbyDatabase::isAccountIDExists(const std::string & accountID)
|
||||
|
||||
std::vector<LobbyGameRoom> LobbyDatabase::getActiveGameRooms()
|
||||
{
|
||||
return {};
|
||||
std::vector<LobbyGameRoom> result;
|
||||
|
||||
while(getActiveGameRoomsStatement->execute())
|
||||
{
|
||||
LobbyGameRoom entry;
|
||||
getActiveGameRoomsStatement->getColumns(entry.roomID, entry.hostAccountID, entry.hostAccountDisplayName, entry.roomStatus, entry.playersCount, entry.playersLimit);
|
||||
result.push_back(entry);
|
||||
}
|
||||
getActiveGameRoomsStatement->reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<LobbyAccount> LobbyDatabase::getActiveAccounts()
|
||||
@ -425,6 +454,7 @@ std::string LobbyDatabase::getIdleGameRoom(const std::string & hostAccountID)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
getIdleGameRoomStatement->setBinds(hostAccountID);
|
||||
if(getIdleGameRoomStatement->execute())
|
||||
getIdleGameRoomStatement->getColumns(result);
|
||||
|
||||
@ -436,6 +466,7 @@ std::string LobbyDatabase::getAccountGameRoom(const std::string & accountID)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
getAccountGameRoomStatement->setBinds(accountID);
|
||||
if(getAccountGameRoomStatement->execute())
|
||||
getAccountGameRoomStatement->getColumns(result);
|
||||
|
||||
|
@ -31,13 +31,15 @@ class LobbyDatabase
|
||||
SQLiteStatementPtr deleteGameRoomPlayersStatement;
|
||||
SQLiteStatementPtr deleteGameRoomInvitesStatement;
|
||||
|
||||
SQLiteStatementPtr setAccountOnlineStatement;
|
||||
SQLiteStatementPtr setGameRoomStatusStatement;
|
||||
SQLiteStatementPtr setGameRoomPlayerLimitStatement;
|
||||
|
||||
SQLiteStatementPtr getRecentMessageHistoryStatement;
|
||||
SQLiteStatementPtr getIdleGameRoomStatement;
|
||||
SQLiteStatementPtr getAccountGameRoomStatement;
|
||||
SQLiteStatementPtr getActiveGameRoomsStatement;
|
||||
SQLiteStatementPtr getActiveAccountsStatement;
|
||||
SQLiteStatementPtr getAccountGameRoomStatement;
|
||||
SQLiteStatementPtr getAccountDisplayNameStatement;
|
||||
|
||||
SQLiteStatementPtr isAccountCookieValidStatement;
|
||||
@ -54,6 +56,7 @@ public:
|
||||
explicit LobbyDatabase(const boost::filesystem::path & databasePath);
|
||||
~LobbyDatabase();
|
||||
|
||||
void setAccountOnline(const std::string & accountID, bool isOnline);
|
||||
void setGameRoomStatus(const std::string & roomID, LobbyRoomState roomStatus);
|
||||
void setGameRoomPlayerLimit(const std::string & roomID, uint32_t playerLimit);
|
||||
|
||||
|
@ -18,7 +18,9 @@ struct LobbyAccount
|
||||
|
||||
struct LobbyGameRoom
|
||||
{
|
||||
std::string roomUUID;
|
||||
std::string roomID;
|
||||
std::string hostAccountID;
|
||||
std::string hostAccountDisplayName;
|
||||
std::string roomStatus;
|
||||
uint32_t playersCount;
|
||||
uint32_t playersLimit;
|
||||
@ -48,10 +50,10 @@ enum class LobbyInviteStatus : int32_t
|
||||
|
||||
enum class LobbyRoomState : int32_t
|
||||
{
|
||||
IDLE, // server is ready but no players are in the room
|
||||
PUBLIC, // host has joined and allows anybody to join
|
||||
PRIVATE, // host has joined but only allows those he invited to join
|
||||
//BUSY, // match is ongoing
|
||||
//CANCELLED, // game room was cancelled without starting the game
|
||||
//CLOSED, // game room was closed after playing for some time
|
||||
IDLE = 0, // server is ready but no players are in the room
|
||||
PUBLIC = 1, // host has joined and allows anybody to join
|
||||
PRIVATE = 2, // host has joined but only allows those he invited to join
|
||||
//BUSY = 3, // match is ongoing
|
||||
//CANCELLED = 4, // game room was cancelled without starting the game
|
||||
CLOSED = 5, // game room was closed after playing for some time
|
||||
};
|
||||
|
@ -140,7 +140,7 @@ void LobbyServer::broadcastActiveAccounts()
|
||||
JsonNode jsonEntry;
|
||||
jsonEntry["accountID"].String() = account.accountID;
|
||||
jsonEntry["displayName"].String() = account.displayName;
|
||||
// jsonEntry["status"].String() = account.status;
|
||||
jsonEntry["status"].String() = "In Lobby"; // TODO: in room status, in match status, offline status(?)
|
||||
reply["accounts"].Vector().push_back(jsonEntry);
|
||||
}
|
||||
|
||||
@ -157,10 +157,13 @@ void LobbyServer::broadcastActiveGameRooms()
|
||||
for(const auto & gameRoom : activeGameRoomStats)
|
||||
{
|
||||
JsonNode jsonEntry;
|
||||
jsonEntry["gameRoomID"].String() = gameRoom.roomUUID;
|
||||
jsonEntry["status"].String() = gameRoom.roomStatus;
|
||||
jsonEntry["status"].Integer() = gameRoom.playersCount;
|
||||
jsonEntry["status"].Integer() = gameRoom.playersLimit;
|
||||
jsonEntry["gameRoomID"].String() = gameRoom.roomID;
|
||||
jsonEntry["hostAccountID"].String() = gameRoom.hostAccountID;
|
||||
jsonEntry["hostAccountDisplayName"].String() = gameRoom.hostAccountDisplayName;
|
||||
jsonEntry["description"].String() = "TODO: ROOM DESCRIPTION";
|
||||
// jsonEntry["status"].String() = gameRoom.roomStatus;
|
||||
jsonEntry["playersCount"].Integer() = gameRoom.playersCount;
|
||||
jsonEntry["playersLimit"].Integer() = gameRoom.playersLimit;
|
||||
reply["gameRooms"].Vector().push_back(jsonEntry);
|
||||
}
|
||||
|
||||
@ -204,6 +207,12 @@ void LobbyServer::onNewConnection(const NetworkConnectionPtr & connection)
|
||||
|
||||
void LobbyServer::onDisconnected(const NetworkConnectionPtr & connection)
|
||||
{
|
||||
if (activeAccounts.count(connection))
|
||||
database->setAccountOnline(activeAccounts.at(connection).accountID, false);
|
||||
|
||||
if (activeGameRooms.count(connection))
|
||||
database->setGameRoomStatus(activeGameRooms.at(connection).roomID, LobbyRoomState::CLOSED);
|
||||
|
||||
// NOTE: lost connection can be in only one of these lists (or in none of them)
|
||||
// calling on all possible containers since calling std::map::erase() with non-existing key is legal
|
||||
activeAccounts.erase(connection);
|
||||
@ -334,6 +343,7 @@ void LobbyServer::receiveClientLogin(const NetworkConnectionPtr & connection, co
|
||||
// prolong existing cookie
|
||||
database->updateAccessCookie(accountID, accountCookie);
|
||||
database->updateAccountLoginTime(accountID);
|
||||
database->setAccountOnline(accountID, true);
|
||||
|
||||
std::string displayName = database->getAccountDisplayName(accountID);
|
||||
|
||||
@ -369,6 +379,8 @@ void LobbyServer::receiveServerLogin(const NetworkConnectionPtr & connection, co
|
||||
activeGameRooms[connection].roomID = gameRoomID;
|
||||
sendLoginSuccess(connection, accountCookie, {});
|
||||
}
|
||||
|
||||
broadcastActiveGameRooms();
|
||||
}
|
||||
|
||||
void LobbyServer::receiveClientProxyLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
|
||||
@ -451,7 +463,7 @@ void LobbyServer::receiveOpenGameRoom(const NetworkConnectionPtr & connection, c
|
||||
database->setGameRoomStatus(gameRoomID, LobbyRoomState::PRIVATE);
|
||||
|
||||
// TODO: additional flags / initial settings, e.g. allowCheats
|
||||
// TODO: connection mode: direct or proxy. For now direct is assumed
|
||||
// TODO: connection mode: direct or proxy. For now direct is assumed. Proxy might be needed later, for hosted servers
|
||||
|
||||
broadcastActiveGameRooms();
|
||||
sendJoinRoomSuccess(connection, gameRoomID);
|
||||
|
@ -15,6 +15,7 @@ set(server_SRCS
|
||||
processors/PlayerMessageProcessor.cpp
|
||||
processors/TurnOrderProcessor.cpp
|
||||
|
||||
EntryPoint.cpp
|
||||
CGameHandler.cpp
|
||||
GlobalLobbyProcessor.cpp
|
||||
ServerSpellCastEnvironment.cpp
|
||||
|
@ -10,44 +10,15 @@
|
||||
#include "StdInc.h"
|
||||
#include "CVCMIServer.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/campaign/CampaignState.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "zlib.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/mapping/CMapHeader.h"
|
||||
#include "../lib/rmg/CMapGenOptions.h"
|
||||
#include "LobbyNetPackVisitors.h"
|
||||
#ifdef VCMI_ANDROID
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include "lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "CGameHandler.h"
|
||||
#include "GlobalLobbyProcessor.h"
|
||||
#include "LobbyNetPackVisitors.h"
|
||||
#include "processors/PlayerMessageProcessor.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/ScopeGuard.h"
|
||||
#include "../lib/serializer/CMemorySerializer.h"
|
||||
#include "../lib/serializer/Cast.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
#include "../lib/UnlockGuard.h"
|
||||
|
||||
// for applier
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/registerTypes/RegisterTypesLobbyPacks.h"
|
||||
#include "../lib/serializer/CMemorySerializer.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
// UUID generation
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
@ -55,8 +26,6 @@
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "../lib/gameState/CGameState.h"
|
||||
|
||||
template<typename T> class CApplyOnServer;
|
||||
|
||||
class CBaseForServerApply
|
||||
@ -147,9 +116,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::string SERVER_NAME_AFFIX = "server";
|
||||
std::string SERVER_NAME = GameConstants::VCMI_VERSION + std::string(" (") + SERVER_NAME_AFFIX + ')';
|
||||
|
||||
CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts)
|
||||
: state(EServerState::LOBBY), cmdLineOptions(opts), currentClientId(1), currentPlayerId(1), restartGameplay(false)
|
||||
{
|
||||
@ -158,20 +124,29 @@ CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts)
|
||||
applier = std::make_shared<CApplier<CBaseForServerApply>>();
|
||||
registerTypesLobbyPacks(*applier);
|
||||
|
||||
networkHandler = INetworkHandler::createHandler();
|
||||
|
||||
if(cmdLineOptions.count("lobby"))
|
||||
lobbyProcessor = std::make_unique<GlobalLobbyProcessor>(*this);
|
||||
else
|
||||
startAcceptingIncomingConnections();
|
||||
}
|
||||
|
||||
CVCMIServer::~CVCMIServer() = default;
|
||||
|
||||
void CVCMIServer::startAcceptingIncomingConnections()
|
||||
{
|
||||
uint16_t port = 3030;
|
||||
|
||||
if(cmdLineOptions.count("port"))
|
||||
port = cmdLineOptions["port"].as<uint16_t>();
|
||||
logNetwork->info("Port %d will be used", port);
|
||||
|
||||
networkHandler = INetworkHandler::createHandler();
|
||||
networkServer = networkHandler->createServerTCP(*this);
|
||||
networkServer->start(port);
|
||||
establishOutgoingConnection();
|
||||
logNetwork->info("Listening for connections at port %d", port);
|
||||
}
|
||||
|
||||
CVCMIServer::~CVCMIServer() = default;
|
||||
|
||||
void CVCMIServer::onNewConnection(const std::shared_ptr<INetworkConnection> & connection)
|
||||
{
|
||||
if(state == EServerState::LOBBY)
|
||||
@ -206,7 +181,7 @@ EServerState CVCMIServer::getState() const
|
||||
|
||||
std::shared_ptr<CConnection> CVCMIServer::findConnection(const std::shared_ptr<INetworkConnection> & netConnection)
|
||||
{
|
||||
for (auto const & gameConnection : activeConnections)
|
||||
for(const auto & gameConnection : activeConnections)
|
||||
{
|
||||
if (gameConnection->isMyConnection(netConnection))
|
||||
return gameConnection;
|
||||
@ -249,12 +224,6 @@ void CVCMIServer::onTimer()
|
||||
networkHandler->createTimer(*this, serverUpdateInterval);
|
||||
}
|
||||
|
||||
void CVCMIServer::establishOutgoingConnection()
|
||||
{
|
||||
if(cmdLineOptions.count("lobby"))
|
||||
lobbyProcessor = std::make_unique<GlobalLobbyProcessor>(*this);
|
||||
}
|
||||
|
||||
void CVCMIServer::prepareToRestart()
|
||||
{
|
||||
if(state == EServerState::GAMEPLAY)
|
||||
@ -304,7 +273,7 @@ bool CVCMIServer::prepareToStartGame()
|
||||
gh = std::make_shared<CGameHandler>(this);
|
||||
switch(si->mode)
|
||||
{
|
||||
case StartInfo::CAMPAIGN:
|
||||
case EStartMode::CAMPAIGN:
|
||||
logNetwork->info("Preparing to start new campaign");
|
||||
si->startTimeIso8601 = vstd::getDateTimeISO8601Basic(std::time(nullptr));
|
||||
si->fileURI = mi->fileURI;
|
||||
@ -313,14 +282,14 @@ bool CVCMIServer::prepareToStartGame()
|
||||
gh->init(si.get(), progressTracking);
|
||||
break;
|
||||
|
||||
case StartInfo::NEW_GAME:
|
||||
case EStartMode::NEW_GAME:
|
||||
logNetwork->info("Preparing to start new game");
|
||||
si->startTimeIso8601 = vstd::getDateTimeISO8601Basic(std::time(nullptr));
|
||||
si->fileURI = mi->fileURI;
|
||||
gh->init(si.get(), progressTracking);
|
||||
break;
|
||||
|
||||
case StartInfo::LOAD_GAME:
|
||||
case EStartMode::LOAD_GAME:
|
||||
logNetwork->info("Preparing to start loaded game");
|
||||
if(!gh->load(si->mapname))
|
||||
{
|
||||
@ -346,7 +315,7 @@ void CVCMIServer::startGameImmediately()
|
||||
for(auto c : activeConnections)
|
||||
c->enterGameplayConnectionMode(gh->gs);
|
||||
|
||||
gh->start(si->mode == StartInfo::LOAD_GAME);
|
||||
gh->start(si->mode == EStartMode::LOAD_GAME);
|
||||
state = EServerState::GAMEPLAY;
|
||||
lastTimerUpdateTime = gameplayStartTime = std::chrono::steady_clock::now();
|
||||
onTimer();
|
||||
@ -360,7 +329,11 @@ void CVCMIServer::onDisconnected(const std::shared_ptr<INetworkConnection> & con
|
||||
vstd::erase(activeConnections, c);
|
||||
|
||||
if(activeConnections.empty() || hostClientId == c->connectionID)
|
||||
{
|
||||
networkHandler->stop();
|
||||
state = EServerState::SHUTDOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
if(gh && state == EServerState::GAMEPLAY)
|
||||
{
|
||||
@ -428,7 +401,7 @@ bool CVCMIServer::passHost(int toConnectionId)
|
||||
return false;
|
||||
}
|
||||
|
||||
void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, StartInfo::EMode mode)
|
||||
void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, EStartMode mode)
|
||||
{
|
||||
if(state != EServerState::LOBBY)
|
||||
throw std::runtime_error("CVCMIServer::clientConnected called while game is not accepting clients!");
|
||||
@ -468,16 +441,16 @@ void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<st
|
||||
|
||||
void CVCMIServer::clientDisconnected(std::shared_ptr<CConnection> c)
|
||||
{
|
||||
networkServer->closeConnection(c->getConnection());
|
||||
vstd::erase(activeConnections, c);
|
||||
|
||||
if(activeConnections.empty() || hostClientId == c->connectionID)
|
||||
{
|
||||
networkHandler->stop();
|
||||
state = EServerState::SHUTDOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: close network connection
|
||||
|
||||
// PlayerReinitInterface startAiPack;
|
||||
// startAiPack.playerConnectionId = PlayerSettings::PLAYER_AI;
|
||||
//
|
||||
@ -565,7 +538,7 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo,
|
||||
if(mi->scenarioOptionsOfSave)
|
||||
{
|
||||
si = CMemorySerializer::deepCopy(*mi->scenarioOptionsOfSave);
|
||||
si->mode = StartInfo::LOAD_GAME;
|
||||
si->mode = EStartMode::LOAD_GAME;
|
||||
if(si->campState)
|
||||
campaignMap = si->campState->currentScenario().value();
|
||||
|
||||
@ -581,7 +554,7 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(si->mode == StartInfo::NEW_GAME || si->mode == StartInfo::CAMPAIGN)
|
||||
else if(si->mode == EStartMode::NEW_GAME || si->mode == EStartMode::CAMPAIGN)
|
||||
{
|
||||
if(mi->campaign)
|
||||
return;
|
||||
@ -634,7 +607,7 @@ void CVCMIServer::updateAndPropagateLobbyState()
|
||||
{
|
||||
// Update player settings for RMG
|
||||
// TODO: find appropriate location for this code
|
||||
if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME)
|
||||
if(si->mapGenOptions && si->mode == EStartMode::NEW_GAME)
|
||||
{
|
||||
for(const auto & psetPair : si->playerInfos)
|
||||
{
|
||||
@ -971,140 +944,3 @@ ui8 CVCMIServer::getIdOfFirstUnallocatedPlayer() const
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handleCommandOptions(int argc, const char * argv[], boost::program_options::variables_map & options)
|
||||
{
|
||||
namespace po = boost::program_options;
|
||||
po::options_description opts("Allowed options");
|
||||
opts.add_options()
|
||||
("help,h", "display help and exit")
|
||||
("version,v", "display version information and exit")
|
||||
("run-by-client", "indicate that server launched by client on same machine")
|
||||
("port", po::value<ui16>(), "port at which server will listen to connections from client")
|
||||
("lobby", "start server in lobby mode in which server connects to a global lobby");
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
po::store(po::parse_command_line(argc, argv, opts), options);
|
||||
}
|
||||
catch(boost::program_options::error & e)
|
||||
{
|
||||
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
options.emplace("run-by-client", po::variable_value{true, true});
|
||||
#endif
|
||||
|
||||
po::notify(options);
|
||||
|
||||
#ifndef SINGLE_PROCESS_APP
|
||||
if(options.count("help"))
|
||||
{
|
||||
auto time = std::time(nullptr);
|
||||
printf("%s - A Heroes of Might and Magic 3 clone\n", GameConstants::VCMI_VERSION.c_str());
|
||||
printf("Copyright (C) 2007-%d VCMI dev team - see AUTHORS file\n", std::localtime(&time)->tm_year + 1900);
|
||||
printf("This is free software; see the source for copying conditions. There is NO\n");
|
||||
printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
printf("\n");
|
||||
std::cout << opts;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(options.count("version"))
|
||||
{
|
||||
printf("%s\n", GameConstants::VCMI_VERSION.c_str());
|
||||
std::cout << VCMIDirs::get().genHelpString();
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
#define main server_main
|
||||
#endif
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
void CVCMIServer::create()
|
||||
{
|
||||
const int argc = 1;
|
||||
const char * argv[argc] = { "android-server" };
|
||||
#else
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
#endif
|
||||
|
||||
#if !defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
|
||||
// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
|
||||
boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
|
||||
#endif
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
console = new CConsoleHandler();
|
||||
#endif
|
||||
CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Server_log.txt", console);
|
||||
logConfig.configureDefault();
|
||||
logGlobal->info(SERVER_NAME);
|
||||
|
||||
boost::program_options::variables_map opts;
|
||||
handleCommandOptions(argc, argv, opts);
|
||||
preinitDLL(console, false);
|
||||
logConfig.configure();
|
||||
|
||||
loadDLLClasses();
|
||||
srand((ui32)time(nullptr));
|
||||
|
||||
CVCMIServer server(opts);
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
boost::condition_variable * cond = reinterpret_cast<boost::condition_variable *>(const_cast<char *>(argv[0]));
|
||||
cond->notify_one();
|
||||
#endif
|
||||
|
||||
server.run();
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer");
|
||||
#endif
|
||||
logConfig.deconfigure();
|
||||
vstd::clear_pointer(VLC);
|
||||
|
||||
#if !VCMI_ANDROID_DUAL_PROCESS
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_createServer(JNIEnv * env, jclass cls)
|
||||
{
|
||||
__android_log_write(ANDROID_LOG_INFO, "VCMI", "Got jni call to init server");
|
||||
CAndroidVMHelper::cacheVM(env);
|
||||
|
||||
CVCMIServer::create();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_initClassloader(JNIEnv * baseEnv, jclass cls)
|
||||
{
|
||||
CAndroidVMHelper::initClassloader(baseEnv);
|
||||
}
|
||||
#elif defined(SINGLE_PROCESS_APP)
|
||||
void CVCMIServer::create(boost::condition_variable * cond, const std::vector<std::string> & args)
|
||||
{
|
||||
std::vector<const void *> argv = {cond};
|
||||
for(auto & a : args)
|
||||
argv.push_back(a.c_str());
|
||||
main(argv.size(), reinterpret_cast<const char **>(&*argv.begin()));
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
void CVCMIServer::reuseClientJNIEnv(void * jniEnv)
|
||||
{
|
||||
CAndroidVMHelper::initClassloader(jniEnv);
|
||||
CAndroidVMHelper::alwaysUseLoadedClass = true;
|
||||
}
|
||||
#endif // VCMI_ANDROID
|
||||
#endif // VCMI_ANDROID_DUAL_PROCESS
|
||||
|
@ -70,8 +70,6 @@ private:
|
||||
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
||||
EServerState state;
|
||||
|
||||
void establishOutgoingConnection();
|
||||
|
||||
std::shared_ptr<CConnection> findConnection(const std::shared_ptr<INetworkConnection> &);
|
||||
|
||||
int currentClientId;
|
||||
@ -84,7 +82,6 @@ public:
|
||||
void onNewConnection(const std::shared_ptr<INetworkConnection> &) override;
|
||||
void onTimer() override;
|
||||
|
||||
|
||||
std::shared_ptr<CGameHandler> gh;
|
||||
boost::program_options::variables_map cmdLineOptions;
|
||||
|
||||
@ -96,6 +93,7 @@ public:
|
||||
bool prepareToStartGame();
|
||||
void prepareToRestart();
|
||||
void startGameImmediately();
|
||||
void startAcceptingIncomingConnections();
|
||||
|
||||
void threadHandleClient(std::shared_ptr<CConnection> c);
|
||||
|
||||
@ -107,7 +105,7 @@ public:
|
||||
void setPlayerConnectedId(PlayerSettings & pset, ui8 player) const;
|
||||
void updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo, std::shared_ptr<CMapGenOptions> mapGenOpt = {});
|
||||
|
||||
void clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, StartInfo::EMode mode);
|
||||
void clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, EStartMode mode);
|
||||
void clientDisconnected(std::shared_ptr<CConnection> c);
|
||||
void reconnectPlayer(int connId);
|
||||
|
||||
|
170
server/EntryPoint.cpp
Normal file
170
server/EntryPoint.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* EntryPoint.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 "CVCMIServer.h"
|
||||
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include "lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
std::string SERVER_NAME_AFFIX = "server";
|
||||
std::string SERVER_NAME = GameConstants::VCMI_VERSION + std::string(" (") + SERVER_NAME_AFFIX + ')';
|
||||
|
||||
static void handleCommandOptions(int argc, const char * argv[], boost::program_options::variables_map & options)
|
||||
{
|
||||
namespace po = boost::program_options;
|
||||
po::options_description opts("Allowed options");
|
||||
opts.add_options()
|
||||
("help,h", "display help and exit")
|
||||
("version,v", "display version information and exit")
|
||||
("run-by-client", "indicate that server launched by client on same machine")
|
||||
("port", po::value<ui16>(), "port at which server will listen to connections from client")
|
||||
("lobby", "start server in lobby mode in which server connects to a global lobby");
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
po::store(po::parse_command_line(argc, argv, opts), options);
|
||||
}
|
||||
catch(boost::program_options::error & e)
|
||||
{
|
||||
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
options.emplace("run-by-client", po::variable_value{true, true});
|
||||
#endif
|
||||
|
||||
po::notify(options);
|
||||
|
||||
#ifndef SINGLE_PROCESS_APP
|
||||
if(options.count("help"))
|
||||
{
|
||||
auto time = std::time(nullptr);
|
||||
printf("%s - A Heroes of Might and Magic 3 clone\n", GameConstants::VCMI_VERSION.c_str());
|
||||
printf("Copyright (C) 2007-%d VCMI dev team - see AUTHORS file\n", std::localtime(&time)->tm_year + 1900);
|
||||
printf("This is free software; see the source for copying conditions. There is NO\n");
|
||||
printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
printf("\n");
|
||||
std::cout << opts;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(options.count("version"))
|
||||
{
|
||||
printf("%s\n", GameConstants::VCMI_VERSION.c_str());
|
||||
std::cout << VCMIDirs::get().genHelpString();
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
#define main server_main
|
||||
#endif
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
void CVCMIServer::create()
|
||||
{
|
||||
const int argc = 1;
|
||||
const char * argv[argc] = { "android-server" };
|
||||
#else
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
#endif
|
||||
|
||||
#if !defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
|
||||
// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
|
||||
boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
|
||||
#endif
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
console = new CConsoleHandler();
|
||||
#endif
|
||||
CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Server_log.txt", console);
|
||||
logConfig.configureDefault();
|
||||
logGlobal->info(SERVER_NAME);
|
||||
|
||||
boost::program_options::variables_map opts;
|
||||
handleCommandOptions(argc, argv, opts);
|
||||
preinitDLL(console, false);
|
||||
logConfig.configure();
|
||||
|
||||
loadDLLClasses();
|
||||
srand((ui32)time(nullptr));
|
||||
|
||||
{
|
||||
CVCMIServer server(opts);
|
||||
|
||||
#ifdef SINGLE_PROCESS_APP
|
||||
boost::condition_variable * cond = reinterpret_cast<boost::condition_variable *>(const_cast<char *>(argv[0]));
|
||||
cond->notify_one();
|
||||
#endif
|
||||
|
||||
server.run();
|
||||
|
||||
// CVCMIServer destructor must be called here - before VLC cleanup
|
||||
}
|
||||
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer");
|
||||
#endif
|
||||
logConfig.deconfigure();
|
||||
vstd::clear_pointer(VLC);
|
||||
|
||||
#if !VCMI_ANDROID_DUAL_PROCESS
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VCMI_ANDROID_DUAL_PROCESS
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_createServer(JNIEnv * env, jclass cls)
|
||||
{
|
||||
__android_log_write(ANDROID_LOG_INFO, "VCMI", "Got jni call to init server");
|
||||
CAndroidVMHelper::cacheVM(env);
|
||||
|
||||
CVCMIServer::create();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_initClassloader(JNIEnv * baseEnv, jclass cls)
|
||||
{
|
||||
CAndroidVMHelper::initClassloader(baseEnv);
|
||||
}
|
||||
#elif defined(SINGLE_PROCESS_APP)
|
||||
void CVCMIServer::create(boost::condition_variable * cond, const std::vector<std::string> & args)
|
||||
{
|
||||
std::vector<const void *> argv = {cond};
|
||||
for(auto & a : args)
|
||||
argv.push_back(a.c_str());
|
||||
main(argv.size(), reinterpret_cast<const char **>(&*argv.begin()));
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
void CVCMIServer::reuseClientJNIEnv(void * jniEnv)
|
||||
{
|
||||
CAndroidVMHelper::initClassloader(jniEnv);
|
||||
CAndroidVMHelper::alwaysUseLoadedClass = true;
|
||||
}
|
||||
#endif // VCMI_ANDROID
|
||||
#endif // VCMI_ANDROID_DUAL_PROCESS
|
@ -57,6 +57,7 @@ void GlobalLobbyProcessor::receiveLoginSuccess(const JsonNode & json)
|
||||
{
|
||||
// no-op, wait just for any new commands from lobby
|
||||
logGlobal->info("Succesfully connected to lobby server");
|
||||
owner.startAcceptingIncomingConnections();
|
||||
}
|
||||
|
||||
void GlobalLobbyProcessor::receiveAccountJoinsRoom(const JsonNode & json)
|
||||
@ -83,6 +84,7 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
|
||||
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "serverLogin";
|
||||
toSend["gameRoomID"].String() = owner.uuid;
|
||||
toSend["accountID"] = settings["lobby"]["accountID"];
|
||||
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
|
||||
sendMessage(toSend);
|
||||
@ -97,7 +99,7 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
|
||||
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "serverProxyLogin";
|
||||
toSend["gameRoomID"].String() = "";
|
||||
toSend["gameRoomID"].String() = owner.uuid;
|
||||
toSend["accountID"].String() = accountID;
|
||||
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
|
||||
sendMessage(toSend);
|
||||
|
@ -158,7 +158,7 @@ void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
|
||||
void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack)
|
||||
{
|
||||
srv.si->mapname = pack.ourCampaign->getFilename();
|
||||
srv.si->mode = StartInfo::CAMPAIGN;
|
||||
srv.si->mode = EStartMode::CAMPAIGN;
|
||||
srv.si->campState = pack.ourCampaign;
|
||||
srv.si->turnTimerInfo = TurnTimerInfo{};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user