Sorry for long question. If you want, skip part about compiling Lua (which almost OK) and get straight to last question.
Let's compile Lua library like a static library for Android.
Download latest source and look into doc/readme.html - Building Lua on other systems section for list of files to compile.
And of course look into makefiles - see what in casual way we must set platform flag such is linux, bsd e.t.c. But of course there is no Android platform, so we have choice to set platform to ANSI, Linux, Posix or Generic.
First question: it builds ok (with one exception about llex.c which i will describe down below) even without any platform flag, so maybe this unnecessary?
I set ANSI flag.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_CFLAGS := -DLUA_ANSI
LOCAL_SRC_FILES := lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c
include $(BUILD_STATIC_LIBRARY)
Application.mk
APP_MODULES := lua
APP_PLATFORM := android-8
APP_OPTIM := release
APP_ABI := armeabi
And got errors of course
Compile thumb : lua <= llex.c
jni/llex.c: In function 'trydecpoint':
jni/llex.c:214:18: error: 'struct lconv' has no member named 'decimal_point'
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() (localeconv()->decimal_point[0]) //Missing struct member
#endif
Fixing it in the most cheap way
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() ('.') //Code-monkey style
#endif
There are some limitations about locale.h in Android NDK, so this error is not what surprising.
Also got errors about size_t, UCHAR_MAX, INT_MAX - adding llimits.h include into llex.c and all errors are gone now.
Only warnings now exist about "missing break at the end of case" and "no return in function returning non-void" in static int llex, but we don't mess with Lua source code no more because it's not what vital.
Second Question: am i going to programmer hell for such quick fixes?
Grab our fresh baked LuaLib in obj/armeabi directory and lets test it. Of course to load scripts from android file system, we need write some file loader with use of AssetManager class in Java, so let's do it far simple by pushing and reading lua global vars.
TestLua - Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_SRC_FILES := liblua.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/lua-inc //Where .h files from lua src stored
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaLibTest
LOCAL_STATIC_LIBRARIES:= lua
LOCAL_SRC_FILES := LuaLibTest.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
LuaLibTest.c
#include "LuaLibTest.h"
#include "lua-inc/lua.h"
#include "lua-inc/lauxlib.h"
#include <android/log.h>
#define INFO_TAG "[INFO]"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, INFO_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_lualib_test_NativeLib_testLua(JNIEnv* env, jclass _class)
{
LOGI("HI FROM C");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_pushstring(L, "Some string from Android C" );
lua_setglobal(L, "TEST" );
lua_getglobal(L, "TEST" );
const char* res = lua_tostring(L, lua_gettop(L));
LOGI("LUA TEST VAL: %s", res);
lua_pop(L, 1);
lua_close(L);
}
It also works if we read script file with AssetManager and put it contents in to string which feed into lual_dostring(). So yes, we build lua for Android (except lua i/o functions, which will not work, because stdio like printf don't work in Android NDK).
However this build have strange errors, for example - fps script update every frame, but it can fall at any moment with next error at function what updates fps with frame delta time
FPS.lua
FPS = {}
function initFPS()
FPS.fps = 0
FPS.last_fps = 0
FPS.frames_count = 0
FPS.frames_time = 0.0
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);//Set some label in app - c function
end
function updateFPS(frameDeltaTime)
FPS.frames_count = FPS.frames_count + 1
FPS.frames_time = FPS.frames_time + frameDeltaTime
if FPS.frames_time >= 1000.0
then
FPS.frames_time = 0.0;
FPS.fps = FPS.frames_count;
FPS.frames_count = 0;
if FPS.last_fps ~= FPS.fps
then
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);
FPS.last_fps = FPS.fps
end
end
end
FPS.c
void update_fps(const double* frame_delta_time) //SEGFAULT at this, at random time
{
lua_State* l = get_lua();
lua_getglobal(l, "updateFPS");
lua_pushnumber(l, *frame_delta_time);
lua_call(l, 1, 0);
}
And get next error message with whole app crash at random time (1 min - 3 min)
Last question (yay, you made it/skip boring part)
Why I get segfaults, why at random time? FPS script is just example, at most my every lua script has chance to crash whole app (more calls==better chance). So some player script which change its dir at new pos crash sometimes too.
I think it's because conflict of Android/Java garbage cleaner and Lua garbage cleaner, so something try to free already freed memory.
EDIT - FROM THERE DOUBLE POINTER COME AND WHY:
#define MS_1_SEC 1000.0
typedef struct time_manager
{
double _time;
double delta_time;
}time_manager;
static double get_ms(s_time* time)//get time in ms
{
return MS_1_SEC * time->tv_sec + (double) time->tv_nsec / NS_1_SEC;
}
double get_time_now()
{
s_time time_now;
clock_gettime(CLOCK_REALTIME, &time_now);
return get_ms(&time_now);
}
void init_time_manager(time_manager* tm)
{
tm->_time = get_time_now();
tm->delta_time = 0.0;
}
void update_time_manager(time_manager* tm)
{
double time_now = get_time_now();
tm->delta_time = time_now - tm->_time;
tm->_time = time_now;
}
static time_manager TM;//Global static var for whole render module
In onInit() function
init_time_manager(&TM);
In onDraw() function
double* frame_time = &TM.delta_time;//get pointer to delta time
update_ui(frame_time);//Pass it every function
update_sprites(frame_time);
update_fps(frame_time);
...
draw_fps();
update_time_manager(&TM);
Why i use pointer to double instead of just double? Well it saves 4 bytes of copying (every pointer has size of 4, double has size of 8) frame_delta_time param to every function like update_ui(), i do the same for every struct/type bigger than 4 bytes, const pointers instead of just struct x for read-only access. Is this bad thing?
frame_delta_time
pointer. How are you calling this function? Where does the pointer come from? – Michal KottmanTM
come from? Is is a global variable? Is it a local variable inonInit
? (nah, it wouldn't work inonDraw
) – Michal Kottman