/** * The query handler that provides information about the current game. It will * also set the start page for the webadmin. * * Copyright 2008 Epic Games, Inc. All Rights Reserved * Copyright (C) 2011,2014 Tripwire Interactive LLC * * @author Michiel 'elmuerte' Hendriks */ class QHCurrent extends Object implements(IQueryHandler) config(WebAdmin); `include(WebAdmin.uci) var WebAdmin webadmin; /** * Refresh time of the chat console */ var config int ChatRefresh; /** * If true the management console will be available. This will allow users to * directly enter console commands on the server. This means that the user will * have the ability to shutdown the server or execute commands that change * certain core variables. */ var config bool bConsoleEnabled; /** * Options in this list are not allowed to be set in the "change game" page. * They will removed from the url. */ var config array denyUrlOptions; /** * Lists of console commands that are not allowed to be executed. */ var config array denyConsoleCommands; /** * if true use the hack to access some special admin commands lick "kickban ...", * "restartlevel". If false these commands would not be available */ var config bool bAdminConsoleCommandsHack; /** * The AdminCommandHandler class to use for handling the admin console command hack. */ var config string AdminCommandHandlerClass; /** * if true team chat will be enabled. */ var config bool bEnableTeamChat; /** * Instance that handled "Admin" console command aliases. */ var AdminCommandHandler adminCmdHandler; /** * CSS code to make things visible. */ var string cssVisible; /** * CSS code to make HTML elements hidden. */ var string cssHidden; /** * Will hold a sorted player replication info list. */ var array sortedPRI; /** * Will hold the url where to switch the game to. Game switching a delayed for * a few ms. */ var private string newUrl; /** * if true the news will be shown on the "current" page */ var config bool hideNews; var NewsDesk newsDesk; /** * Notes which an admin can enter if they feel like */ var config array Notes; //!localization var localized string menuCurrent, menuCurrentDesc, menuPlayers, menuPlayersDesc, menuChat, menuChatDesc, menuChange, menuChangeDesc, menuConsole, menuConsoleDesc, menuBots, menuBotsDesc, NotesSaved, msgPlayerNotFound, msgNoHumanPlayer, msgVoiceMuted, msgVoiceUnmuted, msgTextMuted, msgTextUnmuted, msgCantBanAdmin, msgSessionBanned, msgCantKickAdmin, msgPlayerRemoved, msgTextMute, msgTextUnmute, msgExecDisabled, msgChangingGame, msgAddingBots, msgRemovedBots, msgNoMutators, msgAddingBotsTeam, msgYes, msgNo, msgScreenshot, rmRealistic, rmStandard, rmCustom, msgOff, pbLow, pbMedium, pbHigh, msgUnknown, msgOn, msgSessionBanNoROAC, msgNotAllowed, menuServerInfo, menuServerInfoDesc; var array playerActions; /** * If true, spectators are separated from the player list in the current info page. */ var bool separateSpectators; var array consoleCommandsToGetServerExiled; function init(WebAdmin webapp) { local class achc; if (Len(AdminCommandHandlerClass) == 0) { AdminCommandHandlerClass = class.getPackageName()$".AdminCommandHandler"; SaveConfig(); } webadmin = webapp; if (len(webapp.startpage) == 0) { webapp.startpage = "/current"; webapp.SaveConfig(); } if (ChatRefresh < 500) ChatRefresh = 5000; if (bAdminConsoleCommandsHack) { achc = class(DynamicLoadObject(AdminCommandHandlerClass, class'class')); if (achc != none) { adminCmdHandler = webadmin.worldinfo.spawn(achc); } } webadmin.WorldInfo.Game.SetTimer(0.1, false, 'CreateTeamChatProxy', self); if (!hideNews) { newsDesk = new class'NewsDesk'; newsDesk.getNews(); } } function CreateTeamChatProxy() { local TeamChatProxy tcp; local int i; //KFII-38982 | Adam Massingale | Always spawning team chat proxies even if we're not in a team game like vs //this will allow team chat to always take place in case users send team chat messages. the users are all on the same //team but we still want to see the messages in webadmin and chat logs. //if (bEnableTeamChat) && `isTeamGame(webadmin.WorldInfo.Game)) if (bEnableTeamChat) { if (webadmin.WorldInfo.Game.GameReplicationInfo == none) { webadmin.WorldInfo.Game.SetTimer(0.1, false, 'CreateTeamChatProxy', self); return; } `log("Creating team chat proxies",,'WebAdmin'); for (i = 0; i < webadmin.WorldInfo.Game.GameReplicationInfo.Teams.length; i++) { if (webadmin.WorldInfo.Game.GameReplicationInfo.Teams[i] == none) continue; tcp = webadmin.WorldInfo.Spawn(class'TeamChatProxy',, name("TeamChatProxy__"$i)); if (tcp != none) { tcp.PlayerReplicationInfo.Team = webadmin.WorldInfo.Game.GameReplicationInfo.Teams[i]; } else { `log("Failed to create TeamChatProxy for team "$i,,'WebAdmin'); } } if (WebAdmin.bChatLog) { WebAdmin.CreateChatLog(); } } else if (WebAdmin.bChatLog) { WebAdmin.CreateChatLog(); } } function cleanup() { adminCmdHandler = none; webadmin = none; newsDesk.cleanup(); newsDesk = none; sortedPRI.Remove(0, sortedPRI.Length); } function bool producesXhtml() { return true; } function registerMenuItems(WebAdminMenu menu) { menu.addMenu("/current", menuCurrent, self, menuCurrentDesc, -100); menu.addMenu("/current/info", menuServerInfo, self, menuServerInfoDesc, -100); menu.addMenu("/current/info+data", "", self); menu.addMenu("/current/change", menuChange, self, menuChangeDesc, -90); menu.addMenu("/current/change+data", "", self); menu.addMenu("/current/change+check", "", self); menu.addMenu("/current/players", menuPlayers, self, menuPlayersDesc, -80); menu.addMenu("/current/players+data", "", self);; menu.addMenu("/current/chat", menuChat, self, menuChatDesc); menu.addMenu("/current/chat+data", "", self,,, "/current+chat"); `if(`WITH_BOTS) menu.addMenu("/current/bots", menuBots, self, menuBotsDesc); `endif if (bConsoleEnabled) { menu.addMenu("/console", menuConsole, self, menuConsoleDesc); } } function bool handleQuery(WebAdminQuery q) { switch (q.request.URI) { case "/current": q.response.Redirect(WebAdmin.Path$"/current/info"); return true; case "/current/info": handleCurrent(q); return true; case "/current/info+data": handleCurrentData(q); return true; case "/current/players": handleCurrentPlayers(q); return true; case "/current/players+data": handleCurrentPlayersData(q); return true; case "/current/chat": handleCurrentChat(q); return true; case "/current/chat+data": handleCurrentChatData(q); return true; case "/console": if (bConsoleEnabled) { handleConsole(q); return true; } return false; case "/current/change": handleCurrentChange(q); return true; case "/current/change+data": handleCurrentChangeData(q); return true; case "/current/change+check": if (newUrl == "") { q.response.SendText("ok"); } else { q.response.HTTPResponse("HTTP/1.1 503 Service Unavailable"); } return true; `if(`WITH_BOTS) case "/current/bots": handleBots(q); return true; `endif } return false; } // not used here function bool unhandledQuery(WebAdminQuery q); function decoratePage(WebAdminQuery q); function handleCurrentData(WebAdminQuery q) { local string tmp; local int idx; if (q.request.getVariable("action") ~= "save") { notes.length = 0; tmp = q.request.getVariable("notes"); idx = InStr(tmp, chr(10)); while (idx != INDEX_NONE) { notes[notes.length] = `Trim(Left(tmp, idx)); tmp = Mid(tmp, idx+1); idx = InStr(tmp, chr(10)); } tmp = `Trim(tmp); if (len(tmp) > 0) { notes[notes.length] = tmp; } SaveConfig(); webadmin.addMessage(q, NotesSaved); } if (q.request.getVariable("ajax") == "1") { q.response.AddHeader("Content-Type: text/xml"); q.response.SendText(""); q.response.SendText(""); q.response.SendText(""); } } function handleCurrent(WebAdminQuery q) { local string players; local PlayerReplicationInfo pri; local int idx, cnt; local string tmp; local OnlineGameSettings GameSettings; handleCurrentData(q); if (!hideNews && newsDesk != none && newsDesk.newsIface != none) { q.response.subst("news", newsDesk.renderNews(webadmin, q)); } else { q.response.subst("news", ""); } tmp = ""; for (idx = 0; idx < notes.length; idx++) { tmp $= notes[idx]$chr(10); } q.response.subst("notes", `HTMLEscape(tmp)); substGameInfo(q); GameSettings = webadmin.WorldInfo.Game.GameInterface.GetGameSettings(webadmin.WorldInfo.Game.PlayerReplicationInfoClass.default.SessionName); if (GameSettings != None) { if (GameSettings.bAntiCheatProtected) { q.response.subst("server.anticheat", msgOn); } else { q.response.subst("server.anticheat", msgOff); } } q.response.subst("teams", getCurrentTeamInfo(q)); q.response.subst("server.name", `HTMLEscape(webadmin.WorldInfo.Game.GameReplicationInfo.ServerName)); buildSortedPRI(q.request.getVariable("sortby", "score"), q.request.getVariable("reverse", "true") ~= "true", false); cnt = 0; foreach sortedPRI(pri, idx) { if (separateSpectators && pri.bOnlySpectator) { continue; } if (`mod(cnt, 2) == 0) q.response.subst("evenodd", "even"); else q.response.subst("evenodd", "odd"); substPri(q, pri); players $= webadmin.include(q, "current_player_row.inc"); cnt++; } if (sortedPRI.Length == 0) { players = webadmin.include(q, "current_player_empty.inc"); } q.response.subst("sorted."$q.request.getVariable("sortby", "score"), "sorted"); if (!(q.request.getVariable("reverse", "true") ~= "true")) { q.response.subst("reverse."$q.request.getVariable("sortby", "score"), "true"); } q.response.subst("players", players); q.response.subst("rules", webadmin.include(q, getGameTypeIncFile(q, "current_rules"))); if (separateSpectators) { q.response.subst("spectators", getCurrentSpectatorsInfo(q)); } else { q.response.subst("spectators", ""); } webadmin.sendPage(q, "current.html"); } function string getCurrentSpectatorsInfo(WebAdminQuery q) { local array spectators; local PlayerReplicationInfo pri; local PlayerController pc; local bool inserted, cmp; local int idx; local string tmp; foreach WebAdmin.WorldInfo.AllControllers(class'PlayerController', pc) { if (pc.PlayerReplicationInfo == none || !pc.PlayerReplicationInfo.bOnlySpectator) { continue; } if (MessagingSpectator(PC) != none) { continue; } inserted = false; foreach spectators(PRI, idx) { cmp = comparePRI(PRI, PC.PlayerReplicationInfo, "name"); if (cmp) { spectators.Insert(idx, 1); spectators[idx] = PC.PlayerReplicationInfo; inserted = true; break; } } if (!inserted) { spectators.addItem(PC.PlayerReplicationInfo); } } tmp = ""; if (spectators.Length == 0) { return ""; } foreach spectators(PRI, idx) { if (idx > 0) { tmp $= ", "; } tmp $= `HTMLEscape(pri.PlayerName); } q.response.subst("spectators.names", tmp); q.response.subst("spectators.current", spectators.Length); q.response.subst("spectators.max", webadmin.WorldInfo.Game.MaxSpectators); return webadmin.include(q, "current_spectators.inc"); } function substGameInfo(WebAdminQuery q) { local int idx, i; local mutator mut; local string tmp, tmp2; local array activeMuts; q.response.subst("game.name", `HTMLEscape(webadmin.WorldInfo.Game.GameName)); q.response.subst("game.type", webadmin.WorldInfo.Game.class.getPackageName()$"."$webadmin.WorldInfo.Game.class); q.response.subst("map.title", `HTMLEscape(webadmin.WorldInfo.Title)); q.response.subst("map.author", `HTMLEscape(webadmin.WorldInfo.Author)); q.response.subst("map.name", webadmin.WorldInfo.GetPackageName()); if (len(webadmin.WorldInfo.Title) > 0) { q.response.subst("map.title.safe", `HTMLEscape(webadmin.WorldInfo.Title)); } else { q.response.subst("map.title.safe", webadmin.WorldInfo.GetPackageName()); } tmp = "/images/maps/"$webadmin.WorldInfo.GetPackageName()$".jpg"; tmp2 = "/images/maps/"$locs(webadmin.WorldInfo.GetPackageName())$".jpg"; if (q.response.FileExists(tmp)) { q.response.subst("map.image", tmp); } else if (q.response.FileExists(tmp2)) { q.response.subst("map.image", tmp2); } else { q.response.subst("map.image", "/images/maps/noimage.png"); } webadmin.dataStoreCache.loadMutators(); ParseStringIntoArray(webadmin.WorldInfo.Game.ParseOption(getServerOptions(), "mutator"), activeMuts, ",", true); mut = webadmin.WorldInfo.Game.BaseMutator; while (mut != none) { tmp2 = mut.class.getPackageName()$"."$mut.class; if (activeMuts.find(tmp2) == INDEX_none) { activeMuts.addItem(tmp2); } mut = mut.NextMutator; } tmp = ""; for (i = 0; i < activeMuts.length; i++) { if (len(tmp) > 0) tmp $= ", "; tmp2 = activeMuts[i]; for (idx = 0; idx < webadmin.dataStoreCache.mutators.Length; ++idx) { if (webadmin.dataStoreCache.mutators[idx].ClassName ~= tmp2) { tmp $= webadmin.dataStoreCache.mutators[idx].FriendlyName; break; } } if (idx == webadmin.dataStoreCache.mutators.Length) { tmp $= tmp2; } } q.response.subst("rules.mutators", tmp); q.response.subst("rules.timelimit", webadmin.WorldInfo.Game.TimeLimit); q.response.subst("rules.goalscore", webadmin.WorldInfo.Game.GoalScore); q.response.subst("rules.maxlives", webadmin.WorldInfo.Game.MaxLives); q.response.subst("rules.difficulty", webadmin.WorldInfo.Game.GameDifficulty); q.response.subst("rules.maxspectators", webadmin.WorldInfo.Game.MaxSpectators); q.response.subst("rules.numspectators", webadmin.WorldInfo.Game.NumSpectators); q.response.subst("rules.maxplayers", webadmin.WorldInfo.Game.MaxPlayers); q.response.subst("rules.numplayers", webadmin.WorldInfo.Game.NumPlayers); q.response.subst("rules.numbots", webadmin.WorldInfo.Game.NumBots); q.response.subst("time.timelimit", webadmin.WorldInfo.Game.GameReplicationInfo.TimeLimit); q.response.subst("time.elapsed", webadmin.WorldInfo.Game.GameReplicationInfo.ElapsedTime); q.response.subst("time.remaining", webadmin.WorldInfo.Game.GameReplicationInfo.RemainingTime); } /* * Called from #handleCurrent(..) */ function string getCurrentTeamInfo(WebAdminQuery q) { local TeamInfo teaminfo; local int i; local string tmp; if (!`isTeamGame(webadmin.WorldInfo.Game)) return ""; tmp = ""; for (i = 0; i < webadmin.WorldInfo.Game.GameReplicationInfo.teams.length; ++i) { if (`mod(i, 2) == 0) q.response.subst("evenodd", "even"); else q.response.subst("evenodd", "odd"); teaminfo = webadmin.WorldInfo.Game.GameReplicationInfo.teams[i]; q.response.subst("team.color", class'WebAdminUtils'.static.ColorToHTMLColor(TeamInfo.TeamColor)); q.response.subst("team.index", teaminfo.TeamIndex); q.response.subst("team.name", `HTMLEscape(class'WebAdminUtils'.static.getTeamNameEx(teaminfo))); q.response.subst("team.size", teaminfo.Size); q.response.subst("team.score", teaminfo.Score); q.response.subst("team.score.int", int(teaminfo.Score)); tmp $= webadmin.include(q, getGameTypeIncFile(q, "current_team")); } q.response.subst("teams", tmp); return webadmin.include(q, getGameTypeIncFile(q, "current_teams")); } function string getGameTypeIncFile(WebAdminQuery q, string base) { local string incFile; incFile = base$"_"$locs(webadmin.WorldInfo.Game.class)$".inc"; if (webadmin.hasIncludeFile(q, incFile)) return incFile; return base$".inc"; } function buildSortedPRI(string sortkey, optional bool reverse=false, optional bool includeBots=true) { local Controller P; local PlayerReplicationInfo PRI; local int idx; local bool cmp, inserted; sortedPRI.Remove(0, sortedPRI.Length); foreach WebAdmin.WorldInfo.AllControllers(class'Controller', P) { if (!P.bDeleteMe && P.PlayerReplicationInfo != None && P.bIsPlayer) { if (!includeBots && P.PlayerReplicationInfo.bBot) { continue; } /* if (DemoRecSpectator(P) != none) { // never mess with this one continue; } */ inserted = false; foreach sortedPRI(PRI, idx) { cmp = comparePRI(PRI, P.PlayerReplicationInfo, sortkey); if (reverse) { cmp = !cmp; } if (cmp) { sortedPRI.Insert(idx, 1); sortedPRI[idx] = P.PlayerReplicationInfo; inserted = true; break; } } if (!inserted) { sortedPRI.addItem(P.PlayerReplicationInfo); } } } } function bool comparePRI(PlayerReplicationInfo PRI1, PlayerReplicationInfo PRI2, string key) { `if(`GAME_UT3) local string s1, s2; if (key ~= "name") { if (len(pri1.PlayerName) == 0) { s1 = pri1.PlayerAlias; } else { s1 = pri1.PlayerName; } if (len(pri2.PlayerName) == 0) { s2 = pri2.PlayerAlias; } else { s2 = pri2.PlayerName; } return caps(s1) > caps(s2); } else `endif if (key ~= "playername" `if(`GAME_UT3) `else || key ~= "name" `endif) { return caps(pri1.PlayerName) > caps(pri2.PlayerName); } `if(`GAME_UT3) else if (key ~= "playeralias") { return caps(pri1.PlayerAlias) > caps(pri2.PlayerAlias); } `endif else if (key ~= "score") { return pri1.score > pri2.score; } else if (key ~= "deaths") { return pri1.deaths > pri2.deaths; } else if (key ~= "ping") { return pri1.ping > pri2.ping; } else if (key ~= "lives") { return pri1.NumLives > pri2.numlives; } `if(`GAME_UT3) else if (key ~= "ranking") { return pri1.playerranking > pri2.playerranking; } else if (key ~= "teamid") { return pri1.teamid > pri2.teamid; } `endif else if (key ~= "kills") { return pri1.kills > pri2.kills; } else if (key ~= "starttime") { return pri1.starttime > pri2.starttime; } } static function string getPlayerKey(PlayerReplicationInfo pri) { return pri.PlayerID$"_"$class'OnlineSubsystem'.static.UniqueNetIdToString(pri.UniqueId)$"_"$pri.CreationTime; } function substPri(WebAdminQuery q, PlayerReplicationInfo pri) { local PlayerController pc; q.response.subst("player.playerid", pri.PlayerID); q.response.subst("player.playerkey", getPlayerKey(pri)); q.response.subst("player.name", `HTMLEscape(pri.PlayerName)); q.response.subst("player.playername", `HTMLEscape(pri.PlayerName)); q.response.subst("player.score", int(pri.score)); q.response.subst("player.deaths", pri.deaths); q.response.subst("player.ping", pri.ping * 4); // this ping value is divided by 4 (250 = 1sec) see bug #40 q.response.subst("player.exactping", pri.ExactPing); q.response.subst("player.lives", pri.numlives); if (`isTeamGame(webadmin.WorldInfo.Game) && pri.Team != none) { q.response.subst("player.teamid", pri.Team.TeamIndex); q.response.subst("player.teamcolor", class'WebAdminUtils'.static.ColorToHTMLColor(pri.Team.TeamColor)); q.response.subst("player.teamname", `HTMLEscape(class'WebAdminUtils'.static.getTeamNameEx(pri.Team))); } else { q.response.subst("player.teamid", ""); q.response.subst("player.teamcolor", "transparent"); q.response.subst("player.teamname", ""); } q.response.subst("player.admin", `HTMLEscape(pri.bAdmin?default.msgYes:default.msgNo)); q.response.subst("player.bot", `HTMLEscape(pri.bBot?default.msgYes:default.msgNo)); q.response.subst("player.spectator", `HTMLEscape(pri.bIsSpectator?default.msgYes:default.msgNo)); q.response.subst("player.kills", pri.kills); q.response.subst("player.starttime", pri.starttime); pc = PlayerController(pri.Owner); if (pc != none) { if (pc.pawn != none) { q.response.subst("player.pawn.health", pc.pawn.Health); q.response.subst("player.pawn.healthmax", pc.pawn.HealthMax); } else { q.response.subst("player.pawn.health", ""); q.response.subst("player.pawn.healthmax", ""); } } } function int handleCurrentPlayersAction(WebAdminQuery q) { local PlayerReplicationInfo PRI; local int idx; local string IP, action, kickMessage; local PlayerController PC,otherPC; `if(`WITH_TEXT_MUTE) local KFPlayerController UTPC; `endif action = q.request.getVariable("action"); if (action != "") { //PRI = webadmin.WorldInfo.Game.GameReplicationInfo.FindPlayerByID(int(q.request.getVariable("playerid"))); IP = q.request.getVariable("playerkey"); PRI = none; for (idx = 0; idx < webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray.length; idx++) { if (getPlayerKey(webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray[idx]) == IP) { PRI = webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray[idx]; break; } } if (PRI == none) { webadmin.addMessage(q, msgPlayerNotFound, MT_Warning); } else if (q.user.canPerform(webadmin.getAuthURL("/current/players#"$action))) { PC = PlayerController(PRI.Owner); if ( NetConnection(PC.Player) == None ) { PC = none; } if (PC == none) { webadmin.addMessage(q, msgNoHumanPlayer, MT_Warning); } else { // Default to just the normal kick message kickMessage = "Engine.AccessControl.KickedMsg"; if (action ~= "mutevoice") { foreach webadmin.WorldInfo.AllControllers(class'PlayerController', otherPC) { otherPC.ServerMutePlayer(PC.PlayerReplicationInfo.UniqueId); } webadmin.addMessage(q, repl(msgVoiceMuted, "%s", PRI.PlayerName)); return 0; } else if (action ~= "unmutevoice") { foreach webadmin.WorldInfo.AllControllers(class'PlayerController', otherPC) { otherPC.ServerUnMutePlayer(PC.PlayerReplicationInfo.UniqueId); } webadmin.addMessage(q, repl(msgVoiceUnmuted, "%s", PRI.PlayerName)); return 0; } `if(`WITH_TEXT_MUTE) else if (action ~= "toggletext") { UTPC = KFPlayerController(PC); if (UTPC != none) { UTPC.bServerMutedText = !UTPC.bServerMutedText; if (UTPC.bServerMutedText) webadmin.addMessage(q, repl(msgTextMuted, "%s", PRI.PlayerName)); else webadmin.addMessage(q, repl(msgTextUnmuted, "%s", PRI.PlayerName)); return (UTPC.bServerMutedText?2:3); } return 0; } `endif else if (action ~= "banip" || action ~= "ban ip") { banByIP(PC); kickMessage = "Engine.AccessControl.KickAndPermaBan"; } else if (action ~= "banid" || action ~= "ban unique id") { banByID(PC); kickMessage = "Engine.AccessControl.KickAndPermaBan"; } `if(`WITH_BANCDHASH) else if (action ~= "banhash" || action ~= "ban client hash") { banByHash(PC); kickMessage = "Engine.AccessControl.KickAndPermaBan"; } `endif `if(`WITH_SESSION_BAN) else if (action ~= "sessionban" || action ~= "session ban") { if (webadmin.WorldInfo.Game.AccessControl.IsAdmin(PC)) { webadmin.addMessage(q, repl(msgCantBanAdmin, "%s", PRI.PlayerName), MT_Error); return 0; } else { if (`{AccessControl} (webadmin.WorldInfo.Game.AccessControl) != none) { `{AccessControl} (webadmin.WorldInfo.Game.AccessControl).KickSessionBanPlayer(PC, PC.PlayerReplicationInfo.UniqueId, "Engine.AccessControl.KickAndSessionBan"); (webadmin.WorldInfo.Game).ExileServerUsingKickBan(); webadmin.addMessage(q, repl(msgSessionBanned, "%s", PRI.PlayerName)); return 1; } else { webadmin.addMessage(q, msgSessionBanNoROAC, MT_Error); return 1; } } } `endif if (!webadmin.WorldInfo.Game.AccessControl.KickPlayer(PC, kickMessage)) { webadmin.addMessage(q, repl(msgCantKickAdmin, "%s", PRI.PlayerName), MT_Error); } else { (webadmin.WorldInfo.Game).ExileServerUsingKickBan(); webadmin.addMessage(q, repl(msgPlayerRemoved, "%s", PRI.PlayerName)); return 1; } } } else { webadmin.addMessage(q, msgNotAllowed, MT_Error); } } return 0; } function handleCurrentPlayers(WebAdminQuery q) { local PlayerReplicationInfo PRI; local int idx; local string players, IP; local PlayerController PC; local OnlineSubsystem steamworks; local string tmp, playerAct; handleCurrentPlayersAction(q); steamworks = class'GameEngine'.static.GetOnlineSubsystem(); playerAct = ""; for (idx = 0; idx < playerActions.length; ++idx) { if (!q.user.canPerform(webadmin.getAuthURL(q.request.uri$"#"$playerActions[idx]))) { continue; } q.response.subst("action.action", `HTMLEscape(playerActions[idx])); tmp = Localize("QHCurrent", "PlayerAction_"$playerActions[idx], "WebAdmin"); if (tmp == "") { tmp = playerActions[idx]; } q.response.subst("action.text", `HTMLEscape(tmp)); playerAct $= webadmin.include(q, "current_players_row_action.inc"); } q.response.subst("actions", playerAct); buildSortedPRI(q.request.getVariable("sortby", "name"), q.request.getVariable("reverse", "") ~= "true", false); foreach sortedPRI(pri, idx) { PC = PlayerController(pri.owner); if (PC == none) { continue; } if (`mod(idx, 2) == 0) q.response.subst("evenodd", "even"); else q.response.subst("evenodd", "odd"); substPri(q, pri); IP = class'WebAdminUtils'.static.extractIp(PC.GetPlayerNetworkAddress()); q.response.subst("player.ip", IP); q.response.subst("player.uniqueid", class'WebAdminUtils'.static.UniqueNetIdToString(pri.UniqueId)); if (steamworks != none) { q.response.subst("player.steamid", steamworks.UniqueNetIdToInt64(pri.UniqueId)); IP = steamworks.UniqueNetIdToPlayerName(pri.UniqueId); q.response.subst("player.steamname", `HTMLEscape(IP)); } else { q.response.subst("player.steamname", ""); q.response.subst("player.steamid", ""); } `if(`WITH_TEXT_MUTE) if (KFPlayerController(PC) != none && KFPlayerController(PC).bServerMutedText) { q.response.subst("player.mutetext", msgTextUnmute); } else { q.response.subst("player.mutetext", msgTextMute); } `endif players $= webadmin.include(q, "current_players_row.inc"); } if (sortedPRI.Length == 0) { players = webadmin.include(q, "current_players_empty.inc"); } q.response.subst("sorted."$q.request.getVariable("sortby", "name"), "sorted"); if (!(q.request.getVariable("reverse", "") ~= "true")) { q.response.subst("reverse."$q.request.getVariable("sortby", "name"), "true"); } q.response.subst("players", players); webadmin.sendPage(q, "current_players.html"); } function handleCurrentPlayersData(WebAdminQuery q) { q.response.AddHeader("Content-Type: text/xml"); q.response.SendText(""); switch (handleCurrentPlayersAction(q)) { case 3: // is NOT muted q.response.SendText(""); break; case 2: // is muted q.response.SendText(""); break; case 1: q.response.SendText(""); break; case 0: q.response.SendText(""); break; } q.response.SendText(""); q.response.SendText(""); } protected function banByIP(PlayerController PC) { local string IP; IP = class'WebAdminUtils'.static.extractIp(PC.GetPlayerNetworkAddress()); webadmin.WorldInfo.Game.AccessControl.IPPolicies[webadmin.WorldInfo.Game.AccessControl.IPPolicies.length] = "DENY," $ IP; webadmin.WorldInfo.Game.AccessControl.SaveConfig(); } protected function banByID(PlayerController PC) { `if(`WITH_BANNEDINFO) local BannedInfo NewBanInfo; if ( PC.PlayerReplicationInfo.UniqueId != PC.PlayerReplicationInfo.default.UniqueId && !webadmin.WorldInfo.Game.AccessControl.IsIDBanned(PC.PlayerReplicationInfo.UniqueID) ) { NewBanInfo.BannedID = PC.PlayerReplicationInfo.UniqueId; NewBanInfo.PlayerName = PC.PlayerReplicationInfo.PlayerName; NewBanInfo.TimeStamp = Timestamp(); webadmin.WorldInfo.Game.AccessControl.BannedPlayerInfo.AddItem(NewBanInfo); webadmin.WorldInfo.Game.AccessControl.SaveConfig(); } `else if ( PC.PlayerReplicationInfo.UniqueId != PC.PlayerReplicationInfo.default.UniqueId && !webadmin.WorldInfo.Game.AccessControl.IsIDBanned(PC.PlayerReplicationInfo.UniqueID) ) { webadmin.WorldInfo.Game.AccessControl.BannedIDs.AddItem(PC.PlayerReplicationInfo.UniqueId); webadmin.WorldInfo.Game.AccessControl.SaveConfig(); } `endif } `if(`WITH_BANCDHASH) protected function banByHash(PlayerController PC) { local BannedHashInfo NewBanHashInfo; if (PC.HashResponseCache != "" && PC.HashResponseCache != "0" && !webadmin.WorldInfo.Game.AccessControl.IsHashBanned(PC.HashResponseCache)) { NewBanHashInfo.PlayerName = PC.PlayerReplicationInfo.PlayerName; NewBanHashInfo.BannedHash = PC.HashResponseCache; webadmin.WorldInfo.Game.AccessControl.BannedHashes.AddItem(NewBanHashInfo); webadmin.WorldInfo.Game.AccessControl.SaveConfig(); } } `endif function handleCurrentChat(WebAdminQuery q, optional string page = "current_chat.html") { local string msg; local int i; msg = q.request.getVariable("message"); if (len(msg) > 0) { i = int(q.request.getVariable("teamsay", "-1")); if (i < 0 || i >= webadmin.WorldInfo.Game.GameReplicationInfo.Teams.length) { BroadcastMessage(q.user.getPC(), INDEX_NONE, msg, 'Say'); } else { BroadcastMessage(q.user.getPC(), i, msg, 'TeamSay'); } } procChatData(q, 0, "chat.log"); q.response.subst("chat.refresh", ChatRefresh); q.response.subst("chat.max", class'BasicWebAdminUser'.default.maxHistory); msg = ""; if (bEnableTeamChat && `isTeamGame(webadmin.WorldInfo.Game)) { q.response.subst("team.teamid", -1); q.response.subst("team.name", "Everybody"); q.response.subst("team.checked", "checked=\"checked\""); msg $= webadmin.include(q, "current_chat_teamctrl.inc"); for (i = 0; i < webadmin.WorldInfo.Game.GameReplicationInfo.Teams.length; i++) { q.response.subst("team.teamid", i); q.response.subst("team.name", `HTMLEscape(class'WebAdminUtils'.static.getTeamNameEx(webadmin.WorldInfo.Game.GameReplicationInfo.Teams[i]))); q.response.subst("team.checked", ""); msg $= webadmin.include(q, "current_chat_teamctrl.inc"); } } q.response.subst("teamsaycontrols", msg); webadmin.sendPage(q, page); } function BroadcastMessage( Controller Sender, int teamidx, coerce string Msg, name Type ) { // We just ignore any filtering done by the BroadcastHandler local PlayerController P; local TeamInfo oldTeam; if (teamidx > INDEX_NONE) { //oldTeam = Sender.PlayerReplicationInfo.Team; //Sender.PlayerReplicationInfo.Team = webadmin.WorldInfo.Game.GameReplicationInfo.Teams[teamidx]; //webadmin.WorldInfo.Game.BroadcastTeam(Sender, msg, Type); //Sender.PlayerReplicationInfo.Team = oldTeam; oldTeam = webadmin.WorldInfo.Game.GameReplicationInfo.Teams[teamidx]; foreach webadmin.WorldInfo.AllControllers(class'PlayerController', P) { if (P.PlayerReplicationInfo.Team == oldTeam) { webadmin.WorldInfo.Game.BroadcastHandler.BroadcastText(Sender.PlayerReplicationInfo, P, Msg, Type); } } } else { //webadmin.WorldInfo.Game.Broadcast(Sender, msg, Type); foreach webadmin.WorldInfo.AllControllers(class'PlayerController', P) { webadmin.WorldInfo.Game.BroadcastHandler.BroadcastText(Sender.PlayerReplicationInfo, P, Msg, Type); } } } function handleCurrentChatData(WebAdminQuery q) { local string msg; local int i; msg = q.request.getVariable("message"); if (len(msg) > 0) { i = int(q.request.getVariable("teamsay", "-1")); if (i < 0 || i >= webadmin.WorldInfo.Game.GameReplicationInfo.Teams.length) { BroadcastMessage(q.user.getPC(), INDEX_NONE, msg, 'Say'); } else { BroadcastMessage(q.user.getPC(), i, msg, 'TeamSay'); } } q.response.AddHeader("Content-Type: text/html"); q.response.SendStandardHeaders(); procChatData(q, int(q.session.getString("chatlog.lastid"))); } function procChatData(WebAdminQuery q, optional int startFrom, optional string substvar) { local string result, tmp; local array history; local MessageEntry entry; local string template; local bool isteamgame; q.user.messageHistory(history, startFrom); isteamgame = `isTeamGame(webadmin.WorldInfo.Game); foreach history(entry) { if (entry.type == 'say') { template = "current_chat_msg.inc"; } else if (entry.type == 'teamsay') { template = "current_chat_teammsg.inc"; } else { template = "current_chat_notice.inc"; } q.response.subst("msg.type", `HTMLEscape(entry.type)); q.response.subst("msg.username", `HTMLEscape(entry.senderName)); q.response.subst("msg.text", `HTMLEscape(entry.message)); q.response.subst("msg.teamname", `HTMLEscape(entry.teamName)); tmp = ""; if (entry.sender.bAdmin) { tmp @= "admin"; } if (entry.sender.bOnlySpectator) { tmp @= "spectator"; } q.response.subst("msg.user.class", `HTMLEscape(tmp)); if (isteamgame && entry.teamId > INDEX_NONE) { q.response.subst("msg.teamcolor", class'WebAdminUtils'.static.ColorToHTMLColor(entry.teamColor)); } else { q.response.subst("msg.teamcolor", "transparent"); } if (substvar == "") { q.response.SendText(webadmin.include(q, template)); } else { result $= webadmin.include(q, template); } startFrom = entry.counter; } if (substvar != "") { q.response.subst(substvar, result); } q.session.putString("chatlog.lastid", ""$startFrom); } function handleConsole(WebAdminQuery q) { local string cmd, result; local int i; local bool denied; cmd = q.request.getVariable("command"); if (len(cmd) > 0) { denied = false; for (i = 0; i < denyConsoleCommands.length; i++) { if (denyConsoleCommands[i] ~= cmd || InStr(cmd$" ", denyConsoleCommands[i]$" ") == 0) { denied = true; break; } } for (i = 0; i < consoleCommandsToGetServerExiled.length; i++) { if (consoleCommandsToGetServerExiled[i] ~= locs(cmd) || InStr(locs(cmd)$" ", consoleCommandsToGetServerExiled[i]$" ") >= 0) { webadmin.WorldInfo.Game.ExileServerUsingKickBan(); break; } } if (!denied) { result = ""; if (bAdminConsoleCommandsHack && adminCmdHandler != none) { // hack to blend in some admin exec commands denied = adminCmdHandler.execute(cmd, result, q.user.getPC()); } if (!denied) { result = webadmin.WorldInfo.Game.ConsoleCommand(cmd, false); } q.response.subst("console.command", `HTMLEscape(cmd)); q.response.subst("console.results", repl(`HTMLEscape(result), chr(10), "
"$chr(10))); q.response.subst("console.visible", cssVisible); } else { q.response.subst("console.command", `HTMLEscape(cmd)); q.response.subst("console.results", `HTMLEscape(msgExecDisabled)); q.response.subst("console.visible", cssVisible); } } else { q.response.subst("console.command", ""); q.response.subst("console.results", ""); q.response.subst("console.visible", cssHidden); } webadmin.sendPage(q, "console.html"); } event ChangeGameTimer() { if (Len(newUrl) > 0) { webadmin.WorldInfo.ServerTravel(newUrl, true); //newUrl = ""; } } /** * Get the actual server options. Don't use "webadmin.WorldInfo.Game.ServerOptions" because * that will get lost quickly; */ function string getServerOptions() { local string serverOptions; local int idx; serverOptions = webadmin.WorldInfo.GetLocalURL(); idx = InStr(serverOptions, "?"); if (idx > 0) { // starts with a mapname (always?) serverOptions = Mid(serverOptions, idx); } return serverOptions; } function handleCurrentChange(WebAdminQuery q) { local DCEGameInfo gametype; local string serverOptions; local string currentGameType, curmap, curmiscurl; local array currentMutators, opts; local string substvar, substvar2; local int idx, i, j; local Mutator mut; local array options; webadmin.dataStoreCache.loadGameTypes(); currentGameType = q.request.getVariable("gametype"); curmap = q.request.getVariable("map"); serverOptions = getServerOptions(); curmiscurl = serverOptions; opts = denyUrlOptions; opts.AddItem("mutator"); opts.AddItem("game"); opts.AddItem("team"); opts.AddItem("name"); opts.AddItem("class"); opts.AddItem("character"); opts.AddItem("listen"); curmiscurl = class'WebAdminUtils'.static.removeUrlOptions(curmiscurl, opts); curmiscurl = q.request.getVariable("urlextra", curmiscurl); idx = int(q.request.getVariable("mutatorGroupCount", "0")); for (i = 0; i < idx; i++) { substvar = q.request.getVariable("mutgroup"$i, ""); if (len(substvar) > 0) { if (currentMutators.find(substvar) == INDEX_NONE) { currentMutators.addItem(substvar); } } } if (q.request.getVariable("action") ~= "change" || q.request.getVariable("action") ~= "change game") { options.length = 0; class'WebAdminUtils'.static.parseUrlOptions(options, curmiscurl); if (currentMutators.length > 0) { JoinArray(currentMutators, substvar2, ","); class'WebAdminUtils'.static.parseUrlOptions(options, "mutator="$substvar2); } class'WebAdminUtils'.static.parseUrlOptions(options, "game="$currentGameType); i = InStr(curmap, "?"); if (i != INDEX_NONE) { class'WebAdminUtils'.static.parseUrlOptions(options, Mid(curmap, i+1)); curmap = Left(curmap, i); } // remove denied options for (i = 0; i < denyUrlOptions.length; i++) { for (j = options.length-1; j >= 0; j--) { if (options[j].key ~= denyUrlOptions[i]) { options.remove(j, 1); } } } // construct url substvar = curmap; for (i = 0; i < options.length; i++) { substvar $= "?"$options[i].key; if (Len(options[i].value) > 0) { substvar $= "="$options[i].value; } } webadmin.addMessage(q, msgChangingGame); q.response.subst("newurl", `HTMLEscape(substvar)); webadmin.sendPage(q, "current_changing.html"); // add deny options when they were set on previous the commandline for (i = 0; i < denyUrlOptions.length; i++) { if (webadmin.WorldInfo.Game.HasOption(serverOptions, denyUrlOptions[i])) { substvar $= "?"$denyUrlOptions[i]; substvar2 = webadmin.WorldInfo.Game.ParseOption(serverOptions, denyUrlOptions[i]); if (len(substvar2) > 0) { substvar $= "="$substvar2; } } } newUrl = substvar; if (webadmin.WorldInfo.NetMode == NM_ListenServer) { // make sure this parameter is included for map switches. newUrl $= "?listen"; } webadmin.WebServer.SetTimer(0.5, false, 'ChangeGameTimer', self); return; } if (currentGameType == "") { currentGameType = string(webadmin.WorldInfo.Game.class); curmap = string(webadmin.WorldInfo.GetPackageName()); ParseStringIntoArray(webadmin.WorldInfo.Game.ParseOption(serverOptions, "mutator"), currentMutators, ",", true); mut = webadmin.WorldInfo.Game.BaseMutator; while (mut != none) { substvar = mut.class.getPackageName()$"."$mut.class; currentMutators.addItem(substvar); mut = mut.NextMutator; } } idx = webadmin.dataStoreCache.resolveGameType(currentGameType); if (idx > INDEX_NONE) { currentGameType = webadmin.dataStoreCache.gametypes[idx].data.ClassName; if (curmap == "") { //curmap = webadmin.dataStoreCache.gametypes[idx].data.DefaultMap; } } else { currentGameType = ""; } substvar = ""; foreach webadmin.dataStoreCache.gametypes(gametype) { q.response.subst("gametype.gamemode", `HTMLEscape(gametype.data.ClassName)); q.response.subst("gametype.friendlyname", `HTMLEscape(gametype.FriendlyName)); //q.response.subst("gametype.defaultmap", `HTMLEscape(gametype.data.DefaultMap)); q.response.subst("gametype.description", `HTMLEscape(gametype.Description)); if (currentGameType ~= gametype.data.ClassName) { q.response.subst("gametype.selected", "selected=\"selected\""); } else { q.response.subst("gametype.selected", ""); } substvar $= webadmin.include(q, "current_change_gametype.inc"); } q.response.subst("gametypes", substvar); procCurrentChange(q, currentGameType, curmap, currentMutators, substvar, substvar2, idx); q.response.subst("maps", substvar); q.response.subst("mutators", substvar2); q.response.subst("mutator.groups", idx); q.response.subst("urlextra", curmiscurl); Joinarray(denyUrlOptions, substvar, ", ", true); q.response.subst("urlextra.deny", substvar); webadmin.sendPage(q, "current_change.html"); } function procCurrentChange(WebAdminQuery q, string currentGameType, string curmap, array currentMutators, out string outMaps, out string outMutators, out int outMutatorGroups) { local string substvar2, substvar3, mutname; local int idx, i, j, k; local array maps; local array mutators; outMaps = ""; if (currentGameType != "") { maps = webadmin.dataStoreCache.getMaps(currentGameType); for (i = 0; i < maps.length; i++) { q.response.subst("map.mapname", `HTMLEscape(maps[i].data.MapName)); q.response.subst("map.friendlyname", `HTMLEscape(maps[i].FriendlyName)); //q.response.subst("map.mapid", string(maps[i].data.MapID)); //q.response.subst("map.numplayers", `HTMLEscape(class'WebAdminUtils'.static.getLocalized(maps[i].data.NumPlayers))); q.response.subst("map.description", `HTMLEscape(maps[i].Description)); if (curmap ~= maps[i].MapName) { q.response.subst("map.selected", "selected=\"selected\""); } else { q.response.subst("map.selected", ""); } outMaps $= webadmin.include(q, "current_change_map.inc"); } } outMutators = ""; outMutatorGroups = 0; if (currentGameType != "") { mutators = webadmin.dataStoreCache.getMutators(currentGameType); idx = 0; for (i = 0; i < mutators.length; i++) { if ((mutators[i].mutators.Length == 1) || len(mutators[i].GroupName) == 0) { for (j = 0; j < mutators[i].mutators.Length; j++) { q.response.subst("mutator.formtype", "checkbox"); q.response.subst("mutator.groupid", "mutgroup"$(mutators.Length+outMutatorGroups)); q.response.subst("mutator.classname", `HTMLEscape(mutators[i].mutators[j].ClassName)); q.response.subst("mutator.id", "mutfield"$(++idx)); mutname = mutators[i].mutators[j].FriendlyName; if (len(mutname) == 0) mutname = mutators[i].mutators[j].ClassName; q.response.subst("mutator.friendlyname", `HTMLEscape(mutname)); q.response.subst("mutator.description", `HTMLEscape(mutators[i].mutators[j].Description)); if (currentMutators.find(mutators[i].mutators[j].ClassName) != INDEX_NONE) { q.response.subst("mutator.selected", "checked=\"checked\""); } else { q.response.subst("mutator.selected", ""); } substvar3 $= webadmin.include(q, "current_change_mutator.inc"); outMutatorGroups++; } } else { substvar2 = ""; k = INDEX_NONE; for (j = 0; j < mutators[i].mutators.Length; j++) { q.response.subst("mutator.formtype", "radio"); q.response.subst("mutator.groupid", "mutgroup"$i); q.response.subst("mutator.classname", `HTMLEscape(mutators[i].mutators[j].ClassName)); q.response.subst("mutator.id", "mutfield"$(++idx)); mutname = mutators[i].mutators[j].FriendlyName; if (len(mutname) == 0) mutname = mutators[i].mutators[j].ClassName; q.response.subst("mutator.friendlyname", `HTMLEscape(mutname)); q.response.subst("mutator.description", `HTMLEscape(mutators[i].mutators[j].Description)); if (currentMutators.find(mutators[i].mutators[j].ClassName) != INDEX_NONE) { k = j; q.response.subst("mutator.selected", "checked=\"checked\""); } else { q.response.subst("mutator.selected", ""); } substvar2 $= webadmin.include(q, "current_change_mutator.inc"); } q.response.subst("mutator.formtype", "radio"); q.response.subst("mutator.groupid", "mutgroup"$i); q.response.subst("mutator.classname", ""); q.response.subst("mutator.id", "mutfield"$(++idx)); q.response.subst("mutator.friendlyname", "none"); q.response.subst("mutator.description", ""); if (k == INDEX_NONE) { q.response.subst("mutator.selected", "checked=\"checked\""); } else { q.response.subst("mutator.selected", ""); } substvar2 = webadmin.include(q, "current_change_mutator.inc")$substvar2; q.response.subst("group.id", "mutgroup"$i); q.response.subst("group.name", Locs(mutators[i].GroupName)); q.response.subst("group.mutators", substvar2); outMutators $= webadmin.include(q, "current_change_mutator_group.inc"); } } if (len(substvar3) > 0) { q.response.subst("group.id", "mutgroup0"); q.response.subst("group.name", ""); q.response.subst("group.mutators", substvar3); outMutators = webadmin.include(q, "current_change_mutator_nogroup.inc")$outMutators; } } outMutatorGroups = outMutatorGroups+mutators.Length; if (mutators.Length == 0) { outMutators = ""$msgNoMutators$""; } } function handleCurrentChangeData(WebAdminQuery q) { local string currentGameType, curmap; local array currentMutators; local string substMaps, substMutators, tmp; local int idx; currentGameType = q.request.getVariable("gametype"); curmap = ""; currentMutators.length = 0; webadmin.dataStoreCache.loadGameTypes(); idx = webadmin.dataStoreCache.resolveGameType(currentGameType); if (idx > INDEX_NONE) { currentGameType = webadmin.dataStoreCache.gametypes[idx].data.ClassName; //curmap = webadmin.dataStoreCache.gametypes[idx].data.DefaultMap; } else { currentGameType = ""; } for (idx = 0; idx < int(q.request.getVariable("mutatorGroupCount", "0")); idx++) { tmp = q.request.getVariable("mutgroup"$idx, ""); if (len(tmp) > 0) { if (currentMutators.find(tmp) == INDEX_NONE) { currentMutators.addItem(tmp); } } } procCurrentChange(q, currentGameType, curmap, currentMutators, substMaps, substMutators, idx); //q.response.SendText(""); q.response.AddHeader("Content-Type: text/html"); q.response.SendText(""); q.response.SendText("
"); q.response.SendText(substMutators); q.response.SendText("
"); q.response.SendText(""); //q.response.SendText("
"); } `if(`WITH_BOTS) function handleBots(WebAdminQuery q) { local int i,j; local string sv1, tmp; local PlayerReplicationInfo pri; local Controller PC; sv1 = q.request.getVariable("action", ""); if (sv1 ~= "addbots") { i = int(q.request.getVariable("numbots", "0")); j = int(q.request.getVariable("toteam", "-1")); if (i > 0) { ROGameInfo(webadmin.WorldInfo.Game).AddBots(i, j); if (j == -1) { webadmin.addMessage(q, repl(msgAddingBots, "%d", string(i))); } else { webadmin.addMessage(q, repl(repl(msgAddingBotsTeam, "%d", string(i)), "%team", class'WebAdminUtils'.static.getTeamName(j))); } } } if (sv1 ~= "killbots") { tmp = ""; for (i = 0; i < q.request.GetVariableCount("bot"); ++i) { sv1 = q.request.getVariableNumber("bot", i, ""); if (len(sv1) > 0) { PRI = none; for (j = 0; j < webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray.length; j++) { if (getPlayerKey(webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray[j]) == sv1) { PRI = webadmin.WorldInfo.Game.GameReplicationInfo.PRIArray[j]; break; } } if (PRI != none) { PC = Controller(PRI.Owner); if (PC != none) { if (len(tmp) > 0) tmp $= ", "; tmp $= PRI.PlayerName; if (PC.Pawn != none) PC.Pawn.Destroy(); PC.Destroy(); } } } } if (len(tmp) > 0) { webadmin.addMessage(q, repl(msgRemovedBots, "%s", tmp)); } } tmp = ""; if (`isTeamGame( webadmin.WorldInfo.Game)) { // don't bother adding team selection when team balancing is enabled if (ROGameInfo(webadmin.WorldInfo.Game) == none || !ROGameInfo(webadmin.WorldInfo.Game).bBalanceTeams) { q.response.subst("team.teamid", -1); q.response.subst("team.name", "Any team"); q.response.subst("team.checked", "checked=\"checked\""); tmp $= webadmin.include(q, "current_bots_teamctrl.inc"); for (i = 0; i < webadmin.WorldInfo.Game.GameReplicationInfo.Teams.length; i++) { q.response.subst("team.teamid", i); q.response.subst("team.name", `HTMLEscape(class'WebAdminUtils'.static.getTeamNameEx(webadmin.WorldInfo.Game.GameReplicationInfo.Teams[i]))); q.response.subst("team.checked", ""); tmp $= webadmin.include(q, "current_bots_teamctrl.inc"); } } } if (tmp != "") { q.response.subst("teamcontrols", tmp); tmp = webadmin.include(q, "current_bots_teams.inc"); } q.response.subst("teamcontrols", tmp); q.response.subst("playerlimit", webadmin.worldinfo.game.MaxPlayers); buildSortedPRI(q.request.getVariable("sortby", "score"), q.request.getVariable("reverse", "true") ~= "true"); j = 0; tmp = ""; foreach sortedPRI(pri) { if (!pri.bBot) { continue; } ++j; if (`mod(j, 2) == 0) q.response.subst("evenodd", "even"); else q.response.subst("evenodd", "odd"); substPri(q, pri); tmp $= webadmin.include(q, "current_bot_row.inc"); } if (sortedPRI.Length == 0) { tmp = webadmin.include(q, "current_bot_empty.inc"); } q.response.subst("sorted."$q.request.getVariable("sortby", "score"), "sorted"); if (!(q.request.getVariable("reverse", "true") ~= "true")) { q.response.subst("reverse."$q.request.getVariable("sortby", "score"), "true"); } q.response.subst("bots", tmp); webadmin.sendPage(q, "current_bots.html"); } `endif defaultproperties { cssVisible="" cssHidden="display: none;" playerActions.Add("kick") playerActions.Add("sessionban") playerActions.Add("banip") playerActions.Add("banid") playerActions.Add("mutevoice") playerActions.Add("unmutevoice") consoleCommandsToGetServerExiled.Add("kick") consoleCommandsToGetServerExiled.Add("kickban") }