|
@@ -41,9 +41,7 @@
|
|
|
#include "CMT.h"
|
|
|
|
|
|
extern std::string NAME;
|
|
|
-#ifndef VCMI_ANDROID
|
|
|
-namespace intpr = boost::interprocess;
|
|
|
-#else
|
|
|
+#ifdef VCMI_ANDROID
|
|
|
#include "lib/CAndroidVMHelper.h"
|
|
|
#endif
|
|
|
|
|
@@ -251,24 +249,16 @@ void CClient::endGame(bool closeConnection /*= true*/)
|
|
|
}
|
|
|
|
|
|
#if 1
|
|
|
-void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const std::string & port)
|
|
|
+void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const ui16 port)
|
|
|
{
|
|
|
PlayerColor player(player_); //intentional shadowing
|
|
|
logNetwork->infoStream() << "Loading procedure started!";
|
|
|
|
|
|
- std::string realPort;
|
|
|
- if(settings["testing"]["enabled"].Bool())
|
|
|
- realPort = settings["testing"]["port"].String();
|
|
|
- else if(port.size())
|
|
|
- realPort = port;
|
|
|
- else
|
|
|
- realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
|
|
-
|
|
|
CServerHandler sh;
|
|
|
if(server)
|
|
|
sh.startServer();
|
|
|
else
|
|
|
- serv = sh.justConnectToServer(ipaddr, realPort);
|
|
|
+ serv = sh.justConnectToServer(ipaddr, port);
|
|
|
|
|
|
CStopWatch tmh;
|
|
|
std::unique_ptr<CLoadFile> loader;
|
|
@@ -391,7 +381,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|
|
else
|
|
|
{
|
|
|
serv = con;
|
|
|
- networkMode = (con->connectionID == 1) ? HOST : GUEST;
|
|
|
+ networkMode = con->isHost() ? HOST : GUEST;
|
|
|
}
|
|
|
|
|
|
CConnection &c = *serv;
|
|
@@ -416,10 +406,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|
|
|
|
|
// Initialize game state
|
|
|
gs = new CGameState();
|
|
|
- logNetwork->infoStream() <<"\tCreating gamestate: "<<tmh.getDiff();
|
|
|
+ logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
|
|
|
|
|
|
- gs->scenarioOps = si;
|
|
|
- gs->init(si);
|
|
|
+ gs->init(si, settings["general"]["saveRandomMaps"].Bool());
|
|
|
logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
|
|
|
|
|
|
// Now after possible random map gen, we know exact player count.
|
|
@@ -441,10 +430,13 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|
|
// Init map handler
|
|
|
if(gs->map)
|
|
|
{
|
|
|
- const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
|
|
- CGI->mh->map = gs->map;
|
|
|
- logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff();
|
|
|
- CGI->mh->init();
|
|
|
+ if(!settings["session"]["headless"].Bool())
|
|
|
+ {
|
|
|
+ const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
|
|
+ CGI->mh->map = gs->map;
|
|
|
+ logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff();
|
|
|
+ CGI->mh->init();
|
|
|
+ }
|
|
|
pathInfo = make_unique<CPathsInfo>(getMapSize());
|
|
|
logNetwork->infoStream() << "Initializing mapHandler (together): " << tmh.getDiff();
|
|
|
}
|
|
@@ -481,7 +473,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|
|
|
|
|
if(si->mode == StartInfo::DUEL)
|
|
|
{
|
|
|
- if(!gNoGUI)
|
|
|
+ if(!settings["session"]["headless"].Bool())
|
|
|
{
|
|
|
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
|
|
auto p = std::make_shared<CPlayerInterface>(PlayerColor::NEUTRAL);
|
|
@@ -493,6 +485,10 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ if(settings["session"]["spectate"].Bool())
|
|
|
+ {
|
|
|
+ installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
|
|
|
+ }
|
|
|
loadNeutralBattleAI();
|
|
|
}
|
|
|
|
|
@@ -644,10 +640,33 @@ void CClient::serialize(BinaryDeserializer & h, const int version, const std::se
|
|
|
nInt->human = isHuman;
|
|
|
nInt->playerID = pid;
|
|
|
|
|
|
- if(playerIDs.count(pid))
|
|
|
- installNewPlayerInterface(nInt, pid);
|
|
|
-
|
|
|
nInt->loadGame(h, version);
|
|
|
+ if(settings["session"]["onlyai"].Bool() && isHuman)
|
|
|
+ {
|
|
|
+ removeGUI();
|
|
|
+ nInt.reset();
|
|
|
+ dllname = aiNameForPlayer(false);
|
|
|
+ nInt = CDynLibHandler::getNewAI(dllname);
|
|
|
+ nInt->dllName = dllname;
|
|
|
+ nInt->human = false;
|
|
|
+ nInt->playerID = pid;
|
|
|
+ installNewPlayerInterface(nInt, pid);
|
|
|
+ GH.totalRedraw();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(playerIDs.count(pid))
|
|
|
+ installNewPlayerInterface(nInt, pid);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(settings["session"]["spectate"].Bool())
|
|
|
+ {
|
|
|
+ removeGUI();
|
|
|
+ auto p = std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR);
|
|
|
+ installNewPlayerInterface(p, PlayerColor::SPECTATOR, true);
|
|
|
+ GH.curInt = p.get();
|
|
|
+ LOCPLINT->activateForSpectator();
|
|
|
+ GH.totalRedraw();
|
|
|
}
|
|
|
|
|
|
if(playerIDs.count(PlayerColor::NEUTRAL))
|
|
@@ -693,13 +712,22 @@ void CClient::stopConnection()
|
|
|
{
|
|
|
terminate = true;
|
|
|
|
|
|
- if (serv) //request closing connection
|
|
|
+ if(serv)
|
|
|
{
|
|
|
- logNetwork->infoStream() << "Connection has been requested to be closed.";
|
|
|
boost::unique_lock<boost::mutex>(*serv->wmx);
|
|
|
- CloseServer close_server;
|
|
|
- sendRequest(&close_server, PlayerColor::NEUTRAL);
|
|
|
- logNetwork->infoStream() << "Sent closing signal to the server";
|
|
|
+ if(serv->isHost()) //request closing connection
|
|
|
+ {
|
|
|
+ logNetwork->infoStream() << "Connection has been requested to be closed.";
|
|
|
+ CloseServer close_server;
|
|
|
+ sendRequest(&close_server, PlayerColor::NEUTRAL);
|
|
|
+ logNetwork->infoStream() << "Sent closing signal to the server";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ LeaveGame leave_Game;
|
|
|
+ sendRequest(&leave_Game, PlayerColor::NEUTRAL);
|
|
|
+ logNetwork->infoStream() << "Sent leaving signal to the server";
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if(connectionHandler)//end connection handler
|
|
@@ -708,16 +736,13 @@ void CClient::stopConnection()
|
|
|
connectionHandler->join();
|
|
|
|
|
|
logNetwork->infoStream() << "Connection handler thread joined";
|
|
|
-
|
|
|
- delete connectionHandler;
|
|
|
- connectionHandler = nullptr;
|
|
|
+ vstd::clear_pointer(connectionHandler);
|
|
|
}
|
|
|
|
|
|
if (serv) //and delete connection
|
|
|
{
|
|
|
serv->close();
|
|
|
- delete serv;
|
|
|
- serv = nullptr;
|
|
|
+ vstd::clear_pointer(serv);
|
|
|
logNetwork->warnStream() << "Our socket has been closed.";
|
|
|
}
|
|
|
}
|
|
@@ -750,14 +775,29 @@ void CClient::battleStarted(const BattleInfo * info)
|
|
|
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
|
|
|
}
|
|
|
|
|
|
- if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
|
|
|
+ if(!settings["session"]["headless"].Bool())
|
|
|
{
|
|
|
- boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
|
|
- auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
|
|
- Rect((screen->w - 800)/2,
|
|
|
- (screen->h - 600)/2, 800, 600), att, def);
|
|
|
+ if(!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)
|
|
|
+ {
|
|
|
+ boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
|
|
+ auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
|
|
+ Rect((screen->w - 800)/2,
|
|
|
+ (screen->h - 600)/2, 800, 600), att, def);
|
|
|
|
|
|
- GH.pushInt(bi);
|
|
|
+ GH.pushInt(bi);
|
|
|
+ }
|
|
|
+ else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
|
|
+ {
|
|
|
+ //TODO: This certainly need improvement
|
|
|
+ auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
|
|
|
+ spectratorInt->cb->setBattle(info);
|
|
|
+ boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
|
|
+ auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
|
|
+ Rect((screen->w - 800)/2,
|
|
|
+ (screen->h - 600)/2, 800, 600), att, def, spectratorInt);
|
|
|
+
|
|
|
+ GH.pushInt(bi);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
auto callBattleStart = [&](PlayerColor color, ui8 side){
|
|
@@ -768,6 +808,8 @@ void CClient::battleStarted(const BattleInfo * info)
|
|
|
callBattleStart(leftSide.color, 0);
|
|
|
callBattleStart(rightSide.color, 1);
|
|
|
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
|
|
+ if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
|
|
+ callBattleStart(PlayerColor::SPECTATOR, 1);
|
|
|
|
|
|
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
|
|
|
{
|
|
@@ -780,6 +822,9 @@ void CClient::battleFinished()
|
|
|
for(auto & side : gs->curB->sides)
|
|
|
if(battleCallbacks.count(side.color))
|
|
|
battleCallbacks[side.color]->setBattle(nullptr);
|
|
|
+
|
|
|
+ if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
|
|
+ battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
|
|
|
}
|
|
|
|
|
|
void CClient::loadNeutralBattleAI()
|
|
@@ -877,7 +922,7 @@ void CClient::campaignMapFinished( std::shared_ptr<CCampaignState> camp )
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color)
|
|
|
+void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb)
|
|
|
{
|
|
|
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
|
|
PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
|
|
@@ -893,7 +938,7 @@ void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInte
|
|
|
battleCallbacks[colorUsed] = cb;
|
|
|
gameInterface->init(cb);
|
|
|
|
|
|
- installNewBattleInterface(gameInterface, color, false);
|
|
|
+ installNewBattleInterface(gameInterface, color, battlecb);
|
|
|
}
|
|
|
|
|
|
void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
|
|
@@ -924,6 +969,11 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
|
|
|
return ps.name;
|
|
|
}
|
|
|
|
|
|
+ return aiNameForPlayer(battleAI);
|
|
|
+}
|
|
|
+
|
|
|
+std::string CClient::aiNameForPlayer(bool battleAI)
|
|
|
+{
|
|
|
const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
|
|
|
std::string goodAI = battleAI ? settings["server"]["neutralAI"].String() : settings["server"]["playerAI"].String();
|
|
|
std::string badAI = battleAI ? "StupidAI" : "EmptyAI";
|
|
@@ -966,11 +1016,8 @@ void CServerHandler::waitForServer()
|
|
|
th.update();
|
|
|
|
|
|
#ifndef VCMI_ANDROID
|
|
|
- intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
|
|
|
- while(!shared->sr->ready)
|
|
|
- {
|
|
|
- shared->sr->cond.wait(slock);
|
|
|
- }
|
|
|
+ if(shared)
|
|
|
+ shared->sr->waitTillReady();
|
|
|
#else
|
|
|
logNetwork->infoStream() << "waiting for server";
|
|
|
while (!androidTestServerReadyFlag.load())
|
|
@@ -987,16 +1034,15 @@ void CServerHandler::waitForServer()
|
|
|
|
|
|
CConnection * CServerHandler::connectToServer()
|
|
|
{
|
|
|
-#ifndef VCMI_ANDROID
|
|
|
- if(!shared->sr->ready)
|
|
|
- waitForServer();
|
|
|
-#else
|
|
|
waitForServer();
|
|
|
-#endif
|
|
|
|
|
|
th.update(); //put breakpoint here to attach to server before it does something stupid
|
|
|
|
|
|
- CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
|
|
|
+#ifndef VCMI_ANDROID
|
|
|
+ CConnection *ret = justConnectToServer(settings["server"]["server"].String(), shared ? shared->sr->port : 0);
|
|
|
+#else
|
|
|
+ CConnection *ret = justConnectToServer(settings["server"]["server"].String());
|
|
|
+#endif
|
|
|
|
|
|
if(verbose)
|
|
|
logNetwork->infoStream()<<"\tConnecting to the server: "<<th.getDiff();
|
|
@@ -1004,24 +1050,43 @@ CConnection * CServerHandler::connectToServer()
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ui16 CServerHandler::getDefaultPort()
|
|
|
+{
|
|
|
+ if(settings["session"]["serverport"].Integer())
|
|
|
+ return settings["session"]["serverport"].Integer();
|
|
|
+ else
|
|
|
+ return settings["server"]["port"].Integer();
|
|
|
+}
|
|
|
+
|
|
|
+std::string CServerHandler::getDefaultPortStr()
|
|
|
+{
|
|
|
+ return boost::lexical_cast<std::string>(getDefaultPort());
|
|
|
+}
|
|
|
+
|
|
|
CServerHandler::CServerHandler(bool runServer /*= false*/)
|
|
|
{
|
|
|
serverThread = nullptr;
|
|
|
shared = nullptr;
|
|
|
- if(settings["testing"]["enabled"].Bool())
|
|
|
- port = settings["testing"]["port"].String();
|
|
|
- else
|
|
|
- port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
|
|
verbose = true;
|
|
|
+ uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
|
|
|
|
|
#ifndef VCMI_ANDROID
|
|
|
- boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
|
|
|
+ if(DO_NOT_START_SERVER || settings["session"]["disable-shm"].Bool())
|
|
|
+ return;
|
|
|
+
|
|
|
+ std::string sharedMemoryName = "vcmi_memory";
|
|
|
+ if(settings["session"]["enable-shm-uuid"].Bool())
|
|
|
+ {
|
|
|
+ //used or automated testing when multiple clients start simultaneously
|
|
|
+ sharedMemoryName += "_" + uuid;
|
|
|
+ }
|
|
|
try
|
|
|
{
|
|
|
- shared = new SharedMem();
|
|
|
+ shared = new SharedMemory(sharedMemoryName, true);
|
|
|
}
|
|
|
catch(...)
|
|
|
{
|
|
|
+ vstd::clear_pointer(shared);
|
|
|
logNetwork->error("Cannot open interprocess memory.");
|
|
|
handleException();
|
|
|
throw;
|
|
@@ -1040,7 +1105,18 @@ void CServerHandler::callServer()
|
|
|
#ifndef VCMI_ANDROID
|
|
|
setThreadName("CServerHandler::callServer");
|
|
|
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
|
|
|
- const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
|
|
|
+ std::string comm = VCMIDirs::get().serverPath().string()
|
|
|
+ + " --port=" + getDefaultPortStr()
|
|
|
+ + " --run-by-client"
|
|
|
+ + " --uuid=" + uuid;
|
|
|
+ if(shared)
|
|
|
+ {
|
|
|
+ comm += " --enable-shm";
|
|
|
+ if(settings["session"]["enable-shm-uuid"].Bool())
|
|
|
+ comm += " --enable-shm-uuid";
|
|
|
+ }
|
|
|
+ comm += " > \"" + logName + '\"';
|
|
|
+
|
|
|
int result = std::system(comm.c_str());
|
|
|
if (result == 0)
|
|
|
{
|
|
@@ -1056,16 +1132,8 @@ void CServerHandler::callServer()
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
-CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port)
|
|
|
+CConnection * CServerHandler::justConnectToServer(const std::string &host, const ui16 port)
|
|
|
{
|
|
|
- std::string realPort;
|
|
|
- if(settings["testing"]["enabled"].Bool())
|
|
|
- realPort = settings["testing"]["port"].String();
|
|
|
- else if(port.size())
|
|
|
- realPort = port;
|
|
|
- else
|
|
|
- realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
|
|
-
|
|
|
CConnection *ret = nullptr;
|
|
|
while(!ret)
|
|
|
{
|
|
@@ -1073,8 +1141,9 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const
|
|
|
{
|
|
|
logNetwork->infoStream() << "Establishing connection...";
|
|
|
ret = new CConnection( host.size() ? host : settings["server"]["server"].String(),
|
|
|
- realPort,
|
|
|
+ port ? port : getDefaultPort(),
|
|
|
NAME);
|
|
|
+ ret->connectionID = 1; // TODO: Refactoring for the server so IDs set outside of CConnection
|
|
|
}
|
|
|
catch(...)
|
|
|
{
|