dvander@1945: dvander@2835: #define NAME_LENGTH 64 dvander@1945: #define CMD_LENGTH 255 dvander@1945: dvander@1945: #define ARRAY_STRING_LENGTH 32 dvander@1945: dvander@1945: enum GroupCommands dvander@1945: { dvander@1945: Handle:groupListName, dvander@1945: Handle:groupListCommand dvander@1945: }; dvander@1945: dvander@1945: new g_groupList[GroupCommands]; dvander@1945: new g_groupCount; dvander@1945: dvander@1945: new Handle:g_configParser = INVALID_HANDLE; dvander@1945: dvander@1945: enum Places dvander@1945: { dvander@1945: Place_Category, dvander@1945: Place_Item, dvander@1945: Place_ReplaceNum dvander@1945: }; dvander@1945: dvander@1945: new String:g_command[MAXPLAYERS+1][CMD_LENGTH]; dvander@1945: new g_currentPlace[MAXPLAYERS+1][Places]; dvander@1945: dvander@1945: /** dvander@1945: * What to put in the 'info' menu field (for PlayerList and Player_Team menus only) dvander@1945: */ dvander@1945: enum PlayerMethod dvander@1945: { dvander@1945: ClientId, /** Client id number ( 1 - Maxplayers) */ dvander@1945: UserId, /** Client userid */ dvander@1945: Name, /** Client Name */ dvander@1945: SteamId, /** Client Steamid */ dvander@1945: IpAddress, /** Client's Ip Address */ dvander@1945: UserId2 /** Userid (not prefixed with #) */ dvander@1945: }; dvander@1945: pred@1953: enum ExecuteType pred@1953: { pred@1953: Execute_Player, pred@1953: Execute_Server pred@1953: } pred@1953: pred@1953: enum SubMenu_Type pred@1953: { pred@1953: SubMenu_Group, pred@1953: SubMenu_GroupPlayer, pred@1953: SubMenu_Player, pred@1953: SubMenu_MapCycle, pred@1953: SubMenu_List, pred@1953: SubMenu_OnOff pred@1953: } pred@1953: pred@1953: enum Item pred@1953: { pred@1953: String:Item_cmd[256], pred@1953: ExecuteType:Item_execute, pred@1953: Handle:Item_submenus pred@1953: } pred@1953: pred@1953: enum Submenu pred@1953: { pred@1953: SubMenu_Type:Submenu_type, pred@1953: String:Submenu_title[32], pred@1953: PlayerMethod:Submenu_method, pred@1953: Submenu_listcount, pred@1953: Handle:Submenu_listdata pred@1953: } pred@1953: pred@1953: new Handle:g_DataArray; pred@1953: dvander@1945: BuildDynamicMenu() dvander@1945: { pred@1953: new itemInput[Item]; pred@1953: g_DataArray = CreateArray(sizeof(itemInput)); dvander@1945: pred@1953: new String:executeBuffer[32]; pred@1953: pred@1953: new Handle:kvMenu; pred@1953: kvMenu = CreateKeyValues("Commands"); pred@2103: KvSetEscapeSequences(kvMenu, true); dvander@2099: dvander@1945: new String:file[256]; dvander@2099: dvander@2099: /* As a compatibility shim, we use the old file if it exists. */ dvander@1945: BuildPath(Path_SM, file, 255, "configs/dynamicmenu/menu.ini"); dvander@2099: if (FileExists(file)) dvander@2099: { dvander@2099: LogError("Warning! configs/dynamicmenu/menu.ini is now configs/adminmenu_custom.txt."); dvander@2099: LogError("Read the 1.0.2 release notes, as the dynamicmenu folder has been removed."); dvander@2099: } dvander@2099: else dvander@2099: { dvander@2099: BuildPath(Path_SM, file, 255, "configs/adminmenu_custom.txt"); dvander@2099: } dvander@2099: pred@1953: FileToKeyValues(kvMenu, file); dvander@1945: dvander@1945: new String:name[NAME_LENGTH]; dvander@1945: new String:buffer[NAME_LENGTH]; pred@1953: pred@1953: if (!KvGotoFirstSubKey(kvMenu)) dvander@1945: { dvander@1945: return; dvander@1945: } dvander@1945: dvander@1945: decl String:admin[30]; dvander@1945: dvander@1945: new TopMenuObject:categoryId; dvander@1945: dvander@1945: do dvander@1945: { pred@1953: KvGetSectionName(kvMenu, buffer, sizeof(buffer)); dvander@1945: pred@1953: KvGetString(kvMenu, "admin", admin, sizeof(admin),"sm_admin"); dvander@1945: dvander@1945: if ((categoryId =FindTopMenuCategory(hAdminMenu, buffer)) == INVALID_TOPMENUOBJECT) dvander@1945: { dvander@1945: categoryId = AddToTopMenu(hAdminMenu, dvander@1945: buffer, dvander@1945: TopMenuObject_Category, dvander@1945: DynamicMenuCategoryHandler, dvander@1945: INVALID_TOPMENUOBJECT, dvander@1945: admin, dvander@1945: ADMFLAG_GENERIC, dvander@1945: name); dvander@1945: dvander@1945: } dvander@2836: dvander@2836: decl String:category_name[NAME_LENGTH]; dvander@2836: strcopy(category_name, sizeof(category_name), buffer); dvander@1945: pred@1953: if (!KvGotoFirstSubKey(kvMenu)) dvander@1945: { dvander@1945: return; dvander@1945: } dvander@1945: dvander@1945: do dvander@1945: { pred@1953: KvGetSectionName(kvMenu, buffer, sizeof(buffer)); pred@1953: pred@1953: KvGetString(kvMenu, "admin", admin, sizeof(admin),""); dvander@1945: dvander@1945: if (admin[0] == '\0') dvander@1945: { dvander@1945: //No 'admin' keyvalue was found dvander@1945: //Use the first argument of the 'cmd' string instead dvander@1945: dvander@1945: decl String:temp[64]; pred@1953: KvGetString(kvMenu, "cmd", temp, sizeof(temp),""); dvander@1945: dvander@1945: BreakString(temp, admin, sizeof(admin)); dvander@1945: } pred@1953: pred@1953: pred@1953: KvGetString(kvMenu, "cmd", itemInput[Item_cmd], sizeof(itemInput[Item_cmd])); pred@1953: KvGetString(kvMenu, "execute", executeBuffer, sizeof(executeBuffer)); pred@1953: pred@1953: if (StrEqual(executeBuffer, "server")) dvander@1945: { pred@1953: itemInput[Item_execute] = Execute_Server; pred@1953: } pred@1953: else //assume player type execute pred@1953: { pred@1953: itemInput[Item_execute] = Execute_Player; dvander@1945: } pred@1953: pred@1953: /* iterate all submenus and load data into itemInput[Item_submenus] (adt array handle) */ pred@1953: pred@1953: new count = 1; pred@1953: decl String:countBuffer[10] = "1"; pred@1953: mr@2565: decl String:inputBuffer[48]; pred@1953: pred@1953: while (KvJumpToKey(kvMenu, countBuffer)) pred@1953: { pred@1953: new submenuInput[Submenu]; pred@1953: pred@1953: if (count == 1) pred@1953: { pred@1953: itemInput[Item_submenus] = CreateArray(sizeof(submenuInput)); pred@1953: } pred@1953: pred@1953: KvGetString(kvMenu, "type", inputBuffer, sizeof(inputBuffer)); pred@1953: pred@1953: if (strncmp(inputBuffer,"group",5)==0) pred@1953: { pred@1953: if (StrContains(inputBuffer, "player") != -1) pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_GroupPlayer; pred@1953: } pred@1953: else pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_Group; pred@1953: } pred@1953: } pred@1953: else if (StrEqual(inputBuffer,"mapcycle")) pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_MapCycle; pred@1953: pred@1953: KvGetString(kvMenu, "path", inputBuffer, sizeof(inputBuffer),"mapcycle.txt"); pred@1953: pred@1953: submenuInput[Submenu_listdata] = CreateDataPack(); pred@1953: WritePackString(submenuInput[Submenu_listdata], inputBuffer); pred@1953: ResetPack(submenuInput[Submenu_listdata]); pred@1953: } pred@1953: else if (StrContains(inputBuffer, "player") != -1) pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_Player; pred@1953: } pred@1953: else if (StrEqual(inputBuffer,"onoff")) pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_OnOff; pred@1953: } pred@1953: else //assume 'list' type pred@1953: { pred@1953: submenuInput[Submenu_type] = SubMenu_List; pred@1953: pred@1953: submenuInput[Submenu_listdata] = CreateDataPack(); pred@1953: pred@1953: new String:temp[6]; pred@1953: new String:value[64]; pred@1953: new String:text[64]; mr@2776: new String:subadm[30]; // same as "admin", cf. line 110 pred@1953: new i=1; pred@1953: new bool:more = true; pred@1953: pred@1953: new listcount = 0; pred@1953: pred@1953: do pred@1953: { pred@1953: Format(temp,3,"%i",i); pred@1953: KvGetString(kvMenu, temp, value, sizeof(value), ""); pred@1953: pred@1953: Format(temp,5,"%i.",i); pred@1953: KvGetString(kvMenu, temp, text, sizeof(text), value); pred@1953: pred@1953: Format(temp,5,"%i*",i); mr@2776: KvGetString(kvMenu, temp, subadm, sizeof(subadm),""); pred@1953: pred@1953: if (value[0]=='\0') pred@1953: { pred@1953: more = false; pred@1953: } pred@1953: else pred@1953: { pred@1953: listcount++; pred@1953: WritePackString(submenuInput[Submenu_listdata], value); pred@1953: WritePackString(submenuInput[Submenu_listdata], text); mr@2776: WritePackString(submenuInput[Submenu_listdata], subadm); pred@1953: } pred@1953: pred@1953: i++; pred@1953: pred@1953: } while (more); pred@1953: pred@1953: ResetPack(submenuInput[Submenu_listdata]); pred@1953: submenuInput[Submenu_listcount] = listcount; pred@1953: } pred@1953: pred@1953: if ((submenuInput[Submenu_type] == SubMenu_Player) || (submenuInput[Submenu_type] == SubMenu_GroupPlayer)) pred@1953: { pred@1953: KvGetString(kvMenu, "method", inputBuffer, sizeof(inputBuffer)); pred@1953: pred@1953: if (StrEqual(inputBuffer, "clientid")) pred@1953: { pred@1953: submenuInput[Submenu_method] = ClientId; pred@1953: } pred@1953: else if (StrEqual(inputBuffer, "steamid")) pred@1953: { pred@1953: submenuInput[Submenu_method] = SteamId; pred@1953: } pred@1953: else if (StrEqual(inputBuffer, "userid2")) pred@1953: { pred@1953: submenuInput[Submenu_method] = UserId2; pred@1953: } pred@1953: else if (StrEqual(inputBuffer, "userid")) pred@1953: { pred@1953: submenuInput[Submenu_method] = UserId; pred@1953: } pred@1953: else if (StrEqual(inputBuffer, "ip")) pred@1953: { pred@1953: submenuInput[Submenu_method] = IpAddress; pred@1953: } pred@1953: else pred@1953: { pred@1953: submenuInput[Submenu_method] = Name; pred@2177: } pred@1953: } pred@1953: pred@1953: KvGetString(kvMenu, "title", inputBuffer, sizeof(inputBuffer)); pred@1953: strcopy(submenuInput[Submenu_title], sizeof(submenuInput[Submenu_title]), inputBuffer); pred@1953: pred@1953: count++; pred@1953: Format(countBuffer, sizeof(countBuffer), "%i", count); pred@1953: pred@1953: PushArrayArray(itemInput[Item_submenus], submenuInput[0]); pred@1953: pred@1953: KvGoBack(kvMenu); pred@1953: } pred@1953: pred@1953: /* Save this entire item into the global items array and add it to the menu */ pred@1953: pred@1953: new location = PushArrayArray(g_DataArray, itemInput[0]); dvander@1945: pred@1953: decl String:locString[10]; pred@1953: IntToString(location, locString, sizeof(locString)); pred@1953: dvander@2836: if (AddToTopMenu(hAdminMenu, dvander@2836: buffer, dvander@2836: TopMenuObject_Item, dvander@2836: DynamicMenuItemHandler, dvander@2836: categoryId, dvander@2836: admin, dvander@2836: ADMFLAG_GENERIC, dvander@2836: locString) == INVALID_TOPMENUOBJECT) dvander@2836: { dvander@2836: LogError("Duplicate command name \"%s\" in adminmenu_custom.txt category \"%s\"", buffer, category_name); dvander@2836: } dvander@1945: pred@1953: } while (KvGotoNextKey(kvMenu)); dvander@1945: pred@1953: KvGoBack(kvMenu); dvander@1945: pred@1953: } while (KvGotoNextKey(kvMenu)); dvander@1945: pred@1953: CloseHandle(kvMenu); dvander@1945: } dvander@1945: dvander@1945: ParseConfigs() dvander@1945: { dvander@1945: if (g_configParser == INVALID_HANDLE) dvander@1945: { dvander@1945: g_configParser = SMC_CreateParser(); dvander@1945: } dvander@1945: dvander@1945: SMC_SetReaders(g_configParser, NewSection, KeyValue, EndSection); dvander@1945: dvander@1945: if (g_groupList[groupListName] != INVALID_HANDLE) dvander@1945: { dvander@1945: CloseHandle(g_groupList[groupListName]); dvander@1945: } dvander@1945: dvander@1945: if (g_groupList[groupListCommand] != INVALID_HANDLE) dvander@1945: { dvander@1945: CloseHandle(g_groupList[groupListCommand]); dvander@1945: } dvander@1945: dvander@1945: g_groupList[groupListName] = CreateArray(ARRAY_STRING_LENGTH); dvander@1945: g_groupList[groupListCommand] = CreateArray(ARRAY_STRING_LENGTH); dvander@1945: dvander@1945: decl String:configPath[256]; dvander@1945: BuildPath(Path_SM, configPath, sizeof(configPath), "configs/dynamicmenu/adminmenu_grouping.txt"); dvander@2099: if (FileExists(configPath)) dvander@2099: { dvander@2099: LogError("Warning! configs/dynamicmenu/adminmenu_grouping.txt is now configs/adminmenu_grouping.txt."); dvander@2099: LogError("Read the 1.0.2 release notes, as the dynamicmenu folder has been removed."); dvander@2099: } dvander@2099: else dvander@2099: { dvander@2099: BuildPath(Path_SM, configPath, sizeof(configPath), "configs/adminmenu_grouping.txt"); dvander@2099: } dvander@1945: dvander@1945: if (!FileExists(configPath)) dvander@1945: { ds@1984: LogError("Unable to locate admin menu groups file: %s", configPath); dvander@1945: dvander@1945: return; dvander@1945: } dvander@1945: dvander@1945: new line; dvander@1945: new SMCError:err = SMC_ParseFile(g_configParser, configPath, line); dvander@1945: if (err != SMCError_Okay) dvander@1945: { dvander@1945: decl String:error[256]; dvander@1945: SMC_GetErrorString(err, error, sizeof(error)); dvander@1945: LogError("Could not parse file (line %d, file \"%s\"):", line, configPath); dvander@1945: LogError("Parser encountered error: %s", error); dvander@1945: } dvander@1945: dvander@1945: return; dvander@1945: } dvander@1945: dvander@1945: public SMCResult:NewSection(Handle:smc, const String:name[], bool:opt_quotes) dvander@1945: { dvander@1945: dvander@1945: } dvander@1945: dvander@1945: public SMCResult:KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) dvander@1945: { dvander@1945: PushArrayString(g_groupList[groupListName], key); dvander@1945: PushArrayString(g_groupList[groupListCommand], value); dvander@1945: } dvander@1945: dvander@1945: public SMCResult:EndSection(Handle:smc) dvander@1945: { dvander@1945: g_groupCount = GetArraySize(g_groupList[groupListName]); dvander@1945: } dvander@1945: dvander@1945: public DynamicMenuCategoryHandler(Handle:topmenu, dvander@1945: TopMenuAction:action, dvander@1945: TopMenuObject:object_id, dvander@1945: param, dvander@1945: String:buffer[], dvander@1945: maxlength) dvander@1945: { dvander@1945: if ((action == TopMenuAction_DisplayTitle) || (action == TopMenuAction_DisplayOption)) dvander@1945: { dvander@1945: GetTopMenuObjName(topmenu, object_id, buffer, maxlength); dvander@1945: } dvander@1945: } dvander@1945: dvander@1945: public DynamicMenuItemHandler(Handle:topmenu, dvander@1945: TopMenuAction:action, dvander@1945: TopMenuObject:object_id, dvander@1945: param, dvander@1945: String:buffer[], dvander@1945: maxlength) dvander@1945: { dvander@1945: if (action == TopMenuAction_DisplayOption) dvander@1945: { dvander@1945: GetTopMenuObjName(topmenu, object_id, buffer, maxlength); dvander@1945: } dvander@1945: else if (action == TopMenuAction_SelectOption) dvander@1945: { pred@1953: new String:locString[10]; pred@1953: GetTopMenuInfoString(topmenu, object_id, locString, sizeof(locString)); dvander@1945: pred@1953: new location = StringToInt(locString); dvander@1945: pred@1953: new output[Item]; pred@1953: GetArrayArray(g_DataArray, location, output[0]); dvander@1945: pred@1953: strcopy(g_command[param], sizeof(g_command[]), output[Item_cmd]); dvander@1945: pred@1953: g_currentPlace[param][Place_Item] = location; pred@2078: g_currentPlace[param][Place_ReplaceNum] = 1; dvander@1945: dvander@1945: ParamCheck(param); dvander@1945: } dvander@1945: } dvander@1945: dvander@1945: public ParamCheck(client) dvander@1945: { dvander@1945: new String:buffer[6]; dvander@1945: new String:buffer2[6]; dvander@1945: pred@1953: new outputItem[Item]; pred@1953: new outputSubmenu[Submenu]; pred@1953: pred@1953: GetArrayArray(g_DataArray, g_currentPlace[client][Place_Item], outputItem[0]); dvander@1945: dvander@1945: if (g_currentPlace[client][Place_ReplaceNum] < 1) dvander@1945: { dvander@1945: g_currentPlace[client][Place_ReplaceNum] = 1; dvander@1945: } dvander@1945: dvander@1945: Format(buffer, 5, "#%i", g_currentPlace[client][Place_ReplaceNum]); dvander@1945: Format(buffer2, 5, "@%i", g_currentPlace[client][Place_ReplaceNum]); dvander@1945: dvander@1945: if (StrContains(g_command[client], buffer) != -1 || StrContains(g_command[client], buffer2) != -1) dvander@1945: { pred@1953: GetArrayArray(outputItem[Item_submenus], g_currentPlace[client][Place_ReplaceNum] - 1, outputSubmenu[0]); dvander@1945: dvander@1945: new Handle:itemMenu = CreateMenu(Menu_Selection); pred@2079: SetMenuExitBackButton(itemMenu, true); dvander@1945: pred@1953: if ((outputSubmenu[Submenu_type] == SubMenu_Group) || (outputSubmenu[Submenu_type] == SubMenu_GroupPlayer)) dvander@1945: { dvander@1945: decl String:nameBuffer[ARRAY_STRING_LENGTH]; dvander@1945: decl String:commandBuffer[ARRAY_STRING_LENGTH]; dvander@1945: dvander@1945: for (new i = 0; i= maxlen) pred@2177: { pred@2177: /* Null terminate for safety */ pred@2177: output[maxlen-1] = 0; pred@2177: return false; pred@2177: } pred@2177: pred@2177: if (FindCharInString(quotechars, input[i]) != -1 || input[i] == '\\') pred@2177: { pred@2177: /* This char needs escaping */ pred@2177: output[count] = '\\'; pred@2177: count++; pred@2177: pred@2177: if (count >= maxlen) pred@2177: { pred@2177: /* Null terminate for safety */ pred@2177: output[maxlen-1] = 0; pred@2177: return false; pred@2177: } pred@2177: } pred@2177: } pred@2177: pred@2177: output[count] = 0; pred@2177: pred@2177: return true; pred@2177: } pred@2177: pred@2177: stock bool:UnQuoteString(String:input[], String:output[], maxlen, String:quotechars[]) pred@2177: { pred@2177: new count = 1; pred@2177: new len = strlen(input); pred@2177: pred@2177: output[0] = input[0]; pred@2177: pred@2177: for (new i=1; i= maxlen) pred@2177: { pred@2177: /* Null terminate for safety */ pred@2177: output[maxlen-1] = 0; pred@2177: return false; pred@2177: } pred@2177: } pred@2177: pred@2177: output[count] = 0; pred@2177: pred@2177: return true; mr@2565: }