File Scripting.c
File List > engine > src > scripting > Scripting.c
Go to the documentation of this file
#include "Scripting.h"
#include "lua.h"
#include <lualib.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "AssertLib.h"
#include "XMLUIGameLayer.h"
#include "RootWidget.h"
#include "AssertLib.h"
#include "GameFrameworkEvent.h"
#include "GameFramework.h"
#include "DataNode.h"
#include "main.h"
#include "Game2DLayer.h"
#include "Log.h"
#include "Camera2D.h"
#define GAME_LUA_MINOR_VERSION 1
static lua_State* gL = NULL;
static void OnPropertyChangedInternal(XMLUIData* pUIData, HWidget hWidget, const char* pChangedPropName)
{
//printf("OnPropertyChangedInternal. pChangedPropName: %s", pChangedPropName);
while (hWidget != NULL_HWIDGET)
{
struct UIWidget* pWidget = UI_GetWidget(hWidget);
for (int i = 0; i < pWidget->numBindings; i++)
{
struct WidgetPropertyBinding* pBinding = &pWidget->bindings[i];
if (strcmp(pBinding->name, pChangedPropName) == 0)
{
if(strcmp(pBinding->boundPropertyName, "childrenBinding") == 0)
{
Log_Verbose("childrenBinding changed. pChangedPropName: %s", pChangedPropName);
char* pStr = malloc(strlen(pChangedPropName) + 1);
strcpy(pStr, pChangedPropName);
struct WidgetChildrenChangeRequest r = {
pUIData->hViewModel,
pStr,
hWidget
};
Log_Verbose("pUIData->pChildrenChangeRequests: %p", pUIData->pChildrenChangeRequests);
volatile VectorData* pDbg = VectorData_DEBUG(pUIData->pChildrenChangeRequests);
pUIData->pChildrenChangeRequests = VectorPush(pUIData->pChildrenChangeRequests, &r);
pDbg = VectorData_DEBUG(pUIData->pChildrenChangeRequests);
Log_Verbose("pushed request");
}
else if (pWidget->fnOnBoundPropertyChanged)
{
pWidget->fnOnBoundPropertyChanged(pWidget, pBinding);
}
}
}
OnPropertyChangedInternal(pUIData, pWidget->hFirstChild, pChangedPropName);
hWidget = pWidget->hNext;
}
}
struct LuaListenerUserData
{
int regIndexFn;
int regIndexVmTable;
int numArgs;
};
static void LuaListenerFn(void* pUserData, void* pEventData)
{
struct LuaListenerUserData* ud = pUserData;
struct LuaListenedEventArgs* pArgs = pEventData;
Sc_CallFuncInRegTableEntry(ud->regIndexFn, pArgs->args, pArgs->numArgs, 0, ud->regIndexVmTable);
}
static int L_FireGameFrameworkEvent(lua_State* L)
{
const char* eventName = NULL;
if(lua_isstring(L, -1))
{
eventName = lua_tostring(L, -1);
}
else
{
Log_Error("FireGameFrameworkEvent. First argument wrong type should be string");
}
if(lua_istable(L,-2))
{
EASSERT(gL == L);
lua_pop(gL, 1);
struct DataNode node;
DN_InitForLuaTableOnTopOfStack(&node);
// will this work? I don't know. Is L the same as gL? I hope so
Ev_FireEvent((char*)eventName, &node);
Sc_ResetStack();
}
else
{
Log_Error("FireGameFrameworkEvent. 2nd argument wrong type should be table");
}
return 0;
}
static int L_UnSubscribeToGameFrameworkEvent(lua_State* L)
{
if(lua_islightuserdata(L, -1))
{
struct GameFrameworkEventListener* pListener = lua_touserdata(L, -1);
struct LuaListenerUserData* ud = Ev_GetUserData(pListener);
luaL_unref(gL, LUA_REGISTRYINDEX, ud->regIndexFn);
luaL_unref(gL, LUA_REGISTRYINDEX, ud->regIndexVmTable);
free(ud);
EVERIFY(Ev_UnsubscribeEvent(pListener));
lua_settop(L, 0);
lua_pushboolean(L, true);
return 1;
}
else
{
Log_Error("UnsubscribeFromGameFrameworkEvent - argument wrong type");
lua_settop(L, 0);
lua_pushboolean(L, false);
return 1;
}
}
static int L_SubscribeToGameFrameworkEvent(lua_State* L)
{
// lua args:
// event name, self, listenerLuaFunction
int regIndexFn = 0;
int regIndexVm = 0;
struct LuaListenerUserData* ud = malloc(sizeof(struct LuaListenerUserData));
memset(ud, 0, sizeof(struct LuaListenerUserData));
if (lua_isfunction(L, -1))
{
ud->regIndexFn = luaL_ref(L, LUA_REGISTRYINDEX);
}
else
{
Log_Error("SubscribeGameFrameworkEvent arg 3 wrong type. Is type. Needs to be string");
lua_settop(L, 0);
lua_pushnil(L);
free(ud);
return 1;
}
if(lua_istable(L,-1))
{
ud->regIndexVmTable = luaL_ref(L, LUA_REGISTRYINDEX);
}
else
{
Log_Error("SubscribeGameFrameworkEvent arg 3 wrong type. Is type. Needs to be string");
lua_settop(L, 0);
lua_pushnil(L);
free(ud);
return 1;
}
if (lua_isstring(L, -1))
{
struct GameFrameworkEventListener* pListener = Ev_SubscribeEvent((char*)lua_tostring(L, -1), &LuaListenerFn, ud);
lua_settop(L, 0);
lua_pushlightuserdata(L, pListener);
return 1;
}
else
{
Log_Error("SubscribeGameFrameworkEvent arg 3 wrong type. Is type. Needs to be string");
lua_settop(L, 0);
lua_pushnil(L);
free(ud);
return 1;
}
}
static int L_OnPropertyChanged(lua_State* L)
{
// args: (table) viewmodel, (string) propertyName
int top = lua_gettop(L);
EASSERT(top == 2);
bool bIsTable = lua_istable(L, 1);
EASSERT(bIsTable);
const char* pStr = luaL_checkstring(L, 2);
char* pNameCpy = malloc(strlen(pStr) + 1);
strcpy(pNameCpy, pStr);
lua_pop(L, 1);
lua_getfield(L, -1, "XMLUIDataPtr");
XMLUIData* pUIData = lua_touserdata(L, -1);
HWidget hWidget = pUIData->rootWidget;
OnPropertyChangedInternal(pUIData, hWidget, pNameCpy);
free(pNameCpy);
SetRootWidgetIsDirty(pUIData->rootWidget, true);
return 0;
}
/* Game Framework */
static int L_PopGameFrameworkLayer(lua_State* L)
{
GF_PopGameFrameworkLayer();
return 0;
}
/* Input */
static int L_GetButtonBinding(lua_State* L)
{
int top = lua_gettop(L);
if(top != 1)
{
Log_Error("L_GetButtonBinding Wrong number of args");
lua_pushlightuserdata(L, NULL);
return 1;
}
if(!lua_isstring(L,-1))
{
Log_Error("L_GetButtonBinding Wrong type of arg, string expected");
lua_pushlightuserdata(L, NULL);
return 1;
}
InputContext* pCtx = GetInputContext();
const char* name = lua_tostring(L, -1);
struct ButtonBinding* p = malloc(sizeof(struct ButtonBinding));
*p = In_FindButtonMapping(pCtx, name);
lua_pushlightuserdata(L, p);
return 1;
}
static int L_FreeButtonBinding(lua_State* L)
{
int top = lua_gettop(L);
if(top != 1)
{
Log_Error("L_FreeButtonBinding Wrong number of args");
lua_pushlightuserdata(L, NULL);
return 1;
}
if(!lua_islightuserdata(L,-1))
{
Log_Error("L_FreeButtonBinding Wrong type of arg, lightuserdata expected");
lua_pushlightuserdata(L, NULL);
return 1;
}
void* p = lua_touserdata(L, -1);
free(p);
return 1;
}
static int L_GetButtonPress(lua_State* L)
{
int top = lua_gettop(L);
if(top != 1)
{
Log_Error("L_GetButtonPress Wrong number of args");
lua_pushboolean(L, false);
return 1;
}
if(!lua_islightuserdata(L,-1))
{
Log_Error("L_GetButtonPress Wrong type of arg, lightuserdata expected");
lua_pushboolean(L, false);
return 1;
}
InputContext* pCtx = GetInputContext();
struct ButtonBinding* p = lua_touserdata(L, -1);
bool bPress = In_GetButtonPressThisFrame(pCtx, *p);
lua_pushboolean(L, bPress);
return 1;
}
/* Game2DLayer */
static int L_GetGamelayerZoom(lua_State* L)
{
int top = lua_gettop(L);
if(top != 1)
{
Log_Error("L_GetGamelayerZoom Wrong number of args");
lua_pushnumber(L, 1.0);
return 1;
}
if(!lua_islightuserdata(L,-1))
{
Log_Error("L_GetGamelayerZoom Wrong type of arg, lightuserdata expected");
lua_pushnumber(L, 1.0);
return 1;
}
struct GameLayer2DData* pData = lua_touserdata(L, -1);
lua_pushnumber(L, (lua_Number)pData->camera.scale[0]);
return 1;
}
static int L_CenterCameraAt(lua_State* L)
{
int top = lua_gettop(L);
if(top != 3)
{
Log_Error("L_CenterCameraAt Wrong number of args");
return 0;
}
if(!lua_isnumber(L,1))
{
Log_Error("L_CenterCameraAt Wrong type of arg, arg 1 number expected");
return 0;
}
if(!lua_isnumber(L,2))
{
Log_Error("L_CenterCameraAt Wrong type of arg, arg 2 number expected");
return 0;
}
if(!lua_islightuserdata(L,3))
{
Log_Error("L_CenterCameraAt Wrong type of arg, arg 3 ptr to gamelayerdata expected");
return 0;
}
float x = lua_tonumber(L, 1);
float y = lua_tonumber(L, 2);
struct GameLayer2DData* pData = lua_touserdata(L, 3);
CenterCameraAt(x, y, &pData->camera, pData->windowW, pData->windowH);
UpdateCameraClamp(pData);
return 0;
}
static int L_SetGamelayerZoom(lua_State* L)
{
int top = lua_gettop(L);
if(top != 2)
{
Log_Error("L_SetGamelayerZoom Wrong number of args");
return 0;
}
if(!lua_isnumber(L,1))
{
Log_Error("L_SetGamelayerZoom Wrong type of arg, arg 1 number expected");
return 0;
}
if(!lua_islightuserdata(L,2))
{
Log_Error("L_SetGamelayerZoom Wrong type of arg, arg 2 lightuserdata expected");
return 0;
}
double num = lua_tonumber(L, 1);
struct GameLayer2DData* pData = lua_touserdata(L, 2);
pData->camera.scale[0] = (float)num;
pData->camera.scale[1] = (float)num;
return 0;
}
void Sc_RegisterCFunction(const char* name, int(*fn)(lua_State*))
{
lua_pushcfunction(gL, fn);
lua_setglobal(gL, name);
}
void Sc_InitScripting()
{
gL = luaL_newstate();
luaL_openlibs(gL); /* Load Lua libraries */
//lua_pushcfunction(gL, &L_OnPropertyChanged);
//lua_setglobal(gL, "OnPropertyChanged");
Sc_RegisterCFunction("OnPropertyChanged", &L_OnPropertyChanged);
Sc_RegisterCFunction("SubscribeGameFrameworkEvent", &L_SubscribeToGameFrameworkEvent);
Sc_RegisterCFunction("UnsubscribeGameFrameworkEvent", &L_UnSubscribeToGameFrameworkEvent);
Sc_RegisterCFunction("FireGameFrameworkEvent", &L_FireGameFrameworkEvent);
Sc_RegisterCFunction("PopGameFrameworkLayer", &L_PopGameFrameworkLayer);
Sc_RegisterCFunction("GetButtonBinding", &L_GetButtonBinding);
Sc_RegisterCFunction("FreeButtonBinding", &L_FreeButtonBinding);
Sc_RegisterCFunction("GetButtonPress", &L_GetButtonPress);
Sc_RegisterCFunction("GetGameLayerZoom", &L_GetGamelayerZoom);
Sc_RegisterCFunction("SetGameLayerZoom", &L_SetGamelayerZoom);
Sc_RegisterCFunction("CenterCameraAt", &L_CenterCameraAt);
}
void Sc_DeInitScripting()
{
lua_close(gL);
}
bool Sc_OpenFile(const char* path)
{
int status = luaL_loadfile(gL, path);
if (status)
{
/* If something went wrong, error message is at the top of */
/* the stack */
Log_Error("Couldn't load file: %s", lua_tostring(gL, -1));
return false;
}
lua_pcall(gL, 0, LUA_MULTRET, 0);
return status == 0;
#if GAME_LUA_VERSION > 1
return status == LUA_OK;
#endif
}
static const char* GetTypeOnTopOfStack()
{
if (lua_isnil(gL, -1))
{
return "nil";
}
else if (lua_isnumber(gL, -1))
{
return "number";
}
else if (lua_isstring(gL, -1))
{
return "string";
}
else if (lua_isboolean(gL, -1))
{
return "boolean";
}
else if (lua_istable(gL, -1))
{
return "table";
}
else if (lua_isuserdata(gL, -1))
{
return "userdata";
}
return "unknown";
}
static void PushFunctionCallArgsOntoStack(struct ScriptCallArgument* pArgs, int numArgs)
{
for (int i = 0; i < numArgs; i++)
{
struct ScriptCallArgument* pArg = &pArgs[i];
switch (pArg->type)
{
case SCA_nil:
lua_pushnil(gL);
break;
case SCA_boolean:
lua_pushboolean(gL, pArg->val.boolean);
break;
case SCA_number:
lua_pushnumber(gL, pArg->val.number);
break;
case SCA_string:
lua_pushstring(gL, pArg->val.string);
break;
case SCA_table:
lua_rawgeti(gL, LUA_REGISTRYINDEX, pArg->val.table);
EASSERT(lua_istable(gL, -1));
break;
case SCA_userdata:
lua_pushlightuserdata(gL, pArg->val.userData);
break;
case SCA_int:
lua_pushinteger(gL, pArg->val.i);
break;
}
}
}
int Sc_CallGlobalFuncReturningTableAndStoreResultInReg(const char* funcName, struct ScriptCallArgument* pArgs, int numArgs)
{
int rVal = 0;
lua_getglobal(gL, funcName);
if (lua_isfunction(gL, -1))
{
PushFunctionCallArgsOntoStack(pArgs, numArgs);
const int returnvalues_count = 1; // function returns 0 values
lua_pcall(gL, numArgs, returnvalues_count, 0); // now call the function
EASSERT(lua_istable(gL, -1));
rVal = luaL_ref(gL, LUA_REGISTRYINDEX);
}
else
{
Log_Error("Sc_CallGlobalFuncReturningTableAndStoreResultInReg funcName '%s' was not a function, it was type '%s'", funcName, GetTypeOnTopOfStack());
}
lua_settop(gL, 0);
return rVal;
}
void Sc_CallFuncInRegTableEntryTable(int regIndex, const char* funcName, struct ScriptCallArgument* pArgs, int numArgs, int numReturnVals)
{
lua_rawgeti(gL, LUA_REGISTRYINDEX, regIndex);
bool bIstable = lua_istable(gL, -1);
if (!bIstable)
{
Log_Error("Sc_CallFuncInRegTableEntryTable. Reg table entry %i is not a table, but %s", regIndex, GetTypeOnTopOfStack());
lua_settop(gL, 0);
return;
}
lua_pushstring(gL, funcName);
lua_gettable(gL, -2);
if (lua_isfunction(gL, -1))
{
lua_rawgeti(gL, LUA_REGISTRYINDEX, regIndex);
PushFunctionCallArgsOntoStack(pArgs, numArgs);
lua_call(gL, numArgs + 1, numReturnVals);
}
else
{
Log_Error("object at key '%s' not a function but type %s ", funcName, GetTypeOnTopOfStack());
}
}
void Sc_CallFuncInRegTableEntry(int regIndex, struct ScriptCallArgument* pArgs, int numArgs, int numReturnVals, int selfRegIndex)
{
lua_rawgeti(gL, LUA_REGISTRYINDEX, regIndex);
bool bIsFunc = lua_isfunction(gL, -1);
if (!bIsFunc)
{
Log_Error("Sc_CallFuncInRegTableEntry. Reg table entry %i is not a function, but %s", regIndex, GetTypeOnTopOfStack());
lua_settop(gL, 0);
return ;
}
lua_rawgeti(gL, LUA_REGISTRYINDEX, selfRegIndex);
PushFunctionCallArgsOntoStack(pArgs, numArgs);
lua_pcall(gL, numArgs + 1, numReturnVals, 0);
lua_settop(gL, 0);
return ;
}
void Sc_AddLightUserDataValueToTable(int regIndex, const char* userDataKey, void* userDataValue)
{
lua_rawgeti(gL, LUA_REGISTRYINDEX, regIndex);
bool bIstable = lua_istable(gL, -1);
if (!bIstable)
{
Log_Error("Sc_CallFuncInRegTableEntryTable. Reg table entry %i is not a table, but %s", regIndex, GetTypeOnTopOfStack());
lua_settop(gL, 0);
return;
}
lua_pushlightuserdata(gL, userDataValue);
lua_setfield(gL, -2, userDataKey);
lua_settop(gL, 0);
}
bool Sc_FunctionPresentInTable(int regIndex, const char* funcName)
{
lua_rawgeti(gL, LUA_REGISTRYINDEX, regIndex);
bool bIstable = lua_istable(gL, -1);
if (!bIstable)
{
Log_Error("Sc_FunctionPresentInTable. Reg table entry %i is not a table, but %s", regIndex, GetTypeOnTopOfStack());
lua_settop(gL, 0);
return false;
}
Sc_TableGet(funcName);
bool bPresent = !Sc_IsNil();
lua_settop(gL, 0);
return bPresent;
}
void Sc_DumpStack()
{
int top = lua_gettop(gL);
for (int i = 1; i <= top; i++)
{
Log_Info("%d\t%s\t", i, luaL_typename(gL, i));
switch (lua_type(gL, i))
{
case LUA_TNUMBER:
Log_Info("%g", lua_tonumber(gL, i));
break;
case LUA_TSTRING:
Log_Info("%s", lua_tostring(gL, i));
break;
case LUA_TBOOLEAN:
Log_Info("%s", (lua_toboolean(gL, i) ? "true" : "false"));
break;
case LUA_TNIL:
Log_Info("%s", "nil");
break;
default:
Log_Info("%p", lua_topointer(gL, i));
break;
}
}
}
int Sc_Int()
{
#if GAME_LUA_MINOR_VERSION >= 3
EASSERT(lua_isinteger(gL, -1));
#endif
return lua_tointeger(gL, -1);
}
float Sc_Float()
{
EASSERT(lua_isnumber(gL, -1));
return lua_tonumber(gL, -1);
}
size_t Sc_StackTopStringLen()
{
EASSERT(lua_isstring(gL, -1));
const char* str = lua_tostring(gL, -1);
return strlen(str);
}
void Sc_StackTopStrCopy(char* pOutString)
{
EASSERT(lua_isstring(gL, -1));
const char* str = lua_tostring(gL, -1);
strcpy(pOutString, str);
}
void Sc_ResetStack()
{
lua_settop(gL, 0);
}
void Sc_DeleteTableInReg(int index)
{
luaL_unref(gL, LUA_REGISTRYINDEX, index);
}
bool Sc_IsTable()
{
return lua_istable(gL, -1);
}
int Sc_Type()
{
return lua_type(gL, -1);
}
void Sc_Pop()
{
lua_pop(gL, 1);
}
void Sc_TableGet(const char* key)
{
EASSERT(Sc_IsTable());
lua_getfield(gL, -1, key);
}
void Sc_TableGetIndex(int index)
{
EASSERT(Sc_IsTable());
lua_geti(gL, -1, index);
}
int Sc_TableLen()
{
EASSERT(Sc_IsTable());
lua_len(gL, -1);
int i = Sc_Int();
Sc_Pop();
return i;
}
bool Sc_IsNil()
{
return lua_isnil(gL, -1);
}
bool Sc_IsString()
{
return lua_isstring(gL, -1);
}
bool Sc_IsInteger()
{
return lua_isinteger(gL, -1);
}
bool Sc_IsBool()
{
return lua_isboolean(gL, -1);
}
bool Sc_IsNumber()
{
return lua_isnumber(gL, -1);
}
bool Sc_Bool()
{
return lua_toboolean(gL, -1) != 0;
}
bool Sc_IsFunction()
{
return lua_isfunction(gL, -1) != 0;
}
bool Sc_StringCmp(const char* cmpTo)
{
EASSERT(Sc_IsString());
const char* str = lua_tostring(gL, -1);
return strcmp(str, cmpTo) == 0;
}
void Sc_NewTableOnStack(int arrayElementHint, int nonArrayElementHint)
{
lua_createtable(gL, arrayElementHint, nonArrayElementHint);
}
void Sc_SetIntAtTableIndex(int index, int value)
{
EASSERT(Sc_IsTable());
lua_pushinteger(gL, index);
lua_pushinteger(gL, value);
lua_settable(gL, -3);
}
void Sc_SetTable()
{
lua_settable(gL, -3);
}
void Sc_PushInt(int i)
{
lua_pushinteger(gL, i);
}
void Sc_SetIntAtTableKey(const char* key, int val)
{
EASSERT(Sc_IsTable());
lua_pushinteger(gL, val);
lua_setfield(gL, -2, key);
}
void Sc_SetFloatAtTableKey(const char* key, float val)
{
EASSERT(Sc_IsTable());
lua_pushnumber(gL, (lua_Number)val);
lua_setfield(gL, -2, key);
}
void Sc_SetPointerAtTableKey(const char* key, void* ptr)
{
EASSERT(Sc_IsTable());
lua_pushlightuserdata(gL, ptr);
lua_setfield(gL, -2, key);
}
int Sc_RefTable()
{
return luaL_ref(gL, LUA_REGISTRYINDEX);
}
void Sc_UnRefTable(int ref)
{
luaL_unref(gL, LUA_REGISTRYINDEX, ref);
}