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