1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-27 21:49: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:
Fay 2017-05-25 19:57:20 +02:00 committed by Alexander Shishkin
parent f370cdf1c7
commit b5daa24982
25 changed files with 643 additions and 189 deletions

View File

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

View File

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

View File

@ -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}")

View File

@ -7,12 +7,6 @@
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
#ifdef VCMI_ANDROID
#define GetGlobalAiVersion StupidAI_GetGlobalAiVersion
#define GetAiName StupidAI_GetAiName
#define GetNewBattleAI StupidAI_GetNewBattleAI
#endif
static const char *g_cszAiName = "Stupid AI 0.1";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
@ -28,4 +22,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
{
out = std::make_shared<CStupidAI>();
}
}

View File

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

View File

@ -5,12 +5,6 @@
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
#ifdef VCMI_ANDROID
#define GetGlobalAiVersion VCAI_GetGlobalAiVersion
#define GetAiName VCAI_GetAiName
#define GetNewAI VCAI_GetNewAI
#endif
static const char *g_cszAiName = "VCAI";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
@ -26,4 +20,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
{
out = std::make_shared<VCAI>();
}
}

View File

@ -96,6 +96,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
# define _NO_W32_PSEUDO_MODIFIERS // Exclude more macros for compiling with MinGW on Linux.
#endif
#ifdef VCMI_ANDROID
# define NO_STD_TOSTRING // android runtime (gnustl) currently doesn't support std::to_string, so we provide our impl in this case
#endif // VCMI_ANDROID
/* ---------------------------------------------------------------------------- */
/* A macro to force inlining some of our functions */
/* ---------------------------------------------------------------------------- */
@ -698,3 +702,16 @@ namespace vstd
}
using vstd::operator-=;
using vstd::make_unique;
#ifdef NO_STD_TOSTRING
namespace std
{
template <typename T>
inline std::string to_string(const T& value)
{
std::ostringstream ss;
ss << value;
return ss.str();
}
}
#endif // NO_STD_TOSTRING

View File

@ -49,6 +49,9 @@
#ifdef VCMI_WINDOWS
#include "SDL_syswm.h"
#endif
#ifdef VCMI_ANDROID
#include "lib/CAndroidVMHelper.h"
#endif
#include "../lib/UnlockGuard.h"
#include "CMT.h"
@ -203,7 +206,7 @@ void OSX_checkForUpdates();
#if defined(VCMI_WINDOWS) && !defined (__GNUC__)
int wmain(int argc, wchar_t* argv[])
#elif defined(VCMI_APPLE)
#elif defined(VCMI_APPLE) || defined(VCMI_ANDROID)
int SDL_main(int argc, char *argv[])
#else
int main(int argc, char** argv)
@ -436,11 +439,6 @@ int main(int argc, char** argv)
logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff();
#if defined(VCMI_ANDROID)
//on Android threaded init is broken
#define VCMI_NO_THREADED_LOAD
#endif // defined
//initializing audio
CCS->soundh = new CSoundHandler;
CCS->soundh->init();
@ -466,13 +464,22 @@ int main(int argc, char** argv)
{
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
playIntro();
SDL_FillRect(screen,nullptr,0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderClear(mainRenderer);
}
CSDL_Ext::update(screen);
SDL_RenderPresent(mainRenderer);
#ifndef VCMI_NO_THREADED_LOAD
loading.join();
#endif
#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds
{
CAndroidVMHelper vmHelper;
vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "showProgress");
#endif // ANDROID
loading.join();
#ifdef VCMI_ANDROID
vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "hideProgress");
}
#endif // ANDROID
#endif // THREADED
logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
if(!vm.count("battle"))
@ -1027,6 +1034,9 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
cleanupRenderer();
#ifdef VCMI_ANDROID
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN);
#else
if(fullscreen)
{
//in full-screen mode always use desktop resolution
@ -1037,6 +1047,7 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
{
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
}
#endif
if(nullptr == mainWindow)
{
@ -1058,7 +1069,10 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
SDL_RenderSetLogicalSize(mainRenderer, w, h);
#ifndef VCMI_ANDROID
// on android this stretches the game to fit the screen, not preserving aspect and apparently this also breaks coordinates scaling in mouse events
SDL_RenderSetViewport(mainRenderer, nullptr);
#endif
@ -1149,9 +1163,19 @@ static void handleEvent(SDL_Event & ev)
{
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
{
#ifdef VCMI_ANDROID
handleQuit(false);
#else
handleQuit();
#endif
return;
}
#ifdef VCMI_ANDROID
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
{
handleQuit(true);
}
#endif
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
{
Settings full = settings.write["video"]["fullscreen"];

View File

@ -65,6 +65,10 @@ set(client_HEADERS
gui/SDL_Compat.h
)
if(ANDROID) # android needs client/server to be libraries, not executables, so we can't reuse the build part of this script
return()
endif()
if(APPLE)
# OS X specific includes
include_directories(${SPARKLE_INCLUDE_DIR})
@ -133,4 +137,3 @@ cotire(vcmiclient)
install(TARGETS vcmiclient DESTINATION ${BIN_DIR})

View File

@ -353,7 +353,11 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
for (int i=1; i<32; i+=2*speed)
{
movementPxStep(details, i, hp, hero);
#ifndef VCMI_ANDROID
// currently android doesn't seem to be able to handle all these full redraws here, so let's disable it so at least it looks less choppy;
// most likely this is connected with the way that this manual animation+framerate handling is solved
adventureInt->updateScreen = true;
#endif
adventureInt->show(screen);
{
//evil returns here ...
@ -1647,7 +1651,11 @@ void CPlayerInterface::update()
GH.updateTime();
GH.handleEvents();
#ifdef VCMI_ANDROID
if (adventureInt && !adventureInt->isActive() && (adventureInt->swipeTargetPosition.x >= 0 || adventureInt->swipeTargetPosition.y >= 0))
#else // !VCMI_ANDROID
if (adventureInt && !adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled
#endif // !VCMI_ANDROID
GH.totalRedraw();
else
GH.simpleRedraw();

View File

@ -43,6 +43,8 @@
extern std::string NAME;
#ifndef VCMI_ANDROID
namespace intpr = boost::interprocess;
#else
#include "lib/CAndroidVMHelper.h"
#endif
/*
@ -55,6 +57,10 @@ namespace intpr = boost::interprocess;
*
*/
#ifdef VCMI_ANDROID
std::atomic_bool androidTestServerReadyFlag;
#endif
template <typename T> class CApplyOnCL;
class CBaseForCLApply
@ -913,8 +919,7 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
{
if(ps.name.size())
{
const boost::filesystem::path aiPath =
VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(ps.name);
const boost::filesystem::path aiPath = VCMIDirs::get().fullLibraryPath("AI", ps.name);
if (boost::filesystem::exists(aiPath))
return ps.name;
}
@ -940,7 +945,12 @@ void CServerHandler::startServer()
th.update();
#ifdef VCMI_ANDROID
CAndroidVMHelper envHelper;
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true);
#else
serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable;
#endif
if(verbose)
logNetwork->infoStream() << "Setting up thread calling server: " << th.getDiff();
}
@ -954,12 +964,22 @@ void CServerHandler::waitForServer()
startServer();
th.update();
#ifndef VCMI_ANDROID
intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
while(!shared->sr->ready)
{
shared->sr->cond.wait(slock);
}
#else
logNetwork->infoStream() << "waiting for server";
while (!androidTestServerReadyFlag.load())
{
logNetwork->infoStream() << "still waiting...";
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
}
logNetwork->infoStream() << "waiting for server finished...";
androidTestServerReadyFlag = false;
#endif
if(verbose)
logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
@ -1017,6 +1037,7 @@ CServerHandler::~CServerHandler()
void CServerHandler::callServer()
{
#ifndef VCMI_ANDROID
setThreadName("CServerHandler::callServer");
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
@ -1032,6 +1053,7 @@ void CServerHandler::callServer()
logNetwork->errorStream() << "Check " << logName << " for more info";
exit(1);// exit in case of error. Othervice without working server VCMI will hang
}
#endif
}
CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port)
@ -1062,3 +1084,23 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const
}
return ret;
}
#ifdef VCMI_ANDROID
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jobject cls)
{
logNetwork->infoStream() << "Received server ready signal";
androidTestServerReadyFlag.store(true);
}
extern "C" JNIEXPORT bool JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jobject cls)
{
logGlobal->infoStream() << "Received emergency save game request";
if(!LOCPLINT || !LOCPLINT->cb)
{
return false;
}
LOCPLINT->cb->save("Saves/_Android_Autosave");
return true;
}
#endif

View File

@ -116,13 +116,36 @@ void CTerrainRect::deactivate()
void CTerrainRect::clickLeft(tribool down, bool previousState)
{
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if ((down==false) || indeterminate(down))
if(indeterminate(down))
return;
#ifdef VCMI_ANDROID
if(adventureInt->swipeEnabled)
{
if(down == true)
{
swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
swipeInitialMapPos = int3(adventureInt->position);
return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
}
else if(isSwiping) // only accept this touch if it wasn't a swipe
{
isSwiping = false;
return;
}
}
else
{
#endif
if(down == false)
return;
#ifdef VCMI_ANDROID
}
#endif
int3 mp = whichTileIsIt();
if (mp.x<0 || mp.y<0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
return;
adventureInt->tileLClicked(mp);
@ -130,17 +153,59 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
void CTerrainRect::clickRight(tribool down, bool previousState)
{
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
#ifdef VCMI_ANDROID
if(adventureInt->swipeEnabled && isSwiping)
return;
#endif
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
int3 mp = whichTileIsIt();
if (CGI->mh->map->isInTheMap(mp) && down)
if(CGI->mh->map->isInTheMap(mp) && down)
adventureInt->tileRClicked(mp);
}
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
{
int3 tHovered = whichTileIsIt(sEvent.x,sEvent.y);
handleHover(sEvent);
#ifdef VCMI_ANDROID
if(!adventureInt->swipeEnabled || sEvent.state == 0)
return;
handleSwipeMove(sEvent);
#endif // !VCMI_ANDROID
}
#ifdef VCMI_ANDROID
void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
{
if(!isSwiping)
{
// try to distinguish if this touch was meant to be a swipe or just fat-fingering press
if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop ||
abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop)
{
isSwiping = true;
}
}
if(isSwiping)
{
adventureInt->swipeTargetPosition.x =
swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32;
adventureInt->swipeTargetPosition.y =
swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32;
adventureInt->swipeMovementRequested = true;
}
}
#endif // VCMI_ANDROID
void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
{
int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y);
int3 pom = adventureInt->verifyPos(tHovered);
if(tHovered != pom) //tile outside the map
@ -150,12 +215,12 @@ void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
}
if (pom != curHoveredTile)
{
curHoveredTile = pom;
else
return;
adventureInt->tileHovered(pom);
adventureInt->tileHovered(pom);
}
}
void CTerrainRect::hover(bool on)
{
if (!on)
@ -470,6 +535,10 @@ CAdvMapInt::CAdvMapInt():
spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false)
#ifdef VCMI_ANDROID
, swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
swipeTargetPosition(int3(-1, -1, -1))
#endif
{
adventureInt = this;
pos.x = pos.y = 0;
@ -938,42 +1007,19 @@ void CAdvMapInt::show(SDL_Surface * to)
}
++heroAnim;
int scrollSpeed = settings["adventure"]["scrollSpeed"].Float();
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
if((animValHitCount % (4/scrollSpeed)) == 0
&& (
(GH.topInt() == this)
|| isCtrlKeyDown()
)
)
#ifdef VCMI_ANDROID
if(swipeEnabled)
{
if( (scrollingDir & LEFT) && (position.x>-CGI->mh->frameW) )
position.x--;
if( (scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW) )
position.x++;
if( (scrollingDir & UP) && (position.y>-CGI->mh->frameH) )
position.y--;
if( (scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH) )
position.y++;
if(scrollingDir)
{
setScrollingCursor(scrollingDir);
scrollingState = true;
updateScreen = true;
minimap.redraw();
if (mode == EAdvMapMode::WORLD_VIEW)
terrain.redraw();
}
else if(scrollingState)
{
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
scrollingState = false;
}
handleSwipeUpdate();
}
else
{
#endif // !VCMI_ANDROID
handleMapScrollingUpdate();
#ifdef VCMI_ANDROID
}
#endif
for(int i = 0; i < 4; i++)
gems[i]->setFrame(LOCPLINT->playerID.getNum());
if(updateScreen)
@ -1002,6 +1048,59 @@ void CAdvMapInt::show(SDL_Surface * to)
statusbar.showAll(to);
}
void CAdvMapInt::handleMapScrollingUpdate()
{
int scrollSpeed = settings["adventure"]["scrollSpeed"].Float();
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
if((animValHitCount % (4 / scrollSpeed)) == 0
&& ((GH.topInt() == this) || isCtrlKeyDown()))
{
if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
position.x--;
if((scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW))
position.x++;
if((scrollingDir & UP) && (position.y > -CGI->mh->frameH))
position.y--;
if((scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH))
position.y++;
if(scrollingDir)
{
setScrollingCursor(scrollingDir);
scrollingState = true;
updateScreen = true;
minimap.redraw();
if(mode == EAdvMapMode::WORLD_VIEW)
terrain.redraw();
}
else if(scrollingState)
{
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
scrollingState = false;
}
}
}
#ifdef VCMI_ANDROID
void CAdvMapInt::handleSwipeUpdate()
{
if(swipeMovementRequested)
{
position.x = swipeTargetPosition.x;
position.y = swipeTargetPosition.y;
CCS->curh->changeGraphic(ECursor::DEFAULT, 0);
updateScreen = true;
minimap.redraw();
swipeMovementRequested = false;
}
}
#endif
void CAdvMapInt::selectionChanged()
{
const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
@ -1315,6 +1414,10 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/)
void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
{
#ifdef VCMI_ANDROID
if(swipeEnabled)
return;
#endif
// adventure map scrolling with mouse
// currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed
if(!isCtrlKeyDown() && isActive() && mode == EAdvMapMode::NORMAL)

View File

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

View File

@ -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" : {

View File

@ -38,7 +38,11 @@ namespace ELogLevel
case ERROR:
return "error";
default:
#ifdef NO_STD_TOSTRING
return "invalid";
#else
return std::string("invalid (") + std::to_string(level) + ")";
#endif
}
}
}

105
lib/CAndroidVMHelper.cpp Normal file
View 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
View 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";
};

View File

@ -5,13 +5,21 @@
#include "VCMIDirs.h"
#ifdef VCMI_WINDOWS
#include <windows.h> //for .dll libs
#else
#include <dlfcn.h>
#include <windows.h> //for .dll libs
#elif !defined VCMI_ANDROID
#include <dlfcn.h>
#endif
#include "serializer/BinaryDeserializer.h"
#include "serializer/BinarySerializer.h"
#ifdef VCMI_ANDROID
#include "AI/VCAI/VCAI.h"
#include "AI/BattleAI/BattleAI.h"
#endif
/*
* CGameInterface.cpp, part of VCMI engine
*
@ -22,50 +30,22 @@
*
*/
#ifdef VCMI_ANDROID
// we can't use shared libraries on Android so here's a hack
extern "C" DLL_EXPORT void VCAI_GetAiName(char* name);
extern "C" DLL_EXPORT void VCAI_GetNewAI(std::shared_ptr<CGlobalAI> &out);
extern "C" DLL_EXPORT void StupidAI_GetAiName(char* name);
extern "C" DLL_EXPORT void StupidAI_GetNewBattleAI(std::shared_ptr<CGlobalAI> &out);
extern "C" DLL_EXPORT void BattleAI_GetAiName(char* name);
extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out);
#endif
template<typename rett>
std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const std::string& methodName)
std::shared_ptr<rett> createAny(const boost::filesystem::path & libpath, const std::string & methodName)
{
typedef void(*TGetAIFun)(std::shared_ptr<rett>&);
typedef void(*TGetNameFun)(char*);
#ifdef VCMI_ANDROID
// android currently doesn't support loading libs dynamically, so the access to the known libraries
// is possible only via specializations of this template
throw std::runtime_error("Could not resolve ai library " + libpath.generic_string());
#else
typedef void(* TGetAIFun)(std::shared_ptr<rett> &);
typedef void(* TGetNameFun)(char *);
char temp[150];
TGetAIFun getAI = nullptr;
TGetNameFun getName = nullptr;
#ifdef VCMI_ANDROID
// this is awful but it seems using shared libraries on some devices is even worse
const std::string filename = libpath.filename().string();
if (filename == "libVCAI.so")
{
getName = (TGetNameFun)VCAI_GetAiName;
getAI = (TGetAIFun)VCAI_GetNewAI;
}
else if (filename == "libStupidAI.so")
{
getName = (TGetNameFun)StupidAI_GetAiName;
getAI = (TGetAIFun)StupidAI_GetNewBattleAI;
}
else if (filename == "libBattleAI.so")
{
getName = (TGetNameFun)BattleAI_GetAiName;
getAI = (TGetAIFun)BattleAI_GetNewBattleAI;
}
else
throw std::runtime_error("Don't know what to do with " + libpath.string() + " and method " + methodName);
#else // !VCMI_ANDROID
#ifdef VCMI_WINDOWS
HMODULE dll = LoadLibraryW(libpath.c_str());
if (dll)
@ -83,6 +63,7 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st
else
logGlobal->errorStream() << "Error: " << dlerror();
#endif // VCMI_WINDOWS
if (!dll)
{
logGlobal->errorStream() << "Cannot open dynamic library ("<<libpath<<"). Throwing...";
@ -98,7 +79,6 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st
#endif
throw std::runtime_error("Cannot find method " + methodName);
}
#endif // VCMI_ANDROID
getName(temp);
logGlobal->infoStream() << "Loaded " << temp;
@ -109,14 +89,31 @@ std::shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const st
logGlobal->error("Cannot get AI!");
return ret;
#endif //!VCMI_ANDROID
}
#ifdef VCMI_ANDROID
template<>
std::shared_ptr<CGlobalAI> createAny(const boost::filesystem::path & libpath, const std::string & methodName)
{
return std::make_shared<VCAI>();
}
template<>
std::shared_ptr<CBattleGameInterface> createAny(const boost::filesystem::path & libpath, const std::string & methodName)
{
return std::make_shared<CBattleAI>();
}
#endif
template<typename rett>
std::shared_ptr<rett> createAnyAI(std::string dllname, const std::string& methodName)
std::shared_ptr<rett> createAnyAI(std::string dllname, const std::string & methodName)
{
logGlobal->infoStream() << "Opening " << dllname;
const boost::filesystem::path filePath =
VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname);
const boost::filesystem::path filePath = VCMIDirs::get().fullLibraryPath("AI", dllname);
auto ret = createAny<rett>(filePath, methodName);
ret->dllName = std::move(dllname);
return ret;
@ -127,7 +124,7 @@ std::shared_ptr<CGlobalAI> CDynLibHandler::getNewAI(std::string dllname)
return createAnyAI<CGlobalAI>(dllname, "GetNewAI");
}
std::shared_ptr<CBattleGameInterface> CDynLibHandler::getNewBattleAI(std::string dllname )
std::shared_ptr<CBattleGameInterface> CDynLibHandler::getNewBattleAI(std::string dllname)
{
return createAnyAI<CBattleGameInterface>(dllname, "GetNewBattleAI");
}
@ -137,9 +134,10 @@ std::shared_ptr<CScriptingModule> CDynLibHandler::getNewScriptingModule(std::str
return createAny<CScriptingModule>(dllname, "GetNewModule");
}
BattleAction CGlobalAI::activeStack( const CStack * stack )
BattleAction CGlobalAI::activeStack(const CStack * stack)
{
BattleAction ba; ba.actionType = Battle::DEFEND;
BattleAction ba;
ba.actionType = Battle::DEFEND;
ba.stackNumber = stack->ID;
return ba;
}
@ -159,7 +157,8 @@ void CAdventureAI::battleCatapultAttacked(const CatapultAttack & ca)
battleAI->battleCatapultAttacked(ca);
}
void CAdventureAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile,
const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
{
assert(!battleAI);
assert(cbc);
@ -173,7 +172,7 @@ void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> &
battleAI->battleStacksAttacked(bsa);
}
void CAdventureAI::actionStarted(const BattleAction &action)
void CAdventureAI::actionStarted(const BattleAction & action)
{
battleAI->actionStarted(action);
}
@ -183,7 +182,7 @@ void CAdventureAI::battleNewRoundFirst(int round)
battleAI->battleNewRoundFirst(round);
}
void CAdventureAI::actionFinished(const BattleAction &action)
void CAdventureAI::actionFinished(const BattleAction & action)
{
battleAI->actionFinished(action);
}
@ -213,23 +212,24 @@ void CAdventureAI::battleStackMoved(const CStack * stack, std::vector<BattleHex>
battleAI->battleStackMoved(stack, dest, distance);
}
void CAdventureAI::battleAttack(const BattleAttack *ba)
void CAdventureAI::battleAttack(const BattleAttack * ba)
{
battleAI->battleAttack(ba);
}
void CAdventureAI::battleSpellCast(const BattleSpellCast *sc)
void CAdventureAI::battleSpellCast(const BattleSpellCast * sc)
{
battleAI->battleSpellCast(sc);
}
void CAdventureAI::battleEnd(const BattleResult *br)
void CAdventureAI::battleEnd(const BattleResult * br)
{
battleAI->battleEnd(br);
battleAI.reset();
}
void CAdventureAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom)
void CAdventureAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain,
bool tentHeal, si32 lifeDrainFrom)
{
battleAI->battleStacksHealedRes(healedStacks, lifeDrain, tentHeal, lifeDrainFrom);
}

View File

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

View File

@ -15,6 +15,11 @@ namespace bfs = boost::filesystem;
bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
{
return libraryPath() / desiredFolder / libraryName(baseLibName);
}
void IVCMIDirs::init()
{
// TODO: Log errors
@ -24,6 +29,11 @@ void IVCMIDirs::init()
bfs::create_directories(userSavePath());
}
#ifdef VCMI_ANDROID
#include "CAndroidVMHelper.h"
#endif
#ifdef VCMI_WINDOWS
#ifdef __MINGW32__
@ -532,26 +542,55 @@ bfs::path VCMIDirsXDG::libraryPath() const { return M_LIB_DIR; }
bfs::path VCMIDirsXDG::binaryPath() const { return M_BIN_DIR; }
std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
#ifdef VCMI_ANDROID
class VCMIDirsAndroid : public VCMIDirsXDG
{
std::string basePath;
std::string internalPath;
std::string nativePath;
public:
boost::filesystem::path userDataPath() const override;
boost::filesystem::path userCachePath() const override;
boost::filesystem::path userConfigPath() const override;
bfs::path fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const override;
bfs::path libraryPath() const override;
bfs::path userDataPath() const override;
bfs::path userCachePath() const override;
bfs::path userConfigPath() const override;
std::vector<boost::filesystem::path> dataPaths() const override;
std::vector<bfs::path> dataPaths() const override;
void init() override;
};
// on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/
bfs::path VCMIDirsAndroid::userDataPath() const { return getenv("HOME"); }
bfs::path VCMIDirsAndroid::libraryPath() const { return nativePath; }
bfs::path VCMIDirsAndroid::userDataPath() const { return basePath; }
bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; }
bfs::path VCMIDirsAndroid::userConfigPath() const { return userDataPath() / "config"; }
bfs::path VCMIDirsAndroid::fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const
{
// ignore passed folder (all libraries in android are dumped into a single folder)
return libraryPath() / libraryName(baseLibName);
}
std::vector<bfs::path> VCMIDirsAndroid::dataPaths() const
{
return std::vector<bfs::path>(1, userDataPath());
std::vector<bfs::path> paths(2);
paths.push_back(internalPath);
paths.push_back(userDataPath());
return paths;
}
void VCMIDirsAndroid::init()
{
// asks java code to retrieve needed paths from environment
CAndroidVMHelper envHelper;
basePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "dataRoot");
internalPath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "internalDataRoot");
nativePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "nativePath");
IVCMIDirs::init();
}
#endif // VCMI_ANDROID
#endif // VCMI_APPLE, VCMI_XDG
#endif // VCMI_WINDOWS, VCMI_UNIX
@ -569,7 +608,8 @@ namespace VCMIDirs
static VCMIDirsXDG singleton;
#elif defined(VCMI_APPLE)
static VCMIDirsOSX singleton;
#endif
#endif
static bool initialized = false;
if (!initialized)
{
@ -584,3 +624,4 @@ namespace VCMIDirs
return singleton;
}
}

View File

@ -11,48 +11,53 @@
class DLL_LINKAGE IVCMIDirs
{
public:
// Path to user-specific data directory
virtual boost::filesystem::path userDataPath() const = 0;
public:
// Path to user-specific data directory
virtual boost::filesystem::path userDataPath() const = 0;
// Path to "cache" directory, can be used for any non-essential files
virtual boost::filesystem::path userCachePath() const = 0;
// Path to "cache" directory, can be used for any non-essential files
virtual boost::filesystem::path userCachePath() const = 0;
// Path to writeable directory with user configs
virtual boost::filesystem::path userConfigPath() const = 0;
// Path to writeable directory with user configs
virtual boost::filesystem::path userConfigPath() const = 0;
// Path to saved games
virtual boost::filesystem::path userSavePath() const;
// Path to saved games
virtual boost::filesystem::path userSavePath() const;
// Paths to global system-wide data directories. First items have higher priority
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
// Paths to global system-wide data directories. First items have higher priority
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
virtual boost::filesystem::path clientPath() const = 0;
// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
virtual boost::filesystem::path clientPath() const = 0;
// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
virtual boost::filesystem::path serverPath() const = 0;
// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
virtual boost::filesystem::path serverPath() const = 0;
// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
virtual boost::filesystem::path libraryPath() const = 0;
// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
virtual boost::filesystem::path libraryPath() const = 0;
// Path where vcmi binaries can be found
virtual boost::filesystem::path binaryPath() const = 0;
// absolute path to passed library (needed due to android libs being placed in single dir, not respecting original lib dirs;
// by default just concats libraryPath, given folder and libraryName
virtual boost::filesystem::path fullLibraryPath(const std::string & desiredFolder,
const std::string & baseLibName) const;
// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll")
virtual std::string libraryName(const std::string& basename) const = 0;
// virtual std::string libraryName(const char* basename) const = 0; ?
// virtual std::string libraryName(std::string&& basename) const = 0;?
// Path where vcmi binaries can be found
virtual boost::filesystem::path binaryPath() const = 0;
virtual std::string genHelpString() const = 0;
// Creates not existed, but required directories.
// Updates directories what change name/path between versions.
// Function called automatically.
virtual void init();
// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll")
virtual std::string libraryName(const std::string & basename) const = 0;
// virtual std::string libraryName(const char* basename) const = 0; ?
// virtual std::string libraryName(std::string&& basename) const = 0;?
virtual std::string genHelpString() const = 0;
// Creates not existed, but required directories.
// Updates directories what change name/path between versions.
// Function called automatically.
virtual void init();
};
namespace VCMIDirs
{
extern DLL_LINKAGE const IVCMIDirs& get();
extern DLL_LINKAGE const IVCMIDirs & get();
}

View File

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

View File

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

View File

@ -19,7 +19,9 @@
#include "../lib/StartInfo.h"
#include "../lib/mapping/CMap.h"
#include "../lib/rmg/CMapGenOptions.h"
#ifndef VCMI_ANDROID
#ifdef VCMI_ANDROID
#include "lib/CAndroidVMHelper.h"
#else
#include "../lib/Interprocess.h"
#endif
#include "../lib/VCMI_Lib.h"
@ -417,18 +419,24 @@ void CVCMIServer::start()
#ifndef VCMI_ANDROID
sr->setToTrueAndNotify();
delete mr;
#else
{ // in block to clean-up vm helper after use, because we don't need to keep this thread attached to vm
CAndroidVMHelper envHelper;
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady");
logNetwork->info("Sending server ready message to client");
}
#endif
acc.join();
if (error)
{
logNetwork->warnStream()<<"Got connection but there is an error " << error;
logNetwork->warnStream() << "Got connection but there is an error " << error;
return;
}
logNetwork->info("We've accepted someone... ");
firstConnection = new CConnection(s,NAME);
firstConnection = new CConnection(s, NAME);
logNetwork->info("Got connection!");
while(!end2)
while (!end2)
{
ui8 mode;
*firstConnection >> mode;
@ -490,6 +498,8 @@ void CVCMIServer::loadGame()
gh.run(true);
}
static void handleCommandOptions(int argc, char *argv[])
{
namespace po = boost::program_options;
@ -567,15 +577,16 @@ int main(int argc, char** argv)
// to log stacktrace
#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
signal(SIGSEGV, handleLinuxSignal);
#endif
#endif
console = new CConsoleHandler;
CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Server_log.txt", console);
logConfig.configureDefault();
logGlobal->info(NAME);
handleCommandOptions(argc, argv);
if(cmdLineOptions.count("port"))
if (cmdLineOptions.count("port"))
port = cmdLineOptions["port"].as<int>();
logNetwork->info("Port %d will be used.", port);
@ -592,18 +603,18 @@ int main(int argc, char** argv)
try
{
while(!end2)
while (!end2)
{
server.start();
}
io_service.run();
}
catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
catch (boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
{
logNetwork->error(e.what());
end2 = true;
}
catch(...)
catch (...)
{
handleException();
}
@ -615,9 +626,23 @@ int main(int argc, char** argv)
//and return non-zero status so client can detect error
throw;
}
#ifdef VCMI_ANDROID
CAndroidVMHelper envHelper;
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer");
#endif
delete VLC;
VLC = nullptr;
CResourceHandler::clear();
return 0;
}
#ifdef VCMI_ANDROID
void CVCMIServer::create()
{
const char * foo[1] = {"android-server"};
main(1, const_cast<char **>(foo));
}
#endif

View File

@ -57,8 +57,13 @@ public:
void newGame();
void loadGame();
void newPregame();
#ifdef VCMI_ANDROID
static void create();
#endif
};
struct StartInfo;
class CPregameServer
{
public: