diff --git a/AI/BattleAI/CMakeLists.txt b/AI/BattleAI/CMakeLists.txt index 17a123541..3ca0626c6 100644 --- a/AI/BattleAI/CMakeLists.txt +++ b/AI/BattleAI/CMakeLists.txt @@ -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) diff --git a/AI/BattleAI/main.cpp b/AI/BattleAI/main.cpp index 9c1f08a58..349a205a6 100644 --- a/AI/BattleAI/main.cpp +++ b/AI/BattleAI/main.cpp @@ -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() diff --git a/AI/FuzzyLite/fuzzylite/CMakeLists.txt b/AI/FuzzyLite/fuzzylite/CMakeLists.txt index bb605af3e..ca25fea0e 100644 --- a/AI/FuzzyLite/fuzzylite/CMakeLists.txt +++ b/AI/FuzzyLite/fuzzylite/CMakeLists.txt @@ -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}") diff --git a/AI/StupidAI/main.cpp b/AI/StupidAI/main.cpp index b62f53ba0..43ae27888 100644 --- a/AI/StupidAI/main.cpp +++ b/AI/StupidAI/main.cpp @@ -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 &out) { out = std::make_shared(); -} +} \ No newline at end of file diff --git a/AI/VCAI/CMakeLists.txt b/AI/VCAI/CMakeLists.txt index 067588178..5c0a90204 100644 --- a/AI/VCAI/CMakeLists.txt +++ b/AI/VCAI/CMakeLists.txt @@ -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() diff --git a/AI/VCAI/main.cpp b/AI/VCAI/main.cpp index 26f49cd9e..0ae2d6257 100644 --- a/AI/VCAI/main.cpp +++ b/AI/VCAI/main.cpp @@ -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 &out) { out = std::make_shared(); -} +} \ No newline at end of file diff --git a/Global.h b/Global.h index c795394f4..98dcefaf7 100644 --- a/Global.h +++ b/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 + inline std::string to_string(const T& value) + { + std::ostringstream ss; + ss << value; + return ss.str(); + } +} +#endif // NO_STD_TOSTRING diff --git a/client/CMT.cpp b/client/CMT.cpp index 3620021b3..a76a9f58e 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -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: "<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): "<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(); diff --git a/client/Client.cpp b/client/Client.cpp index 92cf2676e..7779aabe8 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -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 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 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 diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 0c9041603..522068eb6 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -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(swipeInitialRealPos.x - sEvent.x) / 32; + adventureInt->swipeTargetPosition.y = + swipeInitialMapPos.y + static_cast(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) diff --git a/client/windows/CAdvmapInterface.h b/client/windows/CAdvmapInterface.h index d626a88dd..8ae36b743 100644 --- a/client/windows/CAdvmapInterface.h +++ b/client/windows/CAdvmapInterface.h @@ -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; diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 816a00210..285ad42b4 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -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" : { diff --git a/include/vstd/CLoggerBase.h b/include/vstd/CLoggerBase.h index e9e9b7868..c387dc936 100644 --- a/include/vstd/CLoggerBase.h +++ b/include/vstd/CLoggerBase.h @@ -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 } } } diff --git a/lib/CAndroidVMHelper.cpp b/lib/CAndroidVMHelper.cpp new file mode 100644 index 000000000..dd416b140 --- /dev/null +++ b/lib/CAndroidVMHelper.cpp @@ -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(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(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;"); +} diff --git a/lib/CAndroidVMHelper.h b/lib/CAndroidVMHelper.h new file mode 100644 index 000000000..5493a5e0e --- /dev/null +++ b/lib/CAndroidVMHelper.h @@ -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 +#include + +/// 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"; +}; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 6caf1a913..30880332e 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -5,13 +5,21 @@ #include "VCMIDirs.h" #ifdef VCMI_WINDOWS - #include //for .dll libs -#else - #include +#include //for .dll libs +#elif !defined VCMI_ANDROID +#include #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 &out); - -extern "C" DLL_EXPORT void StupidAI_GetAiName(char* name); -extern "C" DLL_EXPORT void StupidAI_GetNewBattleAI(std::shared_ptr &out); - -extern "C" DLL_EXPORT void BattleAI_GetAiName(char* name); -extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(std::shared_ptr &out); -#endif - template -std::shared_ptr createAny(const boost::filesystem::path& libpath, const std::string& methodName) +std::shared_ptr createAny(const boost::filesystem::path & libpath, const std::string & methodName) { - typedef void(*TGetAIFun)(std::shared_ptr&); - 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 &); + 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 createAny(const boost::filesystem::path& libpath, const st else logGlobal->errorStream() << "Error: " << dlerror(); #endif // VCMI_WINDOWS + if (!dll) { logGlobal->errorStream() << "Cannot open dynamic library ("< 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 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 createAny(const boost::filesystem::path & libpath, const std::string & methodName) +{ + return std::make_shared(); +} + +template<> +std::shared_ptr createAny(const boost::filesystem::path & libpath, const std::string & methodName) +{ + return std::make_shared(); +} + +#endif + template -std::shared_ptr createAnyAI(std::string dllname, const std::string& methodName) +std::shared_ptr 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(filePath, methodName); ret->dllName = std::move(dllname); return ret; @@ -127,7 +124,7 @@ std::shared_ptr CDynLibHandler::getNewAI(std::string dllname) return createAnyAI(dllname, "GetNewAI"); } -std::shared_ptr CDynLibHandler::getNewBattleAI(std::string dllname ) +std::shared_ptr CDynLibHandler::getNewBattleAI(std::string dllname) { return createAnyAI(dllname, "GetNewBattleAI"); } @@ -137,9 +134,10 @@ std::shared_ptr CDynLibHandler::getNewScriptingModule(std::str return createAny(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 & 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 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 > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) +void CAdventureAI::battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, + bool tentHeal, si32 lifeDrainFrom) { battleAI->battleStacksHealedRes(healedStacks, lifeDrain, tentHeal, lifeDrainFrom); } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index aa62bf328..ad0ee0b98 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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) diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index a2148a60b..61c580656 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -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 dataPaths() const override; + std::vector 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 VCMIDirsAndroid::dataPaths() const { - return std::vector(1, userDataPath()); + std::vector 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; } } + diff --git a/lib/VCMIDirs.h b/lib/VCMIDirs.h index 0e604346e..ccf225884 100644 --- a/lib/VCMIDirs.h +++ b/lib/VCMIDirs.h @@ -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 dataPaths() const = 0; + // Paths to global system-wide data directories. First items have higher priority + virtual std::vector 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(); } diff --git a/lib/logging/CLogger.cpp b/lib/logging/CLogger.cpp index 0e542e37b..f0081e812 100644 --- a/lib/logging/CLogger.cpp +++ b/lib/logging/CLogger.cpp @@ -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; } diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 563913bd2..5de76501d 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -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() - diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index b745ea940..0222763c4 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -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(); 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(foo)); +} + +#endif diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index 34aa3c6f3..037e219d5 100644 --- a/server/CVCMIServer.h +++ b/server/CVCMIServer.h @@ -57,8 +57,13 @@ public: void newGame(); void loadGame(); void newPregame(); + +#ifdef VCMI_ANDROID + static void create(); +#endif }; +struct StartInfo; class CPregameServer { public: