Added hook_on_sync_table_change() to Lua API

This commit is contained in:
MysterD 2022-02-04 00:15:14 -08:00
parent d57606bfa3
commit ab1e85994e
4 changed files with 282 additions and 130 deletions

View file

@ -5,7 +5,43 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
<br />
## [Hook Event Types](#Hook-Event-Types)
## [hook_chat_command](#hook_chat_command)
`hook_chat_command()` allows Lua mods to react and respond to chat commands. Chat commands start with the `/` character. The function the mod passes to the hook should return `true` when the command was valid and `false` otherwise.
### Parameters
| Field | Type |
| ----- | ---- |
| command | string |
| description | string |
| func | Lua Function |
### Lua Example
```lua
function on_test_command(msg)
if msg == 'on' then
djui_chat_message_create('Test: enabled')
return true
elseif msg == 'off' then
djui_chat_message_create('Test: disabled')
return true
end
return false
end
hook_chat_command('test', "[on|off] turn test on or off", on_hide_and_seek_command)
```
[:arrow_up_small:](#)
<br />
## [hook_event](#hook_event)
The lua functions sent to `hook_event()` will be automatically called by SM64 when certain events occur.
### [Hook Event Types](#Hook-Event-Types)
| Type | Description | Parameters |
| :--- | :---------- | :--------- |
| HOOK_UPDATE | Called once per frame | None |
@ -17,12 +53,6 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
| HOOK_ON_PLAYER_CONNECTED | Called when a player connects | [MarioState](structs.md#MarioState) connector |
| HOOK_ON_PLAYER_DISCONNECTED | Called when a player disconnects | [MarioState](structs.md#MarioState) disconnector |
<br />
## [hook_event](#hook_event)
The lua functions sent to `hook_event()` will be automatically called by SM64 when certain events occur.
### Parameters
| Field | Type |
@ -114,32 +144,39 @@ hook_mario_action(ACT_WALL_SLIDE, act_wall_slide)
[:arrow_up_small:](#)
## [hook_chat_command](#hook_chat_command)
`hook_chat_command()` allows Lua mods to react and respond to chat commands. Chat commands start with the `/` character. The function the mod passes to the hook should return `true` when the command was valid and `false` otherwise.
<br />
## [hook_on_sync_table_change](#hook_on_sync_table_change)
`hook_on_sync_table_change()` allows Lua mods to react to sync table changes.
- `syncTable` parameter must be a sync table, e.g. [gGlobalSyncTable](globals.md#gGlobalSyncTable), [gPlayerSyncTable[]](globals.md#gPlayerSyncTable), or one of their child tables.
- `field` parameter must be one of the fields in the `SyncTable`.
- `tag` parameter can be any type, and is automatically passed to the callback.
- `func` parameter must be a function with three parameters: `tag`, `oldVal`, and `newVal`.
- `tag` will be the same `tag` passed into `hook_on_sync_table_change()`.
- `oldVal` will be the value before it was set.
- `newVal` will be the value that it was set to.
### Parameters
| Field | Type |
| ----- | ---- |
| command | string |
| description | string |
| syncTable | SyncTable |
| field | value |
| tag | value |
| func | Lua Function |
### Lua Example
```lua
function on_test_command(msg)
if msg == 'on' then
djui_chat_message_create('Test: enabled')
return true
elseif msg == 'off' then
djui_chat_message_create('Test: disabled')
return true
end
return false
function on_testing_field_changed(tag, oldVal, newVal)
print('testingField changed:', tag, ',', oldVal, '->', newVal)
end
hook_chat_command('test', "[on|off] turn test on or off", on_hide_and_seek_command)
hook_on_sync_table_change(gGlobalSyncTable, 'testingField', 'tag', on_testing_field_changed)
-- now when testingField is set, either locally or over the network, on_testing_field_changed() will be called
gGlobalSyncTable.testingField = 'hello'
```
[:arrow_up_small:](#)

View file

@ -2,27 +2,19 @@
-- incompatible: gamemode
-- description: A simple manhunt gamemode for Co-op.\n\nThe game is split into two teams:\n\nHiders and Seekers. The goal is for all\n\Hiders to be converted into a Seeker within a certain timeframe.\n\nAll Seekers appear as a metal character, and are given boosted speed\n\and jump height.\n\nHiders are given no enhancements, and\n\become a Seeker upon dying.\n\nEnjoy! :D\n\nConcept by: Super Keeberghrh
-- cache old seeking state for sounds and popups
sCachedState = {}
for i=0,(MAX_PLAYERS-1) do
sCachedState[i] = {}
sCachedState[i].seeking = false
end
-- globally sync enabled state
gGlobalSyncTable.hideAndSeek = true
-- keep track of round info for popup
sCachedRoundNumber = 0
sCachedRoundEnded = true
gGlobalSyncTable.roundNumber = 0
gGlobalSyncTable.roundEnded = true
sRoundEndedTimer = 0
sRoundIntermissionTime = 5 * 30 -- five seconds
-- server keeps track of last player turned seeker
sLastSeekerIndex = 0
-- keep track of distance moved recently
-- keep track of distance moved recently (camping detection)
sLastPos = {}
sLastPos.x = 0
sLastPos.y = 0
@ -54,7 +46,7 @@ function server_update(m)
return
end
-- the following is round-ended code
-- check to see if the round should end
if not gGlobalSyncTable.roundEnded then
if not hasHider or not hasSeeker then
gGlobalSyncTable.roundEnded = true
@ -66,7 +58,7 @@ function server_update(m)
-- if round was over for 5 seconds
sRoundEndedTimer = sRoundEndedTimer + 1
if sRoundEndedTimer >= 30 * 5 then
if sRoundEndedTimer >= sRoundIntermissionTime then
-- reset seekers
if not hasHider then
for i=0,(MAX_PLAYERS-1) do
@ -95,6 +87,36 @@ function server_update(m)
end
end
function camping_detection(m)
-- this code only runs for the local player
local s = gPlayerSyncTable[m.playerIndex]
-- track how far the local player has moved recently
sDistanceMoved = sDistanceMoved - 0.25 + vec3f_dist(sLastPos, m.pos) * 0.02
vec3f_copy(sLastPos, m.pos)
-- clamp between 0 to 100
if sDistanceMoved < 0 then sDistanceMoved = 0 end
if sDistanceMoved > 100 then sDistanceMoved = 100 end
-- if player hasn't moved enough, start a timer
if sDistanceMoved < 10 and not s.seeking then
sDistanceTimer = sDistanceTimer + 1
end
-- if the player has moved enough, reset the timer
if sDistanceMoved > 20 then
sDistanceTimer = 0
end
-- inform the player that they need to move, or make them a seeker
if sDistanceTimer == 30 * 1 then
djui_popup_create('\\#ff4040\\Keep moving!', 3)
elseif sDistanceTimer > 30 * 6 then
s.seeking = true
end
end
function update()
-- check gamemode enabled state
if not gGlobalSyncTable.hideAndSeek then
@ -106,60 +128,10 @@ function update()
server_update(gMarioStates[0])
end
-- inform players when a new round has begun
if sCachedRoundNumber < gGlobalSyncTable.roundNumber then
sCachedRoundNumber = gGlobalSyncTable.roundNumber
djui_popup_create('\\#a0ffa0\\a new round has begun', 2)
sDistanceMoved = 100
sDistanceTimer = 0
play_character_sound(gMarioStates[0], CHAR_SOUND_HERE_WE_GO)
end
-- inform players when a round has ended
if gGlobalSyncTable.roundEnded and not sCachedRoundEnded then
sCachedRoundNumber = gGlobalSyncTable.roundNumber
djui_popup_create('\\#a0a0ff\\the round has ended', 2)
end
sCachedRoundEnded = gGlobalSyncTable.roundEnded
-- check if local player is camping
camping_detection(gMarioStates[0])
end
function camping_detection(m)
-- this code only runs for the local player
local s = gPlayerSyncTable[m.playerIndex]
-- become a seeker if you stop moving
sDistanceMoved = sDistanceMoved - 0.25 + vec3f_dist(sLastPos, m.pos) * 0.02
vec3f_copy(sLastPos, m.pos)
if sDistanceMoved > 100 then
sDistanceMoved = 100
end
if sDistanceMoved < 0 then
sDistanceMoved = 0
end
if sDistanceMoved < 10 and not s.seeking then
sDistanceTimer = sDistanceTimer + 1
if sDistanceTimer == 30 * 1 then
djui_popup_create('\\#ff4040\\Keep moving!', 3)
elseif sDistanceTimer > 30 * 6 then
s.seeking = true
end
end
if sDistanceMoved > 20 then
sDistanceTimer = 0
end
end
function mario_local_update(m)
-- this code only runs for the local player
local s = gPlayerSyncTable[m.playerIndex]
if m.health <= 0x110 then
s.seeking = true
end
end
function mario_update(m)
-- check gamemode enabled state
if not gGlobalSyncTable.hideAndSeek then
@ -169,9 +141,9 @@ function mario_update(m)
-- this code runs for all players
local s = gPlayerSyncTable[m.playerIndex]
-- only run certain code for the local player
if m.playerIndex == 0 then
mario_local_update(m)
-- if the local player died, make them a seeker
if m.playerIndex == 0 and m.health <= 0x110 then
s.seeking = true
end
-- display all seekers as metal
@ -179,18 +151,6 @@ function mario_update(m)
m.marioBodyState.modelState = MODEL_STATE_METAL
m.health = 0x880
end
-- play sound and create popup if seeker state changed
local c = sCachedState[m.playerIndex]
local np = gNetworkPlayers[m.playerIndex]
if s.seeking and not c.seeking then
play_sound(SOUND_OBJ_BOWSER_LAUGH, m.marioObj.header.gfx.cameraToObject)
playerColor = network_get_player_text_color_string(m.playerIndex)
djui_popup_create(playerColor .. np.name .. '\\#ffa0a0\\ is now a seeker', 2)
sLastSeekerIndex = m.playerIndex
end
c.seeking = s.seeking
end
function mario_before_phys_step(m)
@ -271,6 +231,40 @@ function on_hide_and_seek_command(msg)
return false
end
-----------------------
-- network callbacks --
-----------------------
function on_round_number_changed(tag, oldVal, newVal)
-- inform players when a new round has begun
if oldVal < newVal then
djui_popup_create('\\#a0ffa0\\a new round has begun', 2)
sDistanceMoved = 100
sDistanceTimer = 0
play_character_sound(gMarioStates[0], CHAR_SOUND_HERE_WE_GO)
end
end
function on_round_ended_changed(tag, oldVal, newVal)
-- inform players when a round has ended
if newVal and not oldVal then
djui_popup_create('\\#a0a0ff\\the round has ended', 2)
end
end
function on_seeking_changed(tag, oldVal, newVal)
local m = gMarioStates[tag]
local np = gNetworkPlayers[tag]
-- play sound and create popup if became a seeker
if newVal and not oldVal then
play_sound(SOUND_OBJ_BOWSER_LAUGH, m.marioObj.header.gfx.cameraToObject)
playerColor = network_get_player_text_color_string(m.playerIndex)
djui_popup_create(playerColor .. np.name .. '\\#ffa0a0\\ is now a seeker', 2)
sLastSeekerIndex = m.playerIndex
end
end
-----------
-- hooks --
-----------
@ -282,3 +276,10 @@ hook_event(HOOK_ON_PVP_ATTACK, on_pvp_attack)
hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected)
hook_chat_command('hide-and-seek', "[on|off] turn hide-and-seek on or off", on_hide_and_seek_command)
-- call functions when certain sync table values change
hook_on_sync_table_change(gGlobalSyncTable, 'roundNumber', 0, on_round_number_changed)
hook_on_sync_table_change(gGlobalSyncTable, 'roundEnded', 0, on_round_ended_changed)
for i=0,(MAX_PLAYERS-1) do
hook_on_sync_table_change(gPlayerSyncTable[i], 'seeking', i, on_seeking_changed)
end

View file

@ -48,7 +48,7 @@ void smlua_call_event_hooks(enum LuaHookedEventType hookType) {
// call the callback
if (0 != lua_pcall(L, 0, 0, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the event_hook callback: %u, %s", hookType, lua_tostring(L, -1));
continue;
}
}
@ -70,7 +70,7 @@ void smlua_call_event_hooks_mario_param(enum LuaHookedEventType hookType, struct
// call the callback
if (0 != lua_pcall(L, 1, 0, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
continue;
}
}
@ -98,7 +98,7 @@ void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struc
// call the callback
if (0 != lua_pcall(L, 2, 0, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
continue;
}
}
@ -120,7 +120,7 @@ void smlua_call_event_hooks_network_player_param(enum LuaHookedEventType hookTyp
// call the callback
if (0 != lua_pcall(L, 1, 0, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
continue;
}
}
@ -182,10 +182,10 @@ bool smlua_call_action_hook(struct MarioState* m, s32* returnValue) {
lua_pushinteger(L, m->playerIndex);
lua_gettable(L, -2);
lua_remove(L, -2);
// call the callback
if (0 != lua_pcall(L, 1, 1, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the action callback: %u, %s", m->action, lua_tostring(L, -1));
continue;
}
@ -280,7 +280,7 @@ bool smlua_call_chat_command_hook(char* command) {
// call the callback
if (0 != lua_pcall(L, 1, 1, 0)) {
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
LOG_LUA("Failed to call the chat command callback: %s, %s", command, lua_tostring(L, -1));
continue;
}
@ -307,6 +307,62 @@ void smlua_display_chat_commands(void) {
}
}
//////////////////////////////
// hooked sync table change //
//////////////////////////////
int smlua_hook_on_sync_table_change(lua_State* L) {
LUA_STACK_CHECK_BEGIN();
if (L == NULL) { return 0; }
if(!smlua_functions_valid_param_count(L, 4)) { return 0; }
int syncTableIndex = 1;
int keyIndex = 2;
int tagIndex = 3;
int funcIndex = 4;
if (lua_type(L, syncTableIndex) != LUA_TTABLE) {
LOG_LUA("Tried to attach a non-table to hook_on_sync_table_change: %d", lua_type(L, syncTableIndex));
return 0;
}
if (lua_type(L, funcIndex) != LUA_TFUNCTION) {
LOG_LUA("Tried to attach a non-function to hook_on_sync_table_change: %d", lua_type(L, funcIndex));
return 0;
}
// set hook's table
lua_newtable(L);
int valTableIndex = lua_gettop(L);
lua_pushstring(L, "_func");
lua_pushvalue(L, funcIndex);
lua_settable(L, valTableIndex);
lua_pushstring(L, "_tag");
lua_pushvalue(L, tagIndex);
lua_settable(L, valTableIndex);
// get _hook_on_changed
lua_pushstring(L, "_hook_on_changed");
lua_rawget(L, syncTableIndex);
int hookOnChangedIndex = lua_gettop(L);
// attach
lua_pushvalue(L, keyIndex);
lua_pushvalue(L, valTableIndex);
lua_settable(L, hookOnChangedIndex);
// clean up
lua_remove(L, hookOnChangedIndex);
lua_remove(L, valTableIndex);
LUA_STACK_CHECK_END();
return 1;
}
//////////
// misc //
//////////
@ -344,4 +400,5 @@ void smlua_bind_hooks(void) {
smlua_bind_function(L, "hook_event", smlua_hook_event);
smlua_bind_function(L, "hook_mario_action", smlua_hook_mario_action);
smlua_bind_function(L, "hook_chat_command", smlua_hook_chat_command);
smlua_bind_function(L, "hook_on_sync_table_change", smlua_hook_on_sync_table_change);
}

View file

@ -16,6 +16,7 @@ static void smlua_sync_table_create(u16 modRemoteIndex, enum LuaSyncTableType ls
smlua_push_integer_field(t, "_type", lst);
smlua_push_table_field(t, "_seq");
smlua_push_table_field(t, "_table");
smlua_push_table_field(t, "_hook_on_changed");
// set parent
lua_pushstring(L, "_parent");
@ -112,7 +113,36 @@ static bool smlua_sync_table_unwind(int syncTableIndex, int keyIndex) {
return true;
}
static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool alterSeq) {
static void smlua_sync_table_call_hook(int syncTableIndex, int keyIndex, int prevValueIndex, int valueIndex) {
LUA_STACK_CHECK_BEGIN();
lua_State* L = gLuaState;
// get hook table
lua_pushstring(L, "_hook_on_changed"); lua_rawget(L, syncTableIndex);
lua_pushvalue(L, keyIndex); lua_gettable(L, -2);
lua_remove(L, -2); // pop _hook_on_changed
int hookTableIndex = lua_gettop(L);
if (lua_type(L, hookTableIndex) == LUA_TTABLE) {
// push hook func
lua_pushstring(L, "_func"); lua_gettable(L, hookTableIndex);
// push hook params
lua_pushstring(L, "_tag"); lua_gettable(L, hookTableIndex);
lua_pushvalue(L, prevValueIndex);
lua_pushvalue(L, valueIndex);
// call hook
if (0 != lua_pcall(L, 3, 0, 0)) {
LOG_LUA("Failed to call the hook_on_changed callback: %s", lua_tostring(L, -1));
}
}
lua_pop(L, 1); // pop _hook_on_changed's value
LUA_STACK_CHECK_END();
}
static bool smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool alterSeq) {
LUA_STACK_CHECK_BEGIN();
lua_State* L = gLuaState;
@ -120,21 +150,23 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
int keyIndex = stackIndex + 2;
int valueIndex = stackIndex + 3;
bool ret = false;
// get modRemoteIndex
u16 modRemoteIndex = smlua_get_integer_field(syncTableIndex, "_remoteIndex");
if (!gSmLuaConvertSuccess) {
LOG_LUA("Error: tried to alter sync table with an invalid modRemoteIndex: %u", modRemoteIndex);
return;
return false;
}
// get key
struct LSTNetworkType lntKey = smlua_to_lnt(L, keyIndex);
if (!gSmLuaConvertSuccess) {
LOG_LUA("Error: tried to alter sync table with an invalid key");
return;
return false;
}
lntKey = lntKey;
////////////////
// prev value //
@ -144,12 +176,12 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
lua_pushvalue(L, keyIndex);
lua_rawget(L, -2);
int prevValueType = lua_type(L, -1);
lua_pop(L, 1); // pop prev value
lua_pop(L, 1); // pop _table
lua_remove(L, -2); // pop _table
int prevValueIndex = lua_gettop(L);
if (prevValueType == LUA_TTABLE) {
LOG_LUA("Error: tried to assign on top of sync table");
return;
goto CLEANUP_STACK;
}
///////////
@ -161,12 +193,12 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
if (valueType == LUA_TTABLE) {
if (prevValueType != LUA_TNIL) {
LOG_LUA("Error: tried to set a sync table field to a different sync table");
return;
goto CLEANUP_STACK;
}
if (!smlua_is_table_empty(valueIndex)) {
LOG_LUA("Error: tried to generate a sync table with a non-empty table");
return;
goto CLEANUP_STACK;
}
// create sync table
@ -179,19 +211,19 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
lua_settable(L, -3);
lua_pop(L, 1); // pop _table
LUA_STACK_CHECK_END();
return;
ret = true;
goto CLEANUP_STACK;
}
struct LSTNetworkType lntValue = smlua_to_lnt(L, valueIndex);
if (!gSmLuaConvertSuccess) {
LOG_LUA("Error: tried to alter sync table with an invalid value");
return;
goto CLEANUP_STACK;
}
// set value
lua_getfield(L, syncTableIndex, "_table");
lua_pushvalue(L, -3);
lua_pushvalue(L, -3);
lua_pushvalue(L, keyIndex);
lua_pushvalue(L, valueIndex);
lua_settable(L, -3);
lua_pop(L, 1); // pop _table
@ -224,7 +256,7 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
// unwind key + parent tables
if (!smlua_sync_table_unwind(syncTableIndex, keyIndex)) {
LOG_LUA("Error: failed to unwind sync table for sending over the network");
return;
goto CLEANUP_STACK;
}
// send over the network
@ -232,12 +264,23 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
network_send_lua_sync_table(toLocalIndex, seq, modRemoteIndex, sUnwoundLntsCount, sUnwoundLnts, &lntValue);
}
///////////////
// call hook //
///////////////
smlua_sync_table_call_hook(syncTableIndex, keyIndex, prevValueIndex, valueIndex);
CLEANUP_STACK:
lua_remove(L, prevValueIndex); // pop prevValue
LUA_STACK_CHECK_END();
return ret;
}
static int smlua__set_sync_table_field(UNUSED lua_State* L) {
static int smlua__set_sync_table_field(lua_State* L) {
if (!smlua_functions_valid_param_count(L, 3)) { return 0; }
smlua_sync_table_send_field(0, 0, true);
lua_pushboolean(L, smlua_sync_table_send_field(0, 0, true));
return 1;
}
@ -277,17 +320,19 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ln
lua_getglobal(L, "_G"); // get global table
lua_getfield(L, LUA_REGISTRYINDEX, entry->path); // get the file's "global" table
lua_remove(L, -2); // remove global table
int fileGlobalIndex = lua_gettop(L);
// push global sync table
u16 syncTableSize = 1;
smlua_push_lnt(&lntKeys[lntKeyCount - 1]);
lua_gettable(L, fileGlobalIndex);
int syncTableIndex = lua_gettop(L);
if (lua_type(L, -1) != LUA_TTABLE) {
LOG_ERROR("Received sync table field packet with an invalid table");
return;
}
lua_remove(L, fileGlobalIndex); // pop file's "global" table
int syncTableIndex = lua_gettop(L);
for (int i = lntKeyCount - 2; i >= 1; i--) {
// get child sync table
@ -341,8 +386,6 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ln
LOG_INFO("Received outdated sync table field packet: %llu <= %llu", seq, readSeq);
lua_pop(L, 1); // pop seq table
lua_pop(L, syncTableSize); // pop sync table
lua_pop(L, 1); // pop file's "global" table
lua_pop(L, 1); // pop global table
return;
}
@ -355,17 +398,31 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ln
// get internal table
lua_pushstring(L, "_table");
lua_rawget(L, -2);
int t = lua_gettop(L);
int internalTableIndex = lua_gettop(L);
// get prevValue
smlua_push_lnt(&lntKeys[0]);
lua_rawget(L, internalTableIndex);
int prevValueIndex = lua_gettop(L);
// set key/value
smlua_push_lnt(&lntKeys[0]);
smlua_push_lnt(lntValue);
lua_rawset(L, t);
lua_rawset(L, internalTableIndex);
// call hook
smlua_push_lnt(&lntKeys[0]);
int keyIndex = lua_gettop(L);
smlua_push_lnt(lntValue);
int valueIndex = lua_gettop(L);
smlua_sync_table_call_hook(syncTableIndex, keyIndex, prevValueIndex, valueIndex);
lua_pop(L, 1); // pop value
lua_pop(L, 1); // pop key
// cleanup
lua_pop(L, 1); // pop prevValue
lua_pop(L, 1); // pop internal table
lua_pop(L, syncTableSize); // pop sync table
lua_pop(L, 1); // pop file's "global" table
lua_pop(L, 1); // pop global table
LUA_STACK_CHECK_END();
}