mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 13:35:12 +00:00
Rewrite of how lua hooks chat commands
This commit is contained in:
parent
4bf4c476e6
commit
0935eed3b7
9 changed files with 214 additions and 35 deletions
|
@ -532,6 +532,7 @@
|
|||
<ClCompile Include="..\src\pc\network\discord\user.c" />
|
||||
<ClCompile Include="..\src\pc\network\network.c" />
|
||||
<ClCompile Include="..\src\pc\network\network_player.c" />
|
||||
<ClCompile Include="..\src\pc\network\network_utils.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_area.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_area_request.c" />
|
||||
|
@ -993,6 +994,7 @@
|
|||
<ClInclude Include="..\src\pc\network\discord\user.h" />
|
||||
<ClInclude Include="..\src\pc\network\network.h" />
|
||||
<ClInclude Include="..\src\pc\network\network_player.h" />
|
||||
<ClInclude Include="..\src\pc\network\network_utils.h" />
|
||||
<ClInclude Include="..\src\pc\network\packets\packet.h" />
|
||||
<ClInclude Include="..\src\pc\network\reservation_area.h" />
|
||||
<ClInclude Include="..\src\pc\network\socket\socket.h" />
|
||||
|
|
|
@ -4878,6 +4878,9 @@
|
|||
<ClCompile Include="..\src\pc\djui\djui_panel_controls_extra.c">
|
||||
<Filter>Source Files\src\pc\djui\panel</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\pc\network\network_utils.c">
|
||||
<Filter>Source Files\src\pc\network</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\actors\common0.h">
|
||||
|
@ -6034,5 +6037,8 @@
|
|||
<ClInclude Include="..\src\pc\djui\djui_panel_controls_extra.h">
|
||||
<Filter>Source Files\src\pc\djui\panel</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\pc\network\network_utils.h">
|
||||
<Filter>Source Files\src\pc\network</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1390,8 +1390,7 @@
|
|||
| HOOK_ON_PVP_ATTACK | 5 |
|
||||
| HOOK_ON_PLAYER_CONNECTED | 6 |
|
||||
| HOOK_ON_PLAYER_DISCONNECTED | 7 |
|
||||
| HOOK_ON_CHAT_COMMAND | 8 |
|
||||
| HOOK_MAX | 9 |
|
||||
| HOOK_MAX | 8 |
|
||||
|
||||
<br />
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
|
|||
| HOOK_ON_PVP_ATTACK | Called when one player attacks another | [MarioState](structs.md#MarioState) attacker, [MarioState](structs.md#MarioState) victim |
|
||||
| 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 |
|
||||
| HOOK_ON_CHAT_COMMAND | Called when a player types a chat message starting with '/' | `string` message -> `return` `true` on valid command |
|
||||
|
||||
<br />
|
||||
|
||||
|
@ -114,3 +113,33 @@ 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.
|
||||
|
||||
### 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:](#)
|
||||
|
|
|
@ -9,6 +9,9 @@ for i=0,(MAX_PLAYERS-1) do
|
|||
sCachedState[i].seeking = false
|
||||
end
|
||||
|
||||
-- globally sync enabled state
|
||||
gGlobalSyncTable.hideAndSeek = true
|
||||
|
||||
-- keep track of round numbers for popup
|
||||
sCachedRoundNumber = 0
|
||||
gGlobalSyncTable.roundNumber = 0
|
||||
|
@ -32,8 +35,10 @@ function server_update(m)
|
|||
local hasSeeker = false
|
||||
local hasHider = false
|
||||
local activePlayers = {}
|
||||
local connectedCount = 0
|
||||
for i=0,(MAX_PLAYERS-1) do
|
||||
if gNetworkPlayers[i].connected then
|
||||
connectedCount = connectedCount + 1
|
||||
table.insert(activePlayers, gPlayerSyncTable[i])
|
||||
if gPlayerSyncTable[i].seeking then
|
||||
hasSeeker = true
|
||||
|
@ -43,6 +48,12 @@ function server_update(m)
|
|||
end
|
||||
end
|
||||
|
||||
-- only change state if there are 2+ players
|
||||
if connectedCount < 2 then
|
||||
sStaleTimer = 0
|
||||
return
|
||||
end
|
||||
|
||||
-- increment stale timer
|
||||
if not hasHider or not hasSeeker then
|
||||
sStaleTimer = sStaleTimer + 1
|
||||
|
@ -80,6 +91,11 @@ function server_update(m)
|
|||
end
|
||||
|
||||
function update()
|
||||
-- check gamemode enabled state
|
||||
if not gGlobalSyncTable.hideAndSeek then
|
||||
return
|
||||
end
|
||||
|
||||
-- only allow the server to figure out the seeker
|
||||
if network_is_server() then
|
||||
server_update(gMarioStates[0])
|
||||
|
@ -133,6 +149,11 @@ function mario_local_update(m)
|
|||
end
|
||||
|
||||
function mario_update(m)
|
||||
-- check gamemode enabled state
|
||||
if not gGlobalSyncTable.hideAndSeek then
|
||||
return
|
||||
end
|
||||
|
||||
-- this code runs for all players
|
||||
local s = gPlayerSyncTable[m.playerIndex]
|
||||
|
||||
|
@ -161,6 +182,11 @@ function mario_update(m)
|
|||
end
|
||||
|
||||
function mario_before_phys_step(m)
|
||||
-- check gamemode enabled state
|
||||
if not gGlobalSyncTable.hideAndSeek then
|
||||
return
|
||||
end
|
||||
|
||||
local s = gPlayerSyncTable[m.playerIndex]
|
||||
|
||||
-- only make seekers faster
|
||||
|
@ -190,6 +216,11 @@ function mario_before_phys_step(m)
|
|||
end
|
||||
|
||||
function on_pvp_attack(attacker, victim)
|
||||
-- check gamemode enabled state
|
||||
if not gGlobalSyncTable.hideAndSeek then
|
||||
return
|
||||
end
|
||||
|
||||
-- this code runs when a player attacks another player
|
||||
local sAttacker = gPlayerSyncTable[attacker.playerIndex]
|
||||
local sVictim = gPlayerSyncTable[victim.playerIndex]
|
||||
|
@ -211,6 +242,19 @@ function on_player_connected(m)
|
|||
s.seeking = false
|
||||
end
|
||||
|
||||
function on_hide_and_seek_command(msg)
|
||||
if msg == 'on' then
|
||||
djui_chat_message_create('Hide-and-seek mod: enabled')
|
||||
gGlobalSyncTable.hideAndSeek = true
|
||||
return true
|
||||
elseif msg == 'off' then
|
||||
djui_chat_message_create('Hide-and-seek mod: disabled')
|
||||
gGlobalSyncTable.hideAndSeek = false
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-----------
|
||||
-- hooks --
|
||||
-----------
|
||||
|
@ -220,3 +264,5 @@ hook_event(HOOK_MARIO_UPDATE, mario_update)
|
|||
hook_event(HOOK_BEFORE_PHYS_STEP, mario_before_phys_step)
|
||||
hook_event(HOOK_ON_PVP_ATTACK, on_pvp_attack)
|
||||
hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected)
|
||||
|
||||
hook_chat_command('hide', "[on|off] turn hide-and-seek on or off", on_hide_and_seek_command)
|
||||
|
|
|
@ -33,8 +33,9 @@ static void djui_chat_box_input_enter(struct DjuiInputbox* chatInput) {
|
|||
|
||||
if (strlen(chatInput->buffer) != 0) {
|
||||
if (chatInput->buffer[0] == '/') {
|
||||
if (!smlua_call_event_hook_on_chat_command(chatInput->buffer)) {
|
||||
if (!smlua_call_chat_command_hook(chatInput->buffer)) {
|
||||
djui_chat_message_create("Unrecognized chat command.");
|
||||
smlua_display_chat_commands();
|
||||
}
|
||||
} else {
|
||||
djui_chat_message_create_from(gNetworkPlayerLocal->globalIndex, chatInput->buffer);
|
||||
|
|
|
@ -1439,8 +1439,7 @@ char gSmluaConstants[] = ""
|
|||
"HOOK_ON_PVP_ATTACK = 5\n"
|
||||
"HOOK_ON_PLAYER_CONNECTED = 6\n"
|
||||
"HOOK_ON_PLAYER_DISCONNECTED = 7\n"
|
||||
"HOOK_ON_CHAT_COMMAND = 8\n"
|
||||
"HOOK_MAX = 9\n"
|
||||
"HOOK_MAX = 8\n"
|
||||
"SPTASK_STATE_NOT_STARTED = 0\n"
|
||||
"SPTASK_STATE_RUNNING = 1\n"
|
||||
"SPTASK_STATE_INTERRUPTED = 2\n"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "smlua.h"
|
||||
#include "pc/djui/djui_chat_message.h"
|
||||
|
||||
#define MAX_HOOKED_REFERENCES 64
|
||||
|
||||
|
@ -125,31 +126,6 @@ void smlua_call_event_hooks_network_player_param(enum LuaHookedEventType hookTyp
|
|||
}
|
||||
}
|
||||
|
||||
bool smlua_call_event_hook_on_chat_command(char* message) {
|
||||
lua_State* L = gLuaState;
|
||||
if (L == NULL) { return false; }
|
||||
bool ret = false;
|
||||
|
||||
struct LuaHookedEvent* hook = &sHookedEvents[HOOK_ON_CHAT_COMMAND];
|
||||
for (int i = 0; i < hook->count; i++) {
|
||||
// push the callback onto the stack
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
||||
|
||||
// push message
|
||||
lua_pushstring(L, message);
|
||||
|
||||
// call the callback
|
||||
if (0 != lua_pcall(L, 1, 1, 0)) {
|
||||
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ret || smlua_to_boolean(L, -1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// hooked actions //
|
||||
////////////////////
|
||||
|
@ -172,6 +148,11 @@ int smlua_hook_mario_action(lua_State* L) {
|
|||
}
|
||||
|
||||
lua_Integer action = smlua_to_integer(L, -2);
|
||||
if (action == 0 || gSmLuaConvertSuccess) {
|
||||
LOG_LUA("Hook Action: tried to hook invalid action");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (ref == -1) {
|
||||
|
@ -221,6 +202,111 @@ bool smlua_call_action_hook(struct MarioState* m, s32* returnValue) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// hooked chat command //
|
||||
/////////////////////////
|
||||
|
||||
struct LuaHookedChatCommand {
|
||||
char* command;
|
||||
char* description;
|
||||
int reference;
|
||||
};
|
||||
|
||||
#define MAX_HOOKED_CHAT_COMMANDS 64
|
||||
|
||||
static struct LuaHookedChatCommand sHookedChatCommands[MAX_HOOKED_CHAT_COMMANDS] = { 0 };
|
||||
static int sHookedChatCommandsCount = 0;
|
||||
|
||||
int smlua_hook_chat_command(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
if (sHookedChatCommandsCount >= MAX_HOOKED_CHAT_COMMANDS) {
|
||||
LOG_LUA("Hooked chat command exceeded maximum references!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* command = smlua_to_string(L, 1);
|
||||
if (command == NULL || strlen(command) == 0 || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA("Hook chat command: tried to hook invalid command");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* description = smlua_to_string(L, 2);
|
||||
if (description == NULL || strlen(description) == 0 || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA("Hook chat command: tried to hook invalid description");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
if (ref == -1) {
|
||||
LOG_LUA("Hook chat command: tried to hook undefined function '%s'", command);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct LuaHookedChatCommand* hooked = &sHookedChatCommands[sHookedChatCommandsCount];
|
||||
hooked->command = strdup(command);
|
||||
hooked->description = strdup(description);
|
||||
hooked->reference = ref;
|
||||
if (!gSmLuaConvertSuccess) { return 0; }
|
||||
|
||||
sHookedChatCommandsCount++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool smlua_call_chat_command_hook(char* command) {
|
||||
lua_State* L = gLuaState;
|
||||
if (L == NULL) { return false; }
|
||||
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
||||
struct LuaHookedChatCommand* hook = &sHookedChatCommands[i];
|
||||
size_t commandLength = strlen(hook->command);
|
||||
for (size_t j = 0; j < commandLength; j++) {
|
||||
if (hook->command[j] != command[j + 1]) {
|
||||
goto NEXT_HOOK;
|
||||
}
|
||||
}
|
||||
|
||||
char* params = &command[commandLength + 1];
|
||||
if (*params != '\0' && *params != ' ') {
|
||||
goto NEXT_HOOK;
|
||||
}
|
||||
if (*params == ' ') {
|
||||
params++;
|
||||
}
|
||||
|
||||
// push the callback onto the stack
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference);
|
||||
|
||||
// push parameter
|
||||
lua_pushstring(L, params);
|
||||
|
||||
// call the callback
|
||||
if (0 != lua_pcall(L, 1, 1, 0)) {
|
||||
LOG_LUA("Failed to call the callback: %s", lua_tostring(L, -1));
|
||||
continue;
|
||||
}
|
||||
|
||||
// output the return value
|
||||
bool returnValue = smlua_to_boolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (!gSmLuaConvertSuccess) { return false; }
|
||||
|
||||
return returnValue;
|
||||
|
||||
NEXT_HOOK:;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void smlua_display_chat_commands(void) {
|
||||
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
||||
struct LuaHookedChatCommand* hook = &sHookedChatCommands[i];
|
||||
char msg[256] = { 0 };
|
||||
snprintf(msg, 256, "/%s %s", hook->command, hook->description);
|
||||
djui_chat_message_create(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//////////
|
||||
// misc //
|
||||
//////////
|
||||
|
@ -238,6 +324,17 @@ static void smlua_clear_hooks(void) {
|
|||
sHookedMarioActions[i].reference = 0;
|
||||
}
|
||||
sHookedMarioActionsCount = 0;
|
||||
|
||||
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
||||
if (sHookedChatCommands[i].command != NULL) { free(sHookedChatCommands[i].command); }
|
||||
sHookedChatCommands[i].command = NULL;
|
||||
|
||||
if (sHookedChatCommands[i].description != NULL) { free(sHookedChatCommands[i].description); }
|
||||
sHookedChatCommands[i].description = NULL;
|
||||
|
||||
sHookedChatCommands[i].reference = 0;
|
||||
}
|
||||
sHookedChatCommandsCount = 0;
|
||||
}
|
||||
|
||||
void smlua_bind_hooks(void) {
|
||||
|
@ -246,5 +343,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);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ enum LuaHookedEventType {
|
|||
HOOK_ON_PVP_ATTACK,
|
||||
HOOK_ON_PLAYER_CONNECTED,
|
||||
HOOK_ON_PLAYER_DISCONNECTED,
|
||||
HOOK_ON_CHAT_COMMAND,
|
||||
HOOK_MAX,
|
||||
};
|
||||
|
||||
|
@ -25,17 +24,18 @@ static char* LuaHookedEventTypeName[] = {
|
|||
"HOOK_ON_PVP_ATTACK",
|
||||
"HOOK_ON_PLAYER_CONNECTED",
|
||||
"HOOK_ON_PLAYER_DISCONNECTED",
|
||||
"HOOK_ON_CHAT_COMMAND",
|
||||
"HOOK_MAX"
|
||||
};
|
||||
|
||||
void smlua_call_event_hooks(enum LuaHookedEventType hookType);
|
||||
void smlua_call_event_hooks_mario_param(enum LuaHookedEventType hookType, struct MarioState* m);
|
||||
void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2);
|
||||
bool smlua_call_event_hook_on_chat_command(char* message);
|
||||
|
||||
bool smlua_call_action_hook(struct MarioState* m, s32* returnValue);
|
||||
|
||||
bool smlua_call_chat_command_hook(char* command);
|
||||
void smlua_display_chat_commands(void);
|
||||
|
||||
void smlua_bind_hooks(void);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue