/*----------------------------------------------------------------------------*- ============================= YAdmin system - Commands Core ============================= Runs commands registered with the system and calls the required functions. Also handles alternate names and prefixes. Theory based very loosely on dcmd. Changelog: 14/04/07: Updated header documentation with more than changelog/functions. 13/04/07: Added function documentation. Added wrapped functions for e_COMM_FLAG values missing them. Added header function list. 12/04/07: Added command removal. 11/04/07: Changed system slightly to handle names and alt names separately. Still need a better way of ignoring names when alt names are used. 10/04/07: First version. Functions: Public: Command_Add - Adds a command to the array for processing. Command_Remove - Removes a command. Core: Command_Process - Called from OnPlayerCommandText to process entered commands. Command_Parse - Sorts added commands into a binary tree. Stock: Command_SetDisconnectReturn - Sets the return value for unconnected players. Command_UseShortCuts - Toggles use of per-player command shortcuts. Command_SetDeniedReturn - Sets the return value for denied use commands. Command_UseDeniedMessage - Toggles the use of an error message for denied. Command_UseAltNames - Toggles the use of ini defined alternate names. Command_UsePrefix - Toggles the use of a global prefix. Command_UseSpace - Toggles the use of a space between prefix and command. Command_SetAltName - Sets the alternate name of a function. Command_SetPrefix - Sets the pfexix to be typed. Static: Command_SetSorted - Marks the binary tree as sorted. Command_FindSlow - Finds a function by linear search of function name. Command_FindFunc - Finds a function by binary search of function name. Command_FindAlt - Finds a function by binary search of alternate name. Command_Find - Finds a function from entered text. Command_Prefix - Checks the entered prefix. Command_Hash - Hashes a word for command hashing. Inline: Command_Command - Not used, constructor. Command_IsCleared - Checks a player is cleared to use a command. Command_DisconnectReturn - Gets the return value for disconnected players. Command_UsingShortCuts - Checks if short cuts are being used. Command_DeniedReturn - Gets the return value for prohibited commands. Command_DeniedMessage - Checks if a level error message should be sent. Command_IsSorted - Checks if the binary tree has been initialised. Command_UsingAltNames - Checks if alt names are being used. Command_UsingPrefix - Checks if the prefix is being used. Command_UsingSpace - Checks if the space option is being used. Command_CallFunction - Calls the required function. Definitions: MAX_COMMAND_LENGTH - The maximum length of a command string. COMMAND_NOT_FOUND - Indicates that a searched for string is not a function. Enums: e_COMM_FLAG - Bit mappings for command options. E_COMMANDS - Structure of the array holding the string data. Macros: Command_(%1) - Forwards and declares a standard command for calling. ycmd(%1) - Adds a command to the array (wrapper for Command_Add). Tags: e_COMM_FLAG - Flag type. Variables: Global: - Static: g_sCommands - Holds all the textual data of the commands. g_sSearchTree - Tree of hashes for function names. g_sAltTree - Tree of hashes for alternate names. g_sPrefix - The command prefix. g_sPrefixLength - Length of the prefix. g_sCommandIndex - Pointer to the next free index in the function array. g_sAltCount - The number of commands with altnames. g_sCommandFlags - Bit array of command options. -*----------------------------------------------------------------------------*/ #define MAX_COMMAND_LENGTH 16 #define COMMAND_NOT_FOUND -1 enum e_COMM_FLAG (<<= 1) { e_COMM_FLAG_SORTED = 1, e_COMM_FLAG_PERMISSION_WARN, e_COMM_FLAG_USE_ALTNAMES, e_COMM_FLAG_PERMISSION_RET, e_COMM_FLAG_USE_PREFIX, e_COMM_FLAG_USE_SPACE, e_COMM_FLAG_USE_SHORTCUTS, e_COMM_FLAG_DISCONNECT } enum E_COMMANDS { E_COMMANDS_FUNCTION[MAX_COMMAND_LENGTH], E_COMMANDS_ALTNAME[MAX_COMMAND_LENGTH], E_COMMANDS_FUNCNAME[MAX_COMMAND_LENGTH + 5] } forward Command_Add(funcname[]); forward Command_Remove(funcname[]); static g_sCommands[MAX_COMMANDS][E_COMMANDS], Bintree:g_sSearchTree[MAX_COMMANDS][E_BINTREE_TREE], Bintree:g_sAltTree[MAX_COMMANDS][E_BINTREE_TREE], g_sPrefix[MAX_COMMAND_LENGTH], g_sPrefixLength, g_sCommandIndex, g_sAltCount, e_COMM_FLAG:g_sCommandFlags; #define Command_(%1) \ forward ycmd_%1(playerid, params[], help); \ public ycmd_%1(playerid, params[], help) #define ycmd(%1) \ CallRemoteFunction("Command_Add", "s", (%1)) /*----------------------------------------------------------------------------*- Function: Command_Command Params: - Return: - Notes: - -*----------------------------------------------------------------------------*/ #define Command_Command() /*----------------------------------------------------------------------------*- Function: Command_IsCleared Params: playerid - Player to check for. command - Command to check for. Return: TRUE Notes: Not implemented yet. -*----------------------------------------------------------------------------*/ #define Command_IsCleared(%1,%2) \ TRUE /*----------------------------------------------------------------------------*- Function: Command_DisconnectReturn Params: - Return: e_COMM_FLAG_DISCONNECT Notes: - -*----------------------------------------------------------------------------*/ #define Command_DisconnectReturn() \ (g_sCommandFlags & e_COMM_FLAG_DISCONNECT) /*----------------------------------------------------------------------------*- Function: Command_SetDisconnectReturn Params: set - 1/0. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_SetDisconnectReturn(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_DISCONNECT; else g_sCommandFlags &= ~e_COMM_FLAG_USE_DISCONNECT; } /*----------------------------------------------------------------------------*- Function: Command_UsingShortCuts Params: - Return: e_COMM_FLAG_USE_SHORTCUTS Notes: - -*----------------------------------------------------------------------------*/ #define Command_UsingShortCuts() \ (g_sCommandFlags & e_COMM_FLAG_USE_SHORTCUTS) /*----------------------------------------------------------------------------*- Function: Command_UseShortCuts Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_UseShortCuts(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_USE_SHORTCUTS; else g_sCommandFlags &= ~e_COMM_FLAG_USE_SHORTCUTS; } /*----------------------------------------------------------------------------*- Function: Command_DeniedReturn Params: - Return: e_COMM_FLAG_PERMISSION_RET Notes: - -*----------------------------------------------------------------------------*/ #define Command_DeniedReturn() \ (g_sCommandFlags & e_COMM_FLAG_PERMISSION_RET) /*----------------------------------------------------------------------------*- Function: Command_SetDeniedReturn Params: set - 1/0. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_SetDeniedReturn(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_PERMISSION_RET; else g_sCommandFlags &= ~e_COMM_FLAG_USE_PERMISSION_RET; } /*----------------------------------------------------------------------------*- Function: Command_DeniedMessage Params: - Return: e_COMM_FLAG_PERMISSION_WARN Notes: - -*----------------------------------------------------------------------------*/ #define Command_DeniedMessage() \ (g_sCommandFlags & e_COMM_FLAG_PERMISSION_WARN) /*----------------------------------------------------------------------------*- Function: Command_UseDeniedMessage Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_UseDeniedMessage(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_PERMISSION_WARN; else g_sCommandFlags &= ~e_COMM_FLAG_PERMISSION_WARN; } /*----------------------------------------------------------------------------*- Function: Command_IsSorted Params: - Return: e_COMM_FLAG_SORTED Notes: - -*----------------------------------------------------------------------------*/ #define Command_IsSorted() \ (g_sCommandFlags & e_COMM_FLAG_SORTED) /*----------------------------------------------------------------------------*- Function: Command_SetSorted Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ static Command_SetSorted(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_SORTED; else g_sCommandFlags &= ~e_COMM_FLAG_SORTED; } /*----------------------------------------------------------------------------*- Function: Command_UsingAltNames Params: - Return: e_COMM_FLAG_USE_ALTNAMES Notes: - -*----------------------------------------------------------------------------*/ #define Command_UsingAltNames() \ (g_sCommandFlags & e_COMM_FLAG_USE_ALTNAMES) /*----------------------------------------------------------------------------*- Function: Command_UseAltNames Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_UseAltNames(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_USE_ALTNAMES; else g_sCommandFlags &= ~e_COMM_FLAG_USE_ALTNAMES; } /*----------------------------------------------------------------------------*- Function: Command_UsingPrefix Params: - Return: e_COMM_FLAG_USE_PREFIX Notes: - -*----------------------------------------------------------------------------*/ #define Command_UsingPrefix() \ (g_sCommandFlags & e_COMM_FLAG_USE_PREFIX) /*----------------------------------------------------------------------------*- Function: Command_UsePrefix Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_UsePrefix(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_USE_PREFIX; else g_sCommandFlags &= ~e_COMM_FLAG_USE_PREFIX; } /*----------------------------------------------------------------------------*- Function: Command_UsingSpace Params: - Return: e_COMM_FLAG_USE_SPACE Notes: - -*----------------------------------------------------------------------------*/ #define Command_UsingSpace() \ (g_sCommandFlags & e_COMM_FLAG_USE_SPACE) /*----------------------------------------------------------------------------*- Function: Command_UseSpace Params: set - true/false. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_UseSpace(set) { if (set) g_sCommandFlags |= e_COMM_FLAG_USE_SPACE; else g_sCommandFlags &= ~e_COMM_FLAG_USE_SPACE; } /*----------------------------------------------------------------------------*- Function: Command_CallFunction Params: command - Function index to call. playerid - playeris parameter. params[] - params[] parameter. help - help parameter. Return: CallRemoteFunction Notes: - -*----------------------------------------------------------------------------*/ #define Command_CallFunction(%1,%2,%3,%4) \ CallRemoteFunction(g_sCommands[(%1)][E_COMMANDS_FUNCNAME], "isi", (%2), (%3), (%4)) /*----------------------------------------------------------------------------*- Function: Command_Add Params: funcname[] - The function to add to the array. Return: - Notes: If the list of commands have already been sorted into the binary tree the new commands will be appended, otherwise they will just be added to the array. -*----------------------------------------------------------------------------*/ public Command_Add(funcname[]) { if (g_sCommandIndex >= MAX_COMMANDS) return; strcpy(g_sCommands[g_sCommandIndex][E_COMMANDS_FUNCTION], strtolower(funcname)); strcpy(g_sCommands[g_sCommandIndex][E_COMMANDS_FUNCNAME], strcat2("ycmd_", strtolower(funcname))); if (Command_IsSorted()) { new data[E_BINTREE_INPUT], index; data[E_BINTREE_INPUT_VALUE] = Command_Hash(funcname, index); data[E_BINTREE_INPUT_POINTER] = g_sCommandIndex; Bintree_Add(g_sSearchTree, data, g_sCommandIndex, sizeof (g_sSearchTree)); } g_sCommandIndex++; } /*----------------------------------------------------------------------------*- Function: Command_Parse Params: - Return: - Notes: Sorts the commands into the binary tree. -*----------------------------------------------------------------------------*/ Command_Parse() { if (Command_IsSorted()) return; new funcData[MAX_COMMANDS][E_BINTREE_INPUT], altData[MAX_COMMANDS][E_BINTREE_INPUT], j; for (new i = 0; i < g_sCommandIndex; i++) { funcData[i][E_BINTREE_INPUT_VALUE] = bernstein(g_sCommands[i][E_COMMANDS_FUNCTION]); funcData[i][E_BINTREE_INPUT_POINTER] = i; if (g_sCommands[i][E_COMMANDS_ALTNAME][0]) { altData[j][E_BINTREE_INPUT_VALUE] = bernstein(g_sCommands[i][E_COMMANDS_ALTNAME]); altData[j][E_BINTREE_INPUT_POINTER] = i; j++; } } g_sAltCount = j; Bintree_Generate(g_sSearchTree, funcData, g_sCommandIndex); Bintree_Generate(g_sAltTree, altData, g_sAltCount); Command_SetSorted(1); } /*----------------------------------------------------------------------------*- Function: Command_FindSlow Params: funcname[] - Function to find. Return: - Notes: Searches through the array for function linearly - used to set altnames before the data has been sorted. -*----------------------------------------------------------------------------*/ static Command_FindSlow(funcname[]) { for (new i = 0; i < g_sCommandIndex; i++) { if (!strcmp(g_sCommands[i][E_COMMANDS_FUNCTION], funcname, true)) return i; } return COMMAND_NOT_FOUND; } /*----------------------------------------------------------------------------*- Function: Command_SetAltName Params: funcname[] - Function to set the alternate name for. altname[] - The new name. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_SetAltName(funcname[], altname[]) { new pointer; if (!Command_IsSorted()) { if ((pointer = Command_FindSlow(funcname)) == COMMAND_NOT_FOUND) return; } else { new index; if ((pointer = Command_FindFunc(funcname, Command_Hash(funcname, index), index)) == COMMAND_NOT_FOUND) return; if (g_sCommands[pointer][E_COMMANDS_ALTNAME][0]) { index = 0; new pointer2 = Command_FindAlt(g_sCommands[pointer][E_COMMANDS_ALTNAME], Command_Hash(g_sCommands[pointer][E_COMMANDS_ALTNAME], index), index); if (pointer2 != BINTREE_NOT_FOUND) { g_sAltCount = Bintree_Delete(g_sAltTree, pointer2, g_sAltCount); } } index = 0; new data[E_BINTREE_INPUT]; data[E_BINTREE_INPUT_VALUE] = Command_Hash(altname, index); data[E_BINTREE_INPUT_POINTER] = pointer; g_sAltCount = Bintree_Add(g_sAltTree, data, g_sAltCount, sizeof (g_sAltTree)); } strcpy(g_sCommands[pointer][E_COMMANDS_ALTNAME], strtolower(altname)); } /*----------------------------------------------------------------------------*- Function: Command_SetPrefix Params: prefix[] - The new prefix. Return: - Notes: - -*----------------------------------------------------------------------------*/ stock Command_SetPrefix(prefix[]) { strcpy(g_sPrefix, prefix); g_sPrefixLength = strlen(prefix); } /*----------------------------------------------------------------------------*- Function: Command_FindFunc Params: data[] - Function name to find. value - Hash of function name. length - Length of name. &ret - Variable to store leaf to. Return: Position in functions array or COMMAND_NOT_FOUND. Notes: - -*----------------------------------------------------------------------------*/ static Command_FindFunc(data[], value, length, &ret = 0) { new leaf, pointer; while ((pointer = Bintree_FindValue(g_sSearchTree, value, leaf, ret)) != BINTREE_NOT_FOUND) { if (!strcmp(g_sCommands[pointer][E_COMMANDS_FUNCTION], data, true, length)) return pointer; } return COMMAND_NOT_FOUND; } /*----------------------------------------------------------------------------*- Function: Command_FindFunc Params: data[] - Altname to find. value - Hash of altname. length - Length of name. &ret - Variable to store leaf to. Return: Position in functions array or COMMAND_NOT_FOUND. Notes: - -*----------------------------------------------------------------------------*/ static Command_FindAlt(data[], value, length, &ret = 0) { new leaf, pointer; while ((pointer = Bintree_FindValue(g_sAltTree, value, leaf, ret)) != BINTREE_NOT_FOUND) { if (!strcmp(g_sCommands[pointer][E_COMMANDS_ALTNAME], data, true, length)) return pointer; } return COMMAND_NOT_FOUND; } /*----------------------------------------------------------------------------*- Function: Command_Find Params: data[] - string from OnPlayerCommandText to search for. &index - Variable to store end of Return: Command index or COMMAND_NOT_FOUND. Notes: - -*----------------------------------------------------------------------------*/ static Command_Find(data[], &index) { new pointer, start = index, length = index, value = Command_Hash(data, index); length = index - length; if (Command_UsingAltNames() && (pointer = Command_FindAlt(data[start], value, length)) != COMMAND_NOT_FOUND) return pointer; if ((pointer = Command_FindFunc(data[start], value, length)) != COMMAND_NOT_FOUND && (!Command_UsingAltNames() || !g_sCommands[pointer][E_COMMANDS_ALTNAME][0])) return pointer; return COMMAND_NOT_FOUND; } /*----------------------------------------------------------------------------*- Function: Command_Remove Params: funcname[] - Function to remove Return: - Notes: Don't use before the bintree has been sorted or the function will fail. -*----------------------------------------------------------------------------*/ public Command_Remove(funcname[]) { new pointer, leaf; if ((pointer = Command_FindFunc(funcname, Command_Hash(funcname), strlen(funcname), leaf)) == COMMAND_NOT_FOUND) return; g_sCommandIndex = Bintree_Delete(g_sSearchTree, leaf, g_sCommandIndex); Bintree_UpdatePointers(g_sSearchTree, pointer, g_sCommandIndex); new pointer2, altname[MAX_COMMAND_LENGTH]; strcpy(altname, g_sCommands[pointer][E_COMMANDS_ALTNAME]); while ((pointer2 = pointer + 1) <= g_sCommandIndex) { strcpy(g_sCommands[pointer][E_COMMANDS_FUNCTION], g_sCommands[pointer2][E_COMMANDS_FUNCTION]); strcpy(g_sCommands[pointer][E_COMMANDS_ALTNAME], g_sCommands[pointer2][E_COMMANDS_ALTNAME]); strcpy(g_sCommands[pointer][E_COMMANDS_FUNCNAME], g_sCommands[pointer2][E_COMMANDS_FUNCNAME]); pointer++; } if (altname[0]) { if ((pointer = Command_FindAlt(altname, Command_Hash(altname), strlen(altname), leaf)) == COMMAND_NOT_FOUND) return; g_sAltCount = Bintree_Delete(g_sAltTree, leaf, g_sAltCount); Bintree_UpdatePointers(g_sAltTree, pointer, g_sAltCount); } } /*----------------------------------------------------------------------------*- Function: Command_Prefix Params: cmdtext[] - Text from OnPlayerCommandText. &index - Variable to store end of prefix to. Return: true/false. Notes: Checks the player has entered the required command prefix with optional trailing space. -*----------------------------------------------------------------------------*/ static Command_Prefix(cmdtext[], &index) { if (Command_UsingPrefix()) { if (strcmp(cmdtext[index], g_sPrefix, true, g_sPrefixLength)) return 0; index += g_sPrefixLength; if (Command_UsingSpace()) { new ret; while (cmdtext[index] && cmdtext[index] == ' ') { ret = 1; index++; } return ret; } } return 1; } /*----------------------------------------------------------------------------*- Function: Command_Process Params: playerid - Player who entered the command. cmdtext[] - Text entered. help - Called from the help commmand or OnPlayerCommandText. Return: true - success or hidden fail, false - fail. Notes: - -*----------------------------------------------------------------------------*/ Command_Process(playerid, cmdtext[], help = 0) { if (!IsPlayerConnected(playerid)) return Command_DisconnectReturn(); if (!help && Command_UsingShortCuts() && (!cmdtext[2] || cmdtext[2] == ' ')) { new cmdname[MAX_COMMAND_LENGTH]; Player_FindShortCut(playerid, cmdtext[1], cmdname); if (cmdname[0]) { return CallRemoteFunction("OnPlayerCommandText", "is", playerid, strcat2(cmdname, cmdtext[2])); } } new index = 1, command = Command_Find(cmdtext, index); if (!help && !Command_Prefix(cmdtext, index)) return 0; if (command == COMMAND_NOT_FOUND) return 0; if (Command_IsCleared(playerid, command)) { if (index < strlen(cmdtext)) return Command_CallFunction(command, playerid, cmdtext[index], help); return Command_CallFunction(command, playerid, NULL, help); } if (Command_DeniedMessage()) SendClientMessage2(playerid, COLOR_WARN, "TEXT_MAIN_BLEVEL"); return Command_DeniedReturn(); } /*----------------------------------------------------------------------------*- Function: Command_Hash Params: command[] - Command text to hash. &index - Start point and variable to store end point to. Return: hash value. Notes: Based on the bernstein function but only hashes one word, space delimited. -*----------------------------------------------------------------------------*/ static Command_Hash(command[], &index = 0) { new hash = -1, j; while ((j = command[index++]) && j != ' ') hash = hash * 33 + chrtolower(j); while (command[index] == ' ') index++; index--; return hash; }