mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 13:35:12 +00:00
Added hook_on_sync_table_change() to Lua API
This commit is contained in:
parent
d57606bfa3
commit
ab1e85994e
4 changed files with 282 additions and 130 deletions
|
@ -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:](#)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue