mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-31 22:05:10 +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:
parent
f370cdf1c7
commit
b5daa24982
@ -15,6 +15,9 @@ set(battleAI_SRCS
|
|||||||
ThreatMap.cpp
|
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})
|
add_library(BattleAI SHARED ${battleAI_SRCS})
|
||||||
target_link_libraries(BattleAI vcmi)
|
target_link_libraries(BattleAI vcmi)
|
||||||
|
|
||||||
|
@ -15,12 +15,6 @@
|
|||||||
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
|
||||||
#define GetGlobalAiVersion BattleAI_GetGlobalAiVersion
|
|
||||||
#define GetAiName BattleAI_GetAiName
|
|
||||||
#define GetNewBattleAI BattleAI_GetNewBattleAI
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char *g_cszAiName = "Battle AI";
|
static const char *g_cszAiName = "Battle AI";
|
||||||
|
|
||||||
extern "C" DLL_EXPORT int GetGlobalAiVersion()
|
extern "C" DLL_EXPORT int GetGlobalAiVersion()
|
||||||
|
@ -57,9 +57,15 @@ if (APPLE)
|
|||||||
add_definitions(-DFL_APPLE)
|
add_definitions(-DFL_APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY bin)
|
if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY bin)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY bin)
|
||||||
set(CMAKE_RUNTIME_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)
|
if(NOT MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "-pedantic -Werror -Wall -Wextra ${CMAKE_CXX_FLAGS}")
|
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)
|
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
||||||
#endif
|
#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";
|
static const char *g_cszAiName = "Stupid AI 0.1";
|
||||||
|
|
||||||
extern "C" DLL_EXPORT int GetGlobalAiVersion()
|
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)
|
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
|
||||||
{
|
{
|
||||||
out = std::make_shared<CStupidAI>();
|
out = std::make_shared<CStupidAI>();
|
||||||
}
|
}
|
@ -17,16 +17,20 @@ set(VCAI_SRCS
|
|||||||
Fuzzy.cpp
|
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})
|
add_library(VCAI SHARED ${VCAI_SRCS})
|
||||||
if (FL_FOUND)
|
if (FL_FOUND)
|
||||||
target_link_libraries(VCAI ${FL_LIBRARIES} vcmi)
|
target_link_libraries(VCAI ${FL_LIBRARIES} vcmi)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(VCAI fl-static vcmi)
|
target_link_libraries(VCAI fl-static vcmi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES})
|
set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES})
|
||||||
cotire(VCAI)
|
cotire(VCAI)
|
||||||
|
|
||||||
if (NOT APPLE) # Already inside vcmiclient bundle
|
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()
|
endif()
|
||||||
|
@ -5,12 +5,6 @@
|
|||||||
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
#define strcpy_s(a, b, c) strncpy(a, c, b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
|
||||||
#define GetGlobalAiVersion VCAI_GetGlobalAiVersion
|
|
||||||
#define GetAiName VCAI_GetAiName
|
|
||||||
#define GetNewAI VCAI_GetNewAI
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char *g_cszAiName = "VCAI";
|
static const char *g_cszAiName = "VCAI";
|
||||||
|
|
||||||
extern "C" DLL_EXPORT int GetGlobalAiVersion()
|
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)
|
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
|
||||||
{
|
{
|
||||||
out = std::make_shared<VCAI>();
|
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.
|
# define _NO_W32_PSEUDO_MODIFIERS // Exclude more macros for compiling with MinGW on Linux.
|
||||||
#endif
|
#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 */
|
/* A macro to force inlining some of our functions */
|
||||||
/* ---------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------- */
|
||||||
@ -698,3 +702,16 @@ namespace vstd
|
|||||||
}
|
}
|
||||||
using vstd::operator-=;
|
using vstd::operator-=;
|
||||||
using vstd::make_unique;
|
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
|
#ifdef VCMI_WINDOWS
|
||||||
#include "SDL_syswm.h"
|
#include "SDL_syswm.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
#include "lib/CAndroidVMHelper.h"
|
||||||
|
#endif
|
||||||
#include "../lib/UnlockGuard.h"
|
#include "../lib/UnlockGuard.h"
|
||||||
#include "CMT.h"
|
#include "CMT.h"
|
||||||
|
|
||||||
@ -203,7 +206,7 @@ void OSX_checkForUpdates();
|
|||||||
|
|
||||||
#if defined(VCMI_WINDOWS) && !defined (__GNUC__)
|
#if defined(VCMI_WINDOWS) && !defined (__GNUC__)
|
||||||
int wmain(int argc, wchar_t* argv[])
|
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[])
|
int SDL_main(int argc, char *argv[])
|
||||||
#else
|
#else
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
@ -436,11 +439,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff();
|
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
|
//initializing audio
|
||||||
CCS->soundh = new CSoundHandler;
|
CCS->soundh = new CSoundHandler;
|
||||||
CCS->soundh->init();
|
CCS->soundh->init();
|
||||||
@ -466,13 +464,22 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
|
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
|
||||||
playIntro();
|
playIntro();
|
||||||
SDL_FillRect(screen,nullptr,0);
|
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(mainRenderer);
|
||||||
}
|
}
|
||||||
|
SDL_RenderPresent(mainRenderer);
|
||||||
CSDL_Ext::update(screen);
|
|
||||||
#ifndef VCMI_NO_THREADED_LOAD
|
#ifndef VCMI_NO_THREADED_LOAD
|
||||||
loading.join();
|
#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds
|
||||||
#endif
|
{
|
||||||
|
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();
|
logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
|
||||||
|
|
||||||
if(!vm.count("battle"))
|
if(!vm.count("battle"))
|
||||||
@ -1027,6 +1034,9 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
|
|||||||
|
|
||||||
cleanupRenderer();
|
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)
|
if(fullscreen)
|
||||||
{
|
{
|
||||||
//in full-screen mode always use desktop resolution
|
//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);
|
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if(nullptr == mainWindow)
|
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);
|
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);
|
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)))
|
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();
|
handleQuit();
|
||||||
|
#endif
|
||||||
return;
|
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)
|
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
|
||||||
{
|
{
|
||||||
Settings full = settings.write["video"]["fullscreen"];
|
Settings full = settings.write["video"]["fullscreen"];
|
||||||
|
@ -65,6 +65,10 @@ set(client_HEADERS
|
|||||||
gui/SDL_Compat.h
|
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)
|
if(APPLE)
|
||||||
# OS X specific includes
|
# OS X specific includes
|
||||||
include_directories(${SPARKLE_INCLUDE_DIR})
|
include_directories(${SPARKLE_INCLUDE_DIR})
|
||||||
@ -133,4 +137,3 @@ cotire(vcmiclient)
|
|||||||
|
|
||||||
|
|
||||||
install(TARGETS vcmiclient DESTINATION ${BIN_DIR})
|
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)
|
for (int i=1; i<32; i+=2*speed)
|
||||||
{
|
{
|
||||||
movementPxStep(details, i, hp, hero);
|
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;
|
adventureInt->updateScreen = true;
|
||||||
|
#endif
|
||||||
adventureInt->show(screen);
|
adventureInt->show(screen);
|
||||||
{
|
{
|
||||||
//evil returns here ...
|
//evil returns here ...
|
||||||
@ -1647,7 +1651,11 @@ void CPlayerInterface::update()
|
|||||||
GH.updateTime();
|
GH.updateTime();
|
||||||
GH.handleEvents();
|
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
|
if (adventureInt && !adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
|
||||||
|
#endif // !VCMI_ANDROID
|
||||||
GH.totalRedraw();
|
GH.totalRedraw();
|
||||||
else
|
else
|
||||||
GH.simpleRedraw();
|
GH.simpleRedraw();
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
extern std::string NAME;
|
extern std::string NAME;
|
||||||
#ifndef VCMI_ANDROID
|
#ifndef VCMI_ANDROID
|
||||||
namespace intpr = boost::interprocess;
|
namespace intpr = boost::interprocess;
|
||||||
|
#else
|
||||||
|
#include "lib/CAndroidVMHelper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -55,6 +57,10 @@ namespace intpr = boost::interprocess;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
std::atomic_bool androidTestServerReadyFlag;
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T> class CApplyOnCL;
|
template <typename T> class CApplyOnCL;
|
||||||
|
|
||||||
class CBaseForCLApply
|
class CBaseForCLApply
|
||||||
@ -913,8 +919,7 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
|
|||||||
{
|
{
|
||||||
if(ps.name.size())
|
if(ps.name.size())
|
||||||
{
|
{
|
||||||
const boost::filesystem::path aiPath =
|
const boost::filesystem::path aiPath = VCMIDirs::get().fullLibraryPath("AI", ps.name);
|
||||||
VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(ps.name);
|
|
||||||
if (boost::filesystem::exists(aiPath))
|
if (boost::filesystem::exists(aiPath))
|
||||||
return ps.name;
|
return ps.name;
|
||||||
}
|
}
|
||||||
@ -940,7 +945,12 @@ void CServerHandler::startServer()
|
|||||||
|
|
||||||
th.update();
|
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;
|
serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable;
|
||||||
|
#endif
|
||||||
if(verbose)
|
if(verbose)
|
||||||
logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
|
logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
|
||||||
}
|
}
|
||||||
@ -954,12 +964,22 @@ void CServerHandler::waitForServer()
|
|||||||
startServer();
|
startServer();
|
||||||
|
|
||||||
th.update();
|
th.update();
|
||||||
|
|
||||||
#ifndef VCMI_ANDROID
|
#ifndef VCMI_ANDROID
|
||||||
intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
|
intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
|
||||||
while(!shared->sr->ready)
|
while(!shared->sr->ready)
|
||||||
{
|
{
|
||||||
shared->sr->cond.wait(slock);
|
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
|
#endif
|
||||||
if(verbose)
|
if(verbose)
|
||||||
logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
|
logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
|
||||||
@ -1017,6 +1037,7 @@ CServerHandler::~CServerHandler()
|
|||||||
|
|
||||||
void CServerHandler::callServer()
|
void CServerHandler::callServer()
|
||||||
{
|
{
|
||||||
|
#ifndef VCMI_ANDROID
|
||||||
setThreadName("CServerHandler::callServer");
|
setThreadName("CServerHandler::callServer");
|
||||||
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
|
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
|
||||||
const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
|
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";
|
logNetwork->errorStream() << "Check " << logName << " for more info";
|
||||||
exit(1);// exit in case of error. Othervice without working server VCMI will hang
|
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)
|
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;
|
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)
|
void CTerrainRect::clickLeft(tribool down, bool previousState)
|
||||||
{
|
{
|
||||||
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
|
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
|
||||||
return;
|
return;
|
||||||
if ((down==false) || indeterminate(down))
|
if(indeterminate(down))
|
||||||
return;
|
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();
|
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;
|
return;
|
||||||
|
|
||||||
adventureInt->tileLClicked(mp);
|
adventureInt->tileLClicked(mp);
|
||||||
@ -130,17 +153,59 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
|
|||||||
|
|
||||||
void CTerrainRect::clickRight(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;
|
return;
|
||||||
int3 mp = whichTileIsIt();
|
int3 mp = whichTileIsIt();
|
||||||
|
|
||||||
if (CGI->mh->map->isInTheMap(mp) && down)
|
if(CGI->mh->map->isInTheMap(mp) && down)
|
||||||
adventureInt->tileRClicked(mp);
|
adventureInt->tileRClicked(mp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
|
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);
|
int3 pom = adventureInt->verifyPos(tHovered);
|
||||||
|
|
||||||
if(tHovered != pom) //tile outside the map
|
if(tHovered != pom) //tile outside the map
|
||||||
@ -150,12 +215,12 @@ void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pom != curHoveredTile)
|
if (pom != curHoveredTile)
|
||||||
|
{
|
||||||
curHoveredTile = pom;
|
curHoveredTile = pom;
|
||||||
else
|
adventureInt->tileHovered(pom);
|
||||||
return;
|
}
|
||||||
|
|
||||||
adventureInt->tileHovered(pom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTerrainRect::hover(bool on)
|
void CTerrainRect::hover(bool on)
|
||||||
{
|
{
|
||||||
if (!on)
|
if (!on)
|
||||||
@ -470,6 +535,10 @@ CAdvMapInt::CAdvMapInt():
|
|||||||
spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
|
spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
|
||||||
updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
|
updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
|
||||||
activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false)
|
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;
|
adventureInt = this;
|
||||||
pos.x = pos.y = 0;
|
pos.x = pos.y = 0;
|
||||||
@ -938,42 +1007,19 @@ void CAdvMapInt::show(SDL_Surface * to)
|
|||||||
}
|
}
|
||||||
++heroAnim;
|
++heroAnim;
|
||||||
|
|
||||||
int scrollSpeed = settings["adventure"]["scrollSpeed"].Float();
|
#ifdef VCMI_ANDROID
|
||||||
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
|
if(swipeEnabled)
|
||||||
if((animValHitCount % (4/scrollSpeed)) == 0
|
|
||||||
&& (
|
|
||||||
(GH.topInt() == this)
|
|
||||||
|| isCtrlKeyDown()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if( (scrollingDir & LEFT) && (position.x>-CGI->mh->frameW) )
|
handleSwipeUpdate();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#endif // !VCMI_ANDROID
|
||||||
|
handleMapScrollingUpdate();
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++)
|
for(int i = 0; i < 4; i++)
|
||||||
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
||||||
if(updateScreen)
|
if(updateScreen)
|
||||||
@ -1002,6 +1048,59 @@ void CAdvMapInt::show(SDL_Surface * to)
|
|||||||
statusbar.showAll(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()
|
void CAdvMapInt::selectionChanged()
|
||||||
{
|
{
|
||||||
const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
|
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 )
|
void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
|
||||||
{
|
{
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
if(swipeEnabled)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
// adventure map scrolling with mouse
|
// 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
|
// 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)
|
if(!isCtrlKeyDown() && isActive() && mode == EAdvMapMode::NORMAL)
|
||||||
|
@ -56,6 +56,16 @@ class CTerrainRect
|
|||||||
SDL_Surface * fadeSurface;
|
SDL_Surface * fadeSurface;
|
||||||
EMapAnimRedrawStatus lastRedrawStatus;
|
EMapAnimRedrawStatus lastRedrawStatus;
|
||||||
CFadeAnimation * fadeAnim;
|
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:
|
public:
|
||||||
int tilesw, tilesh; //width and height of terrain to blit in tiles
|
int tilesw, tilesh; //width and height of terrain to blit in tiles
|
||||||
int3 curHoveredTile;
|
int3 curHoveredTile;
|
||||||
@ -80,6 +90,7 @@ public:
|
|||||||
/// animates view by caching current surface and crossfading it with normal screen
|
/// animates view by caching current surface and crossfading it with normal screen
|
||||||
void fadeFromCurrentView();
|
void fadeFromCurrentView();
|
||||||
bool needsAnimUpdate();
|
bool needsAnimUpdate();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Resources bar which shows information about how many gold, crystals,... you have
|
/// 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};
|
enum{LEFT=1, RIGHT=2, UP=4, DOWN=8};
|
||||||
ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN
|
ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN
|
||||||
bool scrollingState;
|
bool scrollingState;
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
bool swipeEnabled;
|
||||||
|
bool swipeMovementRequested;
|
||||||
|
int3 swipeTargetPosition;
|
||||||
|
#endif // !VCMI_ANDROID
|
||||||
|
|
||||||
enum{NA, INGAME, WAITING} state;
|
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
|
/// 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 changeMode(EAdvMapMode newMode, float newScale = 0.36f);
|
||||||
|
|
||||||
|
void handleMapScrollingUpdate();
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
void handleSwipeUpdate();
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CAdvMapInt *adventureInt;
|
extern CAdvMapInt *adventureInt;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"default": {},
|
"default": {},
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"required" : [ "playerName", "showfps", "music", "sound", "encoding" ],
|
"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"playerName" : {
|
"playerName" : {
|
||||||
"type":"string",
|
"type":"string",
|
||||||
@ -38,7 +38,11 @@
|
|||||||
"encoding" : {
|
"encoding" : {
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"default" : "CP1252"
|
"default" : "CP1252"
|
||||||
}
|
},
|
||||||
|
"swipe" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"video" : {
|
"video" : {
|
||||||
|
@ -38,7 +38,11 @@ namespace ELogLevel
|
|||||||
case ERROR:
|
case ERROR:
|
||||||
return "error";
|
return "error";
|
||||||
default:
|
default:
|
||||||
|
#ifdef NO_STD_TOSTRING
|
||||||
|
return "invalid";
|
||||||
|
#else
|
||||||
return std::string("invalid (") + std::to_string(level) + ")";
|
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"
|
#include "VCMIDirs.h"
|
||||||
|
|
||||||
#ifdef VCMI_WINDOWS
|
#ifdef VCMI_WINDOWS
|
||||||
#include <windows.h> //for .dll libs
|
#include <windows.h> //for .dll libs
|
||||||
#else
|
#elif !defined VCMI_ANDROID
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "serializer/BinaryDeserializer.h"
|
#include "serializer/BinaryDeserializer.h"
|
||||||
#include "serializer/BinarySerializer.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
|
* 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>
|
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>&);
|
#ifdef VCMI_ANDROID
|
||||||
typedef void(*TGetNameFun)(char*);
|
// 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];
|
char temp[150];
|
||||||
|
|
||||||
TGetAIFun getAI = nullptr;
|
TGetAIFun getAI = nullptr;
|
||||||
TGetNameFun getName = 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
|
#ifdef VCMI_WINDOWS
|
||||||
HMODULE dll = LoadLibraryW(libpath.c_str());
|
HMODULE dll = LoadLibraryW(libpath.c_str());
|
||||||
if (dll)
|
if (dll)
|
||||||
@ -83,6 +63,7 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st
|
|||||||
else
|
else
|
||||||
logGlobal->errorStream() << "Error: " << dlerror();
|
logGlobal->errorStream() << "Error: " << dlerror();
|
||||||
#endif // VCMI_WINDOWS
|
#endif // VCMI_WINDOWS
|
||||||
|
|
||||||
if (!dll)
|
if (!dll)
|
||||||
{
|
{
|
||||||
logGlobal->errorStream() << "Cannot open dynamic library ("<<libpath<<"). Throwing...";
|
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
|
#endif
|
||||||
throw std::runtime_error("Cannot find method " + methodName);
|
throw std::runtime_error("Cannot find method " + methodName);
|
||||||
}
|
}
|
||||||
#endif // VCMI_ANDROID
|
|
||||||
|
|
||||||
getName(temp);
|
getName(temp);
|
||||||
logGlobal->infoStream() << "Loaded " << 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!");
|
logGlobal->error("Cannot get AI!");
|
||||||
|
|
||||||
return ret;
|
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>
|
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;
|
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);
|
auto ret = createAny<rett>(filePath, methodName);
|
||||||
ret->dllName = std::move(dllname);
|
ret->dllName = std::move(dllname);
|
||||||
return ret;
|
return ret;
|
||||||
@ -127,7 +124,7 @@ std::shared_ptr<CGlobalAI> CDynLibHandler::getNewAI(std::string dllname)
|
|||||||
return createAnyAI<CGlobalAI>(dllname, "GetNewAI");
|
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");
|
return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI");
|
||||||
}
|
}
|
||||||
@ -137,9 +134,10 @@ std::shared_ptr<CScriptingModule> CDynLibHandler::getNewScriptingModule(std::str
|
|||||||
return createAny<CScriptingModule>(dllname, "GetNewModule");
|
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;
|
ba.stackNumber = stack->ID;
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
@ -159,7 +157,8 @@ void CAdventureAI::battleCatapultAttacked(const CatapultAttack & ca)
|
|||||||
battleAI->battleCatapultAttacked(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(!battleAI);
|
||||||
assert(cbc);
|
assert(cbc);
|
||||||
@ -173,7 +172,7 @@ void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> &
|
|||||||
battleAI->battleStacksAttacked(bsa);
|
battleAI->battleStacksAttacked(bsa);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::actionStarted(const BattleAction &action)
|
void CAdventureAI::actionStarted(const BattleAction & action)
|
||||||
{
|
{
|
||||||
battleAI->actionStarted(action);
|
battleAI->actionStarted(action);
|
||||||
}
|
}
|
||||||
@ -183,7 +182,7 @@ void CAdventureAI::battleNewRoundFirst(int round)
|
|||||||
battleAI->battleNewRoundFirst(round);
|
battleAI->battleNewRoundFirst(round);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::actionFinished(const BattleAction &action)
|
void CAdventureAI::actionFinished(const BattleAction & action)
|
||||||
{
|
{
|
||||||
battleAI->actionFinished(action);
|
battleAI->actionFinished(action);
|
||||||
}
|
}
|
||||||
@ -213,23 +212,24 @@ void CAdventureAI::battleStackMoved(const CStack * stack, std::vector<BattleHex>
|
|||||||
battleAI->battleStackMoved(stack, dest, distance);
|
battleAI->battleStackMoved(stack, dest, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleAttack(const BattleAttack *ba)
|
void CAdventureAI::battleAttack(const BattleAttack * ba)
|
||||||
{
|
{
|
||||||
battleAI->battleAttack(ba);
|
battleAI->battleAttack(ba);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleSpellCast(const BattleSpellCast *sc)
|
void CAdventureAI::battleSpellCast(const BattleSpellCast * sc)
|
||||||
{
|
{
|
||||||
battleAI->battleSpellCast(sc);
|
battleAI->battleSpellCast(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleEnd(const BattleResult *br)
|
void CAdventureAI::battleEnd(const BattleResult * br)
|
||||||
{
|
{
|
||||||
battleAI->battleEnd(br);
|
battleAI->battleEnd(br);
|
||||||
battleAI.reset();
|
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);
|
battleAI->battleStacksHealedRes(healedStacks, lifeDrain, tentHeal, lifeDrainFrom);
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,8 @@ set(lib_SRCS
|
|||||||
registerTypes/TypesMapObjects3.cpp
|
registerTypes/TypesMapObjects3.cpp
|
||||||
registerTypes/TypesPregamePacks.cpp
|
registerTypes/TypesPregamePacks.cpp
|
||||||
registerTypes/TypesServerPacks.cpp
|
registerTypes/TypesServerPacks.cpp
|
||||||
|
|
||||||
|
${VCMILIB_ADDITIONAL_SOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(lib_HEADERS
|
set(lib_HEADERS
|
||||||
@ -180,6 +182,10 @@ if(WIN32)
|
|||||||
set_target_properties(vcmi PROPERTIES OUTPUT_NAME VCMI_lib)
|
set_target_properties(vcmi PROPERTIES OUTPUT_NAME VCMI_lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ANDROID)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(vcmi PROPERTIES ${PCH_PROPERTIES})
|
set_target_properties(vcmi PROPERTIES ${PCH_PROPERTIES})
|
||||||
cotire(vcmi)
|
cotire(vcmi)
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ namespace bfs = boost::filesystem;
|
|||||||
|
|
||||||
bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
|
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()
|
void IVCMIDirs::init()
|
||||||
{
|
{
|
||||||
// TODO: Log errors
|
// TODO: Log errors
|
||||||
@ -24,6 +29,11 @@ void IVCMIDirs::init()
|
|||||||
bfs::create_directories(userSavePath());
|
bfs::create_directories(userSavePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
#include "CAndroidVMHelper.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef VCMI_WINDOWS
|
#ifdef VCMI_WINDOWS
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
@ -532,26 +542,55 @@ bfs::path VCMIDirsXDG::libraryPath() const { return M_LIB_DIR; }
|
|||||||
bfs::path VCMIDirsXDG::binaryPath() const { return M_BIN_DIR; }
|
bfs::path VCMIDirsXDG::binaryPath() const { return M_BIN_DIR; }
|
||||||
|
|
||||||
std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
|
std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
|
|
||||||
class VCMIDirsAndroid : public VCMIDirsXDG
|
class VCMIDirsAndroid : public VCMIDirsXDG
|
||||||
{
|
{
|
||||||
|
std::string basePath;
|
||||||
|
std::string internalPath;
|
||||||
|
std::string nativePath;
|
||||||
public:
|
public:
|
||||||
boost::filesystem::path userDataPath() const override;
|
bfs::path fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const override;
|
||||||
boost::filesystem::path userCachePath() const override;
|
bfs::path libraryPath() const override;
|
||||||
boost::filesystem::path userConfigPath() 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::libraryPath() const { return nativePath; }
|
||||||
bfs::path VCMIDirsAndroid::userDataPath() const { return getenv("HOME"); }
|
bfs::path VCMIDirsAndroid::userDataPath() const { return basePath; }
|
||||||
bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; }
|
bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; }
|
||||||
bfs::path VCMIDirsAndroid::userConfigPath() const { return userDataPath() / "config"; }
|
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
|
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_ANDROID
|
||||||
#endif // VCMI_APPLE, VCMI_XDG
|
#endif // VCMI_APPLE, VCMI_XDG
|
||||||
#endif // VCMI_WINDOWS, VCMI_UNIX
|
#endif // VCMI_WINDOWS, VCMI_UNIX
|
||||||
@ -569,7 +608,8 @@ namespace VCMIDirs
|
|||||||
static VCMIDirsXDG singleton;
|
static VCMIDirsXDG singleton;
|
||||||
#elif defined(VCMI_APPLE)
|
#elif defined(VCMI_APPLE)
|
||||||
static VCMIDirsOSX singleton;
|
static VCMIDirsOSX singleton;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
@ -584,3 +624,4 @@ namespace VCMIDirs
|
|||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,48 +11,53 @@
|
|||||||
|
|
||||||
class DLL_LINKAGE IVCMIDirs
|
class DLL_LINKAGE IVCMIDirs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Path to user-specific data directory
|
// Path to user-specific data directory
|
||||||
virtual boost::filesystem::path userDataPath() const = 0;
|
virtual boost::filesystem::path userDataPath() const = 0;
|
||||||
|
|
||||||
// Path to "cache" directory, can be used for any non-essential files
|
// Path to "cache" directory, can be used for any non-essential files
|
||||||
virtual boost::filesystem::path userCachePath() const = 0;
|
virtual boost::filesystem::path userCachePath() const = 0;
|
||||||
|
|
||||||
// Path to writeable directory with user configs
|
// Path to writeable directory with user configs
|
||||||
virtual boost::filesystem::path userConfigPath() const = 0;
|
virtual boost::filesystem::path userConfigPath() const = 0;
|
||||||
|
|
||||||
// Path to saved games
|
// Path to saved games
|
||||||
virtual boost::filesystem::path userSavePath() const;
|
virtual boost::filesystem::path userSavePath() const;
|
||||||
|
|
||||||
// Paths to global system-wide data directories. First items have higher priority
|
// Paths to global system-wide data directories. First items have higher priority
|
||||||
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
|
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
|
||||||
|
|
||||||
// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
|
// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
|
||||||
virtual boost::filesystem::path clientPath() const = 0;
|
virtual boost::filesystem::path clientPath() const = 0;
|
||||||
|
|
||||||
// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
|
// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
|
||||||
virtual boost::filesystem::path serverPath() const = 0;
|
virtual boost::filesystem::path serverPath() const = 0;
|
||||||
|
|
||||||
// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
|
// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
|
||||||
virtual boost::filesystem::path libraryPath() const = 0;
|
virtual boost::filesystem::path libraryPath() const = 0;
|
||||||
|
|
||||||
// Path where vcmi binaries can be found
|
// absolute path to passed library (needed due to android libs being placed in single dir, not respecting original lib dirs;
|
||||||
virtual boost::filesystem::path binaryPath() const = 0;
|
// 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")
|
// Path where vcmi binaries can be found
|
||||||
virtual std::string libraryName(const std::string& basename) const = 0;
|
virtual boost::filesystem::path binaryPath() 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;
|
// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll")
|
||||||
|
virtual std::string libraryName(const std::string & basename) const = 0;
|
||||||
// Creates not existed, but required directories.
|
// virtual std::string libraryName(const char* basename) const = 0; ?
|
||||||
// Updates directories what change name/path between versions.
|
// virtual std::string libraryName(std::string&& basename) const = 0;?
|
||||||
// Function called automatically.
|
|
||||||
virtual void init();
|
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
|
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);
|
std::string message = formatter.format(record);
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
__android_log_write(ELogLevel::toAndroid(record.level), "VCMI", message.c_str());
|
__android_log_write(ELogLevel::toAndroid(record.level), ("VCMI-" + record.domain.getName()).c_str(), message.c_str());
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
const bool printToStdErr = record.level >= ELogLevel::WARN;
|
const bool printToStdErr = record.level >= ELogLevel::WARN;
|
||||||
if(console)
|
if(console)
|
||||||
@ -364,6 +364,7 @@ void CLogConsoleTarget::write(const LogRecord & record)
|
|||||||
else
|
else
|
||||||
std::cout << message << std::endl;
|
std::cout << message << std::endl;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLogConsoleTarget::isColoredOutputEnabled() const { return coloredOutputEnabled; }
|
bool CLogConsoleTarget::isColoredOutputEnabled() const { return coloredOutputEnabled; }
|
||||||
|
@ -12,6 +12,10 @@ set(server_SRCS
|
|||||||
NetPacksServer.cpp
|
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})
|
add_executable(vcmiserver ${server_SRCS})
|
||||||
|
|
||||||
target_link_libraries(vcmiserver vcmi ${Boost_LIBRARIES} ${SYSTEM_LIBS})
|
target_link_libraries(vcmiserver vcmi ${Boost_LIBRARIES} ${SYSTEM_LIBS})
|
||||||
@ -27,4 +31,3 @@ cotire(vcmiserver)
|
|||||||
if (NOT APPLE) # Already inside vcmiclient bundle
|
if (NOT APPLE) # Already inside vcmiclient bundle
|
||||||
install(TARGETS vcmiserver DESTINATION ${BIN_DIR})
|
install(TARGETS vcmiserver DESTINATION ${BIN_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
#include "../lib/StartInfo.h"
|
#include "../lib/StartInfo.h"
|
||||||
#include "../lib/mapping/CMap.h"
|
#include "../lib/mapping/CMap.h"
|
||||||
#include "../lib/rmg/CMapGenOptions.h"
|
#include "../lib/rmg/CMapGenOptions.h"
|
||||||
#ifndef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
|
#include "lib/CAndroidVMHelper.h"
|
||||||
|
#else
|
||||||
#include "../lib/Interprocess.h"
|
#include "../lib/Interprocess.h"
|
||||||
#endif
|
#endif
|
||||||
#include "../lib/VCMI_Lib.h"
|
#include "../lib/VCMI_Lib.h"
|
||||||
@ -417,18 +419,24 @@ void CVCMIServer::start()
|
|||||||
#ifndef VCMI_ANDROID
|
#ifndef VCMI_ANDROID
|
||||||
sr->setToTrueAndNotify();
|
sr->setToTrueAndNotify();
|
||||||
delete mr;
|
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
|
#endif
|
||||||
|
|
||||||
acc.join();
|
acc.join();
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
logNetwork->warnStream()<<"Got connection but there is an error " << error;
|
logNetwork->warnStream() << "Got connection but there is an error " << error;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logNetwork->info("We've accepted someone... ");
|
logNetwork->info("We've accepted someone... ");
|
||||||
firstConnection = new CConnection(s,NAME);
|
firstConnection = new CConnection(s, NAME);
|
||||||
logNetwork->info("Got connection!");
|
logNetwork->info("Got connection!");
|
||||||
while(!end2)
|
while (!end2)
|
||||||
{
|
{
|
||||||
ui8 mode;
|
ui8 mode;
|
||||||
*firstConnection >> mode;
|
*firstConnection >> mode;
|
||||||
@ -490,6 +498,8 @@ void CVCMIServer::loadGame()
|
|||||||
gh.run(true);
|
gh.run(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void handleCommandOptions(int argc, char *argv[])
|
static void handleCommandOptions(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
@ -567,15 +577,16 @@ int main(int argc, char** argv)
|
|||||||
// to log stacktrace
|
// to log stacktrace
|
||||||
#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
|
#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
|
||||||
signal(SIGSEGV, handleLinuxSignal);
|
signal(SIGSEGV, handleLinuxSignal);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
console = new CConsoleHandler;
|
console = new CConsoleHandler;
|
||||||
CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console);
|
CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console);
|
||||||
logConfig.configureDefault();
|
logConfig.configureDefault();
|
||||||
logGlobal->info(NAME);
|
logGlobal->info(NAME);
|
||||||
|
|
||||||
|
|
||||||
handleCommandOptions(argc, argv);
|
handleCommandOptions(argc, argv);
|
||||||
if(cmdLineOptions.count("port"))
|
if (cmdLineOptions.count("port"))
|
||||||
port = cmdLineOptions["port"].as<int>();
|
port = cmdLineOptions["port"].as<int>();
|
||||||
logNetwork->info("Port %d will be used.", port);
|
logNetwork->info("Port %d will be used.", port);
|
||||||
|
|
||||||
@ -592,18 +603,18 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while(!end2)
|
while (!end2)
|
||||||
{
|
{
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
io_service.run();
|
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());
|
logNetwork->error(e.what());
|
||||||
end2 = true;
|
end2 = true;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch (...)
|
||||||
{
|
{
|
||||||
handleException();
|
handleException();
|
||||||
}
|
}
|
||||||
@ -615,9 +626,23 @@ int main(int argc, char** argv)
|
|||||||
//and return non-zero status so client can detect error
|
//and return non-zero status so client can detect error
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
CAndroidVMHelper envHelper;
|
||||||
|
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer");
|
||||||
|
#endif
|
||||||
delete VLC;
|
delete VLC;
|
||||||
VLC = nullptr;
|
VLC = nullptr;
|
||||||
CResourceHandler::clear();
|
CResourceHandler::clear();
|
||||||
|
|
||||||
return 0;
|
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 newGame();
|
||||||
void loadGame();
|
void loadGame();
|
||||||
void newPregame();
|
void newPregame();
|
||||||
|
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
static void create();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StartInfo;
|
||||||
class CPregameServer
|
class CPregameServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user