rott/kf_sources/XWebAdmin/Classes/xWebQueryCurrent.uc
2026-07-14 20:27:09 +07:00

1135 lines
35 KiB
Ucode

//==============================================================================
// WebAdmin handler for activities related to the game currently in
// progress on the server
//
// Written by Michael Comeau
// Revised by Ron Prestenback
// © 2003,2004 Epic Games, Inc. All Rights Reserved
//==============================================================================
class xWebQueryCurrent extends xWebQueryHandler
config;
var config string CurrentIndexPage; // This is the page with the Menu
var config string CurrentPlayersPage;
var config string CurrentGamePage;
var config string CurrentConsolePage;
var config string CurrentConsoleLogPage;
var config string CurrentConsoleSendPage;
var config string CurrentMutatorsPage;
var config string CurrentBotsPage;
var config string CurrentRestartPage;
//if _RO_
var config string CurrentResetPage;
//end _RO_
var config string DefaultSendText;
var config string StatTable;
var config string StatTableRow;
// Custom Skin Support
var config string PlayerListHeader;
var config string PlayerListLinkedHeader;
var config string PlayerListMinPlayers;
var config string ConsoleRefreshTag;
var config string MutatorTablePage;
var config string MutatorGroupTitle;
var config string MutatorGroupMember;
// Localization
// Sections & Titles
var localized string BadGameType;
var localized string CurrentLinks[6];
var localized string NoBotsTitle;
// Labels
var localized string KickButtonText[3];
var localized string NoPlayersConnected;
var localized string SelectedMutators;
var localized string PickMutators;
var localized string GameTypeUnsupported;
var localized string NoBots;
var localized string Added;
var localized string Removed;
var localized string BotStatus;
var localized string SingleBotStatus;
var localized string ConsoleUserlist;
// Help messages
var localized string NoteGamePage;
var localized string NotePlayersPage;
var localized string NoteConsolePage;
var localized string NoteMutatorsPage;
var localized string NoteBotsPage;
var StringArray SpeciesNames;
var array<StringArray> BotList; // Sorted bot list by species
function bool Query(WebRequest Request, WebResponse Response)
{
if (!CanPerform(NeededPrivs))
return false;
switch (Mid(Request.URI, 1))
{
case DefaultPage:
QueryCurrentFrame(Request, Response);
return true;
case CurrentIndexPage:
QueryCurrentMenu(Request, Response);
return true;
case CurrentPlayersPage:
if (!MapIsChanging())
QueryCurrentPlayers(Request, Response);
return true;
case CurrentGamePage:
if (!MapIsChanging())
QueryCurrentGame(Request, Response);
return true;
case CurrentConsolePage:
if (!MapIsChanging())
QueryCurrentConsole(Request, Response);
return true;
case CurrentConsoleLogPage:
if (!MapIsChanging())
QueryCurrentConsoleLog(Request, Response);
return true;
case CurrentConsoleSendPage:
QueryCurrentConsoleSend(Request, Response);
return true;
case CurrentMutatorsPage:
if (!MapIsChanging())
QueryCurrentMutators(Request, Response);
return true;
case CurrentBotsPage:
if (!MapIsChanging())
QueryCurrentBots(Request, Response);
return true;
case CurrentRestartPage:
if (!MapIsChanging())
QueryRestartPage(Request, Response);
return true;
}
return false;
}
//*****************************************************************************
function QueryCurrentFrame(WebRequest Request, WebResponse Response)
{
local String Page;
// if no page specified, use the default
Page = Request.GetVariable("Page", CurrentGamePage);
Response.Subst("IndexURI", CurrentIndexPage$"?Page="$Page);
Response.Subst("MainURI", Page);
ShowFrame(Response, DefaultPage);
}
function QueryCurrentMenu(WebRequest Request, WebResponse Response)
{
local String Page;
Page = Request.GetVariable("Page", CurrentGamePage);
// set background colors
Response.Subst("DefaultBG", DefaultBG); // for unused tabs
Response.Subst("PlayersBG", DefaultBG);
Response.Subst("GameBG", DefaultBG);
Response.Subst("ConsoleBG", DefaultBG);
Response.Subst("MutatorsBG",DefaultBG);
Response.Subst("RestartBG", DefaultBG);
switch(Page)
{
case CurrentPlayersPage:
Response.Subst("PlayersBG", HighlightedBG);
break;
case CurrentGamePage:
Response.Subst("GameBG", HighlightedBG);
break;
case CurrentConsolePage:
Response.Subst("ConsoleBG", HighlightedBG);
break;
case CurrentMutatorsPage:
Response.Subst("MutatorsBG",HighlightedBG);
break;
case CurrentRestartPage:
Response.Subst("RestartBG", HighlightedBG);
break;
}
// Set URIs
Response.Subst("PlayersURI", DefaultPage$"?Page="$CurrentPlayersPage);
Response.Subst("GameURI", DefaultPage$"?Page="$CurrentGamePage);
Response.Subst("ConsoleURI", DefaultPage$"?Page="$CurrentConsolePage);
Response.Subst("MutatorsURI", DefaultPage$"?Page="$CurrentMutatorsPage);
Response.Subst("RestartURI", DefaultPage$"?Page="$CurrentRestartPage);
// Set link text
Response.Subst("GameLink", CurrentLinks[0]);
Response.Subst("PlayerLink", CurrentLinks[1]);
Response.Subst("ConsoleLink", CurrentLinks[2]);
Response.Subst("MutatorLink", CurrentLinks[3]);
Response.Subst("RestartLink", CurrentLinks[4]);
ShowPage(Response, CurrentIndexPage);
}
function QueryCurrentPlayers(WebRequest Request, WebResponse Response)
{
local string Sort, PlayerListSubst, TempStr, TempTag, TempData;
local string TableHeaders, GameType, Reverse, ColorNames[2], Last;
local StringArray PlayerList;
local Controller P, NextP;
local int i, Cols, mlength;
local string IP, ID;
local bool bCanKick, bCanBan, bCanKickBots;
Response.Subst("Section", CurrentLinks[1]);
Response.Subst("PostAction", CurrentPlayersPage);
ColorNames[0] = class'TeamInfo'.default.ColorNames[0];
ColorNames[1] = class'TeamInfo'.default.ColorNames[1];
MLength = int(Eval(Len(ColorNames[0]) > Len(ColorNames[1]), string(Len(ColorNames[0])), string(Len(ColorNames[1]))));
if (CanPerform("Xp|Kp|Kb|Ko"))
{
PlayerList = new(None) class'SortedStringArray';
Sort = Request.GetVariable("Sort", "Name");
Last = Request.GetVariable("Last");
Response.Subst("Sort", Sort);
Cols = 0;
bCanKick = CanPerform("Kp");
bCanBan = CanPerform("Kb");
bCanKickBots = CanPerform("Ko|Mb");
if (Last == Sort && Request.GetVariable("ReverseSort") == "")
{
PlayerList.ToggleSort();
Reverse = "?ReverseSort=True";
}
else Reverse = "";
// Count the number of Columns allowed
if (bCanKick || bCanBan || bCanKickBots)
{
// Use 'do-while' to avoid access-none when destroying Controllers within the loop
P = Level.ControllerList;
if (P != None)
{
do {
NextP = P.NextController;
if( PlayerController(P) != None
&& P.PlayerReplicationInfo != None
&& NetConnection(PlayerController(P).Player) != None)
{
if ( bCanBan && Request.GetVariable("Ban" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.KickBanPlayer(PlayerController(P));
//if _RO_
else if ( bCanBan && Request.GetVariable("Session" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.BanPlayer(PlayerController(P), true);
//end _RO_
else if ( bCanKick && Request.GetVariable("Kick" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.KickPlayer(PlayerController(P));
}
else if ( PlayerController(P) == None && bCanKickBots && P.PlayerReplicationInfo != None &&
Request.GetVariable("Kick" $ string(P.PlayerReplicationInfo.PlayerID)) != "")
{ // Kick Bots
P.Destroy();
}
P = NextP;
} until (P == None);
}
if (bCanKick || bCanKickBots) Cols += 1;
if (bCanBan) Cols += 2;
Response.Subst("KickButton", SubmitButton("Kick", KickButtonText[Cols-1]));
// Build of valid TableHeaders
TableHeaders = "";
if (bCanKick || bCanKickBots)
{
Response.Subst("HeadTitle", "Kick");
TableHeaders $= WebInclude(PlayerListHeader);
}
if (bCanBan)
{
//if _RO_
Response.Subst("HeadTitle", "Session");
TableHeaders $= WebInclude(PlayerListHeader);
//end _RO_
Response.Subst("HeadTitle", "Ban");
TableHeaders $= WebInclude(PlayerListHeader);
}
if (Sort ~= "Name") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Name");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
if (Level.Game.GameReplicationInfo.bTeamGame)
{
if (Sort ~= "Team") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Team");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
}
if (Sort ~= "Ping") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Ping");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
if (Sort ~= "Score") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Score");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
//if _RO_
Response.Subst("HeadTitle", "Team Kills");
TableHeaders $= WebInclude(PlayerListHeader);
//end _RO_
Response.Subst("HeadTitle", "IP");
TableHeaders $= WebInclude(PlayerListHeader);
// evo ---
if (Level.Game.AccessControl.bBanbyID)
{
Response.Subst("HeadTitle", "Global ID");
TableHeaders $= WebInclude(PlayerListHeader);
}
// --- evo
Response.Subst("TableHeaders", TableHeaders);
}
if (CanPerform("Ms"))
{
GameType = Level.GetItemName(SetGamePI(GameType));
if (GamePI != None && GamePI.Settings[GamePI.FindIndex(GameType$".MinPlayers")].SecLevel <= CurAdmin.MaxSecLevel())
{
if ((Request.GetVariable("SetMinPlayers", "") != "") && UnrealMPGameInfo(Level.Game) != None)
{
UnrealMPGameInfo(Level.Game).MinPlayers = Min(Max(int(Request.GetVariable("MinPlayers", String(0))), 0), 32);
Level.Game.SaveConfig();
}
Response.Subst("MinPlayers", string(UnrealMPGameInfo(Level.Game).MinPlayers));
Response.Subst("MinPlayerPart", WebInclude(PlayerListMinPlayers));
}
else
{
Response.Subst("MinPlayers", "");
Response.Subst("MinPlayersPart", "");
}
}
for (P=Level.ControllerList; P!=None; P=P.NextController)
{
TempData = "";
if (!P.bDeleteMe && P.bIsPlayer && P.PlayerReplicationInfo != None)
{
Response.Subst("Content", CheckBox("Kick" $ string(P.PlayerReplicationInfo.PlayerID), False));
if (CanPerform("Kp"))
TempData $= WebInclude(CellCenter);
if (CanPerform("Kb"))
{
//if _RO_
if ( PlayerController(P) != None )
{
Response.Subst("Content", Checkbox("Session" $ string(P.PlayerReplicationInfo.PlayerID), False));
TempData $= WebInclude(CellCenter);
Response.Subst("Content", Checkbox("Ban" $ string(P.PlayerReplicationInfo.PlayerID), False));
TempData $= WebInclude(CellCenter);
}
else
{
Response.Subst("Content", "");
TempData $= WebInclude(CellCenter)$WebInclude(CellCenter);
}
//else
//if ( PlayerController(P) != None )
// Response.Subst("Content", Checkbox("Ban" $ string(P.PlayerReplicationInfo.PlayerID), False));
//else Response.Subst("Content", "");
//TempData $= WebInclude(CellCenter);
//end _RO_
}
TempStr = "";
if (DeathMatch(Level.Game) != None && DeathMatch(Level.Game).bTournament && P.PlayerReplicationInfo.bReadyToPlay)
TempStr = " (Ready) ";
else if (P.PlayerReplicationInfo.bIsSpectator)
TempStr = " (Spectator) ";
else if (PlayerController(P) == None)
TempStr = " (Bot) ";
if( PlayerController(P) != None )
{
IP = PlayerController(P).GetPlayerNetworkAddress();
IP = HtmlEncode(" " $ Left(IP, InStr(IP, ":")));
// evo ---
ID = HtmlEncode(" " $ Eval(Level.Game.AccessControl.bBanbyID, PlayerController(P).GetPlayerIDHash(), " "));
// --- evo
}
else
{
IP = HtmlEncode(" ");
ID = HtmlEncode(" ");
}
Response.Subst("Content", HtmlEncode(P.PlayerReplicationInfo.PlayerName $ TempStr));
TempData $= WebInclude(NowrapLeft);
if (Level.Game.bTeamGame)
{
if (P.PlayerReplicationInfo.Team != None && P.PlayerReplicationInfo.Team.TeamIndex < 4)
Response.Subst("Content", "<span style='background-color: "$class'TeamInfo'.default.ColorNames[P.PlayerReplicationInfo.Team.TeamIndex]$"'>"$HtmlEncode(" ")$"</span>"$HtmlEncode(P.PlayerReplicationInfo.Team.GetHumanReadableName()));
else if (P.PlayerReplicationInfo.bIsSpectator)
Response.Subst("Content", HtmlEncode(" "));
TempData $= WebInclude(NowrapCenter);
}
Response.Subst("Content", string(P.PlayerReplicationInfo.Ping*4));
TempData $= WebInclude(CellCenter);
Response.Subst("Content", string(int(P.PlayerReplicationInfo.Score)));
TempData $= WebInclude(CellCenter);
//if _RO_
Response.Subst("Content", string(P.PlayerReplicationInfo.FFKills));
TempData $= WebInclude(CellCenter);
//end _RO_
Response.Subst("Content", IP);
TempData $= WebInclude(CellCenter);
if (Level.Game.AccessControl.bBanbyID)
{
Response.Subst("Content", ID);
TempData $= WebInclude(CellCenter);
}
switch (Sort)
{
case "Name":
TempTag = P.PlayerReplicationInfo.PlayerName; break;
case "Team": // Ordered by Team, then subordered by last selected sort method
TempTag = PadRight(class'TeamInfo'.default.ColorNames[P.PlayerReplicationInfo.Team.TeamIndex],MLength,"0");
switch (Last)
{
case "Name":
TempTag $= P.PlayerReplicationInfo.PlayerName; break;
case "Ping":
TempTag $= PadLeft(string(P.PlayerReplicationInfo.Ping*4), 5, "0"); break;
default:
TempTag $= PadLeft(string(int(P.PlayerReplicationInfo.Score)), 4, "0"); break;
}
break;
case "Ping":
TempTag = PadLeft(string(P.PlayerReplicationInfo.Ping*4), 5, "0"); break;
default:
TempTag = PadLeft(string(int(P.PlayerReplicationInfo.Score)), 4, "0"); break;
}
Response.Subst("RowContent", TempData);
PlayerList.Add( WebInclude(RowLeft), TempTag);
}
}
PlayerListSubst = "";
if (PlayerList.Count() > 0)
{
for ( i=0; i<PlayerList.Count(); i++)
{
if (Sort ~= "Score")
PlayerListSubst = PlayerList.GetItem(i) $ PlayerListSubst;
else PlayerListSubst $= PlayerList.GetItem(i);
}
}
else
{
Response.Subst("SpanContent", NoPlayersConnected);
Response.Subst("SpanLength", "6");
Response.Subst("RowContent", WebInclude(CellColSpan));
PlayerListSubst = WebInclude(RowCenter);
}
Response.Subst("PlayerList", PlayerListSubst);
Response.Subst("MinPlayers", string(UnrealMPGameInfo(Level.Game).MinPlayers));
Response.Subst("PageHelp", NotePlayersPage);
MapTitle(Response);
ShowPage(Response, CurrentPlayersPage);
}
else
AccessDenied(Response);
}
function QueryCurrentGame(WebRequest Request, WebResponse Response)
{
local StringArray ExcludeMaps, IncludeMaps, MovedMaps;
local class<GameInfo> GameClass;
local string NewGameType, SwitchButtonName, GameState, NewMap;
local bool bMakeChanges;
local Controller C;
local xPlayer XP;
local TeamPlayerReplicationInfo PRI;
local int MultiKills, Sprees, GameIndex;
if (CanPerform("Mt|Mm"))
{
if (Request.GetVariable("SwitchGameTypeAndMap", "") != "")
{
if (CanPerform("Mt"))
ServerChangeMap(Request, Response, Request.GetVariable("MapSelect"), Request.GetVariable("GameTypeSelect"));
else AccessDenied(Response);
return;
}
else if (Request.GetVariable("SwitchMap", "") != "")
{
if (CanPerform("Mm|Mt"))
{
NewMap = Request.GetVariable("MapSelect");
Level.ServerTravel(NewMap$"?game="$Level.Game.Class$"?mutator="$UsedMutators(), false);
ShowMessage(Response, WaitTitle, Repl(MapChangingTo, "%MapName%", NewMap));
}
else AccessDenied(Response);
return;
}
bMakeChanges = (Request.GetVariable("ApplySettings", "") != "");
if (CanPerform("Mt") && (bMakeChanges || Request.GetVariable("SwitchGameType", "") != ""))
{
NewGameType = Request.GetVariable("GameTypeSelect");
GameClass = class<GameInfo>(DynamicLoadObject(NewGameType, class'Class'));
}
else GameClass = None;
if (GameClass == None)
{
GameClass = Level.Game.Class;
NewGameType = String(GameClass);
}
GameIndex = Level.Game.MaplistHandler.GetGameIndex(NewGameType);
ExcludeMaps = ReloadExcludeMaps(NewGameType);
IncludeMaps = ReloadIncludeMaps(ExcludeMaps, GameIndex, Level.Game.MaplistHandler.GetActiveList(GameIndex));
GameState = "";
// Show game status if admin has necessary privs
if (CanPerform("Ma"))
{
if (Level.Game.NumPlayers > 0)
{
for (C = Level.ControllerList; C != None; C = C.NextController)
{
MultiKills = 0;
Sprees = 0;
PRI = None;
XP = xPlayer(C);
if (XP != None && !XP.bDeleteMe)
{
if (TeamPlayerReplicationInfo(XP.PlayerReplicationInfo) != None)
PRI = TeamPlayerReplicationInfo(XP.PlayerReplicationInfo);
if (PRI != None)
{
Response.Subst("PlayerName", HtmlEncode(PRI.PlayerName));
Response.Subst("Kills", string(PRI.Kills));
//if _RO_
Response.Subst("FFKills", string(PRI.FFKills));
//end _RO_
Response.Subst("Deaths", string(PRI.Deaths));
Response.Subst("Suicides",string(PRI.Suicides));
//if _RO_
/*
//end _RO_
for (i = 0; i < 7; i++)
MultiKills += PRI.MultiKills[i];
Response.Subst("MultiKills", string(MultiKills));
for (i = 0; i < 6; i++)
Sprees += PRI.Spree[i];
Response.Subst("Sprees", string(Sprees));
//if _RO_
*/
//end _RO_
GameState $= WebInclude(StatTableRow);
}
}
}
}
else
//if _RO_
GameState = "<tr><td colspan=\"5\" align=\"center\">"@NoPlayersConnected@"</td></tr>";
//else
//GameState = "<tr><td colspan=\"6\" align=\"center\">"@NoPlayersConnected@"</td></tr>";
//end _RO_
Response.Subst("StatRows", GameState);
Response.Subst("GameState", WebInclude(StatTable));
}
if (GameClass == Level.Game.Class)
{
SwitchButtonName="SwitchMap";
MovedMaps = New(None) Class'SortedStringArray';
MovedMaps.CopyFromId(IncludeMaps, IncludeMaps.FindTagId(Left(string(Level), InStr(string(Level), "."))));
}
else SwitchButtonName="SwitchGameTypeAndMap";
if (CanPerform("Mt"))
{
Response.Subst("Content", Select("GameTypeSelect", GenerateGameTypeOptions(NewGameType)));
Response.Subst("GameTypeButton", SubmitButton("SwitchGameType", SwitchText));
}
else Response.Subst("Content", Level.Game.Default.GameName);
Response.Subst("GameTypeSelect", WebInclude(CellLeft));
Response.Subst("Content", Select("MapSelect", GenerateMapListSelect(IncludeMaps, MovedMaps)));
Response.Subst("MapSelect", WebInclude(CellLeft));
Response.Subst("MapButton", SubmitButton(SwitchButtonName, SwitchText));
Response.Subst("PostAction", CurrentGamePage);
Response.Subst("Section", CurrentLinks[0]);
Response.Subst("PageHelp", NoteGamePage);
MapTitle(Response);
ShowPage(Response, CurrentGamePage);
}
else AccessDenied(Response);
}
function QueryCurrentConsole(WebRequest Request, WebResponse Response)
{
local String SendStr, OutStr;
if (CanPerform("Xc"))
{
SendStr = Request.GetVariable("SendText", "");
if (SendStr != "" && !(Left(SendStr, 6) ~= "debug " || SendStr ~= "debug"))
{
if (Left(SendStr, 4) ~= "say ")
Level.Game.Broadcast(Spectator, Mid(SendStr, 4), 'Say');
else if (SendStr ~= "pause")
{
if (Level.Pauser == None)
Level.Pauser = Spectator.PlayerReplicationInfo;
else Level.Pauser = None;
}
else if (SendStr ~= "dump")
Spectator.Dump();
else if ((Left(SendStr, 4) ~= "get " || Left(SendStr,4) ~= "set ") &&
(InStr(Caps(SendStr), "XADMINCONFIG") != -1 || !CanPerform("Ms")) )
{
if ( InStr(Caps(SendStr), "XADMINCONFIG") != -1 )
{
StatusError(Response, ConsoleUserlist);
ShowMessage(Response, Error, "");
log("User attempted to modify or enumerate admin account information illegally using the webadmin console. User:"$Request.Username$".",'WebAdmin');
}
else if ( !CanPerform("Ms") )
AccessDenied(Response);
}
else
{
OutStr = Level.ConsoleCommand(SendStr);
if (OutStr != "")
Spectator.AddMessage(None, OutStr, 'Console');
}
}
Response.Subst("LogURI", CurrentConsoleLogPage);
Response.Subst("SayURI", CurrentConsoleSendPage);
ShowPage(Response, CurrentConsolePage);
}
else
AccessDenied(Response);
}
function QueryCurrentConsoleLog(WebRequest Request, WebResponse Response)
{
local String LogSubst, LogStr;
local int i;
if (CanPerform("Xc"))
{
Response.Subst("Section", CurrentLinks[2]);
Response.Subst("SubTitle", Level.Game.GameReplicationInfo.GameName$" in "$Level.Title);
i = Spectator.LastMessage();
LogStr = HtmlEncode(Spectator.NextMessage(i));
while (LogStr != "")
{
LogSubst = LogSubst$"&gt; "$LogStr$"<br>";
LogStr = HtmlEncode(Spectator.NextMessage(i));
}
Response.Subst("RefreshMeta", ConsoleRefreshTag $ CurrentConsoleLogPage $ "#END\">");
Response.Subst("LogText", LogSubst);
Response.Subst("PageHelp", NoteConsolePage);
MapTitle(Response);
ShowPage(Response, CurrentConsoleLogPage);
}
else
AccessDenied(Response);
}
function QueryCurrentConsoleSend(WebRequest Request, WebResponse Response)
{
if (CanPerform("Xc"))
{
Response.Subst("DefaultSendText", DefaultSendText);
Response.Subst("PostAction", CurrentConsolePage);
ShowPage(Response, CurrentConsoleSendPage);
}
else
AccessDenied(Response);
}
function QueryCurrentMutators(WebRequest Request, WebResponse Response)
{
local int i, j, k, z;
local string selectedmutes, lastgroup, nextgroup, thisgroup, Checked, tmp;
local StringArray GroupedMutators, SoloMutators;
if (CanPerform("Mu"))
{
SoloMutators = new(None) class'SortedStringArray';
GroupedMutators = new(None) class'StringArray';
if (Request.GetVariable("SetMutes", "") != "")
{
AIncMutators.Reset();
lastgroup = "";
for (i = 0; i<AExcMutators.Count(); i++)
{
j = int(AExcMutators.GetItem(i));
if (j < 0) continue;
thisgroup = AllMutators[j].GroupName;
if (Request.GetVariable(AExcMutators.GetTag(i), "") != "" || Request.GetVariable(thisgroup) == AllMutators[j].ClassName)
AIncMutators.Add(AExcMutators.GetItem(i), AllMutators[j].FriendlyName);
}
}
// Make a list sorted by friendly name
for (i = 0; i<AExcMutators.Count(); i++)
{
j = int(AExcMutators.GetItem(i));
if (j < 0) continue;
SoloMutators.Add(string(j), AllMutators[j].FriendlyName);
}
// First, Display Selected Mutators, 1 per line
selectedmutes = "";
for (i = 0; i<AIncMutators.Count(); i++)
{
tmp = "";
j = int(AIncMutators.GetItem(i));
if (j < 0) continue;
Response.Subst("Content", HtmlEncode(AllMutators[j].FriendlyName));
tmp = WebInclude(CellLeft);
Response.Subst("Content", HtmlEncode(AllMutators[j].Description));
Response.Subst("RowContent", tmp $ WebInclude(CellLeft));
selectedmutes $= WebInclude(RowLeft);
}
if (selectedmutes != "")
{
Response.Subst("TableTitle", SelectedMutators);
Response.Subst("TableRows", selectedmutes);
Response.Subst("SelectedTable", WebInclude(MutatorTablePage));
}
CreateFullMutatorList(SoloMutators, GroupedMutators);
lastgroup = ""; selectedmutes = "";
// First, display all grouped mutators, sorted by friendly name
for (i = 0; i<GroupedMutators.Count(); i++)
{
j = int(GroupedMutators.GetItem(i));
if (j < 0) continue;
// If mod author has forgotten to specify a group name for this mutator,
// then add unique group name, to avoid all mutes without group names from
// being considered in the same group
if (AllMutators[j].GroupName=="")
thisgroup = "Z" $ string(z++);
else thisgroup = AllMutators[j].GroupName;
if ( (i + 1) == GroupedMutators.Count())
nextgroup = "";
else
{
k = int(GroupedMutators.GetItem(i + 1));
if (k < 0) continue;
if (AllMutators[k].GroupName=="")
nextgroup = "Z" $ string(z);
else nextgroup = AllMutators[k].GroupName;
}
Response.Subst("GroupName", thisgroup);
Response.Subst("MutatorClass", AllMutators[j].ClassName);
Response.Subst("MutatorName", AllMutators[j].FriendlyName);
Response.Subst("MutatorDesc", AllMutators[j].Description);
if (lastgroup != thisgroup && thisgroup == nextgroup) // and the next mut is in the same group as this one
{
Response.Subst("Checked", "checked");
selectedmutes $= WebInclude(MutatorGroupTitle);
}
Checked = Eval(AIncMutators.FindItemId(string(j)) >= 0, " checked", "");
Response.Subst("Checked", Checked);
selectedmutes $= WebInclude(MutatorGroupMember);
lastgroup = thisgroup;
}
for (i = 0; i < SoloMutators.Count(); i++)
{
tmp = ""; // Some sort of bug in WebInclude...must empty this var each time I use it
j = int(SoloMutators.GetItem(i));
Response.Subst("Content", CheckBox(AllMutators[j].ClassName, AIncMutators.FindItemId(string(j)) >= 0) $ "&nbsp;" $ AllMutators[j].FriendlyName);
tmp = WebInclude(NowrapLeft);
Response.Subst("Content", AllMutators[j].Description);
Response.Subst("RowContent", tmp $ WebInclude(CellLeft));
selectedmutes $= WebInclude(RowLeft);
}
Response.Subst("TableTitle", PickMutators);
Response.Subst("TableRows", selectedmutes);
Response.Subst("ChooseTable", WebInclude(MutatorTablePage));
MapTitle(Response);
Response.Subst("Section", CurrentLinks[3]);
Response.Subst("PageHelp", NoteMutatorsPage);
Response.Subst("PostAction", CurrentMutatorsPage);
ShowPage(Response, CurrentMutatorsPage);
}
else AccessDenied(Response);
}
function QueryCurrentBots(WebRequest Request, WebResponse Response)
{
local array<xUtil.PlayerRecord> PlayerRecords;
local string OutStr, BotName, LeftTable, RightTable;
local int i, j, BotCount, maxbots, cnt, Col1Count;
local xBot B;
local bool oldstate, newstate, bInMatch;
local DeathMatch DM;
if (!CanPerform("Mb"))
{
AccessDenied(Response);
return;
}
DM = DeathMatch(Level.Game);
if (DM == None)
{
ShowMessage(Response, BadGameType, Repl(GameTypeUnsupported, "%GameType%", string(Level.Game.Class)));
return;
}
// Disable any type of Bots controls when stats are on
if (DM.bEnableStatLogging && DM.NumBots == 0)
{
ShowMessage(Response, NoBotsTitle, NoBots);
return;
}
// Make a sorted list of all species and group bots
if (SpeciesNames == None)
{
class'xUtil'.static.GetPlayerList(PlayerRecords);
SpeciesNames = new(None) class'SortedStringArray';
for (i = 0; i<PlayerRecords.Length; i++)
SpeciesNames.Add(PlayerRecords[i].Species.default.SpeciesName, PlayerRecords[i].Species.default.SpeciesName, true);
BotList.Length = SpeciesNames.Count(); // Preset Bot list size
for (i = 0; i<PlayerRecords.Length; i++)
{
j = SpeciesNames.FindTagId(PlayerRecords[i].Species.default.SpeciesName);
if (j >= 0 && BotList[j] == None)
BotList[j] = new(None) class'SortedStringArray';
// Add the player record to the BotList
BotList[j].Add(PlayerRecords[i].DefaultName, PlayerRecords[i].DefaultName);
}
}
bInMatch = Level.Game.IsInState('MatchInProgress');
if (Request.GetVariable("addbotnum", "") != "")
{
BotCount = int(Request.GetVariable("addnum", "0"));
if (Request.GetVariable("BotAction", "") ~= "Add")
{
maxbots = 32-(DM.NumPlayers + DM.NumBots);
BotCount = Clamp(BotCount, 0, maxbots);
for (i=0;i<BotCount; i++)
DM.ForceAddBot();
// Save the change
if (BotCount == 0)
StatusError(Response, "0" @ Repl(BotStatus, "%Action%", Added));
else if (BotCount == 1)
StatusOk(Response, "1" @ Repl(SingleBotStatus, "%Action%", Added));
else
StatusOk(Response, BotCount @ Repl(BotStatus, "%Action%", Added));
}
else if (Request.GetVariable("BotAction", "") ~= "Remove")
{
BotCount = Clamp(BotCount, 0, DM.NumBots);
DM.MinPlayers = DM.NumPlayers + DM.NumBots - BotCount;
if (BotCount == 0)
StatusError(Response, "0" @ Repl(BotStatus, "%Action%", Removed));
else if (BotCount == 1)
StatusOk(Response, "1" @ Repl(SingleBotStatus, "%Action%", Removed));
else
StatusOk(Response, BotCount@Repl(BotStatus, "%Action%", Removed));
}
}
else if (Request.GetVariable("selectbots", "") != "" && bInMatch)
{
// Read as many bot infos as available
for (i = 0; i<SpeciesNames.Count(); i++)
{
for (j = 0; j<BotList[i].Count(); j++)
{
oldstate = Request.GetVariable("BotX"$i$"."$j, "") != "";
newstate = Request.GetVariable("Bot"$i$"."$j, "") != "";
BotName = BotList[i].GetItem(j);
if (oldstate != newstate)
{
if (oldstate) // remove the bot
{
B = FindPlayingBot(BotName);
if (B != None)
{
DM.MinPlayers = DM.NumPlayers + DM.NumBots - 1;
B.Destroy();
}
}
else
{
DM.MinPlayers = DM.NumPlayers + DM.NumBots;
DM.AddNamedBot(BotName);
}
}
}
}
}
// Build our BotList
if (SpeciesNames != None)
{
BotCount = 0;
for ( i = 0; i < SpeciesNames.Count(); i++ )
BotCount += BotList[i].Count();
for (i = 0; i<SpeciesNames.Count(); i++)
{
OutStr = "";
Response.Subst("SpeciesName", SpeciesNames.GetItem(i));
OutStr = WebInclude(CurrentBotsPage$"_species");
for (j = 0; j<BotList[i].Count(); j++)
{
Response.Subst("BotChecked", "");
Response.Subst("BotIndex", String(i)$"."$String(j));
Response.Subst("BotName", BotList[i].GetItem(j));
B = FindPlayingBot(BotList[i].GetItem(j));
Response.Subst("DisabledBots", "");
if (!bInMatch)
Response.Subst("DisabledBots", " DISABLED");
// The following are set only if bot is currently in the game
if (B != None)
{
Response.Subst("BotColor", GetTeamColor(B.PlayerReplicationInfo.Team));
Response.Subst("BotTeamName", GetTeamName(B.PlayerReplicationInfo.Team));
OutStr $= WebInclude(CurrentBotsPage$"_row_sel");
}
else OutStr $= WebInclude(CurrentBotsPage$"_row");
}
Response.Subst("BotList", OutStr);
// Attempt to balance the bot lists
if ( cnt + BotList[i].Count() < BotCount / 2 )
{
// Fill the left list until it has more than half the total number of bots
Col1Count = cnt + BotList[i].Count();
LeftTable $= WebInclude(CurrentBotsPage $ "_species_group");
}
else
{
if ( cnt - Col1Count > Col1Count )
{
// Account for any species that contained a large number of bots by adding smaller
// species to the first column
Col1Count += Botlist[i].Count();
LeftTable $= WebInclude(CurrentBotsPage $ "_species_group");
}
else RightTable $= WebInclude(CurrentBotsPage $ "_species_group");
}
cnt += BotList[i].Count();
}
}
Response.Subst("LeftBotTable", LeftTable);
Response.Subst("RightBotTable", RightTable);
// If not in match, make sure that the bots selection button is disabled
Response.Subst("DisabledBots", "");
if (!bInMatch)
Response.Subst("DisabledBots", " DISABLED");
MapTitle(Response);
Response.Subst("PageHelp", NoteBotsPage);
Response.Subst("Section", CurrentLinks[4]);
ShowPage(Response, CurrentBotsPage);
}
function xBot FindPlayingBot(string BotName) // Returns -1 on failure, or index for team/color
{
local Controller C;
local xBot B;
for (C = Level.ControllerList; C != None; C = C.NextController)
{
B = xBot(C);
if (B != None)
if (B.PlayerReplicationInfo.PlayerName == BotName)
return B;
}
return None;
}
function string GetTeamColor(TeamInfo Team)
{
if (Team == None)
return "";
if (Team.TeamIndex < 4)
return Team.ColorNames[Team.TeamIndex];
return "#CCCCCC";
}
function string GetTeamName(TeamInfo Team)
{
if (Team == None)
return "";
return Team.GetHumanReadableName();
}
defaultproperties
{
NeededPrivs="X|K|M|Xs|Xc|Xp|Xi|Kp|Kb|Ko|Mb|Mt|Mm|Mu|Ma"
CurrentIndexPage="current_menu"
CurrentPlayersPage="current_players"
CurrentGamePage="current_game"
CurrentConsolePage="current_console"
CurrentConsoleLogPage="current_console_log"
CurrentConsoleSendPage="current_console_send"
CurrentMutatorsPage="current_mutators"
CurrentBotsPage="current_bots"
CurrentRestartPage="current_restart"
PlayerListHeader="current_players_list_head"
PlayerListLinkedHeader="current_players_list_head_link"
PlayerListMinPlayers="current_players_minp"
MutatorTablePage="current_mutators_table"
MutatorGroupTitle="current_mutators_group"
MutatorGroupMember="current_mutators_group_row"
StatTable="current_game_stat_table"
StatTableRow="current_game_stat_table_row"
ConsoleRefreshTag="<meta http-equiv=\"refresh\" CONTENT=\"5; URL="
DefaultSendText="say "
BadGameType="Unsupported Game Type"
NoBotsTitle="Bots unavailable"
ConsoleUserlist="Getting or setting admin accounts and groups is not allowed through the webadmin console. This action has been logged."
CurrentLinks(0)="Current Game"
CurrentLinks(1)="Player List"
CurrentLinks(2)="Server Console"
CurrentLinks(3)="Mutators"
CurrentLinks(4)="Restart Map"
KickButtonText(0)="Kick"
KickButtonText(1)="Ban"
KickButtonText(2)="Kick/Ban"
NoPlayersConnected="** No Players Connected **"
SelectedMutators="Selected Mutators"
PickMutators="Select desired mutators"
GameTypeUnsupported="The Game Type '%GameType%' does not use standard bots."
NoBots="You cannot add bots while World Stats Logging is enabled."
SingleBotStatus="bot was %Action%"
BotStatus="bots were %Action%"
Added="added."
Removed="removed."
NoteGamePage="You can view and select maps from other gametypes by using the combo box. Selecting maps from other gametypes will automatically switch the server to that gametype."
NotePlayersPage="In order to see the global ID for connected players, change the value for bBanbyID in the [Engine.AccessControl] section of your ini to 'True'. Bots cannot be banned."
NoteConsolePage="You may communicate with the players in the game by entering text at the text box and clicking 'Send'. You can also enter console commands to control the server. Game messages are shown in the log window, with the exception of team messages."
NoteMutatorsPage="Select which mutators you want to be used when you hit the Restart Server Link"
NoteBotsPage="You may only add bots once the game has started. Adding bots has an adverse effect on the MinPlayers setting, and may cause this value to change. If stats are enabled, you may only add bots if bots are already in the game."
DefaultPage="currentframe"
Title="Current"
}