Selaa lähdekoodia

Android port.

Conflicts:
	lib/vcmi_endian.h
Ilya Zhuravlev 11 vuotta sitten
vanhempi
sitoutus
db7cd79cf7

+ 12 - 4
AI/BattleAI/BattleAI.cpp

@@ -8,7 +8,7 @@
 #include "../../lib/VCMI_Lib.h"
 
 using boost::optional;
-shared_ptr<CBattleCallback> cbc;
+static shared_ptr<CBattleCallback> cbc;
 
 #define LOGL(text) print(text)
 #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
@@ -28,8 +28,12 @@ struct Priorities
 		range::copy(VLC->objh->resVals, std::back_inserter(resourceTypeBaseValues));
 		stackEvaluator = [](const CStack*){ return 1.0; };
 	}
-} priorities;
+};
+
+Priorities *priorities = nullptr;
+
 
+namespace {
 
 int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr)
 {
@@ -52,6 +56,8 @@ bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityIn
 	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
 }
 
+}
+
 template <typename Container, typename Pred>
 auto sum(const Container & c, Pred p) -> decltype(p(*std::begin(c)))
 {
@@ -624,8 +630,10 @@ const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, c
 
 int AttackPossibility::damageDiff() const
 {
-	const auto dealtDmgValue = priorities.stackEvaluator(enemy) * damageDealt;
-	const auto receivedDmgValue = priorities.stackEvaluator(attack.attacker) * damageReceived;
+	if (!priorities)
+		priorities = new Priorities;
+	const auto dealtDmgValue = priorities->stackEvaluator(enemy) * damageDealt;
+	const auto receivedDmgValue = priorities->stackEvaluator(attack.attacker) * damageReceived;
 	return dealtDmgValue - receivedDmgValue;
 }
 

+ 8 - 2
AI/BattleAI/main.cpp

@@ -7,7 +7,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "Battle AI";
+#ifdef __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()
 {
@@ -22,4 +28,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
 {
 	out = make_shared<CBattleAI>();
-}
+}

+ 5 - 2
AI/StupidAI/StupidAI.cpp

@@ -5,7 +5,7 @@
 #include "../../CCallback.h"
 #include "../../lib/CCreatureHandler.h"
 
-shared_ptr<CBattleCallback> cbc;
+static shared_ptr<CBattleCallback> cbc;
 
 CStupidAI::CStupidAI(void)
 	: side(-1)
@@ -60,6 +60,8 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
 	return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
 }
 
+namespace {
+
 int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr)
 {
 	int ret = 1000000;
@@ -81,6 +83,8 @@ bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityIn
 	return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
 }
 
+}
+
 static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const BattleHex &h2)
 {
 	int shooters[2] = {0}; //count of shooters on hexes
@@ -328,4 +332,3 @@ void CStupidAI::loadGame(CISer<CLoadFile> &h, const int version)
 	//TODO to be implemented with saving/loading during the battles
 	assert(0);
 }
-

+ 8 - 2
AI/StupidAI/main.cpp

@@ -7,7 +7,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "Stupid AI 0.1";
+#ifdef __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()
 {
@@ -22,4 +28,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewBattleAI(shared_ptr<CBattleGameInterface> &out)
 {
 	out = make_shared<CStupidAI>();
-}
+}

+ 8 - 2
AI/VCAI/main.cpp

@@ -5,7 +5,13 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-const char *g_cszAiName = "VCAI";
+#ifdef __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()
 {
@@ -20,4 +26,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
 extern "C" DLL_EXPORT void GetNewAI(shared_ptr<CGlobalAI> &out)
 {
 	out = make_shared<VCAI>();
-}
+}

+ 11 - 0
client/CMT.cpp

@@ -284,6 +284,10 @@ int main(int argc, char** argv)
 	logGlobal->infoStream() << "Creating console and configuring logger: " << pomtime.getDiff();
 	logGlobal->infoStream() << "The log file will be saved to " << logPath;
 
+#ifdef __ANDROID__
+	// boost will crash without this
+	setenv("LANG", "C", 1);
+#endif
     // Init filesystem and settings
 	preinitDLL(::console);
     settings.init();
@@ -361,8 +365,13 @@ int main(int argc, char** argv)
 
 
 
+#ifndef __ANDROID__
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
 	boost::thread loading(init);
+#else
+	// on Android threaded init is broken
+	init();
+#endif
 
 	if(!gNoGUI )
 	{
@@ -372,7 +381,9 @@ int main(int argc, char** argv)
 	}
 
 	CSDL_Ext::update(screen);
+#ifndef __ANDROID__
 	loading.join();
+#endif
     logGlobal->infoStream()<<"Initialization of VCMI (together): "<<total.getDiff();
 
 	if(!vm.count("battle"))

+ 12 - 0
client/Client.cpp

@@ -19,7 +19,9 @@
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/Connection.h"
+#ifndef __ANDROID__
 #include "../lib/Interprocess.h"
+#endif
 #include "../lib/NetPacks.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/VCMIDirs.h"
@@ -37,7 +39,9 @@
 #include "CMT.h"
 
 extern std::string NAME;
+#ifndef __ANDROID__
 namespace intpr = boost::interprocess;
+#endif
 
 /*
  * Client.cpp, part of VCMI engine
@@ -809,19 +813,25 @@ void CServerHandler::waitForServer()
 		startServer();
 
 	th.update();
+#ifndef __ANDROID__
 	intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
 	while(!shared->sr->ready)
 	{
 		shared->sr->cond.wait(slock);
 	}
+#endif
 	if(verbose)
         logNetwork->infoStream() << "Waiting for server: " << th.getDiff();
 }
 
 CConnection * CServerHandler::connectToServer()
 {
+#ifndef __ANDROID__
 	if(!shared->sr->ready)
 		waitForServer();
+#else
+	waitForServer();
+#endif
 
 	th.update(); //put breakpoint here to attach to server before it does something stupid
 	CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
@@ -839,11 +849,13 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 	port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
 	verbose = true;
 
+#ifndef __ANDROID__
 	boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
 	try
 	{
 		shared = new SharedMem();
     } HANDLE_EXCEPTIONC(logNetwork->errorStream() << "Cannot open interprocess memory: ";)
+#endif
 }
 
 CServerHandler::~CServerHandler()

+ 27 - 0
lib/CGameInterface.cpp

@@ -22,7 +22,17 @@
  *
  */
 
+#ifdef __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(shared_ptr<CGlobalAI> &out);
 
+extern "C" DLL_EXPORT void StupidAI_GetAiName(char* name);
+extern "C" DLL_EXPORT void StupidAI_GetNewBattleAI(shared_ptr<CGlobalAI> &out);
+
+extern "C" DLL_EXPORT void BattleAI_GetAiName(char* name);
+extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr<CBattleGameInterface> &out);
+#endif
 
 template<typename rett>
 shared_ptr<rett> createAny(std::string dllname, std::string methodName)
@@ -35,6 +45,21 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 	TGetAIFun getAI = nullptr;
 	TGetNameFun getName = nullptr;
 
+#ifdef __ANDROID__
+	// this is awful but it seems using shared libraries on some devices is even worse
+	if (dllname.find("libVCAI.so") != std::string::npos) {
+		getName = (TGetNameFun)VCAI_GetAiName;
+		getAI = (TGetAIFun)VCAI_GetNewAI;
+	} else if (dllname.find("libStupidAI.so") != std::string::npos) {
+		getName = (TGetNameFun)StupidAI_GetAiName;
+		getAI = (TGetAIFun)StupidAI_GetNewBattleAI;
+	} else if (dllname.find("libBattleAI.so") != std::string::npos) {
+		getName = (TGetNameFun)BattleAI_GetAiName;
+		getAI = (TGetAIFun)BattleAI_GetNewBattleAI;
+	} else {
+		throw std::runtime_error("Don't know what to do with " + dllname + " and method " + methodName);
+	}
+#else
 
 #ifdef _WIN32
 	HINSTANCE dll = LoadLibraryA(dllname.c_str());
@@ -69,6 +94,8 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 		throw std::runtime_error("Cannot find method " + methodName);
 	}
 
+#endif // __ANDROID__
+
 	getName(temp);
     logGlobal->infoStream() << "Loaded " << temp;
 

+ 17 - 1
lib/VCMIDirs.cpp

@@ -158,11 +158,16 @@ std::string VCMIDirs::serverPath() const
 // $XDG_DATA_HOME, default: $HOME/.local/share
 std::string VCMIDirs::userDataPath() const
 {
+#ifdef __ANDROID__
+	// on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/
+	return std::string(getenv("HOME"));
+#else
 	if (getenv("XDG_DATA_HOME") != nullptr )
 		return std::string(getenv("XDG_DATA_HOME")) + "/vcmi";
 	if (getenv("HOME") != nullptr )
 		return std::string(getenv("HOME")) + "/.local/share" + "/vcmi";
 	return ".";
+#endif
 }
 
 std::string VCMIDirs::userSavePath() const
@@ -173,21 +178,29 @@ std::string VCMIDirs::userSavePath() const
 // $XDG_CACHE_HOME, default: $HOME/.cache
 std::string VCMIDirs::userCachePath() const
 {
+#ifdef __ANDROID__
+	return userDataPath() + "/cache";
+#else
 	if (getenv("XDG_CACHE_HOME") != nullptr )
 		return std::string(getenv("XDG_CACHE_HOME")) + "/vcmi";
 	if (getenv("HOME") != nullptr )
 		return std::string(getenv("HOME")) + "/.cache" + "/vcmi";
 	return ".";
+#endif
 }
 
 // $XDG_CONFIG_HOME, default: $HOME/.config
 std::string VCMIDirs::userConfigPath() const
 {
+#ifdef __ANDROID__
+	return userDataPath() + "/config";
+#else
 	if (getenv("XDG_CONFIG_HOME") != nullptr )
 		return std::string(getenv("XDG_CONFIG_HOME")) + "/vcmi";
 	if (getenv("HOME") != nullptr )
 		return std::string(getenv("HOME")) + "/.config" + "/vcmi";
 	return ".";
+#endif
 }
 
 // $XDG_DATA_DIRS, default: /usr/local/share/:/usr/share/
@@ -198,7 +211,9 @@ std::vector<std::string> VCMIDirs::dataPaths() const
 	// in vcmi fs last directory has highest priority
 
 	std::vector<std::string> ret;
-
+#ifdef __ANDROID__
+	ret.push_back(userDataPath());
+#else
 	if (getenv("HOME") != nullptr ) // compatibility, should be removed after 0.96
 		ret.push_back(std::string(getenv("HOME")) + "/.vcmi");
 	ret.push_back(M_DATA_DIR);
@@ -216,6 +231,7 @@ std::vector<std::string> VCMIDirs::dataPaths() const
 		ret.push_back("/usr/share/");
 		ret.push_back("/usr/local/share/");
 	}
+#endif
 	return ret;
 }
 

+ 9 - 0
lib/logging/CLogger.cpp

@@ -1,3 +1,7 @@
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
 #include "StdInc.h"
 #include "CLogger.h"
 
@@ -398,6 +402,11 @@ void CLogConsoleTarget::write(const LogRecord & record)
 	if(threshold > record.level) return;
 
 	std::string message = formatter.format(record);
+
+#ifdef __ANDROID__
+	__android_log_print(ANDROID_LOG_INFO, "VCMI", "%s", message.c_str());
+#endif
+
 	bool printToStdErr = record.level >= ELogLevel::WARN;
 	if(console)
 	{

+ 11 - 3
server/CVCMIServer.cpp

@@ -19,7 +19,9 @@
 #include "CVCMIServer.h"
 #include "../lib/StartInfo.h"
 #include "../lib/mapping/CMap.h"
+#ifndef __ANDROID__
 #include "../lib/Interprocess.h"
+#endif
 #include "../lib/VCMI_Lib.h"
 #include "../lib/VCMIDirs.h"
 #include "CGameHandler.h"
@@ -32,7 +34,7 @@
 
 #include "../lib/UnlockGuard.h"
 
-#if defined(__GNUC__) && !defined (__MINGW32__)
+#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
 #include <execinfo.h>
 #endif
 
@@ -41,7 +43,9 @@ std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX
 using namespace boost;
 using namespace boost::asio;
 using namespace boost::asio::ip;
+#ifndef __ANDROID__
 namespace intpr = boost::interprocess;
+#endif
 bool end2 = false;
 int port = 3030;
 
@@ -391,6 +395,7 @@ void CVCMIServer::newPregame()
 
 void CVCMIServer::start()
 {
+#ifndef __ANDROID__
 	ServerReady *sr = nullptr;
 	intpr::mapped_region *mr;
 	try
@@ -407,13 +412,16 @@ void CVCMIServer::start()
 		mr = new intpr::mapped_region(smo,intpr::read_write);
 		sr = new(mr->get_address())ServerReady();
 	}
+#endif
 
 	boost::system::error_code error;
     logNetwork->infoStream()<<"Listening for connections at port " << acceptor->local_endpoint().port();
 	auto  s = new tcp::socket(acceptor->get_io_service());
 	boost::thread acc(boost::bind(vaccept,acceptor,s,&error));
+#ifndef __ANDROID__
 	sr->setToTrueAndNotify();
 	delete mr;
+#endif
 
 	acc.join();
 	if (error)
@@ -554,7 +562,7 @@ static void handleCommandOptions(int argc, char *argv[])
 	}
 }
 
-#if defined(__GNUC__) && !defined (__MINGW32__)
+#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
 void handleLinuxSignal(int sig)
 {
 	const int STACKTRACE_SIZE = 100;
@@ -585,7 +593,7 @@ int main(int argc, char** argv)
 {
 	// Installs a sig sev segmentation violation handler
 	// to log stacktrace
-	#if defined(__GNUC__) && !defined (__MINGW32__)
+	#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
 	signal(SIGSEGV, handleLinuxSignal);
 	#endif