mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Android support (#299)
* AI libs registering shenanigans on android; * Fixed resolution aspect + mouse event scaling; * Proper server init/deinit (through android IPC); Enabled threaded init in CMT; * Prevented a deadlock in logger on some devices; * Fixed frozen intro frame after interrupting the video; Added android progressbar displaying during initial data loading; * Hacky fix for choppy animations during heroes movement (should look better now, but it's definitely not a good solution); * Changes/fixes for new android launcher building process; * Fixed app hang after getting SDL_QUIT when activity was destroyed; * Functioanal, configurable advmap swiping support; * VCMI changes cleanup; Added few missing VCMI_ANDROID guards on swipe mechanics; * Removed unneeded sleep in server startup code for android; * Removed android ioapi hack (fixed in newest ndk); * Removed unused android's library loading logic; * Added android's swipe option to settings schema; * Moved NO_STD_TOSTRING to be defined in global.h instead of build files;
This commit is contained in:
		| @@ -15,6 +15,9 @@ set(battleAI_SRCS | ||||
| 		ThreatMap.cpp | ||||
| ) | ||||
|  | ||||
| if (ANDROID) # android compiles ai libs into main lib directly, so we skip this library and just reuse sources list | ||||
| 	return() | ||||
| endif() | ||||
| add_library(BattleAI SHARED ${battleAI_SRCS}) | ||||
| target_link_libraries(BattleAI vcmi) | ||||
|  | ||||
|   | ||||
| @@ -15,12 +15,6 @@ | ||||
| #define strcpy_s(a, b, c) strncpy(a, c, b) | ||||
| #endif | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #define GetGlobalAiVersion BattleAI_GetGlobalAiVersion | ||||
| #define GetAiName BattleAI_GetAiName | ||||
| #define GetNewBattleAI BattleAI_GetNewBattleAI | ||||
| #endif | ||||
|  | ||||
| static const char *g_cszAiName = "Battle AI"; | ||||
|  | ||||
| extern "C" DLL_EXPORT int GetGlobalAiVersion() | ||||
|   | ||||
| @@ -57,9 +57,15 @@ if (APPLE) | ||||
|     add_definitions(-DFL_APPLE) | ||||
| endif() | ||||
|  | ||||
| set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY bin) | ||||
| set(CMAKE_LIBRARY_OUTPUT_DIRECTORY bin) | ||||
| set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) | ||||
| if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) | ||||
| 	set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY bin) | ||||
| endif() | ||||
| if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) | ||||
| 	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY bin) | ||||
| endif() | ||||
| if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) | ||||
| 	set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) | ||||
| endif() | ||||
|  | ||||
| if(NOT MSVC) | ||||
|     set(CMAKE_CXX_FLAGS "-pedantic -Werror -Wall -Wextra ${CMAKE_CXX_FLAGS}") | ||||
|   | ||||
| @@ -7,12 +7,6 @@ | ||||
| #define strcpy_s(a, b, c) strncpy(a, c, b) | ||||
| #endif | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #define GetGlobalAiVersion StupidAI_GetGlobalAiVersion | ||||
| #define GetAiName StupidAI_GetAiName | ||||
| #define GetNewBattleAI StupidAI_GetNewBattleAI | ||||
| #endif | ||||
|  | ||||
| static const char *g_cszAiName = "Stupid AI 0.1"; | ||||
|  | ||||
| extern "C" DLL_EXPORT int GetGlobalAiVersion() | ||||
| @@ -28,4 +22,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name) | ||||
| extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out) | ||||
| { | ||||
| 	out = std::make_shared<CStupidAI>(); | ||||
| } | ||||
| } | ||||
| @@ -17,16 +17,20 @@ set(VCAI_SRCS | ||||
|     Fuzzy.cpp | ||||
| ) | ||||
|  | ||||
| if (ANDROID) # android compiles ai libs into main lib directly, so we skip this library and just reuse sources list | ||||
| 	return() | ||||
| endif() | ||||
|  | ||||
| add_library(VCAI SHARED ${VCAI_SRCS}) | ||||
| if (FL_FOUND) | ||||
|     target_link_libraries(VCAI ${FL_LIBRARIES} vcmi) | ||||
| 	target_link_libraries(VCAI ${FL_LIBRARIES} vcmi) | ||||
| else() | ||||
|     target_link_libraries(VCAI fl-static vcmi) | ||||
| 	target_link_libraries(VCAI fl-static vcmi) | ||||
| endif() | ||||
|  | ||||
| set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(VCAI) | ||||
|  | ||||
| if (NOT APPLE) # Already inside vcmiclient bundle | ||||
|     install(TARGETS VCAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
| 	install(TARGETS VCAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
| endif() | ||||
|   | ||||
| @@ -5,12 +5,6 @@ | ||||
| #define strcpy_s(a, b, c) strncpy(a, c, b) | ||||
| #endif | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #define GetGlobalAiVersion VCAI_GetGlobalAiVersion | ||||
| #define GetAiName VCAI_GetAiName | ||||
| #define GetNewAI VCAI_GetNewAI | ||||
| #endif | ||||
|  | ||||
| static const char *g_cszAiName = "VCAI"; | ||||
|  | ||||
| extern "C" DLL_EXPORT int GetGlobalAiVersion() | ||||
| @@ -26,4 +20,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name) | ||||
| extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out) | ||||
| { | ||||
| 	out = std::make_shared<VCAI>(); | ||||
| } | ||||
| } | ||||
							
								
								
									
										17
									
								
								Global.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Global.h
									
									
									
									
									
								
							| @@ -96,6 +96,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); | ||||
| #  define _NO_W32_PSEUDO_MODIFIERS  // Exclude more macros for compiling with MinGW on Linux. | ||||
| #endif | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #  define NO_STD_TOSTRING // android runtime (gnustl) currently doesn't support std::to_string, so we provide our impl in this case | ||||
| #endif // VCMI_ANDROID | ||||
|  | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| /* A macro to force inlining some of our functions */ | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| @@ -698,3 +702,16 @@ namespace vstd | ||||
| } | ||||
| using vstd::operator-=; | ||||
| using vstd::make_unique; | ||||
|  | ||||
| #ifdef NO_STD_TOSTRING | ||||
| namespace std | ||||
| { | ||||
| 	template <typename T> | ||||
| 	inline std::string to_string(const T& value) | ||||
| 	{ | ||||
| 		std::ostringstream ss; | ||||
| 		ss << value; | ||||
| 		return ss.str(); | ||||
| 	} | ||||
| } | ||||
| #endif // NO_STD_TOSTRING | ||||
|   | ||||
| @@ -49,6 +49,9 @@ | ||||
| #ifdef VCMI_WINDOWS | ||||
| #include "SDL_syswm.h" | ||||
| #endif | ||||
| #ifdef VCMI_ANDROID | ||||
| #include "lib/CAndroidVMHelper.h" | ||||
| #endif | ||||
| #include "../lib/UnlockGuard.h" | ||||
| #include "CMT.h" | ||||
|  | ||||
| @@ -203,7 +206,7 @@ void OSX_checkForUpdates(); | ||||
|  | ||||
| #if defined(VCMI_WINDOWS) && !defined (__GNUC__) | ||||
| int wmain(int argc, wchar_t* argv[]) | ||||
| #elif defined(VCMI_APPLE) | ||||
| #elif defined(VCMI_APPLE) || defined(VCMI_ANDROID) | ||||
| int SDL_main(int argc, char *argv[]) | ||||
| #else | ||||
| int main(int argc, char** argv) | ||||
| @@ -436,11 +439,6 @@ int main(int argc, char** argv) | ||||
|  | ||||
| 	logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff(); | ||||
|  | ||||
| #if defined(VCMI_ANDROID) | ||||
| 	//on Android threaded init is broken | ||||
| 	#define VCMI_NO_THREADED_LOAD | ||||
| #endif // defined | ||||
|  | ||||
| 	//initializing audio | ||||
| 	CCS->soundh = new CSoundHandler; | ||||
| 	CCS->soundh->init(); | ||||
| @@ -466,13 +464,22 @@ int main(int argc, char** argv) | ||||
| 	{ | ||||
| 		if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool()) | ||||
| 			playIntro(); | ||||
| 		SDL_FillRect(screen,nullptr,0); | ||||
| 		SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); | ||||
| 		SDL_RenderClear(mainRenderer); | ||||
| 	} | ||||
|  | ||||
| 	CSDL_Ext::update(screen); | ||||
| 	SDL_RenderPresent(mainRenderer); | ||||
| #ifndef VCMI_NO_THREADED_LOAD | ||||
| 	loading.join(); | ||||
| #endif | ||||
| 	#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds | ||||
| 	{ | ||||
| 		CAndroidVMHelper vmHelper; | ||||
| 		vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "showProgress"); | ||||
| 	#endif // ANDROID | ||||
| 		loading.join(); | ||||
| 	#ifdef VCMI_ANDROID | ||||
| 		vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "hideProgress"); | ||||
| 	} | ||||
| 	#endif // ANDROID | ||||
| #endif // THREADED | ||||
| 	logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff(); | ||||
|  | ||||
| 	if(!vm.count("battle")) | ||||
| @@ -1027,6 +1034,9 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn | ||||
|  | ||||
| 	cleanupRenderer(); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN); | ||||
| #else | ||||
| 	if(fullscreen) | ||||
| 	{ | ||||
| 		//in full-screen mode always use desktop resolution | ||||
| @@ -1037,6 +1047,7 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn | ||||
| 	{ | ||||
| 		mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	if(nullptr == mainWindow) | ||||
| 	{ | ||||
| @@ -1058,7 +1069,10 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn | ||||
|  | ||||
| 	SDL_RenderSetLogicalSize(mainRenderer, w, h); | ||||
|  | ||||
| #ifndef VCMI_ANDROID | ||||
|     // on android this stretches the game to fit the screen, not preserving aspect and apparently this also breaks coordinates scaling in mouse events | ||||
| 	SDL_RenderSetViewport(mainRenderer, nullptr); | ||||
| #endif | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1149,9 +1163,19 @@ static void handleEvent(SDL_Event & ev) | ||||
| { | ||||
| 	if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT))) | ||||
| 	{ | ||||
| #ifdef VCMI_ANDROID | ||||
| 		handleQuit(false); | ||||
| #else | ||||
| 		handleQuit(); | ||||
| #endif | ||||
| 		return; | ||||
| 	} | ||||
| #ifdef VCMI_ANDROID | ||||
| 	else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK) | ||||
| 	{ | ||||
| 		handleQuit(true); | ||||
| 	} | ||||
| #endif | ||||
| 	else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4) | ||||
| 	{ | ||||
| 		Settings full = settings.write["video"]["fullscreen"]; | ||||
|   | ||||
| @@ -65,6 +65,10 @@ set(client_HEADERS | ||||
|                 gui/SDL_Compat.h | ||||
| ) | ||||
|  | ||||
| if(ANDROID) # android needs client/server to be libraries, not executables, so we can't reuse the build part of this script | ||||
| 	return() | ||||
| endif() | ||||
|  | ||||
| if(APPLE) | ||||
| 	# OS X specific includes | ||||
| 	include_directories(${SPARKLE_INCLUDE_DIR}) | ||||
| @@ -133,4 +137,3 @@ cotire(vcmiclient) | ||||
|  | ||||
|  | ||||
| install(TARGETS vcmiclient DESTINATION ${BIN_DIR}) | ||||
|  | ||||
|   | ||||
| @@ -353,7 +353,11 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) | ||||
| 	for (int i=1; i<32; i+=2*speed) | ||||
| 	{ | ||||
| 		movementPxStep(details, i, hp, hero); | ||||
| #ifndef VCMI_ANDROID | ||||
| 		// currently android doesn't seem to be able to handle all these full redraws here, so let's disable it so at least it looks less choppy; | ||||
| 		// most likely this is connected with the way that this manual animation+framerate handling is solved | ||||
| 		adventureInt->updateScreen = true; | ||||
| #endif | ||||
| 		adventureInt->show(screen); | ||||
| 		{ | ||||
| 			//evil returns here ... | ||||
| @@ -1647,7 +1651,11 @@ void CPlayerInterface::update() | ||||
| 	GH.updateTime(); | ||||
| 	GH.handleEvents(); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if (adventureInt && !adventureInt->isActive() && (adventureInt->swipeTargetPosition.x >= 0 || adventureInt->swipeTargetPosition.y >= 0)) | ||||
| #else // !VCMI_ANDROID | ||||
| 	if (adventureInt && !adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled | ||||
| #endif // !VCMI_ANDROID | ||||
| 		GH.totalRedraw(); | ||||
| 	else | ||||
| 		GH.simpleRedraw(); | ||||
|   | ||||
| @@ -43,6 +43,8 @@ | ||||
| extern std::string NAME; | ||||
| #ifndef VCMI_ANDROID | ||||
| namespace intpr = boost::interprocess; | ||||
| #else | ||||
| #include "lib/CAndroidVMHelper.h" | ||||
| #endif | ||||
|  | ||||
| /* | ||||
| @@ -55,6 +57,10 @@ namespace intpr = boost::interprocess; | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| std::atomic_bool androidTestServerReadyFlag; | ||||
| #endif | ||||
|  | ||||
| template <typename T> class CApplyOnCL; | ||||
|  | ||||
| class CBaseForCLApply | ||||
| @@ -913,8 +919,7 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI) | ||||
| { | ||||
| 	if(ps.name.size()) | ||||
| 	{ | ||||
| 		const boost::filesystem::path aiPath = | ||||
| 			VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(ps.name); | ||||
| 		const boost::filesystem::path aiPath = VCMIDirs::get().fullLibraryPath("AI", ps.name); | ||||
| 		if (boost::filesystem::exists(aiPath)) | ||||
| 			return ps.name; | ||||
| 	} | ||||
| @@ -940,7 +945,12 @@ void CServerHandler::startServer() | ||||
|  | ||||
| 	th.update(); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	CAndroidVMHelper envHelper; | ||||
| 	envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true); | ||||
| #else | ||||
| 	serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable; | ||||
| #endif | ||||
| 	if(verbose) | ||||
| 		logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff(); | ||||
| } | ||||
| @@ -954,12 +964,22 @@ void CServerHandler::waitForServer() | ||||
| 		startServer(); | ||||
|  | ||||
| 	th.update(); | ||||
|  | ||||
| #ifndef VCMI_ANDROID | ||||
| 	intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex); | ||||
| 	while(!shared->sr->ready) | ||||
| 	{ | ||||
| 		shared->sr->cond.wait(slock); | ||||
| 	} | ||||
| #else | ||||
| 	logNetwork->infoStream() << "waiting for server"; | ||||
| 	while (!androidTestServerReadyFlag.load()) | ||||
| 	{ | ||||
| 		logNetwork->infoStream() << "still waiting..."; | ||||
| 		boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); | ||||
| 	} | ||||
| 	logNetwork->infoStream() << "waiting for server finished..."; | ||||
| 	androidTestServerReadyFlag = false; | ||||
| #endif | ||||
| 	if(verbose) | ||||
| 		logNetwork->infoStream() << "Waiting for server: " << th.getDiff(); | ||||
| @@ -1017,6 +1037,7 @@ CServerHandler::~CServerHandler() | ||||
|  | ||||
| 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 + '\"'; | ||||
| @@ -1032,6 +1053,7 @@ void CServerHandler::callServer() | ||||
| 		logNetwork->errorStream() << "Check " << logName << " for more info"; | ||||
| 		exit(1);// exit in case of error. Othervice without working server VCMI will hang | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port) | ||||
| @@ -1062,3 +1084,23 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jobject cls) | ||||
| { | ||||
| 	logNetwork->infoStream() << "Received server ready signal"; | ||||
| 	androidTestServerReadyFlag.store(true); | ||||
| } | ||||
|  | ||||
| extern "C" JNIEXPORT bool JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jobject cls) | ||||
| { | ||||
| 	logGlobal->infoStream() << "Received emergency save game request"; | ||||
| 	if(!LOCPLINT || !LOCPLINT->cb) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	LOCPLINT->cb->save("Saves/_Android_Autosave"); | ||||
| 	return true; | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -116,13 +116,36 @@ void CTerrainRect::deactivate() | ||||
|  | ||||
| void CTerrainRect::clickLeft(tribool down, bool previousState) | ||||
| { | ||||
| 	if (adventureInt->mode == EAdvMapMode::WORLD_VIEW) | ||||
| 	if(adventureInt->mode == EAdvMapMode::WORLD_VIEW) | ||||
| 		return; | ||||
| 	if ((down==false) || indeterminate(down)) | ||||
| 	if(indeterminate(down)) | ||||
| 		return; | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if(adventureInt->swipeEnabled) | ||||
| 	{ | ||||
| 		if(down == true) | ||||
| 		{ | ||||
| 			swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0); | ||||
| 			swipeInitialMapPos = int3(adventureInt->position); | ||||
| 			return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture) | ||||
| 		} | ||||
| 		else if(isSwiping) // only accept this touch if it wasn't a swipe | ||||
| 		{ | ||||
| 			isSwiping = false; | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| #endif | ||||
| 		if(down == false) | ||||
| 			return; | ||||
| #ifdef VCMI_ANDROID | ||||
| 	} | ||||
| #endif | ||||
| 	int3 mp = whichTileIsIt(); | ||||
| 	if (mp.x<0 || mp.y<0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y) | ||||
| 	if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y) | ||||
| 		return; | ||||
|  | ||||
| 	adventureInt->tileLClicked(mp); | ||||
| @@ -130,17 +153,59 @@ void CTerrainRect::clickLeft(tribool down, bool previousState) | ||||
|  | ||||
| void CTerrainRect::clickRight(tribool down, bool previousState) | ||||
| { | ||||
| 	if (adventureInt->mode == EAdvMapMode::WORLD_VIEW) | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if(adventureInt->swipeEnabled && isSwiping) | ||||
| 		return; | ||||
| #endif | ||||
| 	if(adventureInt->mode == EAdvMapMode::WORLD_VIEW) | ||||
| 		return; | ||||
| 	int3 mp = whichTileIsIt(); | ||||
|  | ||||
| 	if (CGI->mh->map->isInTheMap(mp) && down) | ||||
| 	if(CGI->mh->map->isInTheMap(mp) && down) | ||||
| 		adventureInt->tileRClicked(mp); | ||||
| } | ||||
|  | ||||
| void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent) | ||||
| { | ||||
| 	int3 tHovered = whichTileIsIt(sEvent.x,sEvent.y); | ||||
| 	handleHover(sEvent); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if(!adventureInt->swipeEnabled || sEvent.state == 0) | ||||
| 		return; | ||||
|  | ||||
| 	handleSwipeMove(sEvent); | ||||
| #endif // !VCMI_ANDROID | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent) | ||||
| { | ||||
| 	if(!isSwiping) | ||||
| 	{ | ||||
| 		// try to distinguish if this touch was meant to be a swipe or just fat-fingering press | ||||
| 		if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop || | ||||
| 		   abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop) | ||||
| 		{ | ||||
| 			isSwiping = true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(isSwiping) | ||||
| 	{ | ||||
| 		adventureInt->swipeTargetPosition.x = | ||||
| 			swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32; | ||||
| 		adventureInt->swipeTargetPosition.y = | ||||
| 			swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32; | ||||
| 		adventureInt->swipeMovementRequested = true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #endif // VCMI_ANDROID | ||||
|  | ||||
| void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent) | ||||
| { | ||||
| 	int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y); | ||||
| 	int3 pom = adventureInt->verifyPos(tHovered); | ||||
|  | ||||
| 	if(tHovered != pom) //tile outside the map | ||||
| @@ -150,12 +215,12 @@ void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent) | ||||
| 	} | ||||
|  | ||||
| 	if (pom != curHoveredTile) | ||||
| 	{ | ||||
| 		curHoveredTile = pom; | ||||
| 	else | ||||
| 		return; | ||||
|  | ||||
| 	adventureInt->tileHovered(pom); | ||||
| 		adventureInt->tileHovered(pom); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTerrainRect::hover(bool on) | ||||
| { | ||||
| 	if (!on) | ||||
| @@ -470,6 +535,10 @@ CAdvMapInt::CAdvMapInt(): | ||||
|   spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr), | ||||
|   updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0), | ||||
| 	activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false) | ||||
| #ifdef VCMI_ANDROID | ||||
| 	, swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false), | ||||
| 	swipeTargetPosition(int3(-1, -1, -1)) | ||||
| #endif | ||||
| { | ||||
|   adventureInt = this; | ||||
| 	pos.x = pos.y = 0; | ||||
| @@ -938,42 +1007,19 @@ void CAdvMapInt::show(SDL_Surface * to) | ||||
| 	} | ||||
| 	++heroAnim; | ||||
|  | ||||
| 	int scrollSpeed = settings["adventure"]["scrollSpeed"].Float(); | ||||
| 	//if advmap needs updating AND (no dialog is shown OR ctrl is pressed) | ||||
| 	if((animValHitCount % (4/scrollSpeed)) == 0 | ||||
| 		&&  ( | ||||
| 			(GH.topInt() == this) | ||||
| 			|| isCtrlKeyDown() | ||||
| 		) | ||||
| 	) | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if(swipeEnabled) | ||||
| 	{ | ||||
| 		if( (scrollingDir & LEFT)   &&  (position.x>-CGI->mh->frameW) ) | ||||
| 			position.x--; | ||||
|  | ||||
| 		if( (scrollingDir & RIGHT)  &&  (position.x   <   CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW) ) | ||||
| 			position.x++; | ||||
|  | ||||
| 		if( (scrollingDir & UP)  &&  (position.y>-CGI->mh->frameH) ) | ||||
| 			position.y--; | ||||
|  | ||||
| 		if( (scrollingDir & DOWN)  &&  (position.y  <  CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH) ) | ||||
| 			position.y++; | ||||
|  | ||||
| 		if(scrollingDir) | ||||
| 		{ | ||||
| 			setScrollingCursor(scrollingDir); | ||||
| 			scrollingState = true; | ||||
| 			updateScreen = true; | ||||
| 			minimap.redraw(); | ||||
| 			if (mode == EAdvMapMode::WORLD_VIEW) | ||||
| 				terrain.redraw(); | ||||
| 		} | ||||
| 		else if(scrollingState) | ||||
| 		{ | ||||
| 			CCS->curh->changeGraphic(ECursor::ADVENTURE, 0); | ||||
| 			scrollingState = false; | ||||
| 		} | ||||
| 		handleSwipeUpdate(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| #endif // !VCMI_ANDROID | ||||
| 		handleMapScrollingUpdate(); | ||||
| #ifdef VCMI_ANDROID | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	for(int i = 0; i < 4; i++) | ||||
| 		gems[i]->setFrame(LOCPLINT->playerID.getNum()); | ||||
| 	if(updateScreen) | ||||
| @@ -1002,6 +1048,59 @@ void CAdvMapInt::show(SDL_Surface * to) | ||||
| 	statusbar.showAll(to); | ||||
| } | ||||
|  | ||||
| void CAdvMapInt::handleMapScrollingUpdate() | ||||
| { | ||||
| 	int scrollSpeed = settings["adventure"]["scrollSpeed"].Float(); | ||||
| 	//if advmap needs updating AND (no dialog is shown OR ctrl is pressed) | ||||
| 	if((animValHitCount % (4 / scrollSpeed)) == 0 | ||||
| 	   && ((GH.topInt() == this) || isCtrlKeyDown())) | ||||
| 	{ | ||||
| 		if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW)) | ||||
| 			position.x--; | ||||
|  | ||||
| 		if((scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW)) | ||||
| 			position.x++; | ||||
|  | ||||
| 		if((scrollingDir & UP) && (position.y > -CGI->mh->frameH)) | ||||
| 			position.y--; | ||||
|  | ||||
| 		if((scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH)) | ||||
| 			position.y++; | ||||
|  | ||||
| 		if(scrollingDir) | ||||
| 		{ | ||||
| 			setScrollingCursor(scrollingDir); | ||||
| 			scrollingState = true; | ||||
| 			updateScreen = true; | ||||
| 			minimap.redraw(); | ||||
| 			if(mode == EAdvMapMode::WORLD_VIEW) | ||||
| 				terrain.redraw(); | ||||
| 		} | ||||
| 		else if(scrollingState) | ||||
| 		{ | ||||
| 			CCS->curh->changeGraphic(ECursor::ADVENTURE, 0); | ||||
| 			scrollingState = false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| void CAdvMapInt::handleSwipeUpdate() | ||||
| { | ||||
| 	if(swipeMovementRequested) | ||||
| 	{ | ||||
| 		position.x = swipeTargetPosition.x; | ||||
| 		position.y = swipeTargetPosition.y; | ||||
| 		CCS->curh->changeGraphic(ECursor::DEFAULT, 0); | ||||
| 		updateScreen = true; | ||||
| 		minimap.redraw(); | ||||
| 		swipeMovementRequested = false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| void CAdvMapInt::selectionChanged() | ||||
| { | ||||
| 	const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()]; | ||||
| @@ -1315,6 +1414,10 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/) | ||||
|  | ||||
| void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent ) | ||||
| { | ||||
| #ifdef VCMI_ANDROID | ||||
| 	if(swipeEnabled) | ||||
| 		return; | ||||
| #endif | ||||
| 	// adventure map scrolling with mouse | ||||
| 	// currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed | ||||
| 	if(!isCtrlKeyDown() &&  isActive() && mode == EAdvMapMode::NORMAL) | ||||
|   | ||||
| @@ -56,6 +56,16 @@ class CTerrainRect | ||||
| 	SDL_Surface * fadeSurface; | ||||
| 	EMapAnimRedrawStatus lastRedrawStatus; | ||||
| 	CFadeAnimation * fadeAnim; | ||||
|  | ||||
| 	int3 swipeInitialMapPos; | ||||
| 	int3 swipeInitialRealPos; | ||||
| 	bool isSwiping; | ||||
| 	static constexpr float SwipeTouchSlop = 16.0f; | ||||
|  | ||||
| 	void handleHover(const SDL_MouseMotionEvent &sEvent); | ||||
| #ifdef VCMI_ANDROID | ||||
| 	void handleSwipeMove(const SDL_MouseMotionEvent &sEvent); | ||||
| #endif // VCMI_ANDROID | ||||
| public: | ||||
| 	int tilesw, tilesh; //width and height of terrain to blit in tiles | ||||
| 	int3 curHoveredTile; | ||||
| @@ -80,6 +90,7 @@ public: | ||||
| 	/// animates view by caching current surface and crossfading it with normal screen | ||||
| 	void fadeFromCurrentView(); | ||||
| 	bool needsAnimUpdate(); | ||||
|  | ||||
| }; | ||||
|  | ||||
| /// Resources bar which shows information about how many gold, crystals,... you have | ||||
| @@ -121,6 +132,11 @@ public: | ||||
| 	enum{LEFT=1, RIGHT=2, UP=4, DOWN=8}; | ||||
| 	ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN | ||||
| 	bool scrollingState; | ||||
| #ifdef VCMI_ANDROID | ||||
| 	bool swipeEnabled; | ||||
| 	bool swipeMovementRequested; | ||||
| 	int3 swipeTargetPosition; | ||||
| #endif // !VCMI_ANDROID | ||||
|  | ||||
| 	enum{NA, INGAME, WAITING} state; | ||||
|  | ||||
| @@ -242,6 +258,12 @@ public: | ||||
|  | ||||
| 	/// changes current adventure map mode; used to switch between default view and world view; scale is ignored if EAdvMapMode == NORMAL | ||||
| 	void changeMode(EAdvMapMode newMode, float newScale = 0.36f); | ||||
|  | ||||
| 	void handleMapScrollingUpdate(); | ||||
| #ifdef VCMI_ANDROID | ||||
| 	void handleSwipeUpdate(); | ||||
| #endif | ||||
|  | ||||
| }; | ||||
|  | ||||
| extern CAdvMapInt *adventureInt; | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
| 			"type" : "object", | ||||
| 			"default": {}, | ||||
| 			"additionalProperties" : false, | ||||
| 			"required" : [ "playerName", "showfps", "music", "sound", "encoding" ], | ||||
| 			"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe" ], | ||||
| 			"properties" : { | ||||
| 				"playerName" : { | ||||
| 					"type":"string", | ||||
| @@ -38,7 +38,11 @@ | ||||
| 				"encoding" : { | ||||
| 					"type" : "string", | ||||
| 					"default" : "CP1252" | ||||
| 				} | ||||
| 				}, | ||||
| 				"swipe" : { | ||||
| 					"type" : "boolean", | ||||
| 					"default" : false | ||||
| 				}, | ||||
| 			} | ||||
| 		}, | ||||
| 		"video" : { | ||||
|   | ||||
| @@ -38,7 +38,11 @@ namespace ELogLevel | ||||
| 			case ERROR: | ||||
| 				return "error"; | ||||
| 			default: | ||||
| #ifdef NO_STD_TOSTRING | ||||
| 				return "invalid"; | ||||
| #else | ||||
| 				return std::string("invalid (") + std::to_string(level) + ")"; | ||||
| #endif | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										105
									
								
								lib/CAndroidVMHelper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/CAndroidVMHelper.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| /* | ||||
|  * CAndroidVMHelper.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 "CAndroidVMHelper.h" | ||||
|  | ||||
| static JavaVM * vmCache = nullptr; | ||||
|  | ||||
| /// cached java classloader so that we can find our classes from other threads | ||||
| static jobject vcmiClassLoader; | ||||
| static jmethodID vcmiFindClassMethod; | ||||
|  | ||||
| void CAndroidVMHelper::cacheVM(JNIEnv * env) | ||||
| { | ||||
| 	env->GetJavaVM(&vmCache); | ||||
| } | ||||
|  | ||||
| void CAndroidVMHelper::cacheVM(JavaVM * vm) | ||||
| { | ||||
| 	vmCache = vm; | ||||
| } | ||||
|  | ||||
| CAndroidVMHelper::CAndroidVMHelper() | ||||
| { | ||||
| 	auto res = vmCache->GetEnv((void **) &envPtr, JNI_VERSION_1_1); | ||||
| 	if(res == JNI_EDETACHED) | ||||
| 	{ | ||||
| 		auto attachRes = vmCache->AttachCurrentThread(&envPtr, nullptr); | ||||
| 		if(attachRes == JNI_OK) | ||||
| 		{ | ||||
| 			detachInDestructor = true; // only detach if we actually attached env | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		detachInDestructor = false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CAndroidVMHelper::~CAndroidVMHelper() | ||||
| { | ||||
| 	if(envPtr && detachInDestructor) | ||||
| 	{ | ||||
| 		vmCache->DetachCurrentThread(); | ||||
| 		envPtr = nullptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| JNIEnv * CAndroidVMHelper::get() | ||||
| { | ||||
| 	return envPtr; | ||||
| } | ||||
|  | ||||
| jclass CAndroidVMHelper::findClassloadedClass(const std::string & name) | ||||
| { | ||||
| 	auto env = get(); | ||||
| 	return static_cast<jclass>(env->CallObjectMethod(vcmiClassLoader, vcmiFindClassMethod, | ||||
| 		env->NewStringUTF(name.c_str())));; | ||||
| } | ||||
|  | ||||
| void CAndroidVMHelper::callStaticVoidMethod(const std::string & cls, const std::string & method, | ||||
| 											bool classloaded /*=false*/) | ||||
| { | ||||
| 	auto env = get(); | ||||
| 	auto javaHelper = findClass(cls, classloaded); | ||||
| 	auto methodId = env->GetStaticMethodID(javaHelper, method.c_str(), "()V"); | ||||
| 	env->CallStaticVoidMethod(javaHelper, methodId); | ||||
| } | ||||
|  | ||||
| std::string CAndroidVMHelper::callStaticStringMethod(const std::string & cls, const std::string & method, | ||||
| 													 bool classloaded /*=false*/) | ||||
| { | ||||
| 	auto env = get(); | ||||
| 	auto javaHelper = findClass(cls, classloaded); | ||||
| 	auto methodId = env->GetStaticMethodID(javaHelper, method.c_str(), "()Ljava/lang/String;"); | ||||
| 	jstring jres = static_cast<jstring>(env->CallStaticObjectMethod(javaHelper, methodId)); | ||||
| 	return std::string(env->GetStringUTFChars(jres, nullptr)); | ||||
| } | ||||
|  | ||||
| jclass CAndroidVMHelper::findClass(const std::string & name, bool classloaded) | ||||
| { | ||||
| 	if(classloaded) | ||||
| 	{ | ||||
| 		return findClassloadedClass(name); | ||||
| 	} | ||||
| 	return get()->FindClass(name.c_str()); | ||||
| } | ||||
|  | ||||
| extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_initClassloader(JNIEnv * baseEnv, jobject * cls) | ||||
| { | ||||
| 	CAndroidVMHelper::cacheVM(baseEnv); | ||||
| 	CAndroidVMHelper envHelper; | ||||
| 	auto env = envHelper.get(); | ||||
| 	auto anyVCMIClass = env->FindClass(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS); | ||||
| 	jclass classClass = env->GetObjectClass(anyVCMIClass); | ||||
| 	auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); | ||||
| 	auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); | ||||
| 	vcmiClassLoader = (jclass) env->NewGlobalRef(env->CallObjectMethod(anyVCMIClass, getClassLoaderMethod)); | ||||
| 	vcmiFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); | ||||
| } | ||||
							
								
								
									
										41
									
								
								lib/CAndroidVMHelper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/CAndroidVMHelper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * CAndroidVMHelper.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 | ||||
|  | ||||
| #include <jni.h> | ||||
| #include <string> | ||||
|  | ||||
| /// helper class that allows access to java vm to communicate with java code from native | ||||
| class CAndroidVMHelper | ||||
| { | ||||
| 	JNIEnv * envPtr; | ||||
| 	bool detachInDestructor; | ||||
|  | ||||
| 	jclass findClass(const std::string & name, bool classloaded); | ||||
|  | ||||
| public: | ||||
| 	CAndroidVMHelper(); | ||||
|  | ||||
| 	~CAndroidVMHelper(); | ||||
|  | ||||
| 	JNIEnv * get(); | ||||
|  | ||||
| 	jclass findClassloadedClass(const std::string & name); | ||||
|  | ||||
| 	void callStaticVoidMethod(const std::string & cls, const std::string & method, bool classloaded = false); | ||||
|  | ||||
| 	std::string callStaticStringMethod(const std::string & cls, const std::string & method, bool classloaded = false); | ||||
|  | ||||
| 	static void cacheVM(JNIEnv * env); | ||||
|  | ||||
| 	static void cacheVM(JavaVM * vm); | ||||
|  | ||||
| 	static constexpr const char * NATIVE_METHODS_DEFAULT_CLASS = "eu/vcmi/vcmi/NativeMethods"; | ||||
| }; | ||||
| @@ -5,13 +5,21 @@ | ||||
| #include "VCMIDirs.h" | ||||
|  | ||||
| #ifdef VCMI_WINDOWS | ||||
| 	#include <windows.h> //for .dll libs | ||||
| #else | ||||
| 	#include <dlfcn.h> | ||||
| #include <windows.h> //for .dll libs | ||||
| #elif !defined VCMI_ANDROID | ||||
| #include <dlfcn.h> | ||||
| #endif | ||||
|  | ||||
| #include "serializer/BinaryDeserializer.h" | ||||
| #include "serializer/BinarySerializer.h" | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| #include "AI/VCAI/VCAI.h" | ||||
| #include "AI/BattleAI/BattleAI.h" | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * CGameInterface.cpp, part of VCMI engine | ||||
|  * | ||||
| @@ -22,50 +30,22 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| // we can't use shared libraries on Android so here's a hack | ||||
| extern "C" DLL_EXPORT void VCAI_GetAiName(char* name); | ||||
| extern "C" DLL_EXPORT void VCAI_GetNewAI(std::shared_ptr<CGlobalAI> &out); | ||||
|  | ||||
| extern "C" DLL_EXPORT void StupidAI_GetAiName(char* name); | ||||
| extern "C" DLL_EXPORT void StupidAI_GetNewBattleAI(std::shared_ptr<CGlobalAI> &out); | ||||
|  | ||||
| extern "C" DLL_EXPORT void BattleAI_GetAiName(char* name); | ||||
| extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out); | ||||
| #endif | ||||
|  | ||||
| template<typename rett> | ||||
| std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const std::string& methodName) | ||||
| std::shared_ptr<rett> createAny(const boost::filesystem::path & libpath, const std::string & methodName) | ||||
| { | ||||
| 	typedef void(*TGetAIFun)(std::shared_ptr<rett>&); | ||||
| 	typedef void(*TGetNameFun)(char*); | ||||
| #ifdef VCMI_ANDROID | ||||
| 	// android currently doesn't support loading libs dynamically, so the access to the known libraries | ||||
| 	// is possible only via specializations of this template | ||||
| 	throw std::runtime_error("Could not resolve ai library " + libpath.generic_string()); | ||||
| #else | ||||
| 	typedef void(* TGetAIFun)(std::shared_ptr<rett> &); | ||||
| 	typedef void(* TGetNameFun)(char *); | ||||
|  | ||||
| 	char temp[150]; | ||||
|  | ||||
| 	TGetAIFun getAI = nullptr; | ||||
| 	TGetNameFun getName = nullptr; | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	// this is awful but it seems using shared libraries on some devices is even worse | ||||
| 	const std::string filename = libpath.filename().string(); | ||||
| 	if (filename == "libVCAI.so") | ||||
| 	{ | ||||
| 		getName = (TGetNameFun)VCAI_GetAiName; | ||||
| 		getAI = (TGetAIFun)VCAI_GetNewAI; | ||||
| 	} | ||||
| 	else if (filename == "libStupidAI.so") | ||||
| 	{ | ||||
| 		getName = (TGetNameFun)StupidAI_GetAiName; | ||||
| 		getAI = (TGetAIFun)StupidAI_GetNewBattleAI; | ||||
| 	} | ||||
| 	else if (filename == "libBattleAI.so") | ||||
| 	{ | ||||
| 		getName = (TGetNameFun)BattleAI_GetAiName; | ||||
| 		getAI = (TGetAIFun)BattleAI_GetNewBattleAI; | ||||
| 	} | ||||
| 	else | ||||
| 		throw std::runtime_error("Don't know what to do with " + libpath.string() + " and method " + methodName); | ||||
| #else // !VCMI_ANDROID | ||||
| #ifdef VCMI_WINDOWS | ||||
| 	HMODULE dll = LoadLibraryW(libpath.c_str()); | ||||
| 	if (dll) | ||||
| @@ -83,6 +63,7 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st | ||||
| 	else | ||||
| 		logGlobal->errorStream() << "Error: " << dlerror(); | ||||
| #endif // VCMI_WINDOWS | ||||
|  | ||||
| 	if (!dll) | ||||
| 	{ | ||||
| 		logGlobal->errorStream() << "Cannot open dynamic library ("<<libpath<<"). Throwing..."; | ||||
| @@ -98,7 +79,6 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st | ||||
| #endif | ||||
| 		throw std::runtime_error("Cannot find method " + methodName); | ||||
| 	} | ||||
| #endif // VCMI_ANDROID | ||||
|  | ||||
| 	getName(temp); | ||||
| 	logGlobal->infoStream() << "Loaded " << temp; | ||||
| @@ -109,14 +89,31 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st | ||||
| 		logGlobal->error("Cannot get AI!"); | ||||
|  | ||||
| 	return ret; | ||||
| #endif //!VCMI_ANDROID | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| template<> | ||||
| std::shared_ptr<CGlobalAI> createAny(const boost::filesystem::path & libpath, const std::string & methodName) | ||||
| { | ||||
| 	return std::make_shared<VCAI>(); | ||||
| } | ||||
|  | ||||
| template<> | ||||
| std::shared_ptr<CBattleGameInterface> createAny(const boost::filesystem::path & libpath, const std::string & methodName) | ||||
| { | ||||
| 	return std::make_shared<CBattleAI>(); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| template<typename rett> | ||||
| std::shared_ptr<rett> createAnyAI(std::string dllname, const std::string& methodName) | ||||
| std::shared_ptr<rett> createAnyAI(std::string dllname, const std::string & methodName) | ||||
| { | ||||
| 	logGlobal->infoStream() << "Opening " << dllname; | ||||
| 	const boost::filesystem::path filePath = | ||||
| 		VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname); | ||||
|  | ||||
| 	const boost::filesystem::path filePath = VCMIDirs::get().fullLibraryPath("AI", dllname); | ||||
| 	auto ret = createAny<rett>(filePath, methodName); | ||||
| 	ret->dllName = std::move(dllname); | ||||
| 	return ret; | ||||
| @@ -127,7 +124,7 @@ std::shared_ptr<CGlobalAI> CDynLibHandler::getNewAI(std::string dllname) | ||||
| 	return createAnyAI<CGlobalAI>(dllname, "GetNewAI"); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<CBattleGameInterface> CDynLibHandler::getNewBattleAI(std::string dllname ) | ||||
| std::shared_ptr<CBattleGameInterface> CDynLibHandler::getNewBattleAI(std::string dllname) | ||||
| { | ||||
| 	return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI"); | ||||
| } | ||||
| @@ -137,9 +134,10 @@ std::shared_ptr<CScriptingModule> CDynLibHandler::getNewScriptingModule(std::str | ||||
| 	return createAny<CScriptingModule>(dllname, "GetNewModule"); | ||||
| } | ||||
|  | ||||
| BattleAction CGlobalAI::activeStack( const CStack * stack ) | ||||
| BattleAction CGlobalAI::activeStack(const CStack * stack) | ||||
| { | ||||
| 	BattleAction ba; ba.actionType = Battle::DEFEND; | ||||
| 	BattleAction ba; | ||||
| 	ba.actionType = Battle::DEFEND; | ||||
| 	ba.stackNumber = stack->ID; | ||||
| 	return ba; | ||||
| } | ||||
| @@ -159,7 +157,8 @@ void CAdventureAI::battleCatapultAttacked(const CatapultAttack & ca) | ||||
| 	battleAI->battleCatapultAttacked(ca); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) | ||||
| void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, | ||||
| 							   const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) | ||||
| { | ||||
| 	assert(!battleAI); | ||||
| 	assert(cbc); | ||||
| @@ -173,7 +172,7 @@ void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & | ||||
| 	battleAI->battleStacksAttacked(bsa); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::actionStarted(const BattleAction &action) | ||||
| void CAdventureAI::actionStarted(const BattleAction & action) | ||||
| { | ||||
| 	battleAI->actionStarted(action); | ||||
| } | ||||
| @@ -183,7 +182,7 @@ void CAdventureAI::battleNewRoundFirst(int round) | ||||
| 	battleAI->battleNewRoundFirst(round); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::actionFinished(const BattleAction &action) | ||||
| void CAdventureAI::actionFinished(const BattleAction & action) | ||||
| { | ||||
| 	battleAI->actionFinished(action); | ||||
| } | ||||
| @@ -213,23 +212,24 @@ void CAdventureAI::battleStackMoved(const CStack * stack, std::vector<BattleHex> | ||||
| 	battleAI->battleStackMoved(stack, dest, distance); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::battleAttack(const BattleAttack *ba) | ||||
| void CAdventureAI::battleAttack(const BattleAttack * ba) | ||||
| { | ||||
| 	battleAI->battleAttack(ba); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::battleSpellCast(const BattleSpellCast *sc) | ||||
| void CAdventureAI::battleSpellCast(const BattleSpellCast * sc) | ||||
| { | ||||
| 	battleAI->battleSpellCast(sc); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::battleEnd(const BattleResult *br) | ||||
| void CAdventureAI::battleEnd(const BattleResult * br) | ||||
| { | ||||
| 	battleAI->battleEnd(br); | ||||
| 	battleAI.reset(); | ||||
| } | ||||
|  | ||||
| void CAdventureAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) | ||||
| void CAdventureAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, | ||||
| 										 bool tentHeal, si32 lifeDrainFrom) | ||||
| { | ||||
| 	battleAI->battleStacksHealedRes(healedStacks, lifeDrain, tentHeal, lifeDrainFrom); | ||||
| } | ||||
|   | ||||
| @@ -127,6 +127,8 @@ set(lib_SRCS | ||||
| 		registerTypes/TypesMapObjects3.cpp | ||||
| 		registerTypes/TypesPregamePacks.cpp | ||||
| 		registerTypes/TypesServerPacks.cpp | ||||
| 		 | ||||
| 		${VCMILIB_ADDITIONAL_SOURCES} | ||||
| ) | ||||
|  | ||||
| set(lib_HEADERS | ||||
| @@ -180,6 +182,10 @@ if(WIN32) | ||||
| 	set_target_properties(vcmi PROPERTIES OUTPUT_NAME VCMI_lib) | ||||
| endif() | ||||
|  | ||||
| if (ANDROID) | ||||
| 	return() | ||||
| endif() | ||||
|  | ||||
| set_target_properties(vcmi PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmi) | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,11 @@ namespace bfs = boost::filesystem; | ||||
|  | ||||
| bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; } | ||||
|  | ||||
| bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const | ||||
| { | ||||
| 	return libraryPath() / desiredFolder / libraryName(baseLibName); | ||||
| } | ||||
|  | ||||
| void IVCMIDirs::init() | ||||
| { | ||||
| 	// TODO: Log errors | ||||
| @@ -24,6 +29,11 @@ void IVCMIDirs::init() | ||||
| 	bfs::create_directories(userSavePath()); | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| #include "CAndroidVMHelper.h" | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef VCMI_WINDOWS | ||||
|  | ||||
| #ifdef __MINGW32__ | ||||
| @@ -532,26 +542,55 @@ bfs::path VCMIDirsXDG::libraryPath() const { return M_LIB_DIR; } | ||||
| bfs::path VCMIDirsXDG::binaryPath() const { return M_BIN_DIR; } | ||||
|  | ||||
| std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| class VCMIDirsAndroid : public VCMIDirsXDG | ||||
| { | ||||
| 	std::string basePath; | ||||
| 	std::string internalPath; | ||||
| 	std::string nativePath; | ||||
| public: | ||||
| 	boost::filesystem::path userDataPath() const override; | ||||
| 	boost::filesystem::path userCachePath() const override; | ||||
| 	boost::filesystem::path userConfigPath() const override; | ||||
| 	bfs::path fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const override; | ||||
| 	bfs::path libraryPath() const override; | ||||
| 	bfs::path userDataPath() const override; | ||||
| 	bfs::path userCachePath() const override; | ||||
| 	bfs::path userConfigPath() const override; | ||||
|  | ||||
| 	std::vector<boost::filesystem::path> dataPaths() const override; | ||||
| 	std::vector<bfs::path> dataPaths() const override; | ||||
|  | ||||
| 	void init() override; | ||||
| }; | ||||
|  | ||||
| // on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/ | ||||
| bfs::path VCMIDirsAndroid::userDataPath() const { return getenv("HOME"); } | ||||
| bfs::path VCMIDirsAndroid::libraryPath() const { return nativePath; } | ||||
| bfs::path VCMIDirsAndroid::userDataPath() const { return basePath; } | ||||
| bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; } | ||||
| bfs::path VCMIDirsAndroid::userConfigPath() const { return userDataPath() / "config"; } | ||||
|  | ||||
| bfs::path VCMIDirsAndroid::fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const | ||||
| { | ||||
| 	// ignore passed folder (all libraries in android are dumped into a single folder) | ||||
| 	return libraryPath() / libraryName(baseLibName); | ||||
| } | ||||
|  | ||||
| std::vector<bfs::path> VCMIDirsAndroid::dataPaths() const | ||||
| { | ||||
| 	return std::vector<bfs::path>(1, userDataPath()); | ||||
| 	std::vector<bfs::path> paths(2); | ||||
| 	paths.push_back(internalPath); | ||||
| 	paths.push_back(userDataPath()); | ||||
| 	return paths; | ||||
| } | ||||
|  | ||||
| void VCMIDirsAndroid::init() | ||||
| { | ||||
| 	// asks java code to retrieve needed paths from environment | ||||
| 	CAndroidVMHelper envHelper; | ||||
| 	basePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "dataRoot"); | ||||
| 	internalPath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "internalDataRoot"); | ||||
| 	nativePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "nativePath"); | ||||
| 	IVCMIDirs::init(); | ||||
| } | ||||
|  | ||||
| #endif // VCMI_ANDROID | ||||
| #endif // VCMI_APPLE, VCMI_XDG | ||||
| #endif // VCMI_WINDOWS, VCMI_UNIX | ||||
| @@ -569,7 +608,8 @@ namespace VCMIDirs | ||||
| 			static VCMIDirsXDG singleton; | ||||
| 		#elif defined(VCMI_APPLE) | ||||
| 			static VCMIDirsOSX singleton; | ||||
| 		#endif | ||||
|         #endif | ||||
|  | ||||
| 		static bool initialized = false; | ||||
| 		if (!initialized) | ||||
| 		{ | ||||
| @@ -584,3 +624,4 @@ namespace VCMIDirs | ||||
| 		return singleton; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,48 +11,53 @@ | ||||
|  | ||||
| class DLL_LINKAGE IVCMIDirs | ||||
| { | ||||
| 	public: | ||||
| 		// Path to user-specific data directory | ||||
| 		virtual boost::filesystem::path userDataPath() const = 0; | ||||
| public: | ||||
| 	// Path to user-specific data directory | ||||
| 	virtual boost::filesystem::path userDataPath() const = 0; | ||||
|  | ||||
| 		// Path to "cache" directory, can be used for any non-essential files | ||||
| 		virtual boost::filesystem::path userCachePath() const = 0; | ||||
| 	// Path to "cache" directory, can be used for any non-essential files | ||||
| 	virtual boost::filesystem::path userCachePath() const = 0; | ||||
|  | ||||
| 		// Path to writeable directory with user configs | ||||
| 		virtual boost::filesystem::path userConfigPath() const = 0; | ||||
| 	// Path to writeable directory with user configs | ||||
| 	virtual boost::filesystem::path userConfigPath() const = 0; | ||||
|  | ||||
| 		// Path to saved games | ||||
| 		virtual boost::filesystem::path userSavePath() const; | ||||
| 	// Path to saved games | ||||
| 	virtual boost::filesystem::path userSavePath() const; | ||||
|  | ||||
| 		// Paths to global system-wide data directories. First items have higher priority | ||||
| 		virtual std::vector<boost::filesystem::path> dataPaths() const = 0; | ||||
| 	// Paths to global system-wide data directories. First items have higher priority | ||||
| 	virtual std::vector<boost::filesystem::path> dataPaths() const = 0; | ||||
|  | ||||
| 		// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient) | ||||
| 		virtual boost::filesystem::path clientPath() const = 0; | ||||
| 	// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient) | ||||
| 	virtual boost::filesystem::path clientPath() const = 0; | ||||
|  | ||||
| 		// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver) | ||||
| 		virtual boost::filesystem::path serverPath() const = 0; | ||||
| 	// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver) | ||||
| 	virtual boost::filesystem::path serverPath() const = 0; | ||||
|  | ||||
| 		// Path where vcmi libraries can be found (in AI and Scripting subdirectories) | ||||
| 		virtual boost::filesystem::path libraryPath() const = 0; | ||||
| 	// Path where vcmi libraries can be found (in AI and Scripting subdirectories) | ||||
| 	virtual boost::filesystem::path libraryPath() const = 0; | ||||
|  | ||||
| 		// Path where vcmi binaries can be found | ||||
| 		virtual boost::filesystem::path binaryPath() const = 0; | ||||
| 	// absolute path to passed library (needed due to android libs being placed in single dir, not respecting original lib dirs; | ||||
| 	// by default just concats libraryPath, given folder and libraryName | ||||
| 	virtual boost::filesystem::path fullLibraryPath(const std::string & desiredFolder, | ||||
| 													const std::string & baseLibName) const; | ||||
|  | ||||
| 		// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll") | ||||
| 		virtual std::string libraryName(const std::string& basename) const = 0; | ||||
| 		// virtual std::string libraryName(const char* basename) const = 0; ? | ||||
| 		// virtual std::string libraryName(std::string&& basename) const = 0;? | ||||
| 	// Path where vcmi binaries can be found | ||||
| 	virtual boost::filesystem::path binaryPath() const = 0; | ||||
|  | ||||
| 		virtual std::string genHelpString() const = 0; | ||||
| 		 | ||||
| 		// Creates not existed, but required directories. | ||||
| 		// Updates directories what change name/path between versions. | ||||
| 		// Function called automatically. | ||||
| 		virtual void init(); | ||||
| 	// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll") | ||||
| 	virtual std::string libraryName(const std::string & basename) const = 0; | ||||
| 	// virtual std::string libraryName(const char* basename) const = 0; ? | ||||
| 	// virtual std::string libraryName(std::string&& basename) const = 0;? | ||||
|  | ||||
| 	virtual std::string genHelpString() const = 0; | ||||
|  | ||||
| 	// Creates not existed, but required directories. | ||||
| 	// Updates directories what change name/path between versions. | ||||
| 	// Function called automatically. | ||||
| 	virtual void init(); | ||||
| }; | ||||
|  | ||||
| namespace VCMIDirs | ||||
| { | ||||
| 	extern DLL_LINKAGE const IVCMIDirs& get(); | ||||
| 	extern DLL_LINKAGE const IVCMIDirs & get(); | ||||
| } | ||||
|   | ||||
| @@ -345,8 +345,8 @@ void CLogConsoleTarget::write(const LogRecord & record) | ||||
| 	std::string message = formatter.format(record); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
| 	__android_log_write(ELogLevel::toAndroid(record.level), "VCMI", message.c_str()); | ||||
| #endif | ||||
|     __android_log_write(ELogLevel::toAndroid(record.level), ("VCMI-" + record.domain.getName()).c_str(), message.c_str()); | ||||
| #else | ||||
|  | ||||
| 	const bool printToStdErr = record.level >= ELogLevel::WARN; | ||||
| 	if(console) | ||||
| @@ -364,6 +364,7 @@ void CLogConsoleTarget::write(const LogRecord & record) | ||||
| 		else | ||||
| 			std::cout << message << std::endl; | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bool CLogConsoleTarget::isColoredOutputEnabled() const { return coloredOutputEnabled; } | ||||
|   | ||||
| @@ -12,6 +12,10 @@ set(server_SRCS | ||||
| 		NetPacksServer.cpp | ||||
| ) | ||||
|  | ||||
| if(ANDROID) # android needs client/server to be libraries, not executables, so we can't reuse the build part of this script | ||||
| 	return() | ||||
| endif() | ||||
|  | ||||
| add_executable(vcmiserver ${server_SRCS}) | ||||
|  | ||||
| target_link_libraries(vcmiserver vcmi ${Boost_LIBRARIES} ${SYSTEM_LIBS}) | ||||
| @@ -27,4 +31,3 @@ cotire(vcmiserver) | ||||
| if (NOT APPLE) # Already inside vcmiclient bundle | ||||
| 	install(TARGETS vcmiserver DESTINATION ${BIN_DIR}) | ||||
| endif() | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,9 @@ | ||||
| #include "../lib/StartInfo.h" | ||||
| #include "../lib/mapping/CMap.h" | ||||
| #include "../lib/rmg/CMapGenOptions.h" | ||||
| #ifndef VCMI_ANDROID | ||||
| #ifdef VCMI_ANDROID | ||||
| #include "lib/CAndroidVMHelper.h" | ||||
| #else | ||||
| #include "../lib/Interprocess.h" | ||||
| #endif | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| @@ -417,18 +419,24 @@ void CVCMIServer::start() | ||||
| #ifndef VCMI_ANDROID | ||||
| 	sr->setToTrueAndNotify(); | ||||
| 	delete mr; | ||||
| #else | ||||
| 	{ // in block to clean-up vm helper after use, because we don't need to keep this thread attached to vm | ||||
| 		CAndroidVMHelper envHelper; | ||||
| 		envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady"); | ||||
| 		logNetwork->info("Sending server ready message to client"); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	acc.join(); | ||||
| 	if (error) | ||||
| 	{ | ||||
| 		logNetwork->warnStream()<<"Got connection but there is an error " << error; | ||||
| 		logNetwork->warnStream() << "Got connection but there is an error " << error; | ||||
| 		return; | ||||
| 	} | ||||
| 	logNetwork->info("We've accepted someone... "); | ||||
| 	firstConnection = new CConnection(s,NAME); | ||||
| 	firstConnection = new CConnection(s, NAME); | ||||
| 	logNetwork->info("Got connection!"); | ||||
| 	while(!end2) | ||||
| 	while (!end2) | ||||
| 	{ | ||||
| 		ui8 mode; | ||||
| 		*firstConnection >> mode; | ||||
| @@ -490,6 +498,8 @@ void CVCMIServer::loadGame() | ||||
| 	gh.run(true); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| static void handleCommandOptions(int argc, char *argv[]) | ||||
| { | ||||
| 	namespace po = boost::program_options; | ||||
| @@ -567,15 +577,16 @@ int main(int argc, char** argv) | ||||
| 	// to log stacktrace | ||||
| 	#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID) | ||||
| 	signal(SIGSEGV, handleLinuxSignal); | ||||
| 	#endif | ||||
|     #endif | ||||
|  | ||||
| 	console = new CConsoleHandler; | ||||
| 	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console); | ||||
| 	logConfig.configureDefault(); | ||||
| 	logGlobal->info(NAME); | ||||
|  | ||||
|  | ||||
| 	handleCommandOptions(argc, argv); | ||||
| 	if(cmdLineOptions.count("port")) | ||||
| 	if (cmdLineOptions.count("port")) | ||||
| 		port = cmdLineOptions["port"].as<int>(); | ||||
| 	logNetwork->info("Port %d will be used.", port); | ||||
|  | ||||
| @@ -592,18 +603,18 @@ int main(int argc, char** argv) | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			while(!end2) | ||||
| 			while (!end2) | ||||
| 			{ | ||||
| 				server.start(); | ||||
| 			} | ||||
| 			io_service.run(); | ||||
| 		} | ||||
| 		catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection | ||||
| 		catch (boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection | ||||
| 		{ | ||||
| 			logNetwork->error(e.what()); | ||||
| 			end2 = true; | ||||
| 		} | ||||
| 		catch(...) | ||||
| 		catch (...) | ||||
| 		{ | ||||
| 			handleException(); | ||||
| 		} | ||||
| @@ -615,9 +626,23 @@ int main(int argc, char** argv) | ||||
| 		//and return non-zero status so client can detect error | ||||
| 		throw; | ||||
| 	} | ||||
| #ifdef VCMI_ANDROID | ||||
| 	CAndroidVMHelper envHelper; | ||||
| 	envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer"); | ||||
| #endif | ||||
| 	delete VLC; | ||||
| 	VLC = nullptr; | ||||
| 	CResourceHandler::clear(); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|  | ||||
| void CVCMIServer::create() | ||||
| { | ||||
| 	const char * foo[1] = {"android-server"}; | ||||
| 	main(1, const_cast<char **>(foo)); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -57,8 +57,13 @@ public: | ||||
| 	void newGame(); | ||||
| 	void loadGame(); | ||||
| 	void newPregame(); | ||||
|  | ||||
| #ifdef VCMI_ANDROID | ||||
|     static void create(); | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| struct StartInfo; | ||||
| class CPregameServer | ||||
| { | ||||
| public: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user