diff --git a/autogen/lua_constants/constants.lua b/autogen/lua_constants/constants.lua
index 0d54477e..458bb884 100644
--- a/autogen/lua_constants/constants.lua
+++ b/autogen/lua_constants/constants.lua
@@ -9,10 +9,6 @@ HOOK_ON_SET_MARIO_ACTION = 3
HOOK_BEFORE_PHYS_STEP = 4
HOOK_MAX = 5
-LST_GLOBAL = 0
-LST_PLAYER = 1
-LST_MAX = 2
-
_CObject = {
__index = function (t,k)
return _get_field(t['_lot'], t['_pointer'], k)
@@ -37,6 +33,15 @@ _SyncTable = {
end
}
+_ReadOnlyTable = {
+ __index = function (t,k)
+ local _table = rawget(t, '_table')
+ return _table[k]
+ end,
+ __newindex = function (t,k,v)
+ end
+}
+
function vec3f_copy(dest, src)
dest.x = src.x
dest.y = src.y
diff --git a/developer/network.sh b/developer/network.sh
index 126e29d4..8fd0a05e 100644
--- a/developer/network.sh
+++ b/developer/network.sh
@@ -24,9 +24,9 @@ $FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
exit
# debug on server
-#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt & > /dev/null
-#winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_server.txt' -ex 'quit'
-#exit
+$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt & > /dev/null
+winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_server.txt' -ex 'quit'
+exit
###################
# debug on client #
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 394b8e6b..e91312b9 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -619,12 +619,6 @@
- HOOK_BEFORE_PHYS_STEP
- HOOK_MAX
-
-
-- LST_GLOBAL
-- LST_PLAYER
-- LST_MAX
-
[:arrow_up_small:](#)
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index b3868b04..eaabc30d 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -4,9 +4,6 @@ char gSmluaConstants[] = "HOOK_UPDATE = 0\n"
"HOOK_ON_SET_MARIO_ACTION = 3\n"
"HOOK_BEFORE_PHYS_STEP = 4\n"
"HOOK_MAX = 5\n"
-"LST_GLOBAL = 0\n"
-"LST_PLAYER = 1\n"
-"LST_MAX = 2\n"
"_CObject = {\n"
" __index = function (t,k)\n"
" return _get_field(t['_lot'], t['_pointer'], k)\n"
@@ -29,6 +26,14 @@ char gSmluaConstants[] = "HOOK_UPDATE = 0\n"
" _set_sync_table_field(t, k, v)\n"
" end\n"
"}\n"
+"_ReadOnlyTable = {\n"
+" __index = function (t,k)\n"
+" local _table = rawget(t, '_table')\n"
+" return _table[k]\n"
+" end,\n"
+" __newindex = function (t,k,v)\n"
+" end\n"
+"}\n"
"function vec3f_copy(dest, src)\n"
" dest.x = src.x\n"
" dest.y = src.y\n"
diff --git a/src/pc/lua/smlua_sync_table.c b/src/pc/lua/smlua_sync_table.c
index cd1fa4e8..41f4f215 100644
--- a/src/pc/lua/smlua_sync_table.c
+++ b/src/pc/lua/smlua_sync_table.c
@@ -3,65 +3,114 @@
#include "pc/network/network.h"
#include "pc/network/network_player.h"
-static bool smlua_value_to_lnt(int index, struct LSTNetworkType* lnt) {
+#define MAX_UNWOUND_SIZE 256
+static struct LSTNetworkType sUnwoundLnts[MAX_UNWOUND_LNT] = { 0 };
+u16 sUnwoundLntsCount = 0;
+
+static void smlua_sync_table_create(u16 modRemoteIndex, enum LuaSyncTableType lst, int* parentIndex, int* keyIndex) {
lua_State* L = gLuaState;
- int valueType = lua_type(L, index);
+ lua_newtable(L);
+ int t = lua_gettop(L);
- if (valueType == LUA_TNUMBER) {
- lnt->type = LST_NETWORK_TYPE_INTEGER;
- lnt->value.integer = lua_tointeger(L, index);
+ // push fields
+ smlua_push_integer_field(t, "_remoteIndex", modRemoteIndex);
+ smlua_push_integer_field(t, "_type", lst);
+ smlua_push_table_field(t, "_seq");
+ smlua_push_table_field(t, "_table");
- if (lnt->value.integer == 0) {
- lnt->type = LST_NETWORK_TYPE_NUMBER;
- lnt->value.number = lua_tonumber(L, index);
- }
- return true;
- }
+ // set parent
+ lua_pushstring(L, "_parent");
+ if (parentIndex == NULL) { lua_pushnil(L); } else { lua_pushvalue(L, *parentIndex); }
+ lua_settable(L, -3);
- if (valueType == LUA_TBOOLEAN) {
- lnt->type = LST_NETWORK_TYPE_BOOLEAN;
- lnt->value.boolean = lua_toboolean(L, index);
- return true;
- }
+ // set key name
+ lua_pushstring(L, "_name");
+ if (keyIndex == NULL) { lua_pushnil(L); } else { lua_pushvalue(L, *keyIndex); }
+ lua_settable(L, -3);
- if (valueType == LUA_TSTRING) {
- lnt->type = LST_NETWORK_TYPE_STRING;
- lnt->value.string = lua_tostring(L, index);
- if (lnt->value.string == NULL || strlen(lnt->value.string) > 256) {
- LOG_LUA("smlua_value_to_lnt on invalid string value: '%s'", (lnt->value.string == NULL) ? "" : lnt->value.string);
- return false;
- }
- return true;
- }
-
- if (valueType == LUA_TNIL) {
- lnt->type = LST_NETWORK_TYPE_NIL;
- return true;
- }
-
- return false;
+ // attach metatable
+ lua_pushglobaltable(L);
+ lua_getfield(L, -1, "_SyncTable");
+ lua_setmetatable(L, -3);
+ lua_pop(L, 1); // pop global table
}
-static void smlua_push_lnt(struct LSTNetworkType* lnt) {
+static bool smlua_sync_table_unwind(int syncTableIndex, int keyIndex) {
+ LUA_STACK_CHECK_BEGIN();
lua_State* L = gLuaState;
- switch (lnt->type) {
- case LST_NETWORK_TYPE_INTEGER:
- lua_pushinteger(L, lnt->value.integer);
- break;
- case LST_NETWORK_TYPE_NUMBER:
- lua_pushnumber(L, lnt->value.number);
- break;
- case LST_NETWORK_TYPE_BOOLEAN:
- lua_pushboolean(L, lnt->value.boolean);
- break;
- case LST_NETWORK_TYPE_STRING:
- lua_pushstring(L, lnt->value.string);
- break;
- case LST_NETWORK_TYPE_NIL:
- lua_pushnil(L);
- break;
- default: SOFT_ASSERT(false);
+ sUnwoundLntsCount = 0;
+
+ // get key
+ sUnwoundLnts[sUnwoundLntsCount++] = smlua_to_lnt(L, keyIndex);
+ if (!gSmLuaConvertSuccess) {
+ LOG_LUA("attempted to unwind sync table with invalid key type");
+ return false;
}
+
+ // duplicate sync table for iteration
+ lua_pushvalue(L, syncTableIndex);
+ syncTableIndex = lua_gettop(L);
+
+ while (true) {
+ // make sure we remain within limits
+ if (sUnwoundLntsCount >= MAX_UNWOUND_LNT) {
+ LOG_LUA("attempted to unwind sync table past its limit");
+ return false;
+ }
+
+ // get name of table
+ lua_pushstring(L, "_name");
+ lua_gettable(L, syncTableIndex);
+ sUnwoundLnts[sUnwoundLntsCount++] = smlua_to_lnt(L, -1);
+
+ // translate player index
+ lua_getfield(L, syncTableIndex, "_type");
+ lua_Integer lst = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ if (lst == LST_PLAYER) {
+ struct LSTNetworkType* n = &sUnwoundLnts[sUnwoundLntsCount - 1];
+ assert(n->type == LST_NETWORK_TYPE_INTEGER);
+ n->value.integer = network_player_global_index_from_local(n->value.integer);
+ }
+
+ lua_pop(L, 1); // pop _name value
+ if (!gSmLuaConvertSuccess) {
+ LOG_LUA("attempted to unwind sync table with invalid parent");
+ lua_pop(L, 1); // pop iterative _parent
+ return false;
+ }
+
+ // get parent of table
+ lua_pushstring(L, "_parent");
+ lua_gettable(L, syncTableIndex);
+ int parentType = lua_type(L, -1);
+ lua_replace(L, syncTableIndex);
+
+ // validate parent type
+ if (parentType != LUA_TTABLE) {
+ if (parentType != LUA_TNIL) {
+ LOG_LUA("attempted to unwind sync table into an invalid parent");
+ return false;
+ }
+ break;
+ }
+ }
+
+ lua_pop(L, 1); // pop iterative _parent
+
+ // check size
+ size_t unwoundSize = 0;
+ for (int i = 0; i < sUnwoundLntsCount; i++) {
+ unwoundSize += sUnwoundLnts[i].size;
+ }
+
+ if (unwoundSize >= MAX_UNWOUND_SIZE) {
+ LOG_LUA("attempted to unwind sync table with too long of a key/parent length");
+ return false;
+ }
+
+ LUA_STACK_CHECK_END();
+ return true;
}
static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool alterSeq) {
@@ -75,29 +124,70 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
// get modRemoteIndex
u16 modRemoteIndex = smlua_get_integer_field(syncTableIndex, "_remoteIndex");
if (!gSmLuaConvertSuccess) {
- LOG_LUA("smlua_sync_table_send_field on invalid modRemoteIndex: %u", modRemoteIndex);
- return;
- }
-
- // get lst
- enum LuaSyncTableType lst = smlua_get_integer_field(syncTableIndex, "_type");
- if (!gSmLuaConvertSuccess || lst >= LST_MAX) {
- LOG_LUA("smlua_sync_table_send_field on invalid LST: %u", lst);
+ LOG_LUA("Error: tried to alter sync table with an invalid modRemoteIndex: %u", modRemoteIndex);
return;
}
// get key
- struct LSTNetworkType lntKey = { 0 };
- if (!smlua_value_to_lnt(keyIndex, &lntKey)) { return; }
+ struct LSTNetworkType lntKey = smlua_to_lnt(L, keyIndex);
+ if (!gSmLuaConvertSuccess) {
+ LOG_LUA("Error: tried to alter sync table with an invalid key");
+ return;
+ }
+ lntKey = lntKey;
+
+ ////////////////
+ // prev value //
+ ////////////////
+
+ lua_getfield(L, syncTableIndex, "_table");
+ 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
+
+ if (prevValueType == LUA_TTABLE) {
+ LOG_LUA("Error: tried to assign on top of sync table");
+ return;
+ }
///////////
// value //
///////////
// get value
- struct LSTNetworkType lntValue = { 0 };
- if (!smlua_value_to_lnt(valueIndex, &lntValue)) { return; }
+ int valueType = lua_type(L, valueIndex);
+ if (valueType == LUA_TTABLE) {
+ if (prevValueType != LUA_TNIL) {
+ LOG_LUA("Error: tried to set a sync table field to a different sync table");
+ return;
+ }
+
+ if (!smlua_is_table_empty(valueIndex)) {
+ LOG_LUA("Error: tried to generate a sync table with a non-empty table");
+ return;
+ }
+
+ // create sync table
+ int modRemoteIndex = smlua_get_integer_field(syncTableIndex, "_remoteIndex");
+
+ // set table
+ lua_getfield(L, syncTableIndex, "_table");
+ lua_pushvalue(L, keyIndex);
+ smlua_sync_table_create(modRemoteIndex, LST_NORMAL, &syncTableIndex, &keyIndex);
+ lua_settable(L, -3);
+ lua_pop(L, 1); // pop _table
+
+ LUA_STACK_CHECK_END();
+ return;
+ }
+ struct LSTNetworkType lntValue = smlua_to_lnt(L, valueIndex);
+ if (!gSmLuaConvertSuccess) {
+ LOG_LUA("Error: tried to alter sync table with an invalid value");
+ return;
+ }
// set value
lua_getfield(L, syncTableIndex, "_table");
@@ -128,22 +218,19 @@ static void smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al
lua_pop(L, 1); // pop seq table
- ///////////
- // index //
- ///////////
-
- // get index
- u16 index = smlua_get_integer_field(syncTableIndex, "_index");
- index = network_player_global_index_from_local(index);
-
-
/////////////
// network //
/////////////
+ // 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;
+ }
+
// send over the network
if (!gLuaInitializingScript && seq > 0) {
- network_send_lua_sync_table(toLocalIndex, seq, modRemoteIndex, lst, index, &lntKey, &lntValue);
+ network_send_lua_sync_table(toLocalIndex, seq, modRemoteIndex, sUnwoundLntsCount, sUnwoundLnts, &lntValue);
}
LUA_STACK_CHECK_END();
@@ -155,11 +242,11 @@ static int smlua__set_sync_table_field(UNUSED lua_State* L) {
return 1;
}
-void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 lst, u16 index, struct LSTNetworkType* lntKey, struct LSTNetworkType* lntValue) {
+void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 lntKeyCount, struct LSTNetworkType* lntKeys, struct LSTNetworkType* lntValue) {
LUA_STACK_CHECK_BEGIN();
lua_State* L = gLuaState;
- // figure out table
+ // figure out mod table
struct ModTable* table = NULL;
if (gNetworkType == NT_SERVER) {
table = &gModTableLocal;
@@ -183,12 +270,6 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ls
return;
}
- // sanity check lst
- if (lst >= LST_MAX) {
- LOG_ERROR("Received sync table field packet with an invalid LST: %u", lst);
- return;
- }
-
// sanity check lntValue
if (lntValue->type >= LST_NETWORK_TYPE_MAX) {
LOG_ERROR("Received sync table field packet with an invalid lnt type: %u", lntValue->type);
@@ -197,28 +278,61 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ls
lua_getglobal(L, "_G"); // get global table
lua_getfield(L, LUA_REGISTRYINDEX, entry->path); // get the file's "global" table
+ int fileGlobalIndex = lua_gettop(L);
- // get sync table
- u16 syncTableSize = 0;
- switch (lst) {
- case LST_GLOBAL:
- syncTableSize = 1;
- lua_getfield(L, -1, "gGlobalSyncTable");
- break;
- case LST_PLAYER:
- syncTableSize = 2;
- lua_getfield(L, -1, "gPlayerSyncTable");
- lua_pushinteger(L, network_player_local_index_from_global(index));
+ // 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;
+ }
+
+ for (int i = lntKeyCount - 2; i >= 1; i--) {
+ // get child sync table
+ smlua_push_lnt(&lntKeys[i]);
+ lua_gettable(L, -2);
+
+ // create missing tables
+ if (lua_type(L, -1) != LUA_TTABLE) {
+ lua_pop(L, 1); // pop non-table
+
+ // push internal table of parent
+ lua_pushstring(L, "_table");
+ lua_gettable(L, syncTableIndex);
+
+ // set internal[key] = new sync table
+ smlua_push_lnt(&lntKeys[i]);
+ int keyIndex = lua_gettop(L);
+ smlua_sync_table_create(modRemoteIndex, LST_NORMAL, &syncTableIndex, &keyIndex);
+ lua_rawset(L, -3);
+
+ // get new sync table
+ smlua_push_lnt(&lntKeys[i]);
lua_gettable(L, -2);
- break;
- default: assert(false);
+
+ lua_remove(L, -2); // remove paren'ts internal table
+ } else {
+
+ // translate player index
+ if (smlua_get_integer_field(-1, "_type") == LST_PLAYER) {
+ lua_pop(L, 1); // pop wrong table
+ assert(lntKeys[i].type == LST_NETWORK_TYPE_INTEGER);
+ lua_pushinteger(L, network_player_local_index_from_global(lntKeys[i].value.integer));
+ lua_gettable(L, -2);
+ }
+ }
+
+ lua_remove(L, -2); // remove parent table
}
// get seq table
lua_getfield(L, -1, "_seq");
// get seq number
- smlua_push_lnt(lntKey);
+ smlua_push_lnt(&lntKeys[0]);
lua_gettable(L, -2);
u64 readSeq = lua_tointeger(L, -1);
lua_pop(L, 1); // pop seq value
@@ -234,7 +348,7 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ls
}
// set seq number
- smlua_push_lnt(lntKey);
+ smlua_push_lnt(&lntKeys[0]);
lua_pushinteger(L, seq);
lua_settable(L, -3);
lua_pop(L, 1); // pop seq table
@@ -245,7 +359,7 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ls
int t = lua_gettop(L);
// set key/value
- smlua_push_lnt(lntKey);
+ smlua_push_lnt(&lntKeys[0]);
smlua_push_lnt(lntValue);
lua_rawset(L, t);
@@ -271,56 +385,45 @@ void smlua_sync_table_init_globals(char* path, u16 modRemoteIndex) {
lua_State* L = gLuaState;
lua_getfield(L, LUA_REGISTRYINDEX, path);
- int base = lua_gettop(L);
+ int fileGlobalIndex = lua_gettop(L);
{
+ // create and attach global sync table
+ lua_pushstring(L, "gGlobalSyncTable");
+ int keyIndex = lua_gettop(L);
+ smlua_sync_table_create(modRemoteIndex, LST_NORMAL, NULL, &keyIndex);
+ lua_settable(L, fileGlobalIndex);
+ }
+ {
+ // create player sync table
+ lua_pushstring(L, "gPlayerSyncTable");
lua_newtable(L);
- int t = lua_gettop(L);
+ int tableIndex = lua_gettop(L);
+ smlua_push_string_field(tableIndex, "_name", "gPlayerSyncTable");
- // push fields
- smlua_push_integer_field(t, "_remoteIndex", modRemoteIndex);
- smlua_push_integer_field(t, "_type", LST_GLOBAL);
- smlua_push_integer_field(t, "_index", 0);
- smlua_push_table_field(t, "_seq");
- smlua_push_table_field(t, "_table");
- smlua_push_string_field(t, "_name", "gGlobalSyncTable");
- smlua_push_nil_field(t, "_parent");
+ // create internal table
+ lua_pushstring(L, "_table");
+ lua_newtable(L);
+ int internalTableIndex = lua_gettop(L);
+
+ // create player sync tables
+ for (int i = 0; i < MAX_PLAYERS; i++) {
+ lua_pushinteger(L, i);
+ int keyIndex = lua_gettop(L);
+ smlua_sync_table_create(modRemoteIndex, LST_PLAYER, &tableIndex, &keyIndex);
+ lua_settable(L, internalTableIndex);
+ }
+
+ // attach internal table
+ lua_settable(L, tableIndex);
// attach metatable
lua_pushglobaltable(L);
- lua_getfield(L, -1, "_SyncTable");
- lua_setmetatable(L, -3);
+ lua_getfield(L, -1, "_ReadOnlyTable");
+ lua_setmetatable(L, tableIndex);
lua_pop(L, 1); // pop global table
- // attach sync table to file's "globals"
- lua_setfield(L, base, "gGlobalSyncTable");
- }
- {
- lua_newtable(L);
- int playerTop = lua_gettop(L);
- for (int i = 0; i < MAX_PLAYERS; i++) {
- lua_pushinteger(L, i);
-
- lua_newtable(L);
- int t = lua_gettop(L);
-
- smlua_push_integer_field(t, "_remoteIndex", modRemoteIndex);
- smlua_push_integer_field(t, "_type", LST_PLAYER);
- smlua_push_integer_field(t, "_index", i);
- smlua_push_table_field(t, "_seq");
- smlua_push_table_field(t, "_table");
- smlua_push_string_field(t, "_name", "gPlayerSyncTable"); // <--- incorrect
- smlua_push_nil_field(t, "_parent"); // <--- incorrect
-
- // attach metatable
- lua_pushglobaltable(L);
- lua_getfield(L, -1, "_SyncTable");
- lua_setmetatable(L, -3);
- lua_pop(L, 1); // pop global table
-
- // attach table to gPlayerSyncTable
- lua_settable(L, playerTop);
- }
- lua_setfield(L, base, "gPlayerSyncTable");
+ // attach player sync table
+ lua_settable(L, fileGlobalIndex);
}
lua_pop(L, 1); // pop file's "global" table
diff --git a/src/pc/lua/smlua_sync_table.h b/src/pc/lua/smlua_sync_table.h
index b6032e0e..b6fdaaa1 100644
--- a/src/pc/lua/smlua_sync_table.h
+++ b/src/pc/lua/smlua_sync_table.h
@@ -1,15 +1,16 @@
#ifndef SMLUA_SYNC_TABLE_H
#define SMLUA_SYNC_TABLE_H
+#define MAX_UNWOUND_LNT 16
+
enum LuaSyncTableType {
- LST_GLOBAL,
+ LST_NORMAL,
LST_PLAYER,
LST_MAX,
};
struct LSTNetworkType;
-
-void smlua_set_sync_table_field_from_network(u64 seq, u16 remoteIndex, u16 lst, u16 index, struct LSTNetworkType* lntKey, struct LSTNetworkType* lntValue);
+void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 lntKeyCount, struct LSTNetworkType* lntKeys, struct LSTNetworkType* lntValue);
void smlua_sync_table_init_globals(char* path, u16 remoteIndex);
void smlua_bind_sync_table(void);
void smlua_sync_table_send_all(u8 toLocalIndex);
diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c
index 0b8e97fa..05d7a14b 100644
--- a/src/pc/lua/smlua_utils.c
+++ b/src/pc/lua/smlua_utils.c
@@ -20,25 +20,24 @@ s16* smlua_get_vec3s_from_buffer(void) {
return sVec3sBuffer[sVec3sBufferIndex++];
}
+///////////////////////////////////////////////////////////////////////////////////////////
+
void smlua_bind_function(lua_State* L, const char* name, void* func) {
lua_pushcfunction(L, func);
lua_setglobal(L, name);
}
-void smlua_logline(void) {
- lua_State* L = gLuaState;
- lua_Debug info;
- int level = 0;
- while (lua_getstack(L, level, &info)) {
- lua_getinfo(L, "nSl", &info);
- LOG_INFO(" [%d] %s:%d -- %s [%s]",
- level, info.short_src, info.currentline,
- (info.name ? info.name : ""), info.what);
- ++level;
+
+bool smlua_is_table_empty(int index) {
+ lua_pushnil(gLuaState); // key
+ if (lua_next(gLuaState, index)) {
+ lua_pop(gLuaState, 2);
+ return false;
}
+ return true;
}
-//////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
lua_Integer smlua_to_integer(lua_State* L, int index) {
if (lua_type(L, index) == LUA_TBOOLEAN) {
@@ -121,7 +120,63 @@ void* smlua_to_cobject(lua_State* L, int index, u16 lot) {
return pointer;
}
-//////////////////////////////////////////////
+struct LSTNetworkType smlua_to_lnt(lua_State* L, int index) {
+ struct LSTNetworkType lnt = { 0 };
+ int valueType = lua_type(L, index);
+
+ if (valueType == LUA_TNUMBER) {
+ lnt.type = LST_NETWORK_TYPE_INTEGER;
+ lnt.value.integer = lua_tointeger(L, index);
+ lnt.size = sizeof(u8) + sizeof(long long);
+
+ if (lnt.value.integer == 0) {
+ lnt.type = LST_NETWORK_TYPE_NUMBER;
+ lnt.value.number = lua_tonumber(L, index);
+ lnt.size = sizeof(u8) + sizeof(double);
+
+ if (lnt.value.number == 0) {
+ lnt.type = LST_NETWORK_TYPE_INTEGER;
+ lnt.size = sizeof(u8) + sizeof(long long);
+ }
+ }
+ gSmLuaConvertSuccess = true;
+ return lnt;
+ }
+
+ if (valueType == LUA_TBOOLEAN) {
+ lnt.type = LST_NETWORK_TYPE_BOOLEAN;
+ lnt.value.boolean = lua_toboolean(L, index);
+ lnt.size = sizeof(u8) + sizeof(u8);
+ gSmLuaConvertSuccess = true;
+ return lnt;
+ }
+
+ if (valueType == LUA_TSTRING) {
+ lnt.type = LST_NETWORK_TYPE_STRING;
+ lnt.value.string = (char*)lua_tostring(L, index);
+ if (lnt.value.string == NULL || strlen(lnt.value.string) > 256) {
+ LOG_LUA("smlua_to_lnt on invalid string value: '%s'", (lnt.value.string == NULL) ? "" : lnt.value.string);
+ gSmLuaConvertSuccess = false;
+ return lnt;
+ }
+ lnt.size = sizeof(u8) + sizeof(u16) + sizeof(u8) * strlen(lnt.value.string);
+ gSmLuaConvertSuccess = true;
+ return lnt;
+ }
+
+ if (valueType == LUA_TNIL) {
+ lnt.type = LST_NETWORK_TYPE_NIL;
+ lnt.size = sizeof(u8);
+ gSmLuaConvertSuccess = true;
+ return lnt;
+ }
+
+ LOG_LUA("smlua_to_lnt on invalid type: '%d'", valueType);
+ gSmLuaConvertSuccess = false;
+ return lnt;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
void smlua_push_object(lua_State* L, u16 lot, void* p) {
if (p == NULL) {
@@ -166,6 +221,32 @@ void smlua_push_table_field(int index, char* name) {
lua_setfield(gLuaState, index, name);
}
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void smlua_push_lnt(struct LSTNetworkType* lnt) {
+ lua_State* L = gLuaState;
+ switch (lnt->type) {
+ case LST_NETWORK_TYPE_INTEGER:
+ lua_pushinteger(L, lnt->value.integer);
+ break;
+ case LST_NETWORK_TYPE_NUMBER:
+ lua_pushnumber(L, lnt->value.number);
+ break;
+ case LST_NETWORK_TYPE_BOOLEAN:
+ lua_pushboolean(L, lnt->value.boolean);
+ break;
+ case LST_NETWORK_TYPE_STRING:
+ lua_pushstring(L, lnt->value.string);
+ break;
+ case LST_NETWORK_TYPE_NIL:
+ lua_pushnil(L);
+ break;
+ default: SOFT_ASSERT(false);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
lua_Integer smlua_get_integer_field(int index, char* name) {
if (lua_type(gLuaState, index) != LUA_TTABLE) {
LOG_LUA("smlua_get_integer_field received improper type '%d'", lua_type(gLuaState, index));
@@ -190,13 +271,29 @@ lua_Number smlua_get_number_field(int index, char* name) {
return val;
}
-bool smlua_is_table_empty(int index) {
- lua_pushnil(gLuaState); // key
- if (lua_next(gLuaState, index)) {
- lua_pop(gLuaState, 2);
- return false;
+///////////////////////////////////////////////////////////////////////////////////////////
+
+char* smlua_lnt_to_str(struct LSTNetworkType* lnt) {
+ static char sLntStr[32] = "";
+ switch (lnt->type) {
+ case LST_NETWORK_TYPE_INTEGER:
+ snprintf(sLntStr, 32, "%lld", lnt->value.integer);
+ break;
+ case LST_NETWORK_TYPE_NUMBER:
+ snprintf(sLntStr, 32, "%f", lnt->value.number);
+ break;
+ case LST_NETWORK_TYPE_BOOLEAN:
+ snprintf(sLntStr, 32, "%u", lnt->value.boolean);
+ break;
+ case LST_NETWORK_TYPE_STRING:
+ snprintf(sLntStr, 32, "%s", lnt->value.string);
+ break;
+ case LST_NETWORK_TYPE_NIL:
+ snprintf(sLntStr, 32, "");
+ break;
+ default: SOFT_ASSERT(false);
}
- return true;
+ return sLntStr;
}
void smlua_dump_stack(void) {
@@ -274,4 +371,17 @@ void smlua_dump_table(int index) {
lua_pop(L, 1);
}
printf("--------------\n");
+}
+
+void smlua_logline(void) {
+ lua_State* L = gLuaState;
+ lua_Debug info;
+ int level = 0;
+ while (lua_getstack(L, level, &info)) {
+ lua_getinfo(L, "nSl", &info);
+ LOG_INFO(" [%d] %s:%d -- %s [%s]",
+ level, info.short_src, info.currentline,
+ (info.name ? info.name : ""), info.what);
+ ++level;
+ }
}
\ No newline at end of file
diff --git a/src/pc/lua/smlua_utils.h b/src/pc/lua/smlua_utils.h
index 79493f54..fa1098c9 100644
--- a/src/pc/lua/smlua_utils.h
+++ b/src/pc/lua/smlua_utils.h
@@ -7,12 +7,14 @@ f32* smlua_get_vec3f_from_buffer(void);
s16* smlua_get_vec3s_from_buffer(void);
void smlua_bind_function(lua_State* L, const char* name, void* func);
-void smlua_logline(void);
+bool smlua_is_table_empty(int index);
+
lua_Integer smlua_to_integer(lua_State* L, int index);
lua_Number smlua_to_number(lua_State* L, int index);
const char* smlua_to_string(lua_State* L, int index);
void* smlua_to_cobject(lua_State* L, int index, u16 lot);
+struct LSTNetworkType smlua_to_lnt(lua_State* L, int index);
void smlua_push_object(lua_State* L, u16 lot, void* p);
void smlua_push_integer_field(int index, char* name, lua_Integer val);
@@ -21,11 +23,13 @@ void smlua_push_string_field(int index, char* name, const char* val);
void smlua_push_nil_field(int index, char* name);
void smlua_push_table_field(int index, char* name);
+void smlua_push_lnt(struct LSTNetworkType* lnt);
+
lua_Integer smlua_get_integer_field(int index, char* name);
lua_Number smlua_get_number_field(int index, char* name);
-bool smlua_is_table_empty(int index);
-
+char* smlua_lnt_to_str(struct LSTNetworkType* lnt);
+void smlua_logline(void);
void smlua_dump_stack(void);
void smlua_dump_globals(void);
void smlua_dump_table(int index);
diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h
index 27028e28..e6f2dff1 100644
--- a/src/pc/network/packets/packet.h
+++ b/src/pc/network/packets/packet.h
@@ -118,8 +118,10 @@ struct LSTNetworkType {
long long integer;
double number;
u8 boolean;
- const char* string;
+ char* string;
} value;
+
+ size_t size;
};
// packet.c
@@ -329,7 +331,8 @@ void network_receive_download(struct Packet* p);
// packet_lua_sync_table.c
void network_send_lua_sync_table_request(void);
void network_receive_lua_sync_table_request(struct Packet* p);
-void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 remoteIndex, u16 lst, u16 index, struct LSTNetworkType* lntKey, struct LSTNetworkType* lntValue);
+
+void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 remoteIndex, u16 lntKeyCount, struct LSTNetworkType* lntKey, struct LSTNetworkType* lntValue);
void network_receive_lua_sync_table(struct Packet* p);
#endif
diff --git a/src/pc/network/packets/packet_lua_sync_table.c b/src/pc/network/packets/packet_lua_sync_table.c
index 369c7f2c..cd591c1c 100644
--- a/src/pc/network/packets/packet_lua_sync_table.c
+++ b/src/pc/network/packets/packet_lua_sync_table.c
@@ -3,8 +3,6 @@
#include "pc/lua/smlua.h"
#include "pc/debuglog.h"
-static char sLuaStrValue[257] = { 0 };
-
/////////////////////////////////////////////////////////////
void network_send_lua_sync_table_request(void) {
@@ -25,8 +23,8 @@ void network_receive_lua_sync_table_request(struct Packet* p) {
/////////////////////////////////////////////////////////////
static bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
- u16 lntType = lnt->type;
- packet_write(p, &lntType, sizeof(u16));
+ u8 lntType = lnt->type;
+ packet_write(p, &lntType, sizeof(u8));
switch (lnt->type) {
case LST_NETWORK_TYPE_NUMBER: {
@@ -47,10 +45,13 @@ static bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
}
case LST_NETWORK_TYPE_STRING: {
- snprintf(sLuaStrValue, 256, "%s", lnt->value.string);
- u16 valueLength = strlen(sLuaStrValue);
+ u16 valueLength = strlen(lnt->value.string);
+ if (valueLength < 1 || valueLength > 256) {
+ LOG_ERROR("attempted to send lua sync table with invalid string length: %u", valueLength);
+ return false;
+ }
packet_write(p, &valueLength, sizeof(u16));
- packet_write(p, &sLuaStrValue, valueLength * sizeof(u8));
+ packet_write(p, lnt->value.string, valueLength * sizeof(u8));
return true;
}
@@ -68,7 +69,7 @@ static bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
}
static bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
- packet_read(p, &lnt->type, sizeof(u16));
+ packet_read(p, &lnt->type, sizeof(u8));
switch (lnt->type) {
case LST_NETWORK_TYPE_NUMBER:
@@ -86,14 +87,12 @@ static bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
case LST_NETWORK_TYPE_STRING: {
u16 valueLength = 0;
packet_read(p, &valueLength, sizeof(u16));
- if (valueLength > 256) {
+ if (valueLength < 1 || valueLength > 256) {
LOG_ERROR("received lua sync table with invalid value length: %d", valueLength);
return false;
}
-
- packet_read(p, &sLuaStrValue, valueLength * sizeof(u8));
- sLuaStrValue[valueLength] = 0;
- lnt->value.string = sLuaStrValue;
+ lnt->value.string = calloc(valueLength + 1, sizeof(u8));
+ packet_read(p, lnt->value.string, valueLength * sizeof(u8));
return true;
}
@@ -111,16 +110,18 @@ static bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
/////////////////////////////////////////////////////////////
-void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 modRemoteIndex, u16 lst, u16 index, struct LSTNetworkType* lntKey, struct LSTNetworkType* lntValue) {
+void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 modRemoteIndex, u16 lntKeyCount, struct LSTNetworkType* lntKeys, struct LSTNetworkType* lntValue) {
struct Packet p = { 0 };
packet_init(&p, PACKET_LUA_SYNC_TABLE, true, PLMT_NONE);
packet_write(&p, &seq, sizeof(u64));
packet_write(&p, &modRemoteIndex, sizeof(u16));
- packet_write(&p, &lst, sizeof(u16));
- packet_write(&p, &index, sizeof(u16));
- if (!packet_write_lnt(&p, lntKey)) { return; }
+ packet_write(&p, &lntKeyCount, sizeof(u16));
+ for (int i = 0; i < lntKeyCount; i++) {
+ if (!packet_write_lnt(&p, &lntKeys[i])) { return; }
+ }
+
if (!packet_write_lnt(&p, lntValue)) { return; }
if (toLocalIndex == 0 || toLocalIndex >= MAX_PLAYERS) {
@@ -133,18 +134,31 @@ void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 modRemoteIndex, u
void network_receive_lua_sync_table(struct Packet* p) {
u64 seq = 0;
u16 modRemoteIndex = 0;
- u16 lst = 0;
- u16 index = 0;
- struct LSTNetworkType lntKey = { 0 };
+ u16 lntKeyCount = 0;
+ struct LSTNetworkType lntKeys[MAX_UNWOUND_LNT] = { 0 };
struct LSTNetworkType lntValue = { 0 };
packet_read(p, &seq, sizeof(u64));
packet_read(p, &modRemoteIndex, sizeof(u16));
- packet_read(p, &lst, sizeof(u16));
- packet_read(p, &index, sizeof(u16));
- if (!packet_read_lnt(p, &lntKey)) { return; }
- if (!packet_read_lnt(p, &lntValue)) { return; }
+ packet_read(p, &lntKeyCount, sizeof(u16));
+ for (int i = 0; i < lntKeyCount; i++) {
+ if (!packet_read_lnt(p, &lntKeys[i])) { goto cleanup; }
+ }
- smlua_set_sync_table_field_from_network(seq, modRemoteIndex, lst, index, &lntKey, &lntValue);
+ if (!packet_read_lnt(p, &lntValue)) { goto cleanup; }
+
+ smlua_set_sync_table_field_from_network(seq, modRemoteIndex, lntKeyCount, lntKeys, &lntValue);
+
+cleanup:
+ for (int i = 0; i < lntKeyCount; i++) {
+ if (lntKeys[i].type != LST_NETWORK_TYPE_STRING) { continue; }
+ if (lntKeys[i].value.string == NULL) { continue; }
+ free(lntKeys[i].value.string);
+ lntKeys[i].value.string = NULL;
+ }
+ if (lntValue.type == LST_NETWORK_TYPE_STRING && lntValue.value.string != NULL) {
+ free(lntValue.value.string);
+ lntValue.value.string = NULL;
+ }
}