/*----------------------------------------------------------------------------*- ============================= 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 -1enum 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_CommandParams: -Return: -Notes: --*----------------------------------------------------------------------------*/#define Command_Command()/*----------------------------------------------------------------------------*-Function: Command_IsClearedParams: playerid - Player to check for. command - Command to check for.Return: TRUENotes: Not implemented yet.-*----------------------------------------------------------------------------*/#define Command_IsCleared(%1,%2) \ TRUE/*----------------------------------------------------------------------------*-Function: Command_DisconnectReturnParams: -Return: e_COMM_FLAG_DISCONNECTNotes: --*----------------------------------------------------------------------------*/#define Command_DisconnectReturn() \ (g_sCommandFlags & e_COMM_FLAG_DISCONNECT)/*----------------------------------------------------------------------------*-Function: Command_SetDisconnectReturnParams: 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_UsingShortCutsParams: -Return: e_COMM_FLAG_USE_SHORTCUTSNotes: --*----------------------------------------------------------------------------*/#define Command_UsingShortCuts() \ (g_sCommandFlags & e_COMM_FLAG_USE_SHORTCUTS)/*----------------------------------------------------------------------------*-Function: Command_UseShortCutsParams: 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_DeniedReturnParams: -Return: e_COMM_FLAG_PERMISSION_RETNotes: --*----------------------------------------------------------------------------*/#define Command_DeniedReturn() \ (g_sCommandFlags & e_COMM_FLAG_PERMISSION_RET)/*----------------------------------------------------------------------------*-Function: Command_SetDeniedReturnParams: 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_DeniedMessageParams: -Return: e_COMM_FLAG_PERMISSION_WARNNotes: --*----------------------------------------------------------------------------*/#define Command_DeniedMessage() \ (g_sCommandFlags & e_COMM_FLAG_PERMISSION_WARN)/*----------------------------------------------------------------------------*-Function: Command_UseDeniedMessageParams: 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_IsSortedParams: -Return: e_COMM_FLAG_SORTEDNotes: --*----------------------------------------------------------------------------*/#define Command_IsSorted() \ (g_sCommandFlags & e_COMM_FLAG_SORTED)/*----------------------------------------------------------------------------*-Function: Command_SetSortedParams: 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_UsingAltNamesParams: -Return: e_COMM_FLAG_USE_ALTNAMESNotes: --*----------------------------------------------------------------------------*/#define Command_UsingAltNames() \ (g_sCommandFlags & e_COMM_FLAG_USE_ALTNAMES)/*----------------------------------------------------------------------------*-Function: Command_UseAltNamesParams: 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_UsingPrefixParams: -Return: e_COMM_FLAG_USE_PREFIXNotes: --*----------------------------------------------------------------------------*/#define Command_UsingPrefix() \ (g_sCommandFlags & e_COMM_FLAG_USE_PREFIX)/*----------------------------------------------------------------------------*-Function: Command_UsePrefixParams: 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_UsingSpaceParams: -Return: e_COMM_FLAG_USE_SPACENotes: --*----------------------------------------------------------------------------*/#define Command_UsingSpace() \ (g_sCommandFlags & e_COMM_FLAG_USE_SPACE)/*----------------------------------------------------------------------------*-Function: Command_UseSpaceParams: 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_CallFunctionParams: command - Function index to call. playerid - playeris parameter. params[] - params[] parameter. help - help parameter.Return: CallRemoteFunctionNotes: --*----------------------------------------------------------------------------*/#define Command_CallFunction(%1,%2,%3,%4) \ CallRemoteFunction(g_sCommands[(%1)][E_COMMANDS_FUNCNAME], "isi", (%2), (%3), (%4))/*----------------------------------------------------------------------------*-Function: Command_AddParams: 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_ParseParams: -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_FindSlowParams: 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_SetAltNameParams: 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_SetPrefixParams: prefix[] - The new prefix.Return: -Notes: --*----------------------------------------------------------------------------*/stock Command_SetPrefix(prefix[]){ strcpy(g_sPrefix, prefix); g_sPrefixLength = strlen(prefix);}/*----------------------------------------------------------------------------*-Function: Command_FindFuncParams: 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_FindFuncParams: 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_FindParams: 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_RemoveParams: funcname[] - Function to removeReturn: -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_PrefixParams: 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_ProcessParams: 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_HashParams: 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;}