1
0
mirror of https://github.com/vcmi/vcmi.git synced 2026-05-22 09:55:17 +02:00

Start game improvements

This commit is contained in:
George King
2026-04-21 14:41:42 +02:00
parent 0baf7d2a4f
commit 1bee7ec174
13 changed files with 261 additions and 17 deletions
@@ -795,6 +795,7 @@
"vcmi.lobby.invite.header": "Pozvat hráče",
"vcmi.lobby.invite.notification": "Hráč vás pozval do své soukromé místnosti. Nyní se k ní můžete připojit.",
"vcmi.lobby.login.as": "Přihlásit se jako %s",
"vcmi.lobby.login.connectionLost": "Spojení s herní lobby bylo ztraceno!",
"vcmi.lobby.login.connecting": "Připojování...",
"vcmi.lobby.login.create": "Nový účet",
"vcmi.lobby.login.error": "Chyba při připojování: %s",
@@ -828,6 +829,12 @@
"vcmi.lobby.preview.version": "Verze hry:",
"vcmi.lobby.pvp.coin.help": "Hodit mincí",
"vcmi.lobby.pvp.coin.hover": "Mince",
"vcmi.lobby.system.waitingForPlayers": "Čekání na připojení hráčů...",
"vcmi.lobby.system.playerJoined": "Hráč %s se připojil",
"vcmi.lobby.system.playerDisconnected": "Hráč %s se odpojil",
"vcmi.lobby.system.hidingIncompatibleMaps": "Skrývám nekompatibilní mapy pro %d hráčů",
"vcmi.lobby.system.unableStartMap": "Mapu nelze spustit!",
"vcmi.lobby.system.reason": "Důvod: %s",
"vcmi.lobby.pvp.randomTown.help": "Napsat náhodné město do chatu",
"vcmi.lobby.pvp.randomTown.hover": "Náhodné město",
"vcmi.lobby.pvp.randomTownVs.help": "Napsat 2 náhodná města do chatu",
@@ -795,6 +795,7 @@
"vcmi.lobby.invite.header": "Invite Players",
"vcmi.lobby.invite.notification": "Player has invited you to their game room. You can now join their private room.",
"vcmi.lobby.login.as": "Login as %s",
"vcmi.lobby.login.connectionLost": "Connection to game lobby was lost!",
"vcmi.lobby.login.connecting": "Connecting...",
"vcmi.lobby.login.create": "New Account",
"vcmi.lobby.login.error": "Connection error: %s",
@@ -828,6 +829,12 @@
"vcmi.lobby.preview.version": "Game version:",
"vcmi.lobby.pvp.coin.help": "Flips a coin",
"vcmi.lobby.pvp.coin.hover": "Coin",
"vcmi.lobby.system.waitingForPlayers": "Waiting for players to join...",
"vcmi.lobby.system.playerJoined": "Player %s joined",
"vcmi.lobby.system.playerDisconnected": "Player %s disconnected",
"vcmi.lobby.system.hidingIncompatibleMaps": "Hiding incompatible maps for %d players",
"vcmi.lobby.system.unableStartMap": "Unable to start map!",
"vcmi.lobby.system.reason": "Reason: %s",
"vcmi.lobby.pvp.randomTown.help": "Write a random town in the chat",
"vcmi.lobby.pvp.randomTown.hover": "Random town",
"vcmi.lobby.pvp.randomTownVs.help": "Write two random towns in the chat",
+19 -1
View File
@@ -109,6 +109,7 @@ CServerHandler::CServerHandler()
, screenType(ESelectionScreen::unknown)
, serverMode(EServerMode::NONE)
, loadMode(ELoadMode::NONE)
, hotseatMode(false)
, battleMode(false)
, client(nullptr)
{
@@ -139,7 +140,10 @@ void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen
hostClientId = GameConnectionID::INVALID;
setState(EClientState::NONE);
serverMode = newServerMode;
loadMode = ELoadMode::NONE;
mapToStart = nullptr;
hotseatMode = false;
battleMode = false;
th = std::make_unique<CStopWatch>();
logicConnection.reset();
si = std::make_shared<StartInfo>();
@@ -338,6 +342,15 @@ bool CServerHandler::isGuest() const
return !logicConnection || hostClientId != logicConnection->connectionID;
}
bool CServerHandler::hasRemoteClientInLobby() const
{
std::set<GameConnectionID> connectedClients;
for(const auto & playerEntry : playerNames)
connectedClients.insert(playerEntry.second.connection);
return connectedClients.size() > 1;
}
const std::string & CServerHandler::getLocalHostname() const
{
return settings["server"]["localHostname"].String();
@@ -586,7 +599,12 @@ bool CServerHandler::validateGameStart(bool allowOnlyAI) const
catch(std::exception & e)
{
logGlobal->error("Exception during startScenario: %s", e.what());
showServerError(std::string("Unable to start map!\nReason: ") + e.what());
MetaString message;
message.appendTextID("vcmi.lobby.system.unableStartMap");
message.appendRawString("\n");
message.appendTextID("vcmi.lobby.system.reason");
message.replaceRawString(e.what());
showServerError(message.toString());
return false;
}
+2
View File
@@ -142,6 +142,7 @@ public:
ESelectionScreen screenType; // To create lobby UI only after server is setup
EServerMode serverMode;
ELoadMode loadMode; // For saves filtering in SelectionTab
bool hotseatMode;
bool battleMode;
////////////////////
@@ -171,6 +172,7 @@ public:
bool isHost() const;
bool isGuest() const;
bool hasRemoteClientInLobby() const;
bool inLobbyRoom() const;
bool inGame() const;
+1 -1
View File
@@ -427,7 +427,7 @@ void GlobalLobbyClient::onDisconnected(const std::shared_ptr<INetworkConnection>
ENGINE->windows().popWindows(1);
}
CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {});
CInfoWindow::showInfoDialog(LIBRARY->generaltexth->translate("vcmi.lobby.login.connectionLost"), {});
}
void GlobalLobbyClient::sendMessage(const JsonNode & data)
+3
View File
@@ -153,6 +153,9 @@ CBonusSelection::CBonusSelection()
buttonExtraOptions = std::make_shared<CButton>(Point(643, 431), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[46], [this]{ tabExtraOptions->setEnabled(!tabExtraOptions->isActive()); ENGINE->windows().totalRedraw(); }, EShortcut::LOBBY_EXTRA_OPTIONS);
buttonExtraOptions->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, Colors::WHITE);
}
// Ensure campaign map info is synchronized even if player doesn't click any region manually.
GAME->server().setCampaignMap(GAME->server().campaignMap);
}
void CBonusSelection::createBonusesIcons()
+74 -7
View File
@@ -20,6 +20,7 @@
#include "../CServerHandler.h"
#include "../GameEngine.h"
#include "../GameChatHandler.h"
#include "../GameInstance.h"
#include "../gui/Shortcut.h"
#include "../widgets/Buttons.h"
@@ -40,6 +41,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
: CSelectionBase(screenType), bonusSel(nullptr)
{
OBJECT_CONSTRUCTION;
addUsedEvents(TIME);
tabSel = std::make_shared<SelectionTab>(screenType);
curTab = tabSel;
@@ -59,7 +61,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
if(settings["general"]["enableUiEnhancements"].Bool())
{
if(screenType == ESelectionScreen::newGame)
buttonBattleMode = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPButton2Arrow"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode.help")), [this](){
buttonBattleMode = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPButton2Arrow"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode.help")), [this](){
updateAfterStateChange(); // creates tabBattleOnlyMode -> cannot created by init of object because GAME->server().isGuest() isn't valid at that point
toggleTab(tabBattleOnlyMode);
}, EShortcut::LOBBY_BATTLE_MODE);
@@ -67,9 +69,9 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
}
};
if(screenType != ESelectionScreen::campaignList)
if(screenType != ESelectionScreen::campaignList && GAME->server().loadMode == ELoadMode::MULTI && !GAME->server().hotseatMode)
{
buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_TOGGLE_CHAT);
buttonChat = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_TOGGLE_CHAT);
buttonChat->setTextOverlay(LIBRARY->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
}
@@ -128,6 +130,8 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
blackScreen = std::make_shared<GraphicalPrimitiveCanvas>(Rect(Point(0, 0), pos.dimensions()));
blackScreen->addBox(Point(0, 0), pos.dimensions(), Colors::BLACK);
}
updateHostLobbyChatState();
}
CLobbyScreen::~CLobbyScreen()
@@ -137,6 +141,53 @@ CLobbyScreen::~CLobbyScreen()
GAME->server().sendClientDisconnecting();
}
bool CLobbyScreen::canStartLobbyGame() const
{
if(GAME->server().isGuest() || GAME->server().mi == nullptr)
return false;
const bool isLanMultiplayerHost = GAME->server().loadMode == ELoadMode::MULTI
&& GAME->server().serverMode == EServerMode::LOCAL
&& !GAME->server().hotseatMode
&& GAME->server().isHost();
if(isLanMultiplayerHost && !GAME->server().hasRemoteClientInLobby())
return false;
return true;
}
void CLobbyScreen::updateHostLobbyChatState()
{
const bool isLanMultiplayerHost = buttonChat
&& GAME->server().loadMode == ELoadMode::MULTI
&& GAME->server().serverMode == EServerMode::LOCAL
&& !GAME->server().hotseatMode
&& GAME->server().isHost();
if(!isLanMultiplayerHost)
return;
buttonChat->setTextOverlay(card->showChat ? LIBRARY->generaltexth->allTexts[531] : LIBRARY->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
if(GAME->server().hasRemoteClientInLobby())
{
waitingForPlayersMessageShown = false;
return;
}
if(!waitingForPlayersMessageShown)
{
GAME->server().getGameChat().onNewLobbyMessageReceived("System", LIBRARY->generaltexth->translate("vcmi.lobby.system.waitingForPlayers"));
waitingForPlayersMessageShown = true;
}
}
void CLobbyScreen::updateStartButtonState()
{
buttonStart->block(!canStartLobbyGame());
}
void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
{
if(tab == curTab)
@@ -161,13 +212,22 @@ void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
}
else
{
buttonStart->block(GAME->server().mi == nullptr || GAME->server().isGuest());
updateStartButtonState();
card->changeSelection();
}
CSelectionBase::toggleTab(tab);
}
void CLobbyScreen::tick(uint32_t msPassed)
{
CSelectionBase::tick(msPassed);
updateHostLobbyChatState();
if(curTab != tabBattleOnlyMode)
updateStartButtonState();
}
void CLobbyScreen::start(bool campaign)
{
if(curTab == tabBattleOnlyMode)
@@ -221,9 +281,11 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
void CLobbyScreen::toggleMode(bool host)
{
tabSel->toggleMode();
buttonStart->block(!host);
if(screenType == ESelectionScreen::campaignList)
{
buttonStart->block(!host);
return;
}
auto buttonColor = host ? Colors::WHITE : Colors::ORANGE;
buttonSelect->setTextOverlay(" " + LIBRARY->generaltexth->allTexts[500], FONT_SMALL, buttonColor);
@@ -255,6 +317,8 @@ void CLobbyScreen::toggleMode(bool host)
tabTurnOptions->recreate();
tabExtraOptions->recreate();
}
updateStartButtonState();
}
void CLobbyScreen::toggleChat()
@@ -269,6 +333,9 @@ void CLobbyScreen::toggleChat()
void CLobbyScreen::updateAfterStateChange()
{
OBJECT_CONSTRUCTION;
updateHostLobbyChatState();
//tabSel->filter(-1);
if(!tabBattleOnlyMode)
{
tabBattleOnlyMode = std::make_shared<BattleOnlyModeTab>();
@@ -298,9 +365,9 @@ void CLobbyScreen::updateAfterStateChange()
tabExtraOptions->recreate();
}
if(curTab && curTab != tabBattleOnlyMode)
if(curTab != tabBattleOnlyMode)
{
buttonStart->block(GAME->server().mi == nullptr || GAME->server().isGuest());
updateStartButtonState();
card->changeSelection();
}
+7
View File
@@ -16,6 +16,12 @@ class GraphicalPrimitiveCanvas;
class CLobbyScreen final : public CSelectionBase
{
bool waitingForPlayersMessageShown = false;
bool canStartLobbyGame() const;
void updateHostLobbyChatState();
void updateStartButtonState();
public:
std::shared_ptr<CButton> buttonChat;
std::shared_ptr<GraphicalPrimitiveCanvas> blackScreen;
@@ -23,6 +29,7 @@ public:
CLobbyScreen(ESelectionScreen type, bool hideScreen = false);
~CLobbyScreen();
void toggleTab(std::shared_ptr<CIntObject> tab) final;
void tick(uint32_t msPassed) override;
void start(bool campaign);
void startCampaign();
void startScenario(bool allowOnlyAI = false);
+72
View File
@@ -16,6 +16,7 @@
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../GameChatHandler.h"
#include "../GameEngine.h"
#include "../GameInstance.h"
#include "../gui/Shortcut.h"
@@ -608,10 +609,19 @@ void SelectionTab::filter(int size, bool selectFirst)
if(buttonDeleteMode)
buttonDeleteMode->setEnabled(tabType != ESelectionScreen::newGame || showRandom);
size_t hiddenIncompatibleMaps = 0;
size_t requiredHumanPlayers = getRequiredHumanPlayers();
for(auto elem : allItems)
{
if((elem->mapHeader && (!size || elem->mapHeader->width == size)) || tabType == ESelectionScreen::campaignList)
{
if(!isMapCompatibleWithLobbyPlayerCount(*elem))
{
++hiddenIncompatibleMaps;
continue;
}
if(showRandom)
curFolder = "RandomMaps/";
@@ -645,6 +655,26 @@ void SelectionTab::filter(int size, bool selectFirst)
}
}
if(hiddenIncompatibleMaps && tabType == ESelectionScreen::newGame && GAME->server().loadMode == ELoadMode::MULTI)
{
MetaString warningText;
warningText.appendTextID("vcmi.lobby.system.hidingIncompatibleMaps");
warningText.replaceNumber(requiredHumanPlayers);
const std::string warningTextFormatted = warningText.toString();
if(lastCompatibilityNotice != warningTextFormatted)
{
logGlobal->info("%s", warningTextFormatted);
if(!GAME->server().hotseatMode)
GAME->server().getGameChat().onNewLobbyMessageReceived("System", warningTextFormatted);
lastCompatibilityNotice = warningTextFormatted;
}
}
else
{
lastCompatibilityNotice.clear();
}
if(curItems.size())
{
slider->block(false);
@@ -660,6 +690,25 @@ void SelectionTab::filter(int size, bool selectFirst)
selectAbs(firstPos);
}
}
else if(tabType == ESelectionScreen::newGame || tabType == ESelectionScreen::campaignList)
{
const std::string selectedMapFileURI = GAME->server().mi ? GAME->server().mi->fileURI : "";
auto selectedMapIt = boost::range::find_if(curItems, [&selectedMapFileURI](std::shared_ptr<ElementInfo> e)
{
return !e->isFolder && e->fileURI == selectedMapFileURI;
});
if(selectedMapIt == curItems.end())
{
int firstPos = boost::range::find_if(curItems, [](std::shared_ptr<ElementInfo> e) { return !e->isFolder; }) - curItems.begin();
if(firstPos < curItems.size())
{
slider->scrollTo(firstPos);
callOnSelect(curItems[firstPos]);
selectAbs(firstPos);
}
}
}
}
else
{
@@ -943,6 +992,29 @@ bool SelectionTab::isMapSupported(const CMapInfo & info)
return false;
}
bool SelectionTab::isMapCompatibleWithLobbyPlayerCount(const ElementInfo & info) const
{
if(tabType != ESelectionScreen::newGame || GAME->server().loadMode != ELoadMode::MULTI || !info.mapHeader)
return true;
const auto requiredHumanPlayers = getRequiredHumanPlayers();
size_t supportedHumanPlayers = 0;
for(const auto & player : info.mapHeader->players)
{
if(player.canHumanPlay)
++supportedHumanPlayers;
}
return supportedHumanPlayers >= requiredHumanPlayers;
}
size_t SelectionTab::getRequiredHumanPlayers() const
{
const size_t minimumPlayers = (GAME->server().loadMode == ELoadMode::MULTI || GAME->server().hotseatMode) ? 2 : 1;
return std::max(minimumPlayers, GAME->server().playerNames.size());
}
void SelectionTab::parseMaps(const std::unordered_set<ResourcePath> & files)
{
logGlobal->debug("Parsing %d maps", files.size());
+3
View File
@@ -90,6 +90,7 @@ public:
bool sortModeAscending;
int currentMapSizeFilter = 0;
bool showRandom;
std::string lastCompatibilityNotice;
std::shared_ptr<CTextInput> inputName;
@@ -133,6 +134,8 @@ private:
std::shared_ptr<CButton> buttonCampaignSet;
auto checkSubfolder(std::string path);
size_t getRequiredHumanPlayers() const;
bool isMapCompatibleWithLobbyPlayerCount(const ElementInfo & info) const;
bool isMapSupported(const CMapInfo & info);
void parseMaps(const std::unordered_set<ResourcePath> & files);
+19 -4
View File
@@ -398,10 +398,11 @@ void CMainMenu::makeActiveInterface()
menu->switchToTab(menu->getActiveTab());
}
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, std::string server, ui16 port)
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, bool hotseatMode, std::string server, ui16 port)
{
GAME->server().resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, EServerMode::LOCAL, names);
GAME->server().loadMode = loadMode;
GAME->server().hotseatMode = hotseatMode;
GAME->server().battleMode = battleMode;
ENGINE->windows().createAndPushWindow<CSimpleJoinScreen>(host, server, port);
@@ -590,7 +591,7 @@ void JoinScreen::onServerDiscovered(const DiscoveredServer & server)
auto savedScreenType = screenType;
auto savedPlayerNames = playerNames;
close();
CMainMenu::openLobby(savedScreenType, false, savedPlayerNames, ELoadMode::MULTI, false, server.address, server.port);
CMainMenu::openLobby(savedScreenType, false, savedPlayerNames, ELoadMode::MULTI, false, false, server.address, server.port);
});
button->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.mainMenu.join"), FONT_SMALL, Colors::WHITE);
buttonsJoin.push_back(button);
@@ -600,7 +601,7 @@ void JoinScreen::onServerDiscovered(const DiscoveredServer & server)
}
CMultiPlayers::CMultiPlayers(const std::vector<std::string>& playerNames, ESelectionScreen ScreenType, bool Host, ELoadMode LoadMode, EShortcut shortcut)
: loadMode(LoadMode), screenType(ScreenType), host(Host)
: host(Host), hotseat(shortcut == EShortcut::MAIN_MENU_HOTSEAT), loadMode(LoadMode), screenType(ScreenType)
{
OBJECT_CONSTRUCTION;
background = std::make_shared<CPicture>(ImagePath::builtin("MUHOTSEA.bmp"));
@@ -637,13 +638,24 @@ CMultiPlayers::CMultiPlayers(const std::vector<std::string>& playerNames, ESelec
{
inputNames[i]->setText(playerNames[i]);
}
buttonOk->block(hotseat && countEnteredNames() < 2);
#ifndef VCMI_MOBILE
inputNames[0]->giveFocus();
#endif
}
size_t CMultiPlayers::countEnteredNames() const
{
return std::count_if(inputNames.begin(), inputNames.end(), [](const auto & playerName)
{
return playerName->getText().length();
});
}
void CMultiPlayers::onChange(std::string newText)
{
buttonOk->block(hotseat && countEnteredNames() < 2);
}
void CMultiPlayers::enterSelectionScreen()
@@ -675,6 +687,9 @@ void CMultiPlayers::enterSelectionScreen()
playerName->clear();
}
if(hotseat && playerNames.size() < 2)
return;
if(!host)
{
auto savedScreenType = screenType;
@@ -683,7 +698,7 @@ void CMultiPlayers::enterSelectionScreen()
ENGINE->windows().createAndPushWindow<JoinScreen>(savedScreenType, savedPlayerNames);
return;
}
CMainMenu::openLobby(screenType, host, playerNames, loadMode, false);
CMainMenu::openLobby(screenType, host, playerNames, loadMode, false, hotseat);
}
CSimpleJoinScreen::CSimpleJoinScreen(bool host, std::string server, ui16 port)
+3 -2
View File
@@ -131,6 +131,7 @@ public:
class CMultiPlayers : public WindowBase
{
bool host;
bool hotseat;
ELoadMode loadMode;
ESelectionScreen screenType;
std::shared_ptr<CPicture> background;
@@ -140,6 +141,7 @@ class CMultiPlayers : public WindowBase
std::shared_ptr<CButton> buttonCancel;
std::shared_ptr<CGStatusBar> statusBar;
size_t countEnteredNames() const;
void onChange(std::string newText);
void enterSelectionScreen();
@@ -178,7 +180,7 @@ public:
void activate() override;
void onScreenResize() override;
void makeActiveInterface();
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, std::string server = {}, ui16 port = 0);
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, bool hotseatMode = false, std::string server = {}, ui16 port = 0);
static void openCampaignLobby(const std::string & campaignFileName, std::string campaignSet = "");
static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
static void startTutorial();
@@ -228,4 +230,3 @@ public:
void tick(uint32_t msPassed) override;
};
+44 -2
View File
@@ -495,7 +495,11 @@ void CVCMIServer::clientConnected(std::shared_ptr<GameConnection> c, std::vector
cp.connection = c->connectionID;
cp.name = name;
playerNames.try_emplace(id, cp);
announceTxt(boost::str(boost::format("%s (pid %d cid %d) joins the game") % name % static_cast<int>(id) % static_cast<int>(c->connectionID)));
logNetwork->info("Player joined lobby: name='%s', playerId=%d, connectionId=%d", name, static_cast<int>(id), static_cast<int>(c->connectionID));
MetaString joinMessage;
joinMessage.appendTextID("vcmi.lobby.system.playerJoined");
joinMessage.replaceRawString(name);
announceTxt(joinMessage);
//put new player in first slot with AI
for(auto & elem : si->playerInfos)
@@ -515,6 +519,45 @@ void CVCMIServer::clientDisconnected(std::shared_ptr<GameConnection> connection)
logGlobal->trace("Received disconnection request");
vstd::erase(activeConnections, connection);
std::vector<PlayerConnectionID> disconnectedPlayerIds;
std::vector<std::string> disconnectedPlayerNames;
for(const auto & playerEntry : playerNames)
{
if(playerEntry.second.connection == connection->connectionID)
{
disconnectedPlayerIds.push_back(playerEntry.first);
disconnectedPlayerNames.push_back(playerEntry.second.name);
}
}
for(const auto & playerName : disconnectedPlayerNames)
{
logNetwork->info("Player disconnected from lobby: name='%s', connectionId=%d", playerName, static_cast<int>(connection->connectionID));
MetaString disconnectMessage;
disconnectMessage.appendTextID("vcmi.lobby.system.playerDisconnected");
disconnectMessage.replaceRawString(playerName);
announceTxt(disconnectMessage);
}
if(disconnectedPlayerNames.empty())
logNetwork->info("Connection %d disconnected from lobby with no mapped player names", static_cast<int>(connection->connectionID));
for(const auto & playerId : disconnectedPlayerIds)
playerNames.erase(playerId);
if(!disconnectedPlayerIds.empty())
{
for(auto & playerInfoEntry : si->playerInfos)
{
auto & connectedPlayerIDs = playerInfoEntry.second.connectedPlayerIDs;
for(const auto & playerId : disconnectedPlayerIds)
connectedPlayerIDs.erase(playerId);
if(connectedPlayerIDs.empty())
setPlayerConnectedId(playerInfoEntry.second, PlayerConnectionID::PLAYER_AI);
}
}
if(activeConnections.empty() || hostClientId == connection->connectionID)
{
setState(EServerState::SHUTDOWN);
@@ -1196,4 +1239,3 @@ void CVCMIServer::sendPack(CPackForClient & pack, GameConnectionID connectionID)
if (c->connectionID == connectionID)
c->sendPack(pack);
}