diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 0ff729ee5..6cd662b67 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -159,6 +159,8 @@ manual_index_documentation = """ - [define_custom_obj_fields](#define_custom_obj_fields) - [network_init_object](#network_init_object) - [network_send_object](#network_send_object) + - [network_send_to](#network_send_to) + - [network_send](#network_send)
@@ -236,6 +238,55 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [network_send_to](#network_send_to) + +Sends a packet to a particular player (using their local index) containing whatever data you want. + +`dataTable` can only contain strings, integers, numbers, booleans, and nil + +The `reliable` field will ensure that the packet arrives, but should be used sparingly and only when missing a packet would cause a desync. + +### Lua Example +`network_send_to(localPlayerIndex, reliable, { data1 = 'hello', data2 = 10})` + +### Parameters +| Field | Type | +| ----- | ---- | +| localPlayerIndex | `integer` | +| reliable | `bool` | +| dataTable | `table` | + +### C Prototype +`N/A` + +[:arrow_up_small:](#) + +
+ +## [network_send](#network_send) + +Sends a packet to all players containing whatever data you want. + +`dataTable` can only contain strings, integers, numbers, booleans, and nil + +The `reliable` field will ensure that the packet arrives, but should be used sparingly and only when missing a packet would cause a desync. + +### Lua Example +`network_send(reliable, { data1 = 'hello', data2 = 10})` + +### Parameters +| Field | Type | +| ----- | ---- | +| reliable | `bool` | +| dataTable | `table` | + +### C Prototype +`N/A` + +[:arrow_up_small:](#) + +
+ """ ############################################################################ diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 701ff3dcb..94ab707c6 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -7852,7 +7852,10 @@ HOOK_ON_OBJECT_RENDER = 19 HOOK_ON_DEATH = 20 --- @type LuaHookedEventType -HOOK_MAX = 21 +HOOK_ON_PACKET_RECEIVE = 21 + +--- @type LuaHookedEventType +HOOK_MAX = 22 --- @class ModelExtendedId diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index dc6e94b3e..da0707344 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -120,3 +120,17 @@ function network_send_object(object, reliable) -- ... end +--- @param reliable boolean +--- @param dataTable table +--- @return nil +function network_send(reliable, dataTable) + -- ... +end + +--- @param toLocalIndex integer +--- @param reliable boolean +--- @param dataTable table +--- @return nil +function network_send_to(toLocalIndex, reliable, dataTable) + -- ... +end diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 0532526c7..da4624be1 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -2755,7 +2755,8 @@ | HOOK_ON_SET_CAMERA_MODE | 18 | | HOOK_ON_OBJECT_RENDER | 19 | | HOOK_ON_DEATH | 20 | -| HOOK_MAX | 21 | +| HOOK_ON_PACKET_RECEIVE | 21 | +| HOOK_MAX | 22 | [:arrow_up_small:](#) diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 584fede32..e38466f67 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -6,6 +6,8 @@ - [define_custom_obj_fields](#define_custom_obj_fields) - [network_init_object](#network_init_object) - [network_send_object](#network_send_object) + - [network_send_to](#network_send_to) + - [network_send](#network_send)
@@ -1500,6 +1502,55 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [network_send_to](#network_send_to) + +Sends a packet to a particular player (using their local index) containing whatever data you want. + +`dataTable` can only contain strings, integers, numbers, booleans, and nil + +The `reliable` field will ensure that the packet arrives, but should be used sparingly and only when missing a packet would cause a desync. + +### Lua Example +`network_send_to(localPlayerIndex, reliable, { data1 = 'hello', data2 = 10})` + +### Parameters +| Field | Type | +| ----- | ---- | +| localPlayerIndex | `integer` | +| reliable | `bool` | +| dataTable | `table` | + +### C Prototype +`N/A` + +[:arrow_up_small:](#) + +
+ +## [network_send](#network_send) + +Sends a packet to all players containing whatever data you want. + +`dataTable` can only contain strings, integers, numbers, booleans, and nil + +The `reliable` field will ensure that the packet arrives, but should be used sparingly and only when missing a packet would cause a desync. + +### Lua Example +`network_send(reliable, { data1 = 'hello', data2 = 10})` + +### Parameters +| Field | Type | +| ----- | ---- | +| reliable | `bool` | +| dataTable | `table` | + +### C Prototype +`N/A` + +[:arrow_up_small:](#) + +
+ --- # functions from behavior_actions.h diff --git a/docs/lua/hooks.md b/docs/lua/hooks.md index 070213277..c3d47d9f8 100644 --- a/docs/lua/hooks.md +++ b/docs/lua/hooks.md @@ -105,8 +105,9 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh | HOOK_ON_PAUSE_EXIT | Called when the local player exits through the pause screen, return `false` to prevent the exit | `boolean` usedExitToCastle | | HOOK_GET_STAR_COLLECTION_DIALOG | Called when the local player collects a star, return a [DialogId](constants.md#enum-DialogId) to show a message | None | | HOOK_ON_SET_CAMERA_MODE | Called when the camera mode gets set, return `false` to prevent the camera mode from being set | [Camera](structs.md#Camera), `integer` mode, `integer` frames | -| HOOK_ON_OBJECT_RENDER | Called right before an object is rendered. **Note:** You must set the `hookRender` field of the object to a non-zero value | [Object](structs.md#Object) | -| HOOK_ON_DEATH | Called when the local player dies, return `false` to prevent normal death sequence | [MarioState](structs.md#MarioState) | +| HOOK_ON_OBJECT_RENDER | Called right before an object is rendered. **Note:** You must set the `hookRender` field of the object to a non-zero value | [Object](structs.md#Object) renderedObj | +| HOOK_ON_DEATH | Called when the local player dies, return `false` to prevent normal death sequence | [MarioState](structs.md#MarioState) localMario | +| HOOK_ON_PACKET_RECEIVE | Called when the mod receives a packet that used `network_send()` or `network_send_to()` | `table` dataTable | ### Parameters diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 330865deb..0590b3010 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2798,7 +2798,8 @@ char gSmluaConstants[] = "" "HOOK_ON_SET_CAMERA_MODE = 18\n" "HOOK_ON_OBJECT_RENDER = 19\n" "HOOK_ON_DEATH = 20\n" -"HOOK_MAX = 21\n" +"HOOK_ON_PACKET_RECEIVE = 21\n" +"HOOK_MAX = 22\n" "E_MODEL_NONE = 0\n" "E_MODEL_MARIO = 1\n" "E_MODEL_SMOKE = 2\n" diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index 12d4b061c..3c2e1bf76 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -176,6 +176,16 @@ int smlua_func_network_send_object(lua_State* L) { return 1; } +int smlua_func_network_send(lua_State* L) { + if (!smlua_functions_valid_param_count(L, 2)) { return 0; } + network_send_lua_custom(true); +} + +int smlua_func_network_send_to(lua_State* L) { + if (!smlua_functions_valid_param_count(L, 3)) { return 0; } + network_send_lua_custom(false); +} + ////////// // bind // ////////// @@ -191,4 +201,6 @@ void smlua_bind_functions(void) { smlua_bind_function(L, "network_init_object", smlua_func_network_init_object); smlua_bind_function(L, "network_send_object", smlua_func_network_send_object); smlua_bind_function(L, "reset_level", smlua_func_reset_level); + smlua_bind_function(L, "network_send", smlua_func_network_send); + smlua_bind_function(L, "network_send_to", smlua_func_network_send_to); } diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index 6d06ea0db..2b3a4ea28 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -1,4 +1,5 @@ #include "smlua.h" +#include "pc/mods/mod.h" #include "src/game/object_list_processor.h" #include "pc/djui/djui_chat_message.h" #include "pc/crash_handler.h" @@ -454,6 +455,27 @@ void smlua_call_event_hooks_set_camera_mode_params(enum LuaHookedEventType hookT } } +void smlua_call_event_hooks_value_param(enum LuaHookedEventType hookType, int modIndex, int valueIndex) { + lua_State* L = gLuaState; + if (L == NULL) { return; } + struct LuaHookedEvent* hook = &sHookedEvents[hookType]; + for (int i = 0; i < hook->count; i++) { + if (hook->mod[i]->index != modIndex) { continue; } + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push value + lua_pushvalue(L, valueIndex); + + // call the callback + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1)); + smlua_logline(); + continue; + } + } +} + //////////////////// // hooked actions // //////////////////// diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index b0804df8b..e7f25c40f 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -29,6 +29,7 @@ enum LuaHookedEventType { HOOK_ON_SET_CAMERA_MODE, HOOK_ON_OBJECT_RENDER, HOOK_ON_DEATH, + HOOK_ON_PACKET_RECEIVE, HOOK_MAX, }; @@ -54,6 +55,7 @@ static char* LuaHookedEventTypeName[] = { "HOOK_ON_SET_CAMERA_MODE", "HOOK_ON_OBJECT_RENDER", "HOOK_ON_DEATH", + "HOOK_ON_PACKET_RECEIVE", "HOOK_MAX" }; @@ -70,6 +72,7 @@ void smlua_call_event_hooks_interact_params(enum LuaHookedEventType hookType, st void smlua_call_event_hooks_object_param(enum LuaHookedEventType hookType, struct Object* obj); bool smlua_call_event_hooks_ret_int(enum LuaHookedEventType hookType, s32* returnValue); void smlua_call_event_hooks_set_camera_mode_params(enum LuaHookedEventType hookType, struct Camera *c, s16 mode, s16 frames, bool* returnValue); +void smlua_call_event_hooks_value_param(enum LuaHookedEventType hookType, int modIndex, int valueIndex); enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior); const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior); diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c index 8db9df131..630d820a9 100644 --- a/src/pc/lua/smlua_utils.c +++ b/src/pc/lua/smlua_utils.c @@ -260,6 +260,94 @@ struct LSTNetworkType smlua_to_lnt(lua_State* L, int index) { /////////////////////////////////////////////////////////////////////////////////////////// +bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) { + u8 lntType = lnt->type; + packet_write(p, &lntType, sizeof(u8)); + + switch (lnt->type) { + case LST_NETWORK_TYPE_NUMBER: { + f64 number = lnt->value.number; + packet_write(p, &number, sizeof(f64)); + return true; + } + + case LST_NETWORK_TYPE_INTEGER: { + s64 integer = lnt->value.integer; + packet_write(p, &integer, sizeof(s64)); + return true; + } + + case LST_NETWORK_TYPE_BOOLEAN: { + packet_write(p, &lnt->value.boolean, sizeof(u8)); + return true; + } + + case LST_NETWORK_TYPE_STRING: { + u16 valueLength = strlen(lnt->value.string); + if (valueLength < 1 || valueLength > 256) { + LOG_ERROR("attempted to send lua variable with invalid string length: %u", valueLength); + return false; + } + packet_write(p, &valueLength, sizeof(u16)); + packet_write(p, lnt->value.string, valueLength * sizeof(u8)); + return true; + } + + case LST_NETWORK_TYPE_NIL: { + // no-op + return true; + } + + default: + break; + } + + LOG_ERROR("attempted to send lua variable with invalid lnt type: %d", lnt->type); + return false; +} + +bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) { + packet_read(p, &lnt->type, sizeof(u8)); + + switch (lnt->type) { + case LST_NETWORK_TYPE_NUMBER: + packet_read(p, &lnt->value.number, sizeof(f64)); + return true; + + case LST_NETWORK_TYPE_INTEGER: + packet_read(p, &lnt->value.integer, sizeof(s64)); + return true; + + case LST_NETWORK_TYPE_BOOLEAN: + packet_read(p, &lnt->value.boolean, sizeof(u8)); + return true; + + case LST_NETWORK_TYPE_STRING: { + u16 valueLength = 0; + packet_read(p, &valueLength, sizeof(u16)); + if (valueLength < 1 || valueLength > 256) { + LOG_ERROR("received lua variable with invalid value length: %d", valueLength); + return false; + } + lnt->value.string = calloc(valueLength + 1, sizeof(char)); + packet_read(p, lnt->value.string, valueLength * sizeof(u8)); + return true; + } + + case LST_NETWORK_TYPE_NIL: + // no-op + return true; + + default: + break; + } + + LOG_ERROR("received lua variable with invalid type: %d", lnt->type); + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + void smlua_push_object(lua_State* L, u16 lot, void* p) { if (p == NULL) { lua_pushnil(L); diff --git a/src/pc/lua/smlua_utils.h b/src/pc/lua/smlua_utils.h index 136fe5c90..f8adf1570 100644 --- a/src/pc/lua/smlua_utils.h +++ b/src/pc/lua/smlua_utils.h @@ -3,6 +3,8 @@ extern u8 gSmLuaConvertSuccess; typedef int LuaFunction; +struct Packet; +struct LSTNetworkType; f32* smlua_get_vec3f_from_buffer(void); s16* smlua_get_vec3s_from_buffer(void); @@ -19,6 +21,9 @@ void* smlua_to_cobject(lua_State* L, int index, u16 lot); void* smlua_to_cpointer(lua_State* L, int index, u16 lvt); struct LSTNetworkType smlua_to_lnt(lua_State* L, int index); +bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt); +bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt); + void smlua_push_object(lua_State* L, u16 lot, void* p); void smlua_push_pointer(lua_State* L, u16 lvt, void* p); void smlua_push_integer_field(int index, char* name, lua_Integer val); diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 84b1efc89..293dabc04 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -92,7 +92,9 @@ void packet_process(struct Packet* p) { case PACKET_NETWORK_PLAYERS_REQUEST: network_receive_network_players_request(p); break; - case PACKET_REQUEST_FAILED: network_receive_request_failed(p); break; + case PACKET_REQUEST_FAILED: network_receive_request_failed(p); break; + + case PACKET_LUA_CUSTOM: network_receive_lua_custom(p); break; // custom case PACKET_CUSTOM: network_receive_custom(p); break; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index a8097b5b5..04c8a30fd 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -69,6 +69,8 @@ enum PacketType { PACKET_REQUEST_FAILED, + PACKET_LUA_CUSTOM, + /// PACKET_CUSTOM = 255, }; @@ -353,4 +355,8 @@ void network_receive_lua_sync_table(struct Packet* p); void network_send_request_failed(struct NetworkPlayer* toNp, u8 requestType); void network_receive_request_failed(struct Packet* p); +// packet_lua_custom.c +void network_send_lua_custom(bool broadcast); +void network_receive_lua_custom(struct Packet* p); + #endif diff --git a/src/pc/network/packets/packet_lua_custom.c b/src/pc/network/packets/packet_lua_custom.c new file mode 100644 index 000000000..9eff14cfa --- /dev/null +++ b/src/pc/network/packets/packet_lua_custom.c @@ -0,0 +1,125 @@ +#include +#include "../network.h" +#include "pc/mods/mod.h" +#include "pc/lua/smlua.h" +#include "pc/lua/smlua_utils.h" +#include "pc/debuglog.h" + +void network_send_lua_custom(bool broadcast) { + LOG_INFO("Sending lua custom packet"); + lua_State* L = gLuaState; + u16 zero = 0; + s32 paramIndex = 1; + + // figure out mod index + if (gLuaActiveMod == NULL) { + LOG_LUA("Could not figure out the current active mod!"); + smlua_logline(); + return; + } + u16 modIndex = gLuaActiveMod->index; + + // get local index + s32 toLocalIndex = 0; + if (!broadcast) { + s32 toLocalIndex = smlua_to_integer(L, paramIndex++); + if (toLocalIndex <= 0 || toLocalIndex >= MAX_PLAYERS) { + LOG_LUA("Tried to send packet to invalid local index: %d", toLocalIndex) + smlua_logline(); + return; + } + if (!gSmLuaConvertSuccess) { return; } + } + + // get reliability + bool reliability = smlua_to_boolean(L, paramIndex++); + if (!gSmLuaConvertSuccess) { return; } + + // write packet header + struct Packet p = { 0 }; + packet_init(&p, PACKET_LUA_CUSTOM, reliability, PLMT_NONE); + packet_write(&p, &modIndex, sizeof(u16)); + u8* keyCount = &p.buffer[p.cursor]; + packet_write(&p, &zero, sizeof(u8)); + + // make sure value passed in is a table + s32 tableIndex = paramIndex; + if (lua_type(L, tableIndex) != LUA_TTABLE) { + LOG_LUA("Tried to send a packet with a non-table"); + smlua_logline(); + return; + } + + // iterate table + s32 iterateIndex = lua_gettop(L); + lua_pushnil(L); // first key + while (lua_next(L, iterateIndex) != 0) { + // convert and write key + struct LSTNetworkType lntKey = smlua_to_lnt(L, -2); + if (!gSmLuaConvertSuccess) { + LOG_LUA("Failed to convert key to LNT (tx)"); + smlua_logline(); + return; + } + if (!packet_write_lnt(&p, &lntKey)) { + return; + } + + // convert and write value + struct LSTNetworkType lntValue = smlua_to_lnt(L, -1); + if (!gSmLuaConvertSuccess) { + LOG_LUA("Failed to convert value to LNT (tx)"); + smlua_logline(); + return; + } + if (!packet_write_lnt(&p, &lntValue)) { + return; + } + + // increment key count + *keyCount = *keyCount + 1; + lua_pop(L, 1); // pop value + } + lua_pop(L, 1); // pop key + + // send packet + if (broadcast) { + network_send(&p); + } else { + network_send_to(toLocalIndex, &p); + } +} + +void network_receive_lua_custom(struct Packet* p) { + LOG_INFO("Receiving lua custom packet"); + lua_State* L = gLuaState; + u16 modIndex = 0; + u8 keyCount = 0; + packet_read(p, &modIndex, sizeof(u16)); + packet_read(p, &keyCount, sizeof(u8)); + + lua_newtable(L); + s32 tableIndex = lua_gettop(L); + for(u16 i = 0; i < keyCount; i++) { + struct LSTNetworkType lntKey = { 0 }; + if (!packet_read_lnt(p, &lntKey)) { + LOG_LUA("Failed to convert key to LNT (rx)"); + smlua_logline(); + return; + } + smlua_push_lnt(&lntKey); + + struct LSTNetworkType lntValue = { 0 }; + if (!packet_read_lnt(p, &lntValue)) { + LOG_LUA("Failed to convert value to LNT (rx)"); + smlua_logline(); + return; + } + smlua_push_lnt(&lntValue); + + lua_settable(L, -3); + } + + smlua_call_event_hooks_value_param(HOOK_ON_PACKET_RECEIVE, modIndex, tableIndex); + lua_pop(L, 1); // pop table +} diff --git a/src/pc/network/packets/packet_lua_sync_table.c b/src/pc/network/packets/packet_lua_sync_table.c index 9c478f584..fd950129d 100644 --- a/src/pc/network/packets/packet_lua_sync_table.c +++ b/src/pc/network/packets/packet_lua_sync_table.c @@ -20,96 +20,6 @@ void network_receive_lua_sync_table_request(struct Packet* p) { LOG_INFO("received lua sync table request"); } -///////////////////////////////////////////////////////////// - -static bool packet_write_lnt(struct Packet* p, struct LSTNetworkType* lnt) { - u8 lntType = lnt->type; - packet_write(p, &lntType, sizeof(u8)); - - switch (lnt->type) { - case LST_NETWORK_TYPE_NUMBER: { - f64 number = lnt->value.number; - packet_write(p, &number, sizeof(f64)); - return true; - } - - case LST_NETWORK_TYPE_INTEGER: { - s64 integer = lnt->value.integer; - packet_write(p, &integer, sizeof(s64)); - return true; - } - - case LST_NETWORK_TYPE_BOOLEAN: { - packet_write(p, &lnt->value.boolean, sizeof(u8)); - return true; - } - - case LST_NETWORK_TYPE_STRING: { - 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, lnt->value.string, valueLength * sizeof(u8)); - return true; - } - - case LST_NETWORK_TYPE_NIL: { - // no-op - return true; - } - - default: - break; - } - - LOG_ERROR("attempted to send lua sync table with invalid lnt type: %d", lnt->type); - return false; -} - -static bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) { - packet_read(p, &lnt->type, sizeof(u8)); - - switch (lnt->type) { - case LST_NETWORK_TYPE_NUMBER: - packet_read(p, &lnt->value.number, sizeof(f64)); - return true; - - case LST_NETWORK_TYPE_INTEGER: - packet_read(p, &lnt->value.integer, sizeof(s64)); - return true; - - case LST_NETWORK_TYPE_BOOLEAN: - packet_read(p, &lnt->value.boolean, sizeof(u8)); - return true; - - case LST_NETWORK_TYPE_STRING: { - u16 valueLength = 0; - packet_read(p, &valueLength, sizeof(u16)); - if (valueLength < 1 || valueLength > 256) { - LOG_ERROR("received lua sync table with invalid value length: %d", valueLength); - return false; - } - lnt->value.string = calloc(valueLength + 1, sizeof(char)); - packet_read(p, lnt->value.string, valueLength * sizeof(u8)); - return true; - } - - case LST_NETWORK_TYPE_NIL: - // no-op - return true; - - default: - break; - } - - LOG_ERROR("received lua sync table with invalid type: %d", lnt->type); - return false; -} - -///////////////////////////////////////////////////////////// - void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 modRemoteIndex, u16 lntKeyCount, struct LSTNetworkType* lntKeys, struct LSTNetworkType* lntValue) { if (gLuaState == NULL) { return; }