diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 926eb0c32..25e23c918 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -8046,7 +8046,8 @@ HOOK_ON_INSTANT_WARP = 55 --- @type LuaHookedEventType HOOK_MARIO_OVERRIDE_FLOOR_CLASS = 56 --- @type LuaHookedEventType HOOK_ON_ADD_SURFACE = 57 --- @type LuaHookedEventType HOOK_ON_CLEAR_AREAS = 58 --- @type LuaHookedEventType -HOOK_MAX = 59 --- @type LuaHookedEventType +HOOK_ON_PACKET_BYTESTRING_RECEIVE = 59 --- @type LuaHookedEventType +HOOK_MAX = 60 --- @type LuaHookedEventType --- @alias LuaHookedEventType --- | `HOOK_UPDATE` @@ -8108,6 +8109,7 @@ HOOK_MAX = 59 --- @type LuaHookedEventType --- | `HOOK_MARIO_OVERRIDE_FLOOR_CLASS` --- | `HOOK_ON_ADD_SURFACE` --- | `HOOK_ON_CLEAR_AREAS` +--- | `HOOK_ON_PACKET_BYTESTRING_RECEIVE` --- | `HOOK_MAX` HUD_DISPLAY_LIVES = 0 --- @type HudDisplayValue diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index 76ad0d265..1e9cbe212 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -264,7 +264,7 @@ end --- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets --- @param dataTable table Table of values to be included in the packet ---- Sends a global Lua packet with the values of `dataTable` +--- Sends a global Lua packet with the values of `dataTable`. Received with the `HOOK_ON_PACKET_RECEIVE` hook. function network_send(reliable, dataTable) -- ... end @@ -272,11 +272,26 @@ end --- @param toLocalIndex integer The local index to send the packet to --- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets --- @param dataTable table Table of values to be included in the packet ---- Sends a Lua packet with the values of `dataTable` to a specific client through local indices +--- Sends a Lua packet with the values of `dataTable` to a specific client through local indices. Received with the `HOOK_ON_PACKET_RECEIVE` hook. function network_send_to(toLocalIndex, reliable, dataTable) -- ... end +--- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets +--- @param bytestring string The bytestring to be included in the packet +--- Sends a global Lua packet with the bytestring of `bytestring`. Received with the `HOOK_ON_PACKET_BYTESTRING_RECEIVE` hook. +function network_send_bytestring(reliable, bytestring) + -- ... +end + +--- @param toLocalIndex integer The local index to send the packet to +--- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets +--- @param bytestring string The bytestring to be included in the packet +--- Sends a Lua packet with the bytestring of `bytestring` to a specific client through local indices. Received with the `HOOK_ON_PACKET_BYTESTRING_RECEIVE` hook. +function network_send_bytestring_to(toLocalIndex, reliable, bytestring) + -- ... +end + --- @param textureName string The texture name --- @return TextureInfo --- Gets the `TextureInfo` of a texture by name diff --git a/docs/lua/constants.md b/docs/lua/constants.md index bfde0e8b9..e1ac36c14 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -3474,7 +3474,8 @@ | HOOK_MARIO_OVERRIDE_FLOOR_CLASS | 56 | | HOOK_ON_ADD_SURFACE | 57 | | HOOK_ON_CLEAR_AREAS | 58 | -| HOOK_MAX | 59 | +| HOOK_ON_PACKET_BYTESTRING_RECEIVE | 59 | +| HOOK_MAX | 60 | [:arrow_up_small:](#) diff --git a/docs/lua/examples/bytestring-packet-example.lua b/docs/lua/examples/bytestring-packet-example.lua new file mode 100644 index 000000000..3c834d5f0 --- /dev/null +++ b/docs/lua/examples/bytestring-packet-example.lua @@ -0,0 +1,132 @@ +-- name: Bytestring Packet Example +-- description: Shows a way to send and receive bytestring packets. Send a packet with /example [1|2]. + +-- Here is the lua documentation for string.pack() and string.unpack(): +-- https://www.lua.org/manual/5.3/manual.html#6.4.2 +-- It explains the different format strings used in this example mod. + +--------------------------------------------------------------------------------------------------- + +local PACKET_EXAMPLE_1 = 1 +local PACKET_EXAMPLE_2 = 2 + +--------------------------------------------------------------------------------------------------- + +function send_example_1(byte_param, short_param, long_param, float_param, double_param) + + local bytestring = '' + -------------- PACKET ID -------------- + .. string.pack("count; i++) { + if (hook->mod[i]->index != modIndex) { continue; } + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push valueIndex + lua_pushvalue(L, valueIndex); + + // call the callback + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PACKET_BYTESTRING_RECEIVE]); + continue; + } + hookResult = true; + + lua_settop(L, prevTop); + } + return hookResult; +} diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index 3862d3d65..12a8af6dc 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -73,6 +73,7 @@ enum LuaHookedEventType { HOOK_MARIO_OVERRIDE_FLOOR_CLASS, HOOK_ON_ADD_SURFACE, HOOK_ON_CLEAR_AREAS, + HOOK_ON_PACKET_BYTESTRING_RECEIVE, HOOK_MAX, }; diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 136d58f7c..199a88e5a 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -137,6 +137,7 @@ void packet_process(struct Packet* p) { case PACKET_REQUEST_FAILED: network_receive_request_failed(p); break; case PACKET_LUA_CUSTOM: network_receive_lua_custom(p); break; + case PACKET_LUA_CUSTOM_BYTESTRING: network_receive_lua_custom_bytestring(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 9ad196989..5ab859760 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -73,6 +73,7 @@ enum PacketType { PACKET_REQUEST_FAILED, PACKET_LUA_CUSTOM, + PACKET_LUA_CUSTOM_BYTESTRING, PACKET_COMMAND, PACKET_MODERATOR, @@ -380,5 +381,7 @@ 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); +void network_send_lua_custom_bytestring(bool broadcast); +void network_receive_lua_custom_bytestring(struct Packet* p); #endif diff --git a/src/pc/network/packets/packet_lua_custom.c b/src/pc/network/packets/packet_lua_custom.c index bdeea5fcd..c40de89c4 100644 --- a/src/pc/network/packets/packet_lua_custom.c +++ b/src/pc/network/packets/packet_lua_custom.c @@ -6,7 +6,6 @@ #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; @@ -97,7 +96,6 @@ void network_send_lua_custom(bool broadcast) { } void network_receive_lua_custom(struct Packet* p) { - LOG_INFO("Receiving lua custom packet"); lua_State* L = gLuaState; u16 modIndex = 0; u8 keyCount = 0; @@ -132,3 +130,117 @@ void network_receive_lua_custom(struct Packet* p) { smlua_call_event_hooks(HOOK_ON_PACKET_RECEIVE, modIndex, tableIndex); lua_pop(L, 1); // pop table } + + //////////////// + // bytestring // +//////////////// + +#define MAX_BYTESTRING_LENGTH (PACKET_LENGTH - 15) + +void network_send_lua_custom_bytestring(bool broadcast) { + lua_State* L = gLuaState; + s32 paramIndex = 1; + + if (!L) { + LOG_ERROR("Sent lua custom bytestring packet when lua is dead"); + return; + } + + // figure out mod index + if (gLuaActiveMod == NULL) { + LOG_LUA_LINE("Could not figure out the current active mod!"); + return; + } + u16 modIndex = gLuaActiveMod->index; + + // get local index + s32 toLocalIndex = 0; + if (!broadcast) { + toLocalIndex = smlua_to_integer(L, paramIndex++); + if (toLocalIndex <= 0 || toLocalIndex >= MAX_PLAYERS) { + LOG_LUA_LINE("Tried to send bytestring packet to invalid local index: %d", toLocalIndex) + return; + } + if (!gSmLuaConvertSuccess) { + LOG_LUA("Invalid 'localIndex' type"); + return; + } + } + + // get reliability + bool reliability = smlua_to_boolean(L, paramIndex++); + if (!gSmLuaConvertSuccess) { + LOG_LUA("Invalid 'reliable' type"); + return; + } + + // write packet header + struct Packet p = { 0 }; + packet_init(&p, PACKET_LUA_CUSTOM_BYTESTRING, reliability, PLMT_NONE); + packet_write(&p, &modIndex, sizeof(u16)); + + // make sure value passed in is a string + s32 bytestringIndex = paramIndex; + if (lua_type(L, bytestringIndex) != LUA_TSTRING) { + LOG_LUA_LINE("Tried to send a bytestring packet with a non-string"); + return; + } + + // get string information + size_t totalLength = 0; + const char* bytestring = lua_tolstring(L, bytestringIndex, &totalLength); + + // check length + if (totalLength <= 0 || totalLength > MAX_BYTESTRING_LENGTH) { + LOG_LUA_LINE("Tried to send a bytestring packet with an invalid length '%llu'. Must be above 0 and below '%u'", (u64)totalLength, MAX_BYTESTRING_LENGTH); + } + + // write length + u16 bytestringLength = totalLength; + packet_write(&p, &bytestringLength, sizeof(u16)); + + // write bytestring + packet_write(&p, (char*)bytestring, totalLength); + + // send packet + if (broadcast) { + network_send(&p); + } else { + network_send_to(toLocalIndex, &p); + } +} + +void network_receive_lua_custom_bytestring(struct Packet* p) { + lua_State* L = gLuaState; + u16 modIndex = 0; + u16 bytestringLength = 0; + packet_read(p, &modIndex, sizeof(u16)); + packet_read(p, &bytestringLength, sizeof(u16)); + + if (!L) { + LOG_ERROR("Received lua custom bytestring packet when lua is dead"); + return; + } + + if (bytestringLength == 0 || bytestringLength > MAX_BYTESTRING_LENGTH) { + LOG_ERROR("Received lua custom bytestring packet with an invalid length"); + return; + } + + // read byte string + static char sBytestring[MAX_BYTESTRING_LENGTH + 1] = ""; + packet_read(p, &sBytestring, bytestringLength); + + if (p->error) { + LOG_ERROR("Received malformed lua custom bytestring packet"); + return; + } + + // push bytestring + lua_pushlstring(L, sBytestring, bytestringLength); + s32 bytestringIndex = lua_gettop(L); + + // call hook + smlua_call_event_hooks(HOOK_ON_PACKET_BYTESTRING_RECEIVE, modIndex, bytestringIndex); + lua_pop(L, 1); // pop bytestring +}