From 2e1bff4f86177c23a79c4332b31d830a888f298b Mon Sep 17 00:00:00 2001 From: Jackz Date: Thu, 28 Mar 2024 20:30:48 -0500 Subject: [PATCH] Update to spec, fix builtins --- plugins/adminpanel.smx | Bin 20068 -> 23426 bytes scripting/adminpanel.sp | 301 +++++++++++++++++++++++++++++++--------- 2 files changed, 235 insertions(+), 66 deletions(-) diff --git a/plugins/adminpanel.smx b/plugins/adminpanel.smx index 4367f5089b50bdfd3540dde26e06f65058d80d06..c4d6ea098a87d34139f77043111acfca9a1797f1 100644 GIT binary patch literal 23426 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 XQes7NukXepFZeuVM1dao>394OdKN9z literal 20068 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 diff --git a/scripting/adminpanel.sp b/scripting/adminpanel.sp index 04b8ff7..f2a9b8d 100644 --- a/scripting/adminpanel.sp +++ b/scripting/adminpanel.sp @@ -27,13 +27,13 @@ public Plugin myinfo = }; int LIVESTATUS_VERSION = 0; - +Regex CommandArgRegex; ConVar cvar_debug; ConVar cvar_gamemode; char gamemode[32]; ConVar cvar_difficulty; int gameDifficulty; -ConVar cvar_id; char serverId[32]; ConVar cvar_address; char serverIp[16] = "127.0.0.1"; int serverPort = DEFAULT_SERVER_PORT; +ConVar cvar_authToken; char authToken[256]; char currentMap[64]; int numberOfPlayers = 0; @@ -55,6 +55,7 @@ bool lateLoaded; Socket g_socket; bool g_isPaused; +bool isAuthenticated; #define BUFFER_SIZE 2048 Buffer sendBuffer; Buffer receiveBuffer; // Unfortunately there's no easy way to have this not be the same as BUFFER_SIZE @@ -67,23 +68,26 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max public void OnPluginStart() { + // TODO: periodic reconnect g_socket = new Socket(SOCKET_TCP, OnSocketError); g_socket.SetOption(SocketKeepAlive, 1); g_socket.SetOption(SocketReuseAddr, 1); uptime = GetTime(); - cvar_debug = CreateConVar("sm_adminpanel_debug", "0", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0); + cvar_debug = CreateConVar("sm_adminpanel_debug", "1", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0); - cvar_id = CreateConVar("sm_adminpanel_id", "", "The server ID to post updates for", FCVAR_NONE); - cvar_id.AddChangeHook(OnCvarChanged); - cvar_id.GetString(serverId, sizeof(serverId)); + cvar_authToken = CreateConVar("sm_adminpanel_authtoken", "", "The token for authentication", FCVAR_PROTECTED); + cvar_authToken.AddChangeHook(OnCvarChanged); + cvar_authToken.GetString(authToken, sizeof(authToken)); - cvar_address = CreateConVar("sm_adminpanel_host", "100.108.152.125:7888", "The IP and port to connect to, default is 7888", FCVAR_NONE); + cvar_address = CreateConVar("sm_adminpanel_host", "127.0.0.1:7888", "The IP and port to connect to, default is 7888", FCVAR_NONE); cvar_address.AddChangeHook(OnCvarChanged); cvar_address.GetString(serverIp, sizeof(serverIp)); OnCvarChanged(cvar_address, "", serverIp); + + cvar_gamemode = FindConVar("mp_gamemode"); cvar_gamemode.AddChangeHook(OnCvarChanged); cvar_gamemode.GetString(gamemode, sizeof(gamemode)); @@ -116,6 +120,8 @@ public void OnPluginStart() RegAdminCmd("sm_panel_debug", Command_PanelDebug, ADMFLAG_GENERIC); RegAdminCmd("sm_panel_request_stop", Command_RequestStop, ADMFLAG_GENERIC); + + CommandArgRegex = new Regex("(?:[^\\s\"]+|\"[^\"]*\")+", 0); } void TriggerHealthUpdate(int client, bool instant = false) { @@ -140,12 +146,41 @@ void OnSocketError(Socket socket, int errorType, int errorNumber, int any) { } } +void SendFullSync() { + if(StartPayload()) { + AddGameRecord(); + int stage = L4D2_GetCurrentFinaleStage(); + if(stage != 0) + AddFinaleRecord(stage); + SendPayload(); + + // Resend all players + SendPlayers(); + } +} + void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int arg) { receiveBuffer.FromArray(receiveData, dataSize); LiveRecordResponse response = view_as(receiveBuffer.ReadByte()); if(cvar_debug.BoolValue) { - PrintToServer("[AdminPanel] Received: %d", response); + PrintToServer("[AdminPanel] Received response=%d size=%d bytes", response, dataSize); } + if(!isAuthenticated) { + if(response == Live_OK) { + isAuthenticated = true; + PrintToServer("[AdminPanel] Authenticated with server successfully."); + SendFullSync(); + } else if(response == Live_Error) { + g_socket.Disconnect(); + char message[128]; + receiveBuffer.ReadString(message, sizeof(message)); + SetFailState("Failed to authenticate with socket: %s", message); + } else { + // Ignore packets when not authenticated + } + return; + } + lastReceiveTime = GetTime(); switch(response) { case Live_RunComand: { @@ -154,95 +189,189 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int int id = receiveBuffer.ReadByte(); receiveBuffer.ReadString(command, sizeof(command)); receiveBuffer.ReadString(cmdNamespace, sizeof(cmdNamespace)); + if(cvar_debug.BoolValue) { + PrintToServer("[AdminPanel] Running %s:%s", cmdNamespace, command); + } ProcessCommand(id, command, cmdNamespace); } case Live_OK: { int viewerCount = receiveBuffer.ReadByte(); g_isPaused = viewerCount == 0; } + case Live_Error: { + + } case Live_Reconnect: CreateTimer(5.0, Timer_Reconnect); case Live_Refresh: { - PrintToServer("[AdminPanel] Refresh requested, performing"); - StartPayload(); - AddGameRecord(); - SendPayload(); - - SendPlayers(); + PrintToServer("[AdminPanel] Sync requested, performing"); + SendFullSync(); } } if(receiveTimeoutTimer != null) { delete receiveTimeoutTimer; } - receiveTimeoutTimer = CreateTimer(SOCKET_TIMEOUT_DURATION, Timer_Reconnect, 1); + // receiveTimeoutTimer = CreateTimer(SOCKET_TIMEOUT_DURATION, Timer_Reconnect, 1); } void ProcessCommand(int id, const char[] command, const char[] cmdNamespace = "") { char output[128]; - if(!StartPayload()) return; + if(!StartPayload(true)) return; if(cmdNamespace[0] == '\0' || StrEqual(cmdNamespace, "default")) { SplitString(command, " ", output, sizeof(output)); - StartRecord(Live_CommandResponse); if(CommandExists(output)) { ServerCommandEx(output, sizeof(output), "%s", command); - AddCommandResponseRecord(id, 1, output); + AddCommandResponseRecord(id, Result_Boolean, 1, output); } else { - AddCommandResponseRecord(id, -1, ""); + AddCommandResponseRecord(id, Result_Error, -1, "Command does not exist"); } - SendPayload(); } else if(StrEqual(cmdNamespace, "builtin")) { - StartRecord(Live_CommandResponse); - int result = ProcessBuiltin(command, output, sizeof(output)); - AddCommandResponseRecord(id, result, output); - SendPayload(); + CommandResultType type; + int result = ProcessBuiltin(command, type, output, sizeof(output)); + AddCommandResponseRecord(id, type, result, output); } else { - AddCommandResponseRecord(id, -1, ""); + AddCommandResponseRecord(id, Result_Error, -2, "Unknown namespace"); } + SendPayload(); } -int ProcessBuiltin(const char[] fullCommand, char[] output, int maxlen) { +int GetPlayersOnline() { + int count; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientInGame(i) && !IsFakeClient(i)) { + count++; + } + } + return count; +} + +int ProcessBuiltin(const char[] fullCommand, CommandResultType &type = Result_Boolean, char[] output, int maxlen) { char command[32]; - int index = SplitString(fullCommand, " ", command, sizeof(command)); + int matches = CommandArgRegex.MatchAll(fullCommand); + CommandArgRegex.GetSubString(0, command, sizeof(command), 0); if(StrEqual(command, "stop")) { + type = Result_Boolean; RequestFrame(StopServer); return 1; } else if(StrEqual(command, "request_stop")) { - if(GetClientCount(false) == 0) return 0; - RequestFrame(StopServer); - return 1; + type = Result_Boolean; + int count = GetPlayersOnline(); + if(count > 0) { + Format(output, maxlen, "There are %d players online", count); + return 0; + } else { + RequestFrame(StopServer); + return 1; + } } else if(StrEqual(command, "kick")) { char arg[32]; - index += SplitString(fullCommand[index], " ", arg, sizeof(arg)); + if(matches >= 2) + CommandArgRegex.GetSubString(0, arg, sizeof(arg), 1); int player = FindPlayer(arg); - if(player == -1) return 0; - - index += SplitString(fullCommand[index], " ", arg, sizeof(arg)); - KickClient(player, arg); - return 1; + if(player > 0) { + // Is a player, kick em + if(matches >= 2) + CommandArgRegex.GetSubString(0, arg, sizeof(arg), 2); + KickClient(player, arg); + type = Result_Integer; + return 1; + } else { + // Get the failure code + type = Result_Error; + GetTargetErrorReason(player, output, maxlen); + return player; + } + } else if(StrEqual(command, "ban")) { + char arg[32]; + if(matches >= 2) + CommandArgRegex.GetSubString(0, arg, sizeof(arg), 1); + int player = FindPlayer(arg); + if(player > 0) { + // Is a player, kick em + if(matches >= 2) + CommandArgRegex.GetSubString(0, arg, sizeof(arg), 2); + int time = StringToInt(arg); + if(matches >= 2) + CommandArgRegex.GetSubString(0, arg, sizeof(arg), 3); + type = Result_Integer; + return BanClient(player, time, BANFLAG_AUTHID, arg, arg, "sm_adminpanel"); + } else { + // Get the failure code + GetTargetErrorReason(player, output, maxlen); + type = Result_Error; + return player; + } + } else if(StrEqual(command, "players")) { + type = Result_Integer; + return GetClientCount(false); + } else if(StrEqual(command, "unreserve")) { + type = Result_Boolean; + if(L4D_LobbyIsReserved()) { + L4D_LobbyUnreserve(); + strcopy(output, maxlen, "Lobby reservation has been removed"); + return true; + } else { + return false; + } } else { + type = Result_Error; + strcopy(output, maxlen, "Unknown builtin command"); return -1; } } +stock void GetTargetErrorReason(int reason, char[] output, int maxlen) { + switch (reason) { + case COMMAND_TARGET_NONE: { + strcopy(output, maxlen, "No matching client"); + } + case COMMAND_TARGET_NOT_ALIVE: { + strcopy(output, maxlen, "Target must be alive"); + } + case COMMAND_TARGET_NOT_DEAD: { + strcopy(output, maxlen, "Target must be dead"); + } + case COMMAND_TARGET_NOT_IN_GAME: { + strcopy(output, maxlen, "Target is not in game"); + } + case COMMAND_TARGET_IMMUNE: { + strcopy(output, maxlen, "Unable to target"); + } + case COMMAND_TARGET_EMPTY_FILTER: { + strcopy(output, maxlen, "No matching clients"); + } + case COMMAND_TARGET_NOT_HUMAN: { + strcopy(output, maxlen, "Cannot target bot"); + } + case COMMAND_TARGET_AMBIGUOUS: { + strcopy(output, maxlen, "More than one client matched"); + } + } +} + +// Returns player index OR a target failure. > 0: player, <= 0: failure int FindPlayer(const char[] arg) { // TODO: IMPLEMENT - return -1; + int targets[1]; + bool is_ml; + int result = ProcessTargetString(arg, 0, targets, 1, 0, "", 0, is_ml); + if(result == 1) return targets[0]; + return result; } void OnSocketConnect(Socket socket, int any) { if(cvar_debug.BoolValue) PrintToServer("[AdminPanel] Connected to %s:%d", serverIp, serverPort); g_socket.SetArg(0); - // Late loads / first setup we can't send - if(currentMap[0] != '\0' && StartPayload()) { - AddGameRecord(); - SendPayload(); - // Resend all players - SendPlayers(); - } + + PrintToServer("[AdminPanel] Authenticating with server"); + StartPayloadEx(); + AddAuthRecord(); + SendPayload(); } void OnSocketDisconnect(Socket socket, int attempt) { + isAuthenticated = false; g_socket.SetArg(attempt + 1); float nextAttempt = Exponential(float(attempt) / 2.0) + 2.0; if(nextAttempt > MAX_ATTEMPT_TIMEOUT) nextAttempt = MAX_ATTEMPT_TIMEOUT; @@ -255,14 +384,14 @@ Action Timer_Reconnect(Handle h, int type) { PrintToServer("[AdminPanel] No response after %f seconds, attempting reconnect", SOCKET_TIMEOUT_DURATION); } ConnectSocket(); - return Plugin_Handled; + return Plugin_Continue; } void ConnectSocket() { if(g_socket == null) LogError("Socket is invalid"); if(g_socket.Connected) g_socket.Disconnect(); - if(serverId[0] == '\0') return; + if(authToken[0] == '\0') return; g_socket.SetOption(DebugMode, cvar_debug.BoolValue); g_socket.Connect(OnSocketConnect, OnSocketReceive, OnSocketDisconnect, serverIp, serverPort); } @@ -272,15 +401,27 @@ Action Command_PanelDebug(int client, int args) { char arg[32]; GetCmdArg(1, arg, sizeof(arg)); if(StrEqual(arg, "connect")) { - if(serverId[0] == '\0') - ReplyToCommand(client, "No server id."); + if(authToken[0] == '\0') + ReplyToCommand(client, "No auth token."); else ConnectSocket(); } else if(StrEqual(arg, "info")) { - ReplyToCommand(client, "Connected: %b\tPaused: %b\t#Player: %d", g_socket.Connected, g_isPaused, numberOfPlayers); - ReplyToCommand(client, "ID: %s", serverId); + ReplyToCommand(client, "Connected: %b\tAuthenticated: %b", g_socket.Connected, isAuthenticated); + ReplyToCommand(client, "Paused: %b\t#Players: %d", g_isPaused, numberOfPlayers); ReplyToCommand(client, "Target Host: %s:%d", serverIp, serverPort); ReplyToCommand(client, "Buffer Size: %d", BUFFER_SIZE); + } else if(StrEqual(arg, "builtin")) { + if(args < 2) { + ReplyToCommand(client, "Usage: builtin "); + return Plugin_Handled; + } + char command[128]; + GetCmdArg(2, command, sizeof(command)); + char output[128]; + CommandResultType type; + int result = ProcessBuiltin(command, type, output, sizeof(output)); + ReplyToCommand(client, "Result: %d (type=%d)", result, view_as(type)); + ReplyToCommand(client, "Output: %s", output); } else if(g_socket.Connected) { if(StrEqual(arg, "game")) { StartPayload(); @@ -288,6 +429,8 @@ Action Command_PanelDebug(int client, int args) { SendPayload(); } else if(StrEqual(arg, "players")) { SendPlayers(); + } else if(StrEqual(arg, "sync")) { + SendFullSync(); } else { ReplyToCommand(client, "Unknown type"); return Plugin_Handled; @@ -400,11 +543,12 @@ public void OnMapStart() { GetCurrentMap(currentMap, sizeof(currentMap)); numberOfPlayers = 0; if(lateLoaded) { - StartPayload(); - AddGameRecord(); - SendPayload(); + if(StartPayload()) { + AddGameRecord(); + SendPayload(); - SendPlayers(); + SendPlayers(); + } } } @@ -543,10 +687,7 @@ public void OnClientDisconnect(int client) { // Cvar updates void OnCvarChanged(ConVar convar, const char[] oldValue, const char[] newValue) { - if(cvar_id == convar) { - strcopy(serverId, sizeof(serverId), newValue); - PrintToServer("[AdminPanel] Server ID changed to: %s", serverId); - } else if(cvar_address == convar) { + if(cvar_address == convar) { if(newValue[0] == '\0') { if(g_socket.Connected) g_socket.Disconnect(); @@ -574,6 +715,10 @@ void OnCvarChanged(ConVar convar, const char[] oldValue, const char[] newValue) AddGameRecord(); SendPayload(); } + } else if(cvar_authToken == convar) { + strcopy(authToken, sizeof(authToken), newValue); + // Token changed, re-try authentication + ConnectSocket(); } } @@ -670,6 +815,14 @@ stock int GetDifficultyInt() { else return 1; } +enum CommandResultType { + Result_Error = -1, + Result_None, + Result_Boolean, + Result_Integer, + Result_Float +} + enum LiveRecordType { Live_Game, Live_Player, @@ -689,21 +842,30 @@ enum LiveRecordResponse { Live_RunComand } -bool StartPayload() { - if(!cvar_debug.BoolValue && (g_isPaused || numberOfPlayers == 0)) return false; +bool StartPayload(bool ignorePause = false) { + // PrintToServer("conn=%b auth=%b igPause=%b debug=%b paused=%b player=%b", ) + if(!g_socket.Connected || !isAuthenticated) return false; + if(!ignorePause && (g_isPaused || numberOfPlayers == 0)) return false; + StartPayloadEx(); + return true; +} + +/// Starts payload, ignoring if the payload can even be sent +bool hasRecord; +void StartPayloadEx() { sendBuffer.Reset(); - sendBuffer.WriteByte(LIVESTATUS_VERSION); - sendBuffer.WriteString(serverId); - return g_socket.Connected; + hasRecord = false; } void StartRecord(LiveRecordType type) { - sendBuffer.WriteChar('\x1e'); // record separator + if(hasRecord) { + sendBuffer.WriteChar('\x1e'); + } sendBuffer.WriteByte(view_as(type)); + hasRecord = true; } void AddGameRecord() { - PrintToServer("pushing Live_Game"); StartRecord(Live_Game); sendBuffer.WriteInt(uptime); sendBuffer.WriteInt(campaignStartTime); @@ -797,13 +959,20 @@ void AddInfectedRecord(int client) { sendBuffer.WriteInt(0); } -void AddCommandResponseRecord(int id, int resultValue = -1, const char[] message = "") { +void AddCommandResponseRecord(int id, CommandResultType resultType = Result_None, int resultValue = 0, const char[] message = "") { StartRecord(Live_CommandResponse); sendBuffer.WriteByte(id); + sendBuffer.WriteByte(view_as(resultType)); sendBuffer.WriteByte(resultValue); sendBuffer.WriteString(message); } +void AddAuthRecord() { + StartRecord(Live_Auth); + sendBuffer.WriteByte(LIVESTATUS_VERSION); + sendBuffer.WriteString(authToken); +} + void SendPayload() { sendBuffer.Finish(); if(cvar_debug.BoolValue)