mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implemented option to run server as a thread with shared VLC
This commit is contained in:
		| @@ -63,8 +63,9 @@ option(ENABLE_CCACHE "Speed up recompilation by caching previous compilations" O | ||||
|  | ||||
| if(ANDROID) | ||||
| 	set(ENABLE_STATIC_AI_LIBS ON) | ||||
| 	set(ENABLE_LAUNCHER OFF) | ||||
| else() | ||||
| 	option(ENABLE_STATIC_AI_LIBS "Add AI code into VCMI lib directly" ON) | ||||
| 	option(ENABLE_STATIC_AI_LIBS "Build all libraries statically (NOT only AI)" OFF) | ||||
| 	option(ENABLE_LAUNCHER "Enable compilation of launcher" ON) | ||||
| endif() | ||||
|  | ||||
| @@ -75,15 +76,17 @@ endif() | ||||
|  | ||||
| if(APPLE_IOS OR ANDROID) | ||||
| 	option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linux and Mac" OFF) # Used for Snap packages and also useful for debugging | ||||
| 	option(ENABLE_LOBBY "Enable compilation of lobby server" OFF) | ||||
| 	set(ENABLE_SINGLE_APP_BUILD ON) | ||||
| 	set(ENABLE_EDITOR OFF) | ||||
| 	set(ENABLE_TEST OFF) | ||||
| 	set(ENABLE_LOBBY OFF) | ||||
| 	set(COPY_CONFIG_ON_BUILD OFF) | ||||
| else() | ||||
| 	option(COPY_CONFIG_ON_BUILD "Copies config folder into output directory at building phase" ON) | ||||
| 	option(ENABLE_EDITOR "Enable compilation of map editor" ON) | ||||
| 	option(ENABLE_SINGLE_APP_BUILD "Builds client and launcher as single executable" OFF) | ||||
| 	option(ENABLE_TEST "Enable compilation of unit tests" OFF) | ||||
| 	option(ENABLE_LOBBY "Enable compilation of lobby server" OFF) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_COLORIZED_COMPILER_OUTPUT) | ||||
| @@ -240,7 +243,7 @@ if(ENABLE_EDITOR) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_SINGLE_APP_BUILD) | ||||
| 	add_definitions(-DSINGLE_PROCESS_APP=1) | ||||
| 	add_definitions(-DENABLE_SINGLE_APP_BUILD) | ||||
| endif() | ||||
|  | ||||
| if(APPLE_IOS) | ||||
|   | ||||
| @@ -164,6 +164,7 @@ set(client_SRCS | ||||
| 	HeroMovementController.cpp | ||||
| 	NetPacksClient.cpp | ||||
| 	NetPacksLobbyClient.cpp | ||||
| 	ServerRunner.cpp | ||||
| ) | ||||
|  | ||||
| set(client_HEADERS | ||||
| @@ -346,6 +347,7 @@ set(client_HEADERS | ||||
| 	ClientNetPackVisitors.h | ||||
| 	HeroMovementController.h | ||||
| 	LobbyClientNetPackVisitors.h | ||||
| 	ServerRunner.h | ||||
| 	resource.h | ||||
| ) | ||||
|  | ||||
| @@ -451,12 +453,12 @@ elseif(APPLE_IOS) | ||||
| 	set(CMAKE_EXE_LINKER_FLAGS "-Wl,-e,_client_main") | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_SINGLE_APP_BUILD) | ||||
| 	target_link_libraries(vcmiclient PRIVATE vcmiserver) | ||||
| 	if(ENABLE_LAUNCHER) | ||||
| 		target_link_libraries(vcmiclient PRIVATE vcmilauncher) | ||||
| 	endif() | ||||
| target_link_libraries(vcmiclient PRIVATE vcmiservercommon) | ||||
|  | ||||
| if(ENABLE_SINGLE_APP_BUILD AND ENABLE_LAUNCHER) | ||||
| 	target_link_libraries(vcmiclient PRIVATE vcmilauncher) | ||||
| endif() | ||||
|  | ||||
| target_link_libraries(vcmiclient PRIVATE | ||||
| 		${VCMI_LIB_TARGET} SDL2::SDL2 SDL2::Image SDL2::Mixer SDL2::TTF | ||||
| ) | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include "CServerHandler.h" | ||||
| #include "Client.h" | ||||
| #include "CGameInfo.h" | ||||
| #include "ServerRunner.h" | ||||
| #include "CPlayerInterface.h" | ||||
| #include "gui/CGuiHandler.h" | ||||
| #include "gui/WindowHandler.h" | ||||
| @@ -25,17 +26,6 @@ | ||||
| #include "mainmenu/CPrologEpilogVideo.h" | ||||
| #include "mainmenu/CHighScoreScreen.h" | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #include "../lib/CAndroidVMHelper.h" | ||||
| #elif defined(VCMI_IOS) | ||||
| #include "ios/utils.h" | ||||
| #include <dispatch/dispatch.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef SINGLE_PROCESS_APP | ||||
| #include "../server/CVCMIServer.h" | ||||
| #endif | ||||
|  | ||||
| #include "../lib/CConfigHandler.h" | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| #include "../lib/CThreadHelper.h" | ||||
| @@ -61,16 +51,8 @@ | ||||
|  | ||||
| #include <vcmi/events/EventBus.h> | ||||
|  | ||||
| #ifdef VCMI_WINDOWS | ||||
| #include <windows.h> | ||||
| #endif | ||||
|  | ||||
| template<typename T> class CApplyOnLobby; | ||||
|  | ||||
| #if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP) | ||||
| extern std::atomic_bool androidTestServerReadyFlag; | ||||
| #endif | ||||
|  | ||||
| class CBaseForLobbyApply | ||||
| { | ||||
| public: | ||||
| @@ -195,67 +177,17 @@ INetworkHandler & CServerHandler::getNetworkHandler() | ||||
|  | ||||
| void CServerHandler::startLocalServerAndConnect(bool connectToLobby) | ||||
| { | ||||
| 	if(threadRunLocalServer.joinable()) | ||||
| 		threadRunLocalServer.join(); | ||||
|  | ||||
| 	th->update(); | ||||
|  | ||||
| #if defined(SINGLE_PROCESS_APP) | ||||
| 	boost::condition_variable cond; | ||||
| 	std::vector<std::string> args{"--port=" + std::to_string(getLocalPort())}; | ||||
| 	if(connectToLobby) | ||||
| 		args.push_back("--lobby"); | ||||
|  | ||||
| 	threadRunLocalServer = boost::thread([&cond, args] { | ||||
| 		setThreadName("CVCMIServer"); | ||||
| 		CVCMIServer::create(&cond, args); | ||||
| 	}); | ||||
| #elif defined(VCMI_ANDROID) | ||||
| 	{ | ||||
| 		CAndroidVMHelper envHelper; | ||||
| 		envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true); | ||||
| 	} | ||||
| #ifdef VCMI_MOBILE | ||||
| 	// mobile apps can't spawn separate processes - only thread mode is available | ||||
| 	serverRunner.reset(new ServerThreadRunner()); | ||||
| #else | ||||
| 	threadRunLocalServer = boost::thread(&CServerHandler::threadRunServer, this, connectToLobby); //runs server executable; | ||||
| #endif | ||||
| 	logNetwork->trace("Setting up thread calling server: %d ms", th->getDiff()); | ||||
|  | ||||
| 	th->update(); | ||||
|  | ||||
| #ifdef SINGLE_PROCESS_APP | ||||
| 	{ | ||||
| #ifdef VCMI_IOS | ||||
| 		dispatch_sync(dispatch_get_main_queue(), ^{ | ||||
| 			iOS_utils::showLoadingIndicator(); | ||||
| 		}); | ||||
| 	if (settings["server"]["useProcess"].Bool()) | ||||
| 		serverRunner.reset(new ServerProcessRunner()); | ||||
| 	else | ||||
| 		serverRunner.reset(new ServerThreadRunner()); | ||||
| #endif | ||||
|  | ||||
| 		boost::mutex m; | ||||
| 		boost::unique_lock<boost::mutex> lock{m}; | ||||
| 		logNetwork->info("waiting for server"); | ||||
| 		cond.wait(lock); | ||||
| 		logNetwork->info("server is ready"); | ||||
|  | ||||
| #ifdef VCMI_IOS | ||||
| 		dispatch_sync(dispatch_get_main_queue(), ^{ | ||||
| 			iOS_utils::hideLoadingIndicator(); | ||||
| 		}); | ||||
| #endif | ||||
| 	} | ||||
| #elif defined(VCMI_ANDROID) | ||||
| 	logNetwork->info("waiting for server"); | ||||
| 	while(!androidTestServerReadyFlag.load()) | ||||
| 	{ | ||||
| 		logNetwork->info("still waiting..."); | ||||
| 		boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); | ||||
| 	} | ||||
| 	logNetwork->info("waiting for server finished..."); | ||||
| 	androidTestServerReadyFlag = false; | ||||
| #endif | ||||
| 	logNetwork->trace("Waiting for server: %d ms", th->getDiff()); | ||||
|  | ||||
| 	th->update(); //put breakpoint here to attach to server before it does something stupid | ||||
|  | ||||
| 	serverRunner->start(getLocalPort(), connectToLobby); | ||||
| 	connectToServer(getLocalHostname(), getLocalPort()); | ||||
|  | ||||
| 	logNetwork->trace("\tConnecting to the server: %d ms", th->getDiff()); | ||||
| @@ -374,7 +306,7 @@ void CServerHandler::setState(EClientState newState) | ||||
|  | ||||
| bool CServerHandler::isServerLocal() const | ||||
| { | ||||
| 	return threadRunLocalServer.joinable(); | ||||
| 	return serverRunner != nullptr; | ||||
| } | ||||
|  | ||||
| bool CServerHandler::isHost() const | ||||
| @@ -758,7 +690,6 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 		threadRunLocalServer.join(); | ||||
| 		if(epilogue.hasPrologEpilog) | ||||
| 		{ | ||||
| 			GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher); | ||||
| @@ -899,6 +830,12 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection> | ||||
|  | ||||
| void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage) | ||||
| { | ||||
| 	if (serverRunner) | ||||
| 	{ | ||||
| 		serverRunner->wait(); | ||||
| 		serverRunner.reset(); | ||||
| 	} | ||||
|  | ||||
| 	if(getState() == EClientState::DISCONNECTING) | ||||
| 	{ | ||||
| 		assert(networkConnection == nullptr); | ||||
| @@ -942,55 +879,6 @@ void CServerHandler::visitForClient(CPackForClient & clientPack) | ||||
| 	client->handlePack(&clientPack); | ||||
| } | ||||
|  | ||||
| void CServerHandler::threadRunServer(bool connectToLobby) | ||||
| { | ||||
| #if !defined(VCMI_MOBILE) | ||||
| 	setThreadName("runServer"); | ||||
| 	const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string(); | ||||
| 	std::string comm = VCMIDirs::get().serverPath().string() | ||||
| 		+ " --port=" + std::to_string(getLocalPort()) | ||||
| 		+ " --run-by-client"; | ||||
| 	if(connectToLobby) | ||||
| 		comm += " --lobby"; | ||||
|  | ||||
| 	comm += " > \"" + logName + '\"'; | ||||
| 	logGlobal->info("Server command line: %s", comm); | ||||
|  | ||||
| #ifdef VCMI_WINDOWS | ||||
| 	int result = -1; | ||||
| 	const auto bufSize = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), nullptr, 0); | ||||
| 	if(bufSize > 0) | ||||
| 	{ | ||||
| 		std::wstring wComm(bufSize, {}); | ||||
| 		const auto convertResult = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), &wComm[0], bufSize); | ||||
| 		if(convertResult > 0) | ||||
| 			result = ::_wsystem(wComm.c_str()); | ||||
| 		else | ||||
| 			logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to convert server launch command to wide string: " + comm); | ||||
| 	} | ||||
| 	else | ||||
| 		logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to obtain buffer length to convert server launch command to wide string : " + comm); | ||||
| #else | ||||
| 	int result = std::system(comm.c_str()); | ||||
| #endif | ||||
| 	if (result == 0) | ||||
| 	{ | ||||
| 		logNetwork->info("Server closed correctly"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); | ||||
|  | ||||
| 		if (getState() == EClientState::CONNECTING) | ||||
| 		{ | ||||
| 			showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess")); | ||||
| 			setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect | ||||
| 		} | ||||
| 		logNetwork->error("Error: server failed to close correctly or crashed!"); | ||||
| 		logNetwork->error("Check %s for more info", logName); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const | ||||
| { | ||||
|   | ||||
| @@ -36,6 +36,7 @@ VCMI_LIB_NAMESPACE_END | ||||
| class CClient; | ||||
| class CBaseForLobbyApply; | ||||
| class GlobalLobbyClient; | ||||
| class IServerRunner; | ||||
|  | ||||
| class HighScoreCalculation; | ||||
| class HighScoreParameter; | ||||
| @@ -100,17 +101,16 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor | ||||
| 	std::shared_ptr<INetworkConnection> networkConnection; | ||||
| 	std::unique_ptr<GlobalLobbyClient> lobbyClient; | ||||
| 	std::unique_ptr<CApplier<CBaseForLobbyApply>> applier; | ||||
| 	std::unique_ptr<IServerRunner> serverRunner; | ||||
| 	std::shared_ptr<CMapInfo> mapToStart; | ||||
| 	std::vector<std::string> myNames; | ||||
| 	std::shared_ptr<HighScoreCalculation> highScoreCalc; | ||||
|  | ||||
| 	boost::thread threadRunLocalServer; | ||||
| 	boost::thread threadNetwork; | ||||
|  | ||||
| 	std::atomic<EClientState> state; | ||||
|  | ||||
| 	void threadRunNetwork(); | ||||
| 	void threadRunServer(bool connectToLobby); | ||||
|  | ||||
| 	void sendLobbyPack(const CPackForLobby & pack) const override; | ||||
|  | ||||
|   | ||||
| @@ -46,10 +46,6 @@ | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #include "lib/CAndroidVMHelper.h" | ||||
|  | ||||
| #ifndef SINGLE_PROCESS_APP | ||||
| std::atomic_bool androidTestServerReadyFlag; | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| ThreadSafeVector<int> CClient::waitingRequest; | ||||
| @@ -718,22 +714,6 @@ void CClient::removeGUI() const | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #ifndef SINGLE_PROCESS_APP | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jclass cls) | ||||
| { | ||||
| 	logNetwork->info("Received server closed signal"); | ||||
| 	if (CSH) { | ||||
| 		CSH->campaignServerRestartLock.setn(false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jclass cls) | ||||
| { | ||||
| 	logNetwork->info("Received server ready signal"); | ||||
| 	androidTestServerReadyFlag.store(true); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| extern "C" JNIEXPORT jboolean JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jclass cls) | ||||
| { | ||||
| 	logGlobal->info("Received emergency save game request"); | ||||
|   | ||||
							
								
								
									
										96
									
								
								client/ServerRunner.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								client/ServerRunner.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /* | ||||
|  * ServerRunner.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "ServerRunner.h" | ||||
|  | ||||
| #include "../lib/VCMIDirs.h" | ||||
| #include "../lib/CThreadHelper.h" | ||||
| #include "../server/CVCMIServer.h" | ||||
|  | ||||
| #ifndef VCMI_MOBILE | ||||
| #include <boost/process/child.hpp> | ||||
| #include <boost/process/io.hpp> | ||||
| #endif | ||||
|  | ||||
| ServerThreadRunner::ServerThreadRunner() = default; | ||||
| ServerThreadRunner::~ServerThreadRunner() = default; | ||||
| ServerProcessRunner::ServerProcessRunner() = default; | ||||
| ServerProcessRunner::~ServerProcessRunner() = default; | ||||
|  | ||||
| void ServerThreadRunner::start(uint16_t port, bool connectToLobby) | ||||
| { | ||||
| 	setThreadName("runServer"); | ||||
|  | ||||
| 	server = std::make_unique<CVCMIServer>(port, connectToLobby, true); | ||||
|  | ||||
| 	threadRunLocalServer = boost::thread([this]{ | ||||
| 		server->run(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void ServerThreadRunner::stop() | ||||
| { | ||||
| 	server->setState(EServerState::SHUTDOWN); | ||||
| } | ||||
|  | ||||
| int ServerThreadRunner::wait() | ||||
| { | ||||
| 	threadRunLocalServer.join(); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void ServerProcessRunner::stop() | ||||
| { | ||||
| 	child->terminate(); | ||||
| } | ||||
|  | ||||
| int ServerProcessRunner::wait() | ||||
| { | ||||
| 	child->wait(); | ||||
|  | ||||
| 	return child->exit_code(); | ||||
|  | ||||
| //	if (child->exit_code() == 0) | ||||
| //	{ | ||||
| //		logNetwork->info("Server closed correctly"); | ||||
| //	} | ||||
| //	else | ||||
| //	{ | ||||
| //		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); | ||||
| // | ||||
| //		if (getState() == EClientState::CONNECTING) | ||||
| //		{ | ||||
| //			showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess")); | ||||
| //			setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect | ||||
| //		} | ||||
| //		logNetwork->error("Error: server failed to close correctly or crashed!"); | ||||
| //		logNetwork->error("Check %s for more info", logName); | ||||
| //	} | ||||
| } | ||||
|  | ||||
| void ServerProcessRunner::start(uint16_t port, bool connectToLobby) | ||||
| { | ||||
| 	boost::filesystem::path serverPath = VCMIDirs::get().serverPath(); | ||||
| 	boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "server_log.txt"; | ||||
| 	std::vector<std::string> args; | ||||
| 	args.push_back("--port=" + std::to_string(port)); | ||||
| 	args.push_back("--run-by-client"); | ||||
| 	if(connectToLobby) | ||||
| 		args.push_back("--lobby"); | ||||
|  | ||||
| 	std::error_code ec; | ||||
| 	child = std::make_unique<boost::process::child>(serverPath, args, ec, boost::process::std_out > logPath); | ||||
|  | ||||
| 	if (ec) | ||||
| 		throw std::runtime_error("Failed to start server! Reason: " + ec.message()); | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										57
									
								
								client/ServerRunner.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								client/ServerRunner.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * ServerRunner.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| class CVCMIServer; | ||||
|  | ||||
| class IServerRunner | ||||
| { | ||||
| public: | ||||
| 	virtual void start(uint16_t port, bool connectToLobby) = 0; | ||||
| 	virtual void stop() = 0; | ||||
| 	virtual int wait() = 0; | ||||
|  | ||||
| 	virtual ~IServerRunner() = default; | ||||
| }; | ||||
|  | ||||
| /// Server instance will run as a thread of client process | ||||
| class ServerThreadRunner : public IServerRunner, boost::noncopyable | ||||
| { | ||||
| 	std::unique_ptr<CVCMIServer> server; | ||||
| 	boost::thread threadRunLocalServer; | ||||
| public: | ||||
| 	void start(uint16_t port, bool connectToLobby) override; | ||||
| 	void stop() override; | ||||
| 	int wait() override; | ||||
|  | ||||
| 	ServerThreadRunner(); | ||||
| 	~ServerThreadRunner(); | ||||
| }; | ||||
|  | ||||
| #ifndef VCMI_MOBILE | ||||
|  | ||||
| namespace boost::process { | ||||
| class child; | ||||
| } | ||||
|  | ||||
| /// Server instance will run as a separate process | ||||
| class ServerProcessRunner : public IServerRunner, boost::noncopyable | ||||
| { | ||||
| 	std::unique_ptr<boost::process::child> child; | ||||
|  | ||||
| public: | ||||
| 	void start(uint16_t port, bool connectToLobby) override; | ||||
| 	void stop() override; | ||||
| 	int wait() override; | ||||
|  | ||||
| 	ServerProcessRunner(); | ||||
| 	~ServerProcessRunner(); | ||||
| }; | ||||
| #endif | ||||
| @@ -60,11 +60,6 @@ | ||||
| #include "../../lib/CRandomGenerator.h" | ||||
| #include "../../lib/CondSh.h" | ||||
|  | ||||
| #if defined(SINGLE_PROCESS_APP) && defined(VCMI_ANDROID) | ||||
| #include "../../server/CVCMIServer.h" | ||||
| #include <SDL.h> | ||||
| #endif | ||||
|  | ||||
| std::shared_ptr<CMainMenu> CMM; | ||||
| ISelectionScreenInfo * SEL; | ||||
|  | ||||
| @@ -599,13 +594,6 @@ void CSimpleJoinScreen::onChange(const std::string & newText) | ||||
|  | ||||
| void CSimpleJoinScreen::startConnection(const std::string & addr, ui16 port) | ||||
| { | ||||
| #if defined(SINGLE_PROCESS_APP) && defined(VCMI_ANDROID) | ||||
| 	// in single process build server must use same JNIEnv as client | ||||
| 	// as server runs in a separate thread, it must not attempt to search for Java classes (and they're already cached anyway) | ||||
| 	// https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md#threads-and-the-java-vm | ||||
| 	CVCMIServer::reuseClientJNIEnv(SDL_AndroidGetJNIEnv()); | ||||
| #endif | ||||
|  | ||||
| 	if(addr.empty()) | ||||
| 		CSH->startLocalServerAndConnect(false); | ||||
| 	else | ||||
|   | ||||
| @@ -14,5 +14,9 @@ else() | ||||
| 	add_main_lib(${VCMI_LIB_TARGET} SHARED) | ||||
| endif() | ||||
|  | ||||
| target_compile_definitions(${VCMI_LIB_TARGET} PUBLIC VCMI_LIB_NAMESPACE=VCMI) | ||||
| # no longer necessary, but might be useful to keep in future | ||||
| # unfortunately at the moment tests do not support namespaced build, so enable only on some systems | ||||
| if(APPLE_IOS OR ANDROID) | ||||
| 	target_compile_definitions(${VCMI_LIB_TARGET} PUBLIC VCMI_LIB_NAMESPACE=VCMI) | ||||
| endif() | ||||
|  | ||||
|   | ||||
| @@ -368,11 +368,7 @@ bool IVCMIDirsUNIX::developmentMode() const | ||||
| { | ||||
| 	// We want to be able to run VCMI from single directory. E.g to run from build output directory | ||||
| 	const bool result = bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiclient"); | ||||
| #if SINGLE_PROCESS_APP | ||||
| 	return result; | ||||
| #else | ||||
| 	return result && bfs::exists("vcmiserver"); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| set(libserver_SRCS | ||||
| set(vcmiservercommon_SRCS | ||||
| 		StdInc.cpp | ||||
|  | ||||
| 		battles/BattleActionProcessor.cpp | ||||
| @@ -24,7 +24,7 @@ set(libserver_SRCS | ||||
| 		TurnTimerHandler.cpp | ||||
| ) | ||||
|  | ||||
| set(libserver_HEADERS | ||||
| set(vcmiservercommon_HEADERS | ||||
| 		StdInc.h | ||||
|  | ||||
| 		battles/BattleActionProcessor.h | ||||
| @@ -50,28 +50,28 @@ set(libserver_HEADERS | ||||
| 		TurnTimerHandler.h | ||||
| ) | ||||
|  | ||||
| assign_source_group(${libserver_SRCS} ${libserver_HEADERS}) | ||||
| assign_source_group(${vcmiservercommon_SRCS} ${vcmiservercommon_HEADERS}) | ||||
|  | ||||
| add_library(libserver STATIC ${libserver_SRCS} ${libserver_HEADERS}) | ||||
| set(libserver_LIBS vcmi) | ||||
| add_library(vcmiservercommon STATIC ${vcmiservercommon_SRCS} ${vcmiservercommon_HEADERS}) | ||||
| set(vcmiservercommon_LIBS vcmi) | ||||
|  | ||||
| if(CMAKE_SYSTEM_NAME MATCHES FreeBSD OR HAIKU) | ||||
| 	set(libserver_LIBS execinfo ${libserver_LIBS}) | ||||
| 	set(vcmiservercommon_LIBS execinfo ${vcmiservercommon_LIBS}) | ||||
| endif() | ||||
|  | ||||
| target_link_libraries(libserver PRIVATE ${libserver_LIBS} minizip::minizip) | ||||
| target_link_libraries(vcmiservercommon PRIVATE ${vcmiservercommon_LIBS} minizip::minizip) | ||||
|  | ||||
| target_include_directories(libserver | ||||
| target_include_directories(vcmiservercommon | ||||
| 	PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} | ||||
| ) | ||||
|  | ||||
| if(WIN32) | ||||
| 	set_target_properties(vcmiserver | ||||
| 	set_target_properties(vcmiservercommon | ||||
| 		PROPERTIES | ||||
| 			OUTPUT_NAME "VCMI_libserver" | ||||
| 			PROJECT_LABEL "VCMI_libserver" | ||||
| 			OUTPUT_NAME "VCMI_vcmiservercommon" | ||||
| 			PROJECT_LABEL "VCMI_vcmiservercommon" | ||||
| 	) | ||||
| endif() | ||||
|  | ||||
| vcmi_set_output_dir(libserver "") | ||||
| enable_pch(libserver) | ||||
| vcmi_set_output_dir(vcmiservercommon "") | ||||
| enable_pch(vcmiservercommon) | ||||
|   | ||||
| @@ -116,10 +116,11 @@ public: | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts) | ||||
| CVCMIServer::CVCMIServer(uint16_t port, bool connectToLobby, bool runByClient) | ||||
| 	: currentClientId(1) | ||||
| 	, currentPlayerId(1) | ||||
| 	, cmdLineOptions(opts) | ||||
| 	, port(port) | ||||
| 	, runByClient(runByClient) | ||||
| { | ||||
| 	uuid = boost::uuids::to_string(boost::uuids::random_generator()()); | ||||
| 	logNetwork->trace("CVCMIServer created! UUID: %s", uuid); | ||||
| @@ -128,7 +129,7 @@ CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts) | ||||
|  | ||||
| 	networkHandler = INetworkHandler::createHandler(); | ||||
|  | ||||
| 	if(cmdLineOptions.count("lobby")) | ||||
| 	if(connectToLobby) | ||||
| 		lobbyProcessor = std::make_unique<GlobalLobbyProcessor>(*this); | ||||
| 	else | ||||
| 		startAcceptingIncomingConnections(); | ||||
| @@ -138,10 +139,6 @@ 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); | ||||
|  | ||||
| 	networkServer = networkHandler->createServerTCP(*this); | ||||
| @@ -197,15 +194,13 @@ std::shared_ptr<CConnection> CVCMIServer::findConnection(const std::shared_ptr<I | ||||
| 	throw std::runtime_error("Unknown connection received in CVCMIServer::findConnection"); | ||||
| } | ||||
|  | ||||
| bool CVCMIServer::wasStartedByClient() const | ||||
| { | ||||
| 	return runByClient; | ||||
| } | ||||
|  | ||||
| void CVCMIServer::run() | ||||
| { | ||||
| #if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP) | ||||
| 	if(!restartGameplay) | ||||
| 	{ | ||||
| 		CAndroidVMHelper vmHelper; | ||||
| 		vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady"); | ||||
| 	} | ||||
| #endif | ||||
| 	networkHandler->run(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,12 +12,6 @@ | ||||
| #include "../lib/network/NetworkInterface.h" | ||||
| #include "../lib/StartInfo.h" | ||||
|  | ||||
| #include <boost/program_options/variables_map.hpp> | ||||
|  | ||||
| #if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP) | ||||
| #define VCMI_ANDROID_DUAL_PROCESS 1 | ||||
| #endif | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CMapInfo; | ||||
| @@ -64,6 +58,8 @@ class CVCMIServer : public LobbyInfo, public INetworkServerListener, public INet | ||||
|  | ||||
| 	int currentClientId; | ||||
| 	ui8 currentPlayerId; | ||||
| 	uint16_t port; | ||||
| 	bool runByClient; | ||||
|  | ||||
| public: | ||||
| 	/// List of all active connections | ||||
| @@ -76,13 +72,13 @@ public: | ||||
| 	void onTimer() override; | ||||
|  | ||||
| 	std::shared_ptr<CGameHandler> gh; | ||||
| 	boost::program_options::variables_map cmdLineOptions; | ||||
|  | ||||
| 	CVCMIServer(boost::program_options::variables_map & opts); | ||||
| 	CVCMIServer(uint16_t port, bool connectToLobby, bool runByClient); | ||||
| 	~CVCMIServer(); | ||||
|  | ||||
| 	void run(); | ||||
|  | ||||
| 	bool wasStartedByClient() const; | ||||
| 	bool prepareToStartGame(); | ||||
| 	void prepareToRestart(); | ||||
| 	void startGameImmediately(); | ||||
| @@ -131,13 +127,4 @@ public: | ||||
| 	void setCampaignBonus(int bonusId); | ||||
|  | ||||
| 	ui8 getIdOfFirstUnallocatedPlayer() const; | ||||
|  | ||||
| #if VCMI_ANDROID_DUAL_PROCESS | ||||
| 	static void create(); | ||||
| #elif defined(SINGLE_PROCESS_APP) | ||||
| 	static void create(boost::condition_variable * cond, const std::vector<std::string> & args); | ||||
| # ifdef VCMI_ANDROID | ||||
| 	static void reuseClientJNIEnv(void * jniEnv); | ||||
| # endif // VCMI_ANDROID | ||||
| #endif // VCMI_ANDROID_DUAL_PROCESS | ||||
| }; | ||||
|   | ||||
| @@ -81,7 +81,7 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyC | ||||
|  | ||||
| 	if(pack.shutdownServer) | ||||
| 	{ | ||||
| 		if(!srv.cmdLineOptions.count("run-by-client")) | ||||
| 		if(!srv.wasStartedByClient()) | ||||
| 		{ | ||||
| 			result = false; | ||||
| 			return; | ||||
|   | ||||
| @@ -1,20 +1,20 @@ | ||||
| set(appserver_SRCS | ||||
| set(serverapp_SRCS | ||||
| 		StdInc.cpp | ||||
| 		EntryPoint.cpp | ||||
| ) | ||||
|  | ||||
| set(appserver_HEADERS | ||||
| set(serverapp_HEADERS | ||||
| 		StdInc.h | ||||
| ) | ||||
|  | ||||
| assign_source_group(${appserver_SRCS} ${appserver_HEADERS}) | ||||
| add_executable(vcmiserver ${appserver_SRCS} ${appserver_HEADERS}) | ||||
| set(appserver_LIBS vcmi) | ||||
| assign_source_group(${serverapp_SRCS} ${serverapp_HEADERS}) | ||||
| add_executable(vcmiserver ${serverapp_SRCS} ${serverapp_HEADERS}) | ||||
| set(serverapp_LIBS vcmi) | ||||
|  | ||||
| if(CMAKE_SYSTEM_NAME MATCHES FreeBSD OR HAIKU) | ||||
| 	set(appserver_LIBS execinfo ${appserver_LIBS}) | ||||
| 	set(serverapp_LIBS execinfo ${serverapp_LIBS}) | ||||
| endif() | ||||
| target_link_libraries(vcmiserver PRIVATE ${appserver_LIBS} minizip::minizip libserver) | ||||
| target_link_libraries(vcmiserver PRIVATE ${serverapp_LIBS} minizip::minizip vcmiservercommon) | ||||
|  | ||||
| target_include_directories(vcmiserver | ||||
| 	PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|   | ||||
| @@ -16,12 +16,6 @@ | ||||
| #include "../lib/VCMIDirs.h" | ||||
| #include "../lib/VCMI_Lib.h" | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #include <jni.h> | ||||
| #include <android/log.h> | ||||
| #include "lib/CAndroidVMHelper.h" | ||||
| #endif | ||||
|  | ||||
| #include <boost/program_options.hpp> | ||||
|  | ||||
| const std::string SERVER_NAME_AFFIX = "server"; | ||||
| @@ -50,13 +44,8 @@ static void handleCommandOptions(int argc, const char * argv[], boost::program_o | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| #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); | ||||
| @@ -75,31 +64,14 @@ static void handleCommandOptions(int argc, const char * argv[], boost::program_o | ||||
| 		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); | ||||
| @@ -113,58 +85,21 @@ int main(int argc, const char * argv[]) | ||||
| 	std::srand(static_cast<uint32_t>(time(nullptr))); | ||||
|  | ||||
| 	{ | ||||
| 		CVCMIServer server(opts); | ||||
| 		bool connectToLobby = opts.count("lobby"); | ||||
| 		bool runByClient = opts.count("runByClient"); | ||||
| 		uint16_t port = 3030; | ||||
| 		if(opts.count("port")) | ||||
| 			port = opts["port"].as<uint16_t>(); | ||||
|  | ||||
| #ifdef SINGLE_PROCESS_APP | ||||
| 		boost::condition_variable * cond = reinterpret_cast<boost::condition_variable *>(const_cast<char *>(argv[0])); | ||||
| 		cond->notify_one(); | ||||
| #endif | ||||
| 		CVCMIServer server(port, connectToLobby, runByClient); | ||||
|  | ||||
| 		server.run(); | ||||
|  | ||||
| 		// CVCMIServer destructor must be called here - before VLC cleanup | ||||
| 	} | ||||
|  | ||||
|  | ||||
| #if VCMI_ANDROID_DUAL_PROCESS | ||||
| 	CAndroidVMHelper envHelper; | ||||
| 	envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer"); | ||||
| #endif | ||||
| 	logConfig.deconfigure(); | ||||
| 	vstd::clear_pointer(VLC); | ||||
|  | ||||
| #if !VCMI_ANDROID_DUAL_PROCESS | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #if VCMI_ANDROID_DUAL_PROCESS | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_createServer(JNIEnv * env, jclass cls) | ||||
| { | ||||
| 	__android_log_write(ANDROID_LOG_INFO, "VCMI", "Got jni call to init server"); | ||||
| 	CAndroidVMHelper::cacheVM(env); | ||||
|  | ||||
| 	CVCMIServer::create(); | ||||
| } | ||||
|  | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_initClassloader(JNIEnv * baseEnv, jclass cls) | ||||
| { | ||||
| 	CAndroidVMHelper::initClassloader(baseEnv); | ||||
| } | ||||
| #elif defined(SINGLE_PROCESS_APP) | ||||
| void CVCMIServer::create(boost::condition_variable * cond, const std::vector<std::string> & args) | ||||
| { | ||||
| 	std::vector<const void *> argv = {cond}; | ||||
| 	for(auto & a : args) | ||||
| 		argv.push_back(a.c_str()); | ||||
| 	main(argv.size(), reinterpret_cast<const char **>(&*argv.begin())); | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| void CVCMIServer::reuseClientJNIEnv(void * jniEnv) | ||||
| { | ||||
| 	CAndroidVMHelper::initClassloader(jniEnv); | ||||
| 	CAndroidVMHelper::alwaysUseLoadedClass = true; | ||||
| } | ||||
| #endif // VCMI_ANDROID | ||||
| #endif // VCMI_ANDROID_DUAL_PROCESS | ||||
|   | ||||
		Reference in New Issue
	
	Block a user