mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-24 03:47:18 +02:00
Merge pull request #1052 from Nordsoft91/mp-disconnection
MP handle disconnection
This commit is contained in:
commit
288f36c0c3
@ -284,6 +284,8 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu
|
|||||||
{
|
{
|
||||||
ASSERT_IF_CALLED_WITH_PLAYER
|
ASSERT_IF_CALLED_WITH_PLAYER
|
||||||
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
|
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
|
||||||
|
if(player)
|
||||||
|
pm.player = *player;
|
||||||
sendRequest(&pm);
|
sendRequest(&pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,6 +399,7 @@ int main(int argc, char * argv[])
|
|||||||
CCS = new CClientState();
|
CCS = new CClientState();
|
||||||
CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.)
|
CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.)
|
||||||
CSH = new CServerHandler();
|
CSH = new CServerHandler();
|
||||||
|
|
||||||
// Initialize video
|
// Initialize video
|
||||||
#ifdef DISABLE_VIDEO
|
#ifdef DISABLE_VIDEO
|
||||||
CCS->videoh = new CEmptyVideoPlayer();
|
CCS->videoh = new CEmptyVideoPlayer();
|
||||||
@ -500,6 +501,12 @@ int main(int argc, char * argv[])
|
|||||||
GH.curInt = CMainMenu::create().get();
|
GH.curInt = CMainMenu::create().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore remote session - start game immediately
|
||||||
|
if(settings["server"]["reconnect"].Bool())
|
||||||
|
{
|
||||||
|
CSH->restoreLastSession();
|
||||||
|
}
|
||||||
|
|
||||||
if(!settings["session"]["headless"].Bool())
|
if(!settings["session"]["headless"].Bool())
|
||||||
{
|
{
|
||||||
mainLoop();
|
mainLoop();
|
||||||
|
@ -559,6 +559,23 @@ void CServerHandler::startGameplay(CGameState * gameState)
|
|||||||
// After everything initialized we can accept CPackToClient netpacks
|
// After everything initialized we can accept CPackToClient netpacks
|
||||||
c->enterGameplayConnectionMode(client->gameState());
|
c->enterGameplayConnectionMode(client->gameState());
|
||||||
state = EClientState::GAMEPLAY;
|
state = EClientState::GAMEPLAY;
|
||||||
|
|
||||||
|
//store settings to continue game
|
||||||
|
if(!isServerLocal() && isGuest())
|
||||||
|
{
|
||||||
|
Settings saveSession = settings.write["server"]["reconnect"];
|
||||||
|
saveSession->Bool() = true;
|
||||||
|
Settings saveUuid = settings.write["server"]["uuid"];
|
||||||
|
saveUuid->String() = uuid;
|
||||||
|
Settings saveNames = settings.write["server"]["names"];
|
||||||
|
saveNames->Vector().clear();
|
||||||
|
for(auto & name : myNames)
|
||||||
|
{
|
||||||
|
JsonNode jsonName;
|
||||||
|
jsonName.String() = name;
|
||||||
|
saveNames->Vector().push_back(jsonName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::endGameplay(bool closeConnection, bool restart)
|
void CServerHandler::endGameplay(bool closeConnection, bool restart)
|
||||||
@ -589,6 +606,10 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
|
|||||||
|
|
||||||
c->enterLobbyConnectionMode();
|
c->enterLobbyConnectionMode();
|
||||||
c->disableStackSendingByID();
|
c->disableStackSendingByID();
|
||||||
|
|
||||||
|
//reset settings
|
||||||
|
Settings saveSession = settings.write["server"]["reconnect"];
|
||||||
|
saveSession->Bool() = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::startCampaignScenario(std::shared_ptr<CCampaignState> cs)
|
void CServerHandler::startCampaignScenario(std::shared_ptr<CCampaignState> cs)
|
||||||
@ -639,6 +660,28 @@ ui8 CServerHandler::getLoadMode()
|
|||||||
return loadMode;
|
return loadMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CServerHandler::restoreLastSession()
|
||||||
|
{
|
||||||
|
auto loadSession = [this]()
|
||||||
|
{
|
||||||
|
uuid = settings["server"]["uuid"].String();
|
||||||
|
for(auto & name : settings["server"]["names"].Vector())
|
||||||
|
myNames.push_back(name.String());
|
||||||
|
resetStateForLobby(StartInfo::LOAD_GAME, &myNames);
|
||||||
|
screenType = ESelectionScreen::loadGame;
|
||||||
|
justConnectToServer(settings["server"]["server"].String(), settings["server"]["port"].Integer());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto cleanUpSession = []()
|
||||||
|
{
|
||||||
|
//reset settings
|
||||||
|
Settings saveSession = settings.write["server"]["reconnect"];
|
||||||
|
saveSession->Bool() = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
CInfoWindow::showYesNoDialog(VLC->generaltexth->localizedTexts["server"]["confirmReconnect"].String(), {}, loadSession, cleanUpSession);
|
||||||
|
}
|
||||||
|
|
||||||
void CServerHandler::debugStartTest(std::string filename, bool save)
|
void CServerHandler::debugStartTest(std::string filename, bool save)
|
||||||
{
|
{
|
||||||
logGlobal->info("Starting debug test with file: %s", filename);
|
logGlobal->info("Starting debug test with file: %s", filename);
|
||||||
|
@ -157,6 +157,7 @@ public:
|
|||||||
ui8 getLoadMode();
|
ui8 getLoadMode();
|
||||||
|
|
||||||
void debugStartTest(std::string filename, bool save = false);
|
void debugStartTest(std::string filename, bool save = false);
|
||||||
|
void restoreLastSession();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CServerHandler * CSH;
|
extern CServerHandler * CSH;
|
||||||
|
@ -345,6 +345,34 @@ void PlayerEndsGame::applyCl(CClient *cl)
|
|||||||
handleQuit(settings["session"]["spectate"].Bool()); // if spectator is active ask to close client or not
|
handleQuit(settings["session"]["spectate"].Bool()); // if spectator is active ask to close client or not
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerReinitInterface::applyCl(CClient * cl)
|
||||||
|
{
|
||||||
|
auto initInterfaces = [cl]()
|
||||||
|
{
|
||||||
|
cl->initPlayerInterfaces();
|
||||||
|
auto currentPlayer = cl->gameState()->currentPlayer;
|
||||||
|
callAllInterfaces(cl, &IGameEventsReceiver::playerStartsTurn, currentPlayer);
|
||||||
|
callOnlyThatInterface(cl, currentPlayer, &CGameInterface::yourTurn);
|
||||||
|
};
|
||||||
|
|
||||||
|
for(auto player : players)
|
||||||
|
{
|
||||||
|
auto & plSettings = CSH->si->getIthPlayersSettings(player);
|
||||||
|
if(playerConnectionId == PlayerSettings::PLAYER_AI)
|
||||||
|
{
|
||||||
|
plSettings.connectedPlayerIDs.clear();
|
||||||
|
cl->initPlayerEnvironments();
|
||||||
|
initInterfaces();
|
||||||
|
}
|
||||||
|
else if(playerConnectionId == CSH->c->connectionID)
|
||||||
|
{
|
||||||
|
plSettings.connectedPlayerIDs.insert(playerConnectionId);
|
||||||
|
cl->playerint.clear();
|
||||||
|
initInterfaces();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RemoveBonus::applyCl(CClient *cl)
|
void RemoveBonus::applyCl(CClient *cl)
|
||||||
{
|
{
|
||||||
cl->invalidatePaths();
|
cl->invalidatePaths();
|
||||||
@ -790,7 +818,11 @@ void YourTurn::applyCl(CClient *cl)
|
|||||||
void SaveGameClient::applyCl(CClient *cl)
|
void SaveGameClient::applyCl(CClient *cl)
|
||||||
{
|
{
|
||||||
const auto stem = FileInfo::GetPathStem(fname);
|
const auto stem = FileInfo::GetPathStem(fname);
|
||||||
CResourceHandler::get("local")->createResource(stem.to_string() + ".vcgm1");
|
if(!CResourceHandler::get("local")->createResource(stem.to_string() + ".vcgm1"))
|
||||||
|
{
|
||||||
|
logNetwork->error("Failed to create resource %s", stem.to_string() + ".vcgm1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
|
|||||||
|
|
||||||
void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||||
{
|
{
|
||||||
if(!handler->isGuest())
|
if(!lobby || !handler->isGuest())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch(action)
|
switch(action)
|
||||||
@ -109,10 +109,15 @@ bool LobbyEndGame::applyOnLobbyHandler(CServerHandler * handler)
|
|||||||
|
|
||||||
bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
|
bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
|
||||||
{
|
{
|
||||||
|
if(clientId != -1 && clientId != handler->c->connectionID)
|
||||||
|
return false;
|
||||||
|
|
||||||
handler->state = EClientState::STARTING;
|
handler->state = EClientState::STARTING;
|
||||||
if(handler->si->mode != StartInfo::LOAD_GAME)
|
if(handler->si->mode != StartInfo::LOAD_GAME || clientId == handler->c->connectionID)
|
||||||
{
|
{
|
||||||
|
auto modeBackup = handler->si->mode;
|
||||||
handler->si = initializedStartInfo;
|
handler->si = initializedStartInfo;
|
||||||
|
handler->si->mode = modeBackup;
|
||||||
}
|
}
|
||||||
if(settings["session"]["headless"].Bool())
|
if(settings["session"]["headless"].Bool())
|
||||||
handler->startGameplay(initializedGameState);
|
handler->startGameplay(initializedGameState);
|
||||||
@ -121,6 +126,9 @@ bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
|
|||||||
|
|
||||||
void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||||
{
|
{
|
||||||
|
if(clientId != -1 && clientId != handler->c->connectionID)
|
||||||
|
return;
|
||||||
|
|
||||||
GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, handler, initializedGameState));
|
GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, handler, initializedGameState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +141,9 @@ bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler)
|
|||||||
|
|
||||||
void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||||
{
|
{
|
||||||
|
if(!lobby) //stub: ignore message for game mode
|
||||||
|
return;
|
||||||
|
|
||||||
if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
|
if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
|
||||||
{
|
{
|
||||||
lobby->bonusSel = std::make_shared<CBonusSelection>();
|
lobby->bonusSel = std::make_shared<CBonusSelection>();
|
||||||
@ -150,6 +161,9 @@ void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
|
|||||||
|
|
||||||
void LobbyShowMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
void LobbyShowMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||||
{
|
{
|
||||||
|
if(!lobby) //stub: ignore message for game mode
|
||||||
|
return;
|
||||||
|
|
||||||
lobby->buttonStart->block(false);
|
lobby->buttonStart->block(false);
|
||||||
handler->showServerError(message);
|
handler->showServerError(message);
|
||||||
}
|
}
|
||||||
|
@ -934,6 +934,10 @@ void CAdvMapInt::activate()
|
|||||||
|
|
||||||
screenBuf = screen;
|
screenBuf = screen;
|
||||||
GH.statusbar = statusbar;
|
GH.statusbar = statusbar;
|
||||||
|
|
||||||
|
if(LOCPLINT)
|
||||||
|
LOCPLINT->cingconsole->activate();
|
||||||
|
|
||||||
if(!duringAITurn)
|
if(!duringAITurn)
|
||||||
{
|
{
|
||||||
activeMapPanel->activate();
|
activeMapPanel->activate();
|
||||||
@ -945,8 +949,6 @@ void CAdvMapInt::activate()
|
|||||||
}
|
}
|
||||||
minimap.activate();
|
minimap.activate();
|
||||||
terrain.activate();
|
terrain.activate();
|
||||||
if(LOCPLINT)
|
|
||||||
LOCPLINT->cingconsole->activate();
|
|
||||||
|
|
||||||
GH.fakeMouseMove(); //to restore the cursor
|
GH.fakeMouseMove(); //to restore the cursor
|
||||||
}
|
}
|
||||||
@ -970,8 +972,6 @@ void CAdvMapInt::deactivate()
|
|||||||
}
|
}
|
||||||
minimap.deactivate();
|
minimap.deactivate();
|
||||||
terrain.deactivate();
|
terrain.deactivate();
|
||||||
if(LOCPLINT)
|
|
||||||
LOCPLINT->cingconsole->deactivate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1532,8 +1532,6 @@ void CAdvMapInt::endingTurn()
|
|||||||
if(settings["session"]["spectate"].Bool())
|
if(settings["session"]["spectate"].Bool())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(LOCPLINT->cingconsole->active)
|
|
||||||
LOCPLINT->cingconsole->deactivate();
|
|
||||||
LOCPLINT->makingTurn = false;
|
LOCPLINT->makingTurn = false;
|
||||||
LOCPLINT->cb->endTurn();
|
LOCPLINT->cb->endTurn();
|
||||||
CCS->soundh->ambientStopAllChannels();
|
CCS->soundh->ambientStopAllChannels();
|
||||||
|
@ -253,7 +253,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default": {},
|
"default": {},
|
||||||
"required" : [ "server", "port", "localInformation", "playerAI", "friendlyAI","neutralAI", "enemyAI" ],
|
"required" : [ "server", "port", "localInformation", "playerAI", "friendlyAI","neutralAI", "enemyAI", "reconnect", "uuid", "names" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"server" : {
|
"server" : {
|
||||||
"type":"string",
|
"type":"string",
|
||||||
@ -282,6 +282,23 @@
|
|||||||
"enemyAI" : {
|
"enemyAI" : {
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"default" : "BattleAI"
|
"default" : "BattleAI"
|
||||||
|
},
|
||||||
|
"reconnect" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : false
|
||||||
|
},
|
||||||
|
"uuid" : {
|
||||||
|
"type" : "string",
|
||||||
|
"default" : ""
|
||||||
|
},
|
||||||
|
"names" : {
|
||||||
|
"type" : "array",
|
||||||
|
"default" : {},
|
||||||
|
"items":
|
||||||
|
{
|
||||||
|
"type" : "string",
|
||||||
|
"default" : ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
{
|
{
|
||||||
"existingProcess" : "Another vcmiserver process is running, please terminate it first",
|
"existingProcess" : "Another vcmiserver process is running, please terminate it first",
|
||||||
"modsIncompatibility" : "Required mods to load game:"
|
"modsIncompatibility" : "Required mods to load game:"
|
||||||
}
|
},
|
||||||
|
"confirmReconnect" : "Connect to the last session?"
|
||||||
},
|
},
|
||||||
"systemOptions" :
|
"systemOptions" :
|
||||||
{
|
{
|
||||||
|
@ -421,6 +421,21 @@ struct PlayerEndsGame : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PlayerReinitInterface : public CPackForClient
|
||||||
|
{
|
||||||
|
void applyCl(CClient * cl);
|
||||||
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
|
|
||||||
|
std::vector<PlayerColor> players;
|
||||||
|
ui8 playerConnectionId; //PLAYER_AI for AI player
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & players;
|
||||||
|
h & playerConnectionId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct RemoveBonus : public CPackForClient
|
struct RemoveBonus : public CPackForClient
|
||||||
{
|
{
|
||||||
RemoveBonus(ui8 Who = 0)
|
RemoveBonus(ui8 Who = 0)
|
||||||
|
@ -365,6 +365,19 @@ DLL_LINKAGE void PlayerEndsGame::applyGs(CGameState *gs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLL_LINKAGE void PlayerReinitInterface::applyGs(CGameState *gs)
|
||||||
|
{
|
||||||
|
if(!gs || !gs->scenarioOps)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//TODO: what does mean if more that one player connected?
|
||||||
|
if(playerConnectionId == PlayerSettings::PLAYER_AI)
|
||||||
|
{
|
||||||
|
for(auto player : players)
|
||||||
|
gs->scenarioOps->getIthPlayersSettings(player).connectedPlayerIDs.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void RemoveBonus::applyGs(CGameState *gs)
|
DLL_LINKAGE void RemoveBonus::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
CBonusSystemNode *node;
|
CBonusSystemNode *node;
|
||||||
|
@ -159,8 +159,9 @@ struct LobbyStartGame : public CLobbyPackToPropagate
|
|||||||
// Set by server
|
// Set by server
|
||||||
std::shared_ptr<StartInfo> initializedStartInfo;
|
std::shared_ptr<StartInfo> initializedStartInfo;
|
||||||
CGameState * initializedGameState;
|
CGameState * initializedGameState;
|
||||||
|
int clientId; //-1 means to all clients
|
||||||
|
|
||||||
LobbyStartGame() : initializedStartInfo(nullptr), initializedGameState(nullptr) {}
|
LobbyStartGame() : initializedStartInfo(nullptr), initializedGameState(nullptr), clientId(-1) {}
|
||||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||||
bool applyOnServer(CVCMIServer * srv);
|
bool applyOnServer(CVCMIServer * srv);
|
||||||
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||||
@ -169,6 +170,7 @@ struct LobbyStartGame : public CLobbyPackToPropagate
|
|||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
|
h & clientId;
|
||||||
h & initializedStartInfo;
|
h & initializedStartInfo;
|
||||||
bool sps = h.smartPointerSerialization;
|
bool sps = h.smartPointerSerialization;
|
||||||
h.smartPointerSerialization = true;
|
h.smartPointerSerialization = true;
|
||||||
|
@ -240,6 +240,7 @@ void registerTypesClientPacks1(Serializer &s)
|
|||||||
s.template registerType<CPackForClient, GiveBonus>();
|
s.template registerType<CPackForClient, GiveBonus>();
|
||||||
s.template registerType<CPackForClient, ChangeObjPos>();
|
s.template registerType<CPackForClient, ChangeObjPos>();
|
||||||
s.template registerType<CPackForClient, PlayerEndsGame>();
|
s.template registerType<CPackForClient, PlayerEndsGame>();
|
||||||
|
s.template registerType<CPackForClient, PlayerReinitInterface>();
|
||||||
s.template registerType<CPackForClient, RemoveBonus>();
|
s.template registerType<CPackForClient, RemoveBonus>();
|
||||||
s.template registerType<CPackForClient, UpdateArtHandlerLists>();
|
s.template registerType<CPackForClient, UpdateArtHandlerLists>();
|
||||||
s.template registerType<CPackForClient, UpdateMapEvents>();
|
s.template registerType<CPackForClient, UpdateMapEvents>();
|
||||||
|
@ -1324,23 +1324,21 @@ void CGameHandler::addGenericKilledLog(BattleLogMessage & blm, const CStack * de
|
|||||||
|
|
||||||
void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
|
void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
|
||||||
{
|
{
|
||||||
for(auto playerConns : connections)
|
if(lobby->state == EServerState::SHUTDOWN || !gs || !gs->scenarioOps)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(auto & playerConnections : connections)
|
||||||
{
|
{
|
||||||
for(auto i = playerConns.second.begin(); i != playerConns.second.end(); )
|
PlayerColor playerId = playerConnections.first;
|
||||||
|
auto * playerSettings = gs->scenarioOps->getPlayersSettings(playerId.getNum());
|
||||||
|
if(!playerSettings)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto playerConnection = vstd::find(playerConnections.second, c);
|
||||||
|
if(playerConnection != playerConnections.second.end())
|
||||||
{
|
{
|
||||||
if(lobby->state != EServerState::SHUTDOWN && *i == c)
|
std::string messageText = boost::str(boost::format("%s (cid %d) was disconnected") % playerSettings->name % c->connectionID);
|
||||||
{
|
playerMessage(playerId, messageText, ObjectInstanceID{});
|
||||||
i = playerConns.second.erase(i);
|
|
||||||
if(playerConns.second.size())
|
|
||||||
continue;
|
|
||||||
PlayerCheated pc;
|
|
||||||
pc.player = playerConns.first;
|
|
||||||
pc.losingCheatCode = true;
|
|
||||||
sendAndApply(&pc);
|
|
||||||
checkVictoryLossConditionsForPlayer(playerConns.first);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3396,6 +3394,11 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CGameHandler::hasPlayerAt(PlayerColor player, std::shared_ptr<CConnection> c) const
|
||||||
|
{
|
||||||
|
return connections.at(player).count(c);
|
||||||
|
}
|
||||||
|
|
||||||
PlayerColor CGameHandler::getPlayerAt(std::shared_ptr<CConnection> c) const
|
PlayerColor CGameHandler::getPlayerAt(std::shared_ptr<CConnection> c) const
|
||||||
{
|
{
|
||||||
std::set<PlayerColor> all;
|
std::set<PlayerColor> all;
|
||||||
@ -5003,16 +5006,65 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
|
|
||||||
void CGameHandler::playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj)
|
void CGameHandler::playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj)
|
||||||
{
|
{
|
||||||
bool cheated = true;
|
bool cheated = false;
|
||||||
PlayerMessageClient temp_message(player, message);
|
PlayerMessageClient temp_message(player, message);
|
||||||
sendAndApply(&temp_message);
|
sendAndApply(&temp_message);
|
||||||
|
|
||||||
std::vector<std::string> cheat;
|
std::vector<std::string> words;
|
||||||
boost::split(cheat, message, boost::is_any_of(" "));
|
boost::split(words, message, boost::is_any_of(" "));
|
||||||
int obj = 0;
|
|
||||||
if (cheat.size() == 2)
|
bool isHost = false;
|
||||||
|
for(auto & c : connections[player])
|
||||||
|
if(lobby->isClientHost(c->connectionID))
|
||||||
|
isHost = true;
|
||||||
|
|
||||||
|
if(isHost && words.size() >= 2 && words[0] == "game")
|
||||||
{
|
{
|
||||||
obj = std::atoi(cheat[1].c_str());
|
if(words[1] == "exit" || words[1] == "quit" || words[1] == "end")
|
||||||
|
{
|
||||||
|
SystemMessage temp_message("game was terminated");
|
||||||
|
sendAndApply(&temp_message);
|
||||||
|
lobby->state = EServerState::SHUTDOWN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(words.size() == 3 && words[1] == "save")
|
||||||
|
{
|
||||||
|
save("Saves/" + words[2]);
|
||||||
|
SystemMessage temp_message("game saved as " + words[2]);
|
||||||
|
sendAndApply(&temp_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(words.size() == 3 && words[1] == "kick")
|
||||||
|
{
|
||||||
|
auto playername = words[2];
|
||||||
|
PlayerColor playerToKick(PlayerColor::CANNOT_DETERMINE);
|
||||||
|
if(std::all_of(playername.begin(), playername.end(), ::isdigit))
|
||||||
|
playerToKick = PlayerColor(std::stoi(playername));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(auto & c : connections)
|
||||||
|
{
|
||||||
|
if(c.first.getStr(false) == playername)
|
||||||
|
playerToKick = c.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(playerToKick != PlayerColor::CANNOT_DETERMINE)
|
||||||
|
{
|
||||||
|
PlayerCheated pc;
|
||||||
|
pc.player = playerToKick;
|
||||||
|
pc.losingCheatCode = true;
|
||||||
|
sendAndApply(&pc);
|
||||||
|
checkVictoryLossConditionsForPlayer(playerToKick);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int obj = 0;
|
||||||
|
if (words.size() == 2)
|
||||||
|
{
|
||||||
|
obj = std::atoi(words[1].c_str());
|
||||||
if (obj)
|
if (obj)
|
||||||
currObj = ObjectInstanceID(obj);
|
currObj = ObjectInstanceID(obj);
|
||||||
}
|
}
|
||||||
@ -5022,38 +5074,38 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message,
|
|||||||
if (!town && hero)
|
if (!town && hero)
|
||||||
town = hero->visitedTown;
|
town = hero->visitedTown;
|
||||||
|
|
||||||
if (cheat.size() == 1 || obj)
|
if (words.size() == 1 || obj)
|
||||||
handleCheatCode(cheat[0], player, hero, town, cheated);
|
handleCheatCode(words[0], player, hero, town, cheated);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto & i : gs->players)
|
for (const auto & i : gs->players)
|
||||||
{
|
{
|
||||||
if (i.first == PlayerColor::NEUTRAL)
|
if (i.first == PlayerColor::NEUTRAL)
|
||||||
continue;
|
continue;
|
||||||
if (cheat[1] == "ai")
|
if (words[1] == "ai")
|
||||||
{
|
{
|
||||||
if (i.second.human)
|
if (i.second.human)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cheat[1] != "all" && cheat[1] != i.first.getStr())
|
else if (words[1] != "all" && words[1] != i.first.getStr())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (cheat[0] == "vcmiformenos" || cheat[0] == "vcmieagles" || cheat[0] == "vcmiungoliant")
|
if (words[0] == "vcmiformenos" || words[0] == "vcmieagles" || words[0] == "vcmiungoliant")
|
||||||
{
|
{
|
||||||
handleCheatCode(cheat[0], i.first, nullptr, nullptr, cheated);
|
handleCheatCode(words[0], i.first, nullptr, nullptr, cheated);
|
||||||
}
|
}
|
||||||
else if (cheat[0] == "vcmiarmenelos")
|
else if (words[0] == "vcmiarmenelos")
|
||||||
{
|
{
|
||||||
for (const auto & t : i.second.towns)
|
for (const auto & t : i.second.towns)
|
||||||
{
|
{
|
||||||
handleCheatCode(cheat[0], i.first, nullptr, t, cheated);
|
handleCheatCode(words[0], i.first, nullptr, t, cheated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto & h : i.second.heroes)
|
for (const auto & h : i.second.heroes)
|
||||||
{
|
{
|
||||||
handleCheatCode(cheat[0], i.first, h, nullptr, cheated);
|
handleCheatCode(words[0], i.first, h, nullptr, cheated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6931,6 +6983,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
{
|
{
|
||||||
if (cheat == "vcmiistari")
|
if (cheat == "vcmiistari")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///Give hero spellbook
|
///Give hero spellbook
|
||||||
if (!hero->hasSpellbook())
|
if (!hero->hasSpellbook())
|
||||||
@ -6956,6 +7009,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmiarmenelos")
|
else if (cheat == "vcmiarmenelos")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!town) return;
|
if (!town) return;
|
||||||
///Build all buildings in selected town
|
///Build all buildings in selected town
|
||||||
for (auto & build : town->town->buildings)
|
for (auto & build : town->town->buildings)
|
||||||
@ -6970,6 +7024,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmiainur" || cheat == "vcmiangband" || cheat == "vcmiglaurung")
|
else if (cheat == "vcmiainur" || cheat == "vcmiangband" || cheat == "vcmiglaurung")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///Gives N creatures into each slot
|
///Gives N creatures into each slot
|
||||||
std::map<std::string, std::pair<int, int>> creatures;
|
std::map<std::string, std::pair<int, int>> creatures;
|
||||||
@ -6984,6 +7039,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcminoldor")
|
else if (cheat == "vcminoldor")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///Give all war machines to hero
|
///Give all war machines to hero
|
||||||
if (!hero->getArt(ArtifactPosition::MACH1))
|
if (!hero->getArt(ArtifactPosition::MACH1))
|
||||||
@ -6995,6 +7051,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmiforgeofnoldorking")
|
else if (cheat == "vcmiforgeofnoldorking")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///Give hero all artifacts except war machines, spell scrolls and spell book
|
///Give hero all artifacts except war machines, spell scrolls and spell book
|
||||||
for (int g = 7; g < VLC->arth->objects.size(); ++g) //including artifacts from mods
|
for (int g = 7; g < VLC->arth->objects.size(); ++g) //including artifacts from mods
|
||||||
@ -7002,12 +7059,14 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmiglorfindel")
|
else if (cheat == "vcmiglorfindel")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///selected hero gains a new level
|
///selected hero gains a new level
|
||||||
changePrimSkill(hero, PrimarySkill::EXPERIENCE, VLC->heroh->reqExp(hero->level + 1) - VLC->heroh->reqExp(hero->level));
|
changePrimSkill(hero, PrimarySkill::EXPERIENCE, VLC->heroh->reqExp(hero->level + 1) - VLC->heroh->reqExp(hero->level));
|
||||||
}
|
}
|
||||||
else if (cheat == "vcminahar")
|
else if (cheat == "vcminahar")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
if (!hero) return;
|
if (!hero) return;
|
||||||
///Give 1000000 movement points to hero
|
///Give 1000000 movement points to hero
|
||||||
SetMovePoints smp;
|
SetMovePoints smp;
|
||||||
@ -7024,6 +7083,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmiformenos")
|
else if (cheat == "vcmiformenos")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
///Give resources to player
|
///Give resources to player
|
||||||
TResources resources;
|
TResources resources;
|
||||||
resources[Res::GOLD] = 100000;
|
resources[Res::GOLD] = 100000;
|
||||||
@ -7034,6 +7094,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmisilmaril")
|
else if (cheat == "vcmisilmaril")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
///Player wins
|
///Player wins
|
||||||
PlayerCheated pc;
|
PlayerCheated pc;
|
||||||
pc.player = player;
|
pc.player = player;
|
||||||
@ -7042,6 +7103,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmimelkor")
|
else if (cheat == "vcmimelkor")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
///Player looses
|
///Player looses
|
||||||
PlayerCheated pc;
|
PlayerCheated pc;
|
||||||
pc.player = player;
|
pc.player = player;
|
||||||
@ -7050,6 +7112,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
}
|
}
|
||||||
else if (cheat == "vcmieagles" || cheat == "vcmiungoliant")
|
else if (cheat == "vcmieagles" || cheat == "vcmiungoliant")
|
||||||
{
|
{
|
||||||
|
cheated = true;
|
||||||
///Reveal or conceal FoW
|
///Reveal or conceal FoW
|
||||||
FoWChange fc;
|
FoWChange fc;
|
||||||
fc.mode = (cheat == "vcmieagles" ? 1 : 0);
|
fc.mode = (cheat == "vcmieagles" ? 1 : 0);
|
||||||
@ -7068,8 +7131,6 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
|||||||
delete [] hlp_tab;
|
delete [] hlp_tab;
|
||||||
sendAndApply(&fc);
|
sendAndApply(&fc);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
cheated = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::removeObstacle(const CObstacleInstance & obstacle)
|
void CGameHandler::removeObstacle(const CObstacleInstance & obstacle)
|
||||||
|
@ -223,6 +223,7 @@ public:
|
|||||||
void handleClientDisconnection(std::shared_ptr<CConnection> c);
|
void handleClientDisconnection(std::shared_ptr<CConnection> c);
|
||||||
void handleReceivedPack(CPackForServer * pack);
|
void handleReceivedPack(CPackForServer * pack);
|
||||||
PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;
|
PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;
|
||||||
|
bool hasPlayerAt(PlayerColor player, std::shared_ptr<CConnection> c) const;
|
||||||
|
|
||||||
void playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj);
|
void playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj);
|
||||||
void updateGateState();
|
void updateGateState();
|
||||||
|
@ -206,11 +206,6 @@ void CVCMIServer::threadAnnounceLobby()
|
|||||||
announcePack(std::move(announceQueue.front()));
|
announcePack(std::move(announceQueue.front()));
|
||||||
announceQueue.pop_front();
|
announceQueue.pop_front();
|
||||||
}
|
}
|
||||||
if(state != EServerState::LOBBY)
|
|
||||||
{
|
|
||||||
if(acceptor)
|
|
||||||
acceptor->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(acceptor)
|
if(acceptor)
|
||||||
{
|
{
|
||||||
@ -308,11 +303,14 @@ void CVCMIServer::connectionAccepted(const boost::system::error_code & ec)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
logNetwork->info("We got a new connection! :)");
|
if(state == EServerState::LOBBY || !hangingConnections.empty())
|
||||||
auto c = std::make_shared<CConnection>(upcomingConnection, SERVER_NAME, uuid);
|
{
|
||||||
upcomingConnection.reset();
|
logNetwork->info("We got a new connection! :)");
|
||||||
connections.insert(c);
|
auto c = std::make_shared<CConnection>(upcomingConnection, SERVER_NAME, uuid);
|
||||||
c->handler = std::make_shared<boost::thread>(&CVCMIServer::threadHandleClient, this, c);
|
upcomingConnection.reset();
|
||||||
|
connections.insert(c);
|
||||||
|
c->handler = std::make_shared<boost::thread>(&CVCMIServer::threadHandleClient, this, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception & e)
|
catch(std::exception & e)
|
||||||
{
|
{
|
||||||
@ -343,10 +341,15 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
|||||||
catch(boost::system::system_error & e)
|
catch(boost::system::system_error & e)
|
||||||
{
|
{
|
||||||
logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what());
|
logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what());
|
||||||
|
hangingConnections.insert(c);
|
||||||
|
connections.erase(c);
|
||||||
|
if(connections.empty() || hostClient == c)
|
||||||
|
state = EServerState::SHUTDOWN;
|
||||||
|
|
||||||
if(state != EServerState::LOBBY)
|
if(gh && state == EServerState::GAMEPLAY)
|
||||||
|
{
|
||||||
gh->handleClientDisconnection(c);
|
gh->handleClientDisconnection(c);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +456,8 @@ bool CVCMIServer::passHost(int toConnectionId)
|
|||||||
|
|
||||||
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, StartInfo::EMode mode)
|
||||||
{
|
{
|
||||||
c->connectionID = currentClientId++;
|
if(state == EServerState::LOBBY)
|
||||||
|
c->connectionID = currentClientId++;
|
||||||
|
|
||||||
if(!hostClient)
|
if(!hostClient)
|
||||||
{
|
{
|
||||||
@ -463,24 +467,28 @@ void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<st
|
|||||||
}
|
}
|
||||||
|
|
||||||
logNetwork->info("Connection with client %d established. UUID: %s", c->connectionID, c->uuid);
|
logNetwork->info("Connection with client %d established. UUID: %s", c->connectionID, c->uuid);
|
||||||
for(auto & name : names)
|
|
||||||
|
if(state == EServerState::LOBBY)
|
||||||
{
|
{
|
||||||
logNetwork->info("Client %d player: %s", c->connectionID, name);
|
for(auto & name : names)
|
||||||
ui8 id = currentPlayerId++;
|
|
||||||
|
|
||||||
ClientPlayer cp;
|
|
||||||
cp.connection = c->connectionID;
|
|
||||||
cp.name = name;
|
|
||||||
playerNames.insert(std::make_pair(id, cp));
|
|
||||||
announceTxt(boost::str(boost::format("%s (pid %d cid %d) joins the game") % name % id % c->connectionID));
|
|
||||||
|
|
||||||
//put new player in first slot with AI
|
|
||||||
for(auto & elem : si->playerInfos)
|
|
||||||
{
|
{
|
||||||
if(elem.second.isControlledByAI() && !elem.second.compOnly)
|
logNetwork->info("Client %d player: %s", c->connectionID, name);
|
||||||
|
ui8 id = currentPlayerId++;
|
||||||
|
|
||||||
|
ClientPlayer cp;
|
||||||
|
cp.connection = c->connectionID;
|
||||||
|
cp.name = name;
|
||||||
|
playerNames.insert(std::make_pair(id, cp));
|
||||||
|
announceTxt(boost::str(boost::format("%s (pid %d cid %d) joins the game") % name % id % c->connectionID));
|
||||||
|
|
||||||
|
//put new player in first slot with AI
|
||||||
|
for(auto & elem : si->playerInfos)
|
||||||
{
|
{
|
||||||
setPlayerConnectedId(elem.second, id);
|
if(elem.second.isControlledByAI() && !elem.second.compOnly)
|
||||||
break;
|
{
|
||||||
|
setPlayerConnectedId(elem.second, id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,23 +497,73 @@ void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<st
|
|||||||
void CVCMIServer::clientDisconnected(std::shared_ptr<CConnection> c)
|
void CVCMIServer::clientDisconnected(std::shared_ptr<CConnection> c)
|
||||||
{
|
{
|
||||||
connections -= c;
|
connections -= c;
|
||||||
|
if(connections.empty() || hostClient == c)
|
||||||
|
{
|
||||||
|
state = EServerState::SHUTDOWN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerReinitInterface startAiPack;
|
||||||
|
startAiPack.playerConnectionId = PlayerSettings::PLAYER_AI;
|
||||||
|
|
||||||
for(auto it = playerNames.begin(); it != playerNames.end();)
|
for(auto it = playerNames.begin(); it != playerNames.end();)
|
||||||
{
|
{
|
||||||
if(it->second.connection != c->connectionID)
|
if(it->second.connection != c->connectionID)
|
||||||
{
|
{
|
||||||
it++;
|
++it;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = it->first;
|
int id = it->first;
|
||||||
announceTxt(boost::str(boost::format("%s (pid %d cid %d) left the game") % id % playerNames[id].name % c->connectionID));
|
std::string playerLeftMsgText = boost::str(boost::format("%s (pid %d cid %d) left the game") % id % playerNames[id].name % c->connectionID);
|
||||||
playerNames.erase(it++);
|
announceTxt(playerLeftMsgText); //send lobby text, it will be ignored for non-lobby clients
|
||||||
|
auto * playerSettings = si->getPlayersSettings(id);
|
||||||
// Reset in-game players client used back to AI
|
if(!playerSettings)
|
||||||
if(PlayerSettings * s = si->getPlayersSettings(id))
|
|
||||||
{
|
{
|
||||||
setPlayerConnectedId(*s, PlayerSettings::PLAYER_AI);
|
++it;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it = playerNames.erase(it);
|
||||||
|
setPlayerConnectedId(*playerSettings, PlayerSettings::PLAYER_AI);
|
||||||
|
|
||||||
|
if(gh && si && state == EServerState::GAMEPLAY)
|
||||||
|
{
|
||||||
|
gh->playerMessage(playerSettings->color, playerLeftMsgText, ObjectInstanceID{});
|
||||||
|
gh->connections[playerSettings->color].insert(hostClient);
|
||||||
|
startAiPack.players.push_back(playerSettings->color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!startAiPack.players.empty())
|
||||||
|
gh->sendAndApply(&startAiPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVCMIServer::reconnectPlayer(int connId)
|
||||||
|
{
|
||||||
|
PlayerReinitInterface startAiPack;
|
||||||
|
startAiPack.playerConnectionId = connId;
|
||||||
|
|
||||||
|
if(gh && si && state == EServerState::GAMEPLAY)
|
||||||
|
{
|
||||||
|
for(auto it = playerNames.begin(); it != playerNames.end(); ++it)
|
||||||
|
{
|
||||||
|
if(it->second.connection != connId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int id = it->first;
|
||||||
|
auto * playerSettings = si->getPlayersSettings(id);
|
||||||
|
if(!playerSettings)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string messageText = boost::str(boost::format("%s (cid %d) is connected") % playerSettings->name % connId);
|
||||||
|
gh->playerMessage(playerSettings->color, messageText, ObjectInstanceID{});
|
||||||
|
|
||||||
|
startAiPack.players.push_back(playerSettings->color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!startAiPack.players.empty())
|
||||||
|
gh->sendAndApply(&startAiPack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,8 @@ public:
|
|||||||
|
|
||||||
boost::program_options::variables_map cmdLineOptions;
|
boost::program_options::variables_map cmdLineOptions;
|
||||||
std::set<std::shared_ptr<CConnection>> connections;
|
std::set<std::shared_ptr<CConnection>> connections;
|
||||||
|
std::set<std::shared_ptr<CConnection>> hangingConnections; //keep connections of players disconnected during the game
|
||||||
|
|
||||||
std::atomic<int> currentClientId;
|
std::atomic<int> currentClientId;
|
||||||
std::atomic<ui8> currentPlayerId;
|
std::atomic<ui8> currentPlayerId;
|
||||||
std::shared_ptr<CConnection> hostClient;
|
std::shared_ptr<CConnection> hostClient;
|
||||||
@ -90,6 +92,7 @@ public:
|
|||||||
|
|
||||||
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, StartInfo::EMode mode);
|
||||||
void clientDisconnected(std::shared_ptr<CConnection> c);
|
void clientDisconnected(std::shared_ptr<CConnection> c);
|
||||||
|
void reconnectPlayer(int connId);
|
||||||
|
|
||||||
void updateAndPropagateLobbyState();
|
void updateAndPropagateLobbyState();
|
||||||
|
|
||||||
|
@ -34,11 +34,58 @@ void CLobbyPackToServer::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
|||||||
|
|
||||||
bool LobbyClientConnected::checkClientPermissions(CVCMIServer * srv) const
|
bool LobbyClientConnected::checkClientPermissions(CVCMIServer * srv) const
|
||||||
{
|
{
|
||||||
return true;
|
if(srv->gh)
|
||||||
|
{
|
||||||
|
for(auto & connection : srv->hangingConnections)
|
||||||
|
{
|
||||||
|
if(connection->uuid == uuid)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(srv->state == EServerState::LOBBY)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//disconnect immediately and ignore this client
|
||||||
|
srv->connections.erase(c);
|
||||||
|
if(c && c->isOpen())
|
||||||
|
{
|
||||||
|
c->close();
|
||||||
|
c->connected = false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LobbyClientConnected::applyOnServer(CVCMIServer * srv)
|
bool LobbyClientConnected::applyOnServer(CVCMIServer * srv)
|
||||||
{
|
{
|
||||||
|
if(srv->gh)
|
||||||
|
{
|
||||||
|
for(auto & connection : srv->hangingConnections)
|
||||||
|
{
|
||||||
|
if(connection->uuid == uuid)
|
||||||
|
{
|
||||||
|
logNetwork->info("Reconnection player");
|
||||||
|
c->connectionID = connection->connectionID;
|
||||||
|
for(auto & playerConnection : srv->gh->connections)
|
||||||
|
{
|
||||||
|
for(auto & existingConnection : playerConnection.second)
|
||||||
|
{
|
||||||
|
if(existingConnection == connection)
|
||||||
|
{
|
||||||
|
playerConnection.second.erase(existingConnection);
|
||||||
|
playerConnection.second.insert(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srv->hangingConnections.erase(connection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
srv->clientConnected(c, names, uuid, mode);
|
srv->clientConnected(c, names, uuid, mode);
|
||||||
// Server need to pass some data to newly connected client
|
// Server need to pass some data to newly connected client
|
||||||
clientId = c->connectionID;
|
clientId = c->connectionID;
|
||||||
@ -53,6 +100,15 @@ void LobbyClientConnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
|||||||
// Until UUID set we only pass LobbyClientConnected to this client
|
// Until UUID set we only pass LobbyClientConnected to this client
|
||||||
c->uuid = uuid;
|
c->uuid = uuid;
|
||||||
srv->updateAndPropagateLobbyState();
|
srv->updateAndPropagateLobbyState();
|
||||||
|
if(srv->state == EServerState::GAMEPLAY)
|
||||||
|
{
|
||||||
|
//immediately start game
|
||||||
|
std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame);
|
||||||
|
startGameForReconnectedPlayer->initializedStartInfo = srv->si;
|
||||||
|
startGameForReconnectedPlayer->initializedGameState = srv->gh->gameState();
|
||||||
|
startGameForReconnectedPlayer->clientId = c->connectionID;
|
||||||
|
srv->addToAnnounceQueue(std::move(startGameForReconnectedPlayer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
|
bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
|
||||||
@ -82,7 +138,7 @@ bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
|
|||||||
|
|
||||||
void LobbyClientDisconnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
void LobbyClientDisconnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||||
{
|
{
|
||||||
if(c->isOpen())
|
if(c && c->isOpen())
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> lock(*c->mutexWrite);
|
boost::unique_lock<boost::mutex> lock(*c->mutexWrite);
|
||||||
c->close();
|
c->close();
|
||||||
@ -209,7 +265,19 @@ bool LobbyStartGame::applyOnServer(CVCMIServer * srv)
|
|||||||
|
|
||||||
void LobbyStartGame::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
void LobbyStartGame::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||||
{
|
{
|
||||||
srv->startGameImmidiately();
|
if(clientId == -1) //do not restart game for single client only
|
||||||
|
srv->startGameImmidiately();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(auto & c : srv->connections)
|
||||||
|
{
|
||||||
|
if(c->connectionID == clientId)
|
||||||
|
{
|
||||||
|
c->enterGameplayConnectionMode(srv->gh->gameState());
|
||||||
|
srv->reconnectPlayer(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LobbyChangeHost::checkClientPermissions(CVCMIServer * srv) const
|
bool LobbyChangeHost::checkClientPermissions(CVCMIServer * srv) const
|
||||||
|
@ -61,7 +61,7 @@ void CPackForServer::throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id)
|
|||||||
|
|
||||||
void CPackForServer::throwOnWrongPlayer(CGameHandler * gh, PlayerColor player)
|
void CPackForServer::throwOnWrongPlayer(CGameHandler * gh, PlayerColor player)
|
||||||
{
|
{
|
||||||
if(player != gh->getPlayerAt(c))
|
if(!gh->hasPlayerAt(player, c) && player != gh->getPlayerAt(c))
|
||||||
{
|
{
|
||||||
wrongPlayerMessage(gh, player);
|
wrongPlayerMessage(gh, player);
|
||||||
throwNotAllowedAction();
|
throwNotAllowedAction();
|
||||||
@ -381,11 +381,8 @@ bool CastAdvSpell::applyGh(CGameHandler * gh)
|
|||||||
bool PlayerMessage::applyGh(CGameHandler * gh)
|
bool PlayerMessage::applyGh(CGameHandler * gh)
|
||||||
{
|
{
|
||||||
if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
|
if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
|
||||||
{
|
|
||||||
throwOnWrongPlayer(gh, player);
|
throwOnWrongPlayer(gh, player);
|
||||||
if(gh->getPlayerAt(this->c) != player)
|
|
||||||
throwNotAllowedAction();
|
|
||||||
}
|
|
||||||
gh->playerMessage(player, text, currObj);
|
gh->playerMessage(player, text, currObj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user