1
0
This commit is contained in:
2020-12-13 18:01:13 +03:00
commit dd42f84140
3764 changed files with 596895 additions and 0 deletions

View File

@ -0,0 +1,105 @@
/**
* A base implementation of the ISettingsModifier interface
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class AbstractSettingsModifier extends Object implements(ISettingsModifier);
/**
* Current setting instance being augemented. Only set when augmentation will be
* done.
*/
var WebAdminSettings curSettings;
var class<WebAdminSettings> selectedClass;
struct ClassEntry
{
/**
* The WebAdminSettings class
*/
var class<WebAdminSettings> settingsClass;
/**
* If true it applies to the subclasses as well.
*/
var bool bIncludeSubclasses;
};
/**
* Defines which classes this modifier will affecr
*/
var array<ClassEntry> modifiesClasses;
/**
* Some meta data for settings to check
*/
struct SettingValue
{
/**
* True if the value is fixed. If false, it is ranged.
*/
var bool bFixed;
// the values. if bFixed=false, the first value is the minimum
var int intVal, intValMax;
var float floatVal, floatValMax;
var string stringVal;
structdefaultproperties
{
bFixed=true
}
};
function bool modifierAppliesTo(WebAdminSettings settings)
{
selectedClass = getSelectedClass(settings);
if (selectedClass != none)
{
curSettings = settings;
return true;
}
return false;
}
function class<WebAdminSettings> getSelectedClass(WebAdminSettings settings)
{
local ClassEntry entry;
local bool augmentEnabled;
if (settings == none) return none;
foreach modifiesClasses(entry)
{
if (entry.bIncludeSubclasses)
{
augmentEnabled = ClassIsChildOf(settings.class, entry.settingsClass);
}
else {
augmentEnabled = settings.class == entry.settingsClass;
}
if (augmentEnabled)
{
return entry.settingsClass;
}
}
return none;
}
function string finalizeAugmentation(WebResponse response, String path)
{
curSettings = none;
selectedClass = none;
return "";
}
function augmentSetting(SettingRendererState renderState, optional int index = -1, optional bool inContainer);
function ensureSettingValues(WebAdminSettings settings);
defaultProperties
{
}

View File

@ -0,0 +1,230 @@
/**
* Simulates execution of certain admin commands
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class AdminCommandHandler extends Info;
`include(WebAdmin.uci)
/**
* Returns true when it was handled
*/
function bool execute(string cmd, out string result, PlayerController pc)
{
local int i;
local Admin adminuser;
local string args;
i = InStr(cmd, " ");
if (i != INDEX_NONE)
{
args = Mid(cmd, i+1);
cmd = Left(cmd, i);
}
if (cmd ~= "Admin")
{
i = InStr(args, " ");
if (i != INDEX_NONE)
{
cmd = Left(args, i);
args = Mid(args, i+1);
}
else {
cmd = args;
args = "";
}
}
if (cmd ~= "AdminChangeMap" || cmd ~= "ChangeMap")
{
WorldInfo.ServerTravel(cmd);
return true;
}
else if (cmd ~= "AdminChangeOption")
{
result = "AdminChangeOption is not available";
return true;
}
`if(`WITH_TEXT_MUTE)
else if (cmd ~= "AdminForceTextMute")
{
result = MutePlayer(args, true, false);
return true;
}
else if (cmd ~= "AdminForceTextUnMute")
{
result = MutePlayer(args, false, false);
return true;
}
`endif
else if (cmd ~= "AdminForceVoiceMute")
{
result = MutePlayer(args, true, true);
return true;
}
else if (cmd ~= "AdminForceVoiceUnMute")
{
result = MutePlayer(args, false, true);
return true;
}
else if (cmd ~= "AdminKick" || cmd ~= "Kick")
{
WorldInfo.Game.AccessControl.Kick(args/*, "Engine.AccessControl.KickedMsg"*/);
return true;
}
else if (cmd ~= "AdminKickBan" || cmd ~= "KickBan")
{
WorldInfo.Game.AccessControl.KickBan(args/*, "Engine.AccessControl.KickAndPermaBan"*/);
return true;
}
`if(`WITH_SESSION_BAN)
else if (cmd ~= "AdminSessionBan" || cmd ~= "SessionBan")
{
if (`{AccessControl} (WorldInfo.Game.AccessControl) != none)
{
result = SessionBan(args, "Engine.AccessControl.KickAndSessionBan");
}
else {
result = "Session banning not available with the current access control";
}
return true;
}
`endif
else if (cmd ~= "AdminLogin")
{
result = "AdminLogin is not available";
return true;
}
else if (cmd ~= "AdminLogOut")
{
result = "AdminLogOut is not available";
return true;
}
else if (cmd ~= "AdminPlayerList")
{
result = "AdminPlayerList is not available";
return true;
}
else if (cmd ~= "PlayerList")
{
result = "PlayerList is not available";
return true;
}
else if (cmd ~= "AdminPublishMapList")
{
result = "AdminPublishMapList is not available";
return true;
}
else if (cmd ~= "AdminRestartMap" || cmd ~= "RestartMap")
{
WorldInfo.ServerTravel("?restart", false);
return true;
}
adminuser = Admin(pc);
if (adminuser != none)
{
if (cmd ~= "KickBan")
{
adminuser.KickBan(args/*, "Engine.AccessControl.KickAndPermaBan"*/);
return true;
}
else if (cmd ~= "Kick")
{
adminuser.Kick(args/*, "Engine.AccessControl.KickedMsg"*/);
return true;
}
else if (cmd ~= "PlayerList")
{
adminuser.PlayerList();
return true;
}
else if (cmd ~= "RestartMap")
{
adminuser.RestartMap();
return true;
}
else if (cmd ~= "switch")
{
adminuser.switch(args);
return true;
}
}
if (adminuser.CheatManager != none)
{
if (cmd ~= "SloMo")
{
adminuser.CheatManager.SloMo(float(args));
return true;
}
else if (cmd ~= "SetJumpZ")
{
adminuser.CheatManager.SetJumpZ(float(args));
return true;
}
else if (cmd ~= "SetGravity")
{
adminuser.CheatManager.SetGravity(float(args));
return true;
}
else if (cmd ~= "SetSpeed")
{
adminuser.CheatManager.SetSpeed(float(args));
return true;
}
}
return false;
}
function string MutePlayer(String TargetPlayer, bool bMute, bool bVoice)
{
local PlayerController PC;
local `{PlayerController} TargetPlayerPC;
TargetPlayer -= " ";
TargetPlayerPC = `{PlayerController} (WorldInfo.Game.AccessControl.GetControllerFromString(TargetPlayer));
if ( TargetPlayerPC != none )
{
if (bVoice)
{
foreach WorldInfo.AllControllers(class'PlayerController', PC)
{
if (bMute)
{
PC.ServerMutePlayer(TargetPlayerPC.PlayerReplicationInfo.UniqueId);
}
else {
PC.ServerUnMutePlayer(TargetPlayerPC.PlayerReplicationInfo.UniqueId);
}
}
}
else {
`if(`WITH_TEXT_MUTE)
TargetPlayerPC.bServerMutedText = bMute;
`endif
}
return "Mute player: "$TargetPlayerPC.PlayerReplicationInfo.PlayerName$" mute="$bMute$" voice="$bVoice;
}
return "";
}
function string SessionBan(string TargetPlayer, optional string reason)
{
local PlayerController TargetPlayerPC;
TargetPlayer -= " ";
TargetPlayerPC = PlayerController(WorldInfo.Game.AccessControl.GetControllerFromString(TargetPlayer));
if ( TargetPlayerPC != none )
{
`{AccessControl} (WorldInfo.Game.AccessControl).KickSessionBanPlayer(TargetPlayerPC, TargetPlayerPC.PlayerReplicationInfo.UniqueId, "Engine.AccessControl.KickAndSessionBan");
return TargetPlayer$" banned for this session";
}
return "Player "$TargetPlayer$" not found";
}

View File

@ -0,0 +1,181 @@
/**
* This class will do the actual importing of bans from an URL (json data)
*
* Copyright 2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class BanImporter extends Object;
enum ImportStatus
{
IS_DOWNLOADING,
IS_PARSING,
IS_DONE,
IS_ERROR
};
var ImportStatus status;
var string importFromUrl;
var string errorMessage;
var localized string msgIOError, msgInvalidResponse, msgInvalidJson, msgJsonNoBans,
msgStatusDownloading, msgStatusParsing, msgStatusDone, msgStatusError, msgBadUrl;
var JsonObject jsonBans;
function bool isFinished()
{
return status == IS_DONE || status == IS_ERROR;
}
function string getStatusStr()
{
switch (status)
{
case IS_DOWNLOADING:
return msgStatusDownloading;
case IS_PARSING:
return msgStatusParsing;
case IS_DONE:
return msgStatusDone;
case IS_ERROR:
return msgStatusError;
}
}
function importFrom(string url)
{
local bool result;
jsonBans = none;
errorMessage = "";
status = IS_DOWNLOADING;
importFromUrl = url;
if (url == "")
{
status = IS_ERROR;
errorMessage = msgBadUrl;
return;
}
`log("Importing bans from: "$url,,'WebAdmin');
result = class'HttpFactory'.static.CreateRequest()
.SetURL(importFromUrl)
.SetVerb("GET")
.SetProcessRequestCompleteDelegate(downloadFinished)
.SetHeader("X-WebAdmin", "KF2 WebAdmin; BanImporter")
// This header prevents caching in the client which could prevent a download.
.SetHeader("If-Modified-Since", "Thu, 1 Jan 1970 00:00:00 GMT")
.ProcessRequest();
if (!result)
{
`log("Failed to initiate HTTP request.",,'WebAdmin');
status = IS_ERROR;
errorMessage = msgIOError;
}
}
function downloadFinished(HttpRequestInterface request, HttpResponseInterface response, bool bDidSucceed)
{
`log("Webserver responded with code: "$response.GetResponseCode(),, 'WebAdmin');
if (!bDidSucceed)
{
`log("Ban import failed. IO Error?",,'WebAdmin');
status = IS_ERROR;
errorMessage = msgIOError;
return;
}
if (response.GetResponseCode() == 200)
{
if (parseBans(response.GetContentAsString()))
{
status = IS_DONE;
return;
}
status = IS_ERROR;
errorMessage = Repl(errorMessage, "%content-type%", response.GetContentType());
errorMessage = Repl(errorMessage, "%content-length%", response.GetContentLength());
return;
}
status = IS_ERROR;
errorMessage = msgInvalidResponse;
errorMessage = Repl(errorMessage, "%content-type%", response.GetContentType());
errorMessage = Repl(errorMessage, "%content-length%", response.GetContentLength());
errorMessage = Repl(errorMessage, "%response-code%", response.GetResponseCode());
return;
}
function bool parseBans(string data)
{
local JsonObject json;
json = class'JsonObject'.static.DecodeJson(data);
if (json == none)
{
errorMessage = msgInvalidJson;
return false;
}
jsonBans = json.GetObject("bans");
if (jsonBans == none)
{
errorMessage = msgJsonNoBans;
return false;
}
foreach jsonBans.ObjectArray(json)
{
if (json.HasKey("uniqueNetId") || json.HasKey("steamId64") )
{
return true;
}
}
errorMessage = msgJsonNoBans;
return false;
}
function int applyBansTo(AccessControl accessControl, OnlineSubsystem steamWorks)
{
local JsonObject json;
local string tmp;
local UniqueNetId netid;
local int cnt, idx;
foreach jsonBans.ObjectArray(json)
{
tmp = json.GetStringValue("uniqueNetId");
tmp -= " ";
if (tmp != "") {
class'OnlineSubsystem'.static.StringToUniqueNetId(tmp, netid);
}
else if (steamWorks != none) {
tmp = json.GetStringValue("steamId64");
tmp -= " ";
steamWorks.Int64ToUniqueNetId(tmp, netid);
}
if (class'WebAdminUtils'.static.UniqueNetIdToString(netid) == "")
{
// invalid id
continue;
}
for (idx = 0; idx < accessControl.BannedIDs.length; ++idx)
{
if (accessControl.BannedIDs[idx] == netid)
{
break;
}
}
if (idx == accessControl.BannedIDs.length)
{
// does not exist yet
accessControl.BannedIDs.addItem(netid);
++cnt;
}
}
return cnt;
}

View File

@ -0,0 +1,114 @@
/**
* Default "simple" authentication handler implementation.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class BasicWebAdminAuth extends Object implements(IWebAdminAuth) config(WebAdmin);
var AccessControl ac;
var WorldInfo worldinfo;
var array<BasicWebAdminUser> users;
/**
* If this is not empty the simple authentication handler will require this
* username. Otherwise any username accepted by the current AccessControl will
* be accepted. In case of the standard AccessControl this means that any
* username is ok.
*/
var config array<string> RequireUsername;
//!localization
var localized string NoAccessControl, InvalidCreds, NoPassSet;
function init(WorldInfo wi)
{
local int i;
worldinfo = wi;
ac = worldinfo.Game.AccessControl;
for (i = RequireUsername.length-1; i >= 0; i--)
{
if (Len(RequireUsername[i]) == 0)
{
RequireUsername.remove(i, 1);
}
}
}
function cleanup()
{
local BasicWebAdminUser user;
foreach users(user)
{
user.logout();
}
users.remove(0, users.length);
worldinfo = none;
ac = none;
}
function IWebAdminUser authenticate(string username, string password, string hashAlg, out string errorMsg)
{
local BasicWebAdminUser user;
if (ac == none)
{
`Log("No AccessControl instance.",,'WebAdminAuth');
errorMsg = NoAccessControl;
return none;
}
if (RequireUsername.length > 0 && RequireUsername.find(username) == INDEX_NONE)
{
errorMsg = InvalidCreds;
return none;
}
if (ac.ValidLogin(username, password))
{
user = worldinfo.spawn(class'BasicWebAdminUser');
user.init();
user.setUsername(username);
users.AddItem(user);
return user;
}
errorMsg = InvalidCreds;
if (len(worldinfo.game.consolecommand("get engine.accesscontrol adminpassword", false)) == 0)
{
errorMsg @= NoPassSet;
}
return none;
}
function bool logout(IWebAdminUser user)
{
user.logout();
users.RemoveItem(BasicWebAdminUser(user));
return true;
}
function bool validate(string username, string password, string hashAlg, out string errorMsg)
{
if (RequireUsername.length > 0 && RequireUsername.find(username) == INDEX_NONE)
{
errorMsg = InvalidCreds;
return false;
}
if (ac.ValidLogin(username, password))
{
return true;
}
errorMsg = InvalidCreds;
return false;
}
function bool validateUser(IWebAdminUser user, out string errorMsg)
{
return true;
}
function bool supportHashAlgorithm(string hashAlg)
{
// can't read password, and thus not hash it to validate
return false;
}

View File

@ -0,0 +1,175 @@
/**
* Basic webadmin user.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class BasicWebAdminUser extends Info implements(IWebAdminUser);
var class<MessagingSpectator> PCClass;
var MessagingSpectator PC;
var int maxHistory;
var int counter;
var array<MessageEntry> msgHistory;
var array<string> checkedPrivileges;
function ReceiveMessage( PlayerReplicationInfo Sender, string Msg, name Type )
{
local int idx;
idx = msgHistory.length;
msgHistory.Add(1);
msgHistory[idx].counter = ++counter;
msgHistory[idx].Sender = Sender;
if (Sender != none)
{
msgHistory[idx].senderName = Sender.PlayerName;
}
else {
msgHistory[idx].senderName = "";
}
msgHistory[idx].message = msg;
msgHistory[idx].type = type;
if (Sender.Team != none)
{
msgHistory[idx].teamName = class'WebAdminUtils'.static.getTeamNameEx(Sender.Team);
msgHistory[idx].teamColor = Sender.Team.TeamColor;
msgHistory[idx].teamId = Sender.Team.TeamIndex;
}
else {
msgHistory[idx].teamId = INDEX_NONE;
}
idx = msgHistory.Length - maxHistory;
if (idx > 0)
{
msgHistory.Remove(0, idx);
}
}
function init()
{
local TeamChatProxy tcp;
foreach WorldInfo.AllControllers(class'TeamChatProxy', tcp)
{
tcp.AddReceiver(ReceiveMessage);
}
}
function logout()
{
Destroy();
}
event Destroyed()
{
local TeamChatProxy tcp;
if (PC != none)
{
PC.ClearReceiver(ReceiveMessage);
PC = none;
}
foreach WorldInfo.AllControllers(class'TeamChatProxy', tcp)
{
tcp.ClearReceiver(ReceiveMessage);
}
super.Destroyed();
}
function setUsername(string username)
{
linkPlayerController(username);
}
/**
* Reuse an existing MessagingSpectator with the same name.
*/
protected function linkPlayerController(string username)
{
if (PC != none)
{
if (PC.PlayerReplicationInfo.PlayerName == username)
{
return;
}
PC.ClearReceiver(ReceiveMessage);
PC = none;
}
foreach WorldInfo.AllControllers(class'MessagingSpectator', PC)
{
if (PC.IsA(PCClass.name) && PC.PlayerReplicationInfo.PlayerName == username)
{
PC.AddReceiver(ReceiveMessage);
return;
}
}
//`Log("Creating new MessagingSpectator",,'WebAdmin');
PC = WorldInfo.Spawn(PCClass);
PC.PlayerReplicationInfo.PlayerName = username;
PC.PlayerReplicationInfo.bAdmin = true;
PC.AddReceiver(ReceiveMessage);
}
function string getUsername()
{
if (PC == none || PC.PlayerReplicationInfo == none) return "";
return PC.PlayerReplicationInfo.PlayerName;
}
function string getUserid()
{
return getUsername();
}
function PlayerController getPC()
{
return PC;
}
function bool canPerform(string uri)
{
addCheckedPrivileges(uri);
// only one admin type, can perform whatever (s)he wants
return true;
}
function addCheckedPrivileges(string uri)
{
if (checkedPrivileges.find(uri) == INDEX_NONE)
{
checkedPrivileges.AddItem(uri);
}
}
function clearCheckedPrivileges()
{
checkedPrivileges.length = 0;
}
function array<string> getCheckedPrivileges()
{
return checkedPrivileges;
}
function messageHistory(out array<MessageEntry> history, optional int startingFrom)
{
local int idx, i;
idx = msgHistory.find('counter', startingFrom);
for (i = idx+1; i < msgHistory.Length; i++)
{
history.addItem(msgHistory[i]);
}
}
// not needed
function ISettingsPrivileges getSettingsPrivileges();
defaultproperties
{
PCClass=class'MessagingSpectator'
maxHistory = 50
}

158
WebAdmin/Classes/ChatLog.uc Normal file
View File

@ -0,0 +1,158 @@
/**
* Chatlogger. Writes files with the following content:
* timestamp<tab>username<tab>uniqueid<tab>type<tab>teamid<tab>message
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class ChatLog extends MessagingSpectator config (WebAdmin);
/**
* Mask for the filename. Following place holders can be used:
* %i = server ip
* %p = server port
* %c = computer name (as reported by the OS)
* %v = engine version
*/
var config string Filename;
/**
* Enforce unique filenames. This will simply add a number to the end of the
* filename until its unique.
*/
var config bool bUnique;
/**
* Append a timestamp to the filename
*/
var config bool bIncludeTimeStamp;
var FileWriter writer;
var string tab;
function ReceiveMessage( PlayerReplicationInfo Sender, string Msg, name Type )
{
local string uniqueid;
local int teamindex;
if (writer == none)
{
CreateFileWriter();
}
if (sender == none)
{
writer.Logf(TimeStamp()$tab$""$tab$""$tab$type$tab$INDEX_NONE$tab$msg);
return;
}
uniqueid = class'OnlineSubsystem'.static.UniqueNetIdToString(Sender.UniqueId);
if (Sender.Team == none)
{
teamindex = INDEX_NONE;
}
else {
teamindex = Sender.Team.TeamIndex;
}
writer.Logf(TimeStamp() $ tab $ class'WebAdminUtils'.static.translitText(Sender.PlayerName) $
tab $ uniqueid $ tab $ type $ tab $ teamindex $ tab $ class'WebAdminUtils'.static.translitText(msg));
}
reliable client event ReceiveLocalizedMessage( class<LocalMessage> Message, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
{
if (ClassIsChildOf(Message, class'GameMessage'))
{
ReceiveMessage(RelatedPRI_1, Message.static.GetString(switch, false, RelatedPRI_1, RelatedPRI_2, OptionalObject), name("GameMessage_"$switch));
}
}
reliable client event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type, optional float MsgLifeTime )
{
ReceiveMessage(pri, s, type);
}
function CreateFileWriter()
{
writer = spawn(class'FileWriter');
writer.OpenFile(createFilename(), FWFT_Log,, bUnique, bIncludeTimeStamp);
writer.Logf("--- OPEN "$TimeStamp());
}
function string createFilename()
{
local string result, tmp;
local InternetLink il;
local IpAddr addr, serverIp;
local int serverPort;
if (WorldInfo.Game.OnlineSub.AuthInterface != none)
{
WorldInfo.Game.OnlineSub.AuthInterface.GetServerAddr(serverIp, serverPort);
}
else {
serverPort = 0;
serverIp.AddrD = 0;
}
result = filename;
result = repl(result, "%p", serverPort);
result = repl(result, "%c", WorldInfo.ComputerName);
result = repl(result, "%v", WorldInfo.EngineVersion);
if (InStr(result, "%i") > INDEX_NONE)
{
il = spawn(class'InternetLink');
il.GetLocalIP(addr);
tmp = il.IpAddrToString(addr);
if (InStr(tmp, ":") > INDEX_NONE)
{
tmp = Left(tmp, InStr(tmp, ":"));
}
result = repl(result, "%i", tmp);
il.Destroy();
}
return result;
}
simulated function PostBeginPlay()
{
local TeamChatProxy tcp;
super.PostBeginPlay();
`Log("Chat logging enabled",,'WebAdmin');
if (Len(filename) == 0)
{
filename = "Chatlog_%i_%p";
}
tab = chr(9);
foreach WorldInfo.AllControllers(class'TeamChatProxy', tcp)
{
tcp.AddReceiver(ReceiveMessage);
}
}
event Destroyed()
{
local TeamChatProxy tcp;
foreach WorldInfo.AllControllers(class'TeamChatProxy', tcp)
{
tcp.ClearReceiver(ReceiveMessage);
}
if (writer != none)
{
writer.Logf("--- CLOSE "$TimeStamp());
writer.CloseFile();
}
super.Destroyed();
}
function InitPlayerReplicationInfo()
{
super.InitPlayerReplicationInfo();
PlayerReplicationInfo.PlayerName = "<<ChatLogger>>";
}
defaultproperties
{
bKeepAlive=true
}

View File

@ -0,0 +1,25 @@
/**
* Cache entry for UIGameInfoSummary
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DCEGameInfo extends DataStoreCacheEntry;
var string ClassName;
var UIGameInfoSummary data;
function init(UIGameInfoSummary source)
{
data = source;
if (data == none)
{
`warn("No UIGameInfoSummary",,'WebAdmin');
return;
}
FriendlyName = ensureFriendlyName(data.GameName, data.ClassName, string(data.name));
Description = class'WebAdminUtils'.static.getLocalized(data.Description);
ClassName = data.ClassName;
}

View File

@ -0,0 +1,25 @@
/**
* Cache entry for UIMapSummary
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DCEMapInfo extends DataStoreCacheEntry;
var string MapName;
var UIMapSummary data;
function init(UIMapSummary source)
{
data = source;
if (data == none)
{
`warn("No UIMapSummary",,'WebAdmin');
return;
}
friendlyName = ensureFriendlyName(data.DisplayName, data.MapName, string(data.name));
description = class'WebAdminUtils'.static.getLocalized(data.Description);
MapName = data.MapName;
}

View File

@ -0,0 +1,25 @@
/**
* Cache entry for KFMutatorSummary
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DCEMutator extends DataStoreCacheEntry;
var string ClassName;
var KFMutatorSummary data;
function init(KFMutatorSummary source)
{
data = source;
if (data == none)
{
`warn("No UIMutatorSummary",,'WebAdmin');
return;
}
friendlyName = ensureFriendlyName(data.FriendlyName, data.ClassName, string(data.name));
description = class'WebAdminUtils'.static.getLocalized(data.Description);
ClassName = data.ClassName;
}

View File

@ -0,0 +1,757 @@
/**
* DataStore access class.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DataStoreCache extends Object config(WebAdmin);
`include(WebAdmin.uci)
/**
* Instance providing lookup functionality. Don't access directly, use getDatasource()
*/
var UIDataStore_GameResource datasource;
/**
* List of gametypes
*/
var array<DCEGameInfo> gametypes;
/**
* List of all maps
*/
var array<DCEMapInfo> maps;
struct GameTypeMaps
{
var string gametype;
var array<DCEMapInfo> maps;
};
var array<GameTypeMaps> gameTypeMapCache;
struct MutatorGroup
{
var string GroupName;
var array<DCEMutator> mutators;
};
/**
* List of mutators grouped by group
*/
var array<MutatorGroup> mutatorGroups;
/**
* Simple list of all mutators
*/
var array<DCEMutator> mutators;
struct GameTypeMutators
{
var string gametype;
var array<MutatorGroup> mutatorGroups;
};
/**
* Cache of the mutators available for a specific gametype
*/
var array<GameTypeMutators> gameTypeMutatorCache;
struct MutatorAllowance
{
var string id;
var bool allowed;
};
/**
* Cache mutator allowance to avoid loading packages at runtime as much as
* possible.
*/
var config array<MutatorAllowance> allowanceCache;
`define WITH_WEAPONS
`if(`WITH_WEAPONS)
var array<UIWeaponSummary> weapons;
`endif
function cleanup()
{
gametypes.remove(0, gametypes.length);
maps.remove(0, maps.length);
gameTypeMapCache.remove(0, gameTypeMapCache.length);
mutatorGroups.remove(0, mutatorGroups.length);
gameTypeMutatorCache.remove(0, gameTypeMutatorCache.length);
`if(`WITH_WEAPONS)
weapons.remove(0, weapons.length);
`endif
}
function array<DCEGameInfo> getGameTypes(optional string sorton = "FriendlyName")
{
local array<DCEGameInfo> result;
local int i, j;
if (gametypes.Length == 0)
{
loadGameTypes();
}
if (sorton ~= "FriendlyName")
{
result = gametypes;
return result;
}
for (i = 0; i < gametypes.length; i++)
{
for (j = 0; j < result.length; j++)
{
if (compareGameType(result[j], gametypes[i], sorton))
{
result.Insert(j, 1);
result[j] = gametypes[i];
break;
}
}
if (j == result.length)
{
result.AddItem(gametypes[i]);
}
}
return result;
}
/**
* Resolve a partial classname of a gametype (e.g. without package name) to the
* entry in the cache list.
*/
function int resolveGameType(coerce string classname)
{
local int idx;
if (gametypes.Length == 0)
{
loadGameTypes();
}
classname = "."$classname;
for (idx = 0; idx < gametypes.length; idx++)
{
if (Right("."$gametypes[idx].data.ClassName, Len(classname)) ~= classname)
{
return idx;
}
}
return INDEX_NONE;
}
function loadGameTypes()
{
local array<UIResourceDataProvider> ProviderList;
local UIGameInfoSummary item;
local DCEGameInfo entry;
local int i, j;
if (gametypes.Length > 0)
{
return;
}
getDatasource().GetResourceProviders('GameTypes', ProviderList);
for (i = 0; i < ProviderList.length; i++)
{
item = UIGameInfoSummary(ProviderList[i]);
if (item.bIsDisabled) {
continue;
}
for (j = 0; j < gametypes.length; j++)
{
if (gametypes[j].name == item.name)
{
`log("Found duplicate game mode with name: "$item.name,,'WebAdmin');
break;
}
}
if (j != gametypes.length)
{
continue;
}
entry = new(self, string(item.name)) class'DCEGameInfo';
entry.init(item);
for (j = 0; j < gametypes.length; j++)
{
if (compareGameType(gametypes[j], entry, "FriendlyName"))
{
gametypes.Insert(j, 1);
gametypes[j] = entry;
break;
}
}
if (j == gametypes.length)
{
gametypes.AddItem(entry);
}
}
}
static function bool compareGameType(DCEGameInfo g1, DCEGameInfo g2, string sorton)
{
if (sorton ~= "FriendlyName")
{
return g1.FriendlyName > g2.FriendlyName;
}
else if (sorton ~= "GameName")
{
return g1.data.GameName > g2.data.GameName;
}
else if (sorton ~= "Description")
{
return g1.Description > g2.Description;
}
else if (sorton ~= "ClassName" || sorton ~= "GameMode")
{
return g1.data.ClassName > g2.data.ClassName;
}
else if (sorton ~= "GameAcronym" || sorton ~= "Acronym")
{
return g1.data.GameAcronym > g2.data.GameAcronym;
}
else if (sorton ~= "GameSettingsClass")
{
return g1.data.GameSettingsClassName > g2.data.GameSettingsClassName;
}
}
static function string getMapPrefix(string MapName)
{
local int idx;
idx = InStr(MapName, "-");
if (idx == INDEX_NONE) idx = InStr(MapName, "-");
if (idx == INDEX_NONE) return "";
return Caps(Left(MapName, idx));
}
function array<DCEMapInfo> getMaps(optional string gametype = "", optional string sorton = "MapName")
{
local array<DCEMapInfo> result, workset;
local int i, j, idx;
local array<string> prefixes;
local string prefix;
if (maps.Length == 0)
{
loadMaps();
}
if (gametype == "")
{
workset = maps;
}
else {
idx = resolveGameType(gametype);
if (idx == INDEX_NONE)
{
`Log("gametype not found "$gametype);
return result;
}
j = gameTypeMapCache.find('gametype', gametypes[idx].data.ClassName);
if (j == INDEX_NONE)
{
ParseStringIntoArray(Caps(gametypes[idx].data.MapPrefix), prefixes, "|", true);
for (i = 0; i < maps.length; i++)
{
prefix = getMapPrefix(maps[i].data.MapName);
if (prefixes.find(prefix) > INDEX_NONE)
{
workset.AddItem(maps[i]);
}
}
gameTypeMapCache.add(1);
gameTypeMapCache[gameTypeMapCache.length-1].gametype = gametypes[idx].data.ClassName;
gameTypeMapCache[gameTypeMapCache.length-1].maps = workset;
}
else {
workset = gameTypeMapCache[j].maps;
}
}
if (sorton ~= "MapName")
{
return workset;
}
for (i = 0; i < workset.length; i++)
{
for (j = 0; j < result.length; j++)
{
if (compareMap(result[j], workset[i], sorton))
{
result.Insert(j, 1);
result[j] = workset[i];
break;
}
}
if (j == result.length)
{
result.AddItem(workset[i]);
}
}
return result;
}
/**
* Get a list of gametypes for the given map
*/
function array<string> getGametypesByMap(string MapName)
{
local string prefix;
local array<string> result, prefixes;
local int i;
prefix = getMapPrefix(MapName);
if (len(prefix) == 0) return result;
loadGameTypes();
for (i = 0; i < gametypes.length; ++i)
{
ParseStringIntoArray(Caps(gametypes[i].data.MapPrefix), prefixes, "|", true);
if (prefixes.find(prefix) > INDEX_NONE)
{
result.AddItem(gametypes[i].data.ClassName);
}
}
return result;
}
function loadMaps()
{
local array<UIResourceDataProvider> ProviderList;
local UIMapSummary item;
local DCEMapInfo entry;
local int i, j;
if (maps.Length > 0)
{
return;
}
gameTypeMapCache.Remove(0, gameTypeMapCache.length);
getDatasource().GetResourceProviders('Maps', ProviderList);
for (i = 0; i < ProviderList.length; i++)
{
item = UIMapSummary(ProviderList[i]);
for (j = 0; j < maps.length; j++)
{
if (maps[j].name == item.name)
{
`log("Found duplicate map with name: "$item.name,,'WebAdmin');
break;
}
}
if (j != maps.length)
{
continue;
}
entry = new(self, string(item.name)) class'DCEMapInfo';
entry.init(item);
for (j = 0; j < maps.length; j++)
{
if (compareMap(maps[j], entry, "MapName"))
{
maps.Insert(j, 1);
maps[j] = entry;
break;
}
}
if (j == maps.length)
{
maps.AddItem(entry);
}
}
}
static function bool compareMap(DCEMapInfo g1, DCEMapInfo g2, string sorton)
{
if (sorton ~= "MapName")
{
return g1.data.MapName > g2.data.MapName;
}
else if (sorton ~= "DisplayName")
{
return g1.data.DisplayName > g2.data.DisplayName;
}
else if (sorton ~= "FriendlyName")
{
return g1.FriendlyName > g2.FriendlyName;
}
else if (sorton ~= "Description")
{
return g1.Description > g2.Description;
}
}
function array<MutatorGroup> getMutators(optional string gametype = "", optional string sorton = "FriendlyName")
{
local array<MutatorGroup> result, workset;
local int j, idx;
if (mutatorGroups.Length == 0)
{
loadMutators();
}
if (gametype == "")
{
workset = mutatorGroups;
}
else {
idx = resolveGameType(gametype);
if (idx == INDEX_NONE)
{
`Log("gametype not found "$gametype);
result.length = 0;
return result;
}
j = gameTypeMutatorCache.find('gametype', gametypes[idx].data.ClassName);
if (j == INDEX_NONE)
{
workset = filterMutators(mutatorGroups, gametypes[idx].data.ClassName);
gameTypeMutatorCache.add(1);
gameTypeMutatorCache[gameTypeMutatorCache.length-1].gametype = gametypes[idx].data.ClassName;
gameTypeMutatorCache[gameTypeMutatorCache.length-1].mutatorGroups = workset;
}
else {
workset = gameTypeMutatorCache[j].mutatorGroups;
}
}
if (sorton ~= "FriendlyName")
{
return workset;
}
return workset;
// TODO: implement sorting
/*
for (i = 0; i < workset.length; i++)
{
for (j = 0; j < result.length; j++)
{
if (compareMap(result[j], workset[i], sorton))
{
result.Insert(j, 1);
result[j] = workset[i];
break;
}
}
if (j == result.length)
{
result.AddItem(workset[i]);
}
}
return result;
*/
}
/**
* Filter the source mutator group list on the provided gametype
*/
function array<MutatorGroup> filterMutators(array<MutatorGroup> source, string gametype)
{
local int i, j, k;
local array<MutatorGroup> result;
local MutatorGroup group;
local class<GameInfo> GameModeClass;
local bool findGameType, allowanceChanged;
local string GameTypeMutatorId;
findGameType = true;
for (i = 0; i < source.length; i++)
{
group.GroupName = source[i].groupname;
group.mutators.length = 0;
for (j = 0; j < source[i].mutators.length; j++)
{
if (source[i].mutators[j].data.SupportedGameTypes.length > 0)
{
k = source[i].mutators[j].data.SupportedGameTypes.Find(gametype);
if (k != INDEX_NONE)
{
group.mutators.AddItem(source[i].mutators[j]);
}
}
else {
GameTypeMutatorId = gametype$"@"$source[i].mutators[j].data.ClassName;
k = allowanceCache.find('id', GameTypeMutatorId);
if (k != INDEX_NONE)
{
if (allowanceCache[k].allowed)
{
group.mutators.AddItem(source[i].mutators[j]);
}
}
else {
if (GameModeClass == none && findGameType)
{
findGameType = false;
GameModeClass = class<GameInfo>(DynamicLoadObject(gametype, class'class'));
if (GameModeClass == none)
{
`Log("DataStoreCache::filterMutators() - Unable to find game class: "$gametype);
}
}
if(GameModeClass != none)
{
allowanceChanged = true;
k = allowanceCache.length;
allowanceCache.length = k+1;
allowanceCache[k].id = GameTypeMutatorId;
if (GameModeClass.static.AllowMutator(source[i].mutators[j].data.ClassName))
{
group.mutators.AddItem(source[i].mutators[j]);
allowanceCache[k].allowed = true;
}
}
}
}
}
if (group.mutators.length > 0)
{
result.AddItem(group);
}
}
if (allowanceChanged)
{
SaveConfig();
}
return result;
}
function loadMutators()
{
local array<UIResourceDataProvider> ProviderList;
local KFMutatorSummary item;
local DCEMutator entry;
local int i, j, groupid, emptyGroupId;
local array<string> groups;
local string group;
if (mutatorGroups.Length > 0)
{
return;
}
mutators.Remove(0, mutators.length);
gameTypeMutatorCache.Remove(0, gameTypeMutatorCache.length);
emptyGroupId = 0;
// the empty group
mutatorGroups.Length = 1;
getDatasource().GetResourceProviders('Mutators', ProviderList);
for (i = 0; i < ProviderList.length; i++)
{
item = KFMutatorSummary(ProviderList[i]);
if (item.bIsDisabled)
{
continue;
}
for (j = 0; j < mutators.length; j++)
{
if (mutators[j].name == item.name)
{
`log("Found duplicate mutator with name: "$item.name,,'WebAdmin');
break;
}
}
if (j != mutators.length)
{
continue;
}
entry = new(self, string(item.Name)) class'DCEMutator';
entry.init(item);
groups = item.GroupNames;
if (groups.length == 0)
{
groups.AddItem("");
}
foreach groups(group)
{
groupid = mutatorGroups.find('GroupName', group);
if (groupid == INDEX_NONE)
{
for (groupid = 0; groupid < mutatorGroups.length; groupid++)
{
if (mutatorGroups[groupid].GroupName > group)
{
break;
}
}
mutatorGroups.Insert(groupid, 1);
mutatorGroups[groupid].GroupName = Caps(group);
}
if (emptyGroupId == -1 && len(group) == 0)
{
emptyGroupId = groupid;
}
for (j = 0; j < mutatorGroups[groupid].mutators.length; j++)
{
if (compareMutator(mutatorGroups[groupid].mutators[j], entry, "FriendlyName"))
{
mutatorGroups[groupid].mutators.Insert(j, 1);
mutatorGroups[groupid].mutators[j] = entry;
break;
}
}
if (j == mutatorGroups[groupid].mutators.length)
{
mutatorGroups[groupid].mutators.AddItem(entry);
}
}
for (j = 0; j < mutators.length; j++)
{
if (compareMutator(mutators[j], entry, "FriendlyName"))
{
mutators.Insert(j, 1);
mutators[j] = entry;
break;
}
}
if (j == mutators.length)
{
mutators.AddItem(entry);
}
}
if (emptyGroupId == -1)
{
emptyGroupId = mutatorGroups.length;
mutatorGroups[emptyGroupId].GroupName = "";
}
// remove groups with single entries
for (i = mutatorGroups.length-1; i >= 0 ; i--)
{
if (i == emptyGroupId) continue;
if (mutatorGroups[i].mutators.length > 1) continue;
entry = mutatorGroups[i].mutators[0];
for (j = 0; j < mutatorGroups[emptyGroupId].mutators.length; j++)
{
if (mutatorGroups[emptyGroupId].mutators[j] == entry)
{
break;
}
if (compareMutator(mutatorGroups[emptyGroupId].mutators[j], entry, "FriendlyName"))
{
mutatorGroups[emptyGroupId].mutators.Insert(j, 1);
mutatorGroups[emptyGroupId].mutators[j] = entry;
break;
}
}
if (j == mutatorGroups[emptyGroupId].mutators.length)
{
mutatorGroups[emptyGroupId].mutators.AddItem(entry);
}
mutatorGroups.Remove(i, 1);
}
if (mutatorGroups[emptyGroupId].mutators.Length == 0)
{
mutatorGroups.Remove(emptyGroupId, 1);
}
}
static function bool compareMutator(DCEMutator m1, DCEMutator m2, string sorton)
{
if (sorton ~= "ClassName")
{
return m1.data.ClassName > m2.data.ClassName;
}
else if (sorton ~= "FriendlyName")
{
return m1.FriendlyName > m2.FriendlyName;
}
else if (sorton ~= "Description")
{
return m1.Description > m2.Description;
}
}
`if(`WITH_WEAPONS)
function loadWeapons()
{
local array<UIResourceDataProvider> ProviderList;
local UIWeaponSummary item;
local int i, j;
if (weapons.Length > 0)
{
return;
}
getDatasource().GetResourceProviders('Weapons', ProviderList);
for (i = 0; i < ProviderList.length; i++)
{
item = UIWeaponSummary(ProviderList[i]);
for (j = 0; j < weapons.length; j++)
{
if (compareWeapon(weapons[j], item, "FriendlyName"))
{
weapons.Insert(j, 1);
weapons[j] = item;
break;
}
}
if (j == weapons.length)
{
weapons.AddItem(item);
}
}
}
static function bool compareWeapon(UIWeaponSummary w1, UIWeaponSummary w2, string sorton)
{
if (sorton ~= "ClassName")
{
return w1.ClassName > w2.ClassName;
}
else if (sorton ~= "FriendlyName")
{
return w1.FriendlyName > w2.FriendlyName;
}
else if (sorton ~= "Description")
{
return w1.Description > w2.Description;
}
/*
else if (sorton ~= "AmmoClassPath")
{
return w1.AmmoClassPath > w2.AmmoClassPath;
}
*/
/*
// not available everywhere, and not really that interesting
else if (sorton ~= "MeshReference")
{
return w1.MeshReference > w2.MeshReference;
}
*/
}
`endif
function UIDataStore_GameResource getDatasource()
{
if (datasource == none)
{
datasource = UIDataStore_GameResource(class'UIRoot'.static.StaticResolveDataStore(class'UIDataStore_GameResource'.default.Tag));
}
return datasource;
}

View File

@ -0,0 +1,81 @@
/**
* Base class for entries in the data store cache. Used to make certain usage
* easier and to work around some data transformation entires. It encapsulates
* specific ROUIResourceDataProvider subclasses.
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DataStoreCacheEntry extends Object abstract;
/**
* Localized/fixed values
*/
var string FriendlyName, Description;
final static function string ensureFriendlyName(string currentValue, string className, string fallback)
{
if (len(currentValue) != 0)
{
// might be a <String:...> value
currentValue = class'WebAdminUtils'.static.getLocalized(currentValue);
}
// check if localized
if (len(currentValue) != 0)
{
return currentValue;
}
currentValue = constructFriendlyName(className);
if (len(currentValue) != 0)
{
return currentValue;
}
return fallback;
}
/**
* Helper function to create a friendly name from a classname
*/
final static function string constructFriendlyName(String className)
{
local int i;
local string pkg, cls, res;
if (len(className) == 0) return "";
i = InStr(className, ".");
if (i > 0)
{
pkg = left(className, i);
cls = mid(className, i+1);
}
else {
pkg = "";
cls = className;
}
res = Localize(cls, "DisplayName", pkg);
if (InStr(res, className$".DisplayName?") == INDEX_NONE)
{
if (len(res) > 0) return res;
}
return "";
// FooBarQuux -> Foo Bar Quux
/*
for (i = 0; i < len(cls); i++) {
pkg = Mid(cls, i, 1);
if (pkg == "_" || pkg == "-")
{
pkg = " ";
}
else if (Caps(pkg) == pkg && string(int(pkg)) != pkg)
{
pkg = " "$pkg;
}
res = res$pkg;
}
return res;
*/
}

View File

@ -0,0 +1,41 @@
/**
* Load the Killing Floor 2 datastore provider
*
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class DataStoreCacheKF extends DataStoreCache;
function UIDataStore_GameResource getDatasource()
{
if (datasource == none)
{
class'KFUIDataStore_GameResource'.static.InitializeProviders();
datasource = KFUIDataStore_GameResource(class'UIRoot'.static.StaticResolveDataStore(class'KFUIDataStore_GameResource'.default.Tag));
}
return datasource;
}
// FIXME: temp solution because gametypes are not defined in DefaultKFGameContent.ini yet
/*
function loadGameTypes()
{
local UIGameInfoSummary item;
local DCEGameInfo entry;
if (gametypes.Length > 0)
{
return;
}
item = new(self, "Survival") class'UIGameInfoSummary';
item.ClassName = "KFGameContent.KFGameInfo_Survival";
item.MapPrefix = "KF";
item.GameSettingsClassName = "KFGame.KFOnlineGameSettings";
entry = new(self, string(item.name)) class'DCEGameInfo';
entry.init(item);
gametypes.AddItem(entry);
}
*/

View File

@ -0,0 +1,266 @@
/**
* Server wide settings
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class GeneralSettings extends WebAdminSettings;
`include(WebAdmin.uci)
`include(SettingsMacros.uci)
var GameInfo gameinfo;
function setCurrentGameInfo(GameInfo instance)
{
super.setCurrentGameInfo(instance);
gameinfo = instance;
// reload values
if (gameinfo != none)
{
initSettings();
}
}
function cleanupSettings()
{
gameinfo = none;
super.cleanupSettings();
}
function initSettings()
{
local OnlineGameSettings GameSettings;
local GameEngine GEngine;
GEngine = GameEngine(class'Engine'.static.GetEngine());
// Server Information
if (gameinfo != none && gameinfo.GameReplicationInfo != none)
{
SetStringPropertyByName('ServerName', gameinfo.GameReplicationInfo.ServerName);
}
else
{
SetStringPropertyByName('ServerName', class'GameReplicationInfo'.default.ServerName);
}
if (GEngine != none)
{
`SetBoolPropertyByName('bUsedForTakeover', GEngine.bUsedForTakeover);
}
else
{
`SetBoolPropertyByName('bUsedForTakeover', class'GameEngine'.default.bUsedForTakeover);
}
// Connection settings
`GI_SetIntPropertyByName(,, MaxPlayers);
`GI_SetFloatPropertyByName(,, MaxIdleTime);
// Cheat Detection
if (gameinfo != none)
{
GameSettings = gameinfo.GameInterface.GetGameSettings(gameinfo.PlayerReplicationInfoClass.default.SessionName);
}
if (GameSettings != None)
{
SetIntPropertyByName('bAntiCheatProtected', int(GameSettings.bAntiCheatProtected));
}
else
{
SetIntPropertyByName('bAntiCheatProtected', int(false));
}
// Game settings
`GI_SetFloatPropertyByName(,, GameDifficulty);
`GI_SetIntPropertyByName(, KFGameInfo, GameLength);
`GI_SetBoolPropertyByName(, KFGameInfo, bDisableTeamCollision);
// TODO: use custom map list?
// Administration settings
`GI_SetBoolPropertyByName(,, bAdminCanPause);
if (gameinfo != none && KFAccessControl(gameinfo.AccessControl) != none)
{
`SetBoolPropertyByName('bSilentAdminLogin', KFAccessControl(gameinfo.AccessControl).bSilentAdminLogin);
}
else
{
`SetBoolPropertyByName('bSilentAdminLogin', class'KFAccessControl'.default.bSilentAdminLogin);
}
// Map Voting
`GI_SetBoolPropertyByName(, KFGameInfo, bDisableMapVote);
`GI_SetFloatPropertyByName(, KFGameInfo, MapVoteDuration);
`GI_SetFloatPropertyByName(, KFGameInfo, MapVotePercentage);
// Kick voting
`GI_SetBoolPropertyByName(, KFGameInfo, bDisableKickVote);
`GI_SetFloatPropertyByName(, KFGameInfo, KickVotePercentage);
// Chat
`GI_SetBoolPropertyByName(, KFGameInfo, bDisableVOIP);
`GI_SetBoolPropertyByName(, KFGameInfo, bDisablePublicTextChat);
`GI_SetBoolPropertyByName(, KFGameInfo, bPartitionSpectators);
}
function saveSettings()
{
local int val;
local OnlineGameSettings GameSettings;
local GameEngine GEngine;
local bool bWasUsedForTakeover;
GEngine = GameEngine(class'Engine'.static.GetEngine());
// Cheat Detection
if (gameinfo != none)
{
GameSettings = gameinfo.GameInterface.GetGameSettings(gameinfo.PlayerReplicationInfoClass.default.SessionName);
}
if (GameSettings != None)
{
GetIntPropertyByName('bAntiCheatProtected', val);
if (GameSettings.bAntiCheatProtected != (val != 0))
{
GameSettings.bAntiCheatProtected = val != 0;
gameinfo.GameInterface.UpdateOnlineGame(gameinfo.PlayerReplicationInfoClass.default.SessionName, GameSettings, true);
}
}
// GRI
GetStringPropertyByName('ServerName', class'GameReplicationInfo'.default.ServerName);
class'GameReplicationInfo'.static.StaticSaveConfig();
if (gameinfo != none && gameinfo.GameReplicationInfo != none)
{
GetStringPropertyByName('ServerName', gameinfo.GameReplicationInfo.ServerName);
gameinfo.GameReplicationInfo.SaveConfig();
}
`GetBoolPropertyByName('bUsedForTakeover', class'GameEngine'.default.bUsedForTakeover);
class'GameEngine'.static.StaticSaveConfig();
if (GEngine != none)
{
bWasUsedForTakeover = GEngine.bUsedForTakeover;
`GetBoolPropertyByName('bUsedForTakeover', GEngine.bUsedForTakeover);
GEngine.SaveConfig();
if (!GEngine.bUsedForTakeover)
{
GEngine.bAvailableForTakeover = false;
}
else if (!bWasUsedForTakeover)
{
GEngine.bAvailableForTakeover = true;
}
}
// AccessControl
`GetBoolPropertyByName('bSilentAdminLogin', class'KFAccessControl'.default.bSilentAdminLogin);
class'KFAccessControl'.static.StaticSaveConfig();
if (gameinfo != none && `{AccessControl} (gameinfo.AccessControl) != none)
{
`GetBoolPropertyByName('bSilentAdminLogin', KFAccessControl(gameinfo.AccessControl).bSilentAdminLogin);
gameinfo.AccessControl.SaveConfig();
}
// KFGameInfo
`GI_GetBoolPropertyByName(, KFGameInfo, bDisableTeamCollision);
`GI_GetBoolPropertyByName(, KFGameInfo, bDisableVOIP);
`GI_GetBoolPropertyByName(, KFGameInfo, bDisablePublicTextChat);
`GI_GetBoolPropertyByName(, KFGameInfo, bPartitionSpectators);
`GI_GetBoolPropertyByName(, KFGameInfo, bDisableMapVote);
`GI_GetFloatPropertyByName(, KFGameInfo, MapVoteDuration);
`GI_GetFloatPropertyByName(, KFGameInfo, MapVotePercentage);
`GI_GetBoolPropertyByName(, KFGameInfo, bDisableKickVote);
`GI_GetFloatPropertyByName(, KFGameInfo, KickVotePercentage);
class'KFGameInfo'.static.StaticSaveConfig();
// GameInfo
`GI_GetIntPropertyByName('MaxPlayers',, MaxPlayers);
`GI_GetBoolPropertyByName('bAdminCanPause',, bAdminCanPause);
`GI_GetFloatPropertyByName('MaxIdleTime',, MaxIdleTime);
class'GameInfo'.static.StaticSaveConfig();
if (gameinfo != none) {
gameinfo.SaveConfig();
// WD JMH - Make sure the advertised settings get updated now
gameinfo.UpdateGameSettings();
gameinfo.UpdateGameSettingsCounts();
}
// The following values should only change on map change
`GI_GetIntPropertyByName(, KFGameInfo, GameLength);
class'KFGameInfo'.static.StaticSaveConfig();
`GI_GetFloatPropertyByName('GameDifficulty',, GameDifficulty);
class'GameInfo'.static.StaticSaveConfig();
}
defaultproperties
{
SettingsGroups.Add((groupId="Server",pMin=0,pMax=100))
SettingsGroups.Add((groupId="Connection",pMin=100,pMax=200))
SettingsGroups.Add((groupId="CheatDetection",pMin=200,pMax=300))
SettingsGroups.Add((groupId="Game",pMin=300,pMax=400))
SettingsGroups.Add((groupId="Administration",pMin=500,pMax=600))
SettingsGroups.Add((groupId="MapVoting",pMin=600,pMax=650))
SettingsGroups.Add((groupId="KickVoting",pMin=650,pMax=700))
SettingsGroups.Add((groupId="Chat",pMin=700,pMax=800))
//The labels for all of these properties are in WebAdmin.int
//They MUST mirror the order of these entries, or the labels and choices will be on the wrong property
// Server Information
Properties.Add((PropertyId=0,Data=(Type=SDT_String)))
PropertyMappings.Add((Id=0,name="ServerName",MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
Properties.Add((PropertyId=1,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=1,name="bUsedForTakeover",MappingType=PVMT_IdMapped,ValueMappings=((Id=1),(Id=0))))
// Connection settings
Properties.Add((PropertyId=101,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=101,name="MaxPlayers",MappingType=PVMT_Ranged,MinVal=0,MaxVal=12,RangeIncrement=1))
Properties.Add((PropertyId=102,Data=(Type=SDT_Float)))
PropertyMappings.Add((Id=102,name="MaxIdleTime",MappingType=PVMT_Ranged,MinVal=0,MaxVal=300,RangeIncrement=5))
// Cheat Detection
Properties.Add((PropertyId=200,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=200,name="bAntiCheatProtected",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
// Game settings
Properties.Add((PropertyId=302,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=302,name="bDisableTeamCollision",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
Properties.Add((PropertyId=303,Data=(Type=SDT_Float)))
PropertyMappings.Add((Id=303,name="GameDifficulty",MappingType=PVMT_PredefinedValues,ValueMappings=((Id=0),(Id=1),(Id=2),(Id=3)),PredefinedValues=((Type=SDT_Float),(Type=SDT_Float),(Type=SDT_Float),(Type=SDT_Float)),MinVal=0,MaxVal=3,RangeIncrement=1))
FloatPredefinedValues.Add((PropertyId=303,Values=(0,1,2,3)))
Properties.Add((PropertyId=304,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=304,name="GameLength",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1),(Id=2),(Id=3)),PredefinedValues=((Type=SDT_Int32),(Type=SDT_Int32),(Type=SDT_Int32),(Type=SDT_Int32)),MinVal=0,MaxVal=3,RangeIncrement=1))
// Administration settings
Properties.Add((PropertyId=500,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=500,name="bAdminCanPause",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
Properties.Add((PropertyId=501,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=501,name="bSilentAdminLogin",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
// Chat
Properties.Add((PropertyId=701,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=701,name="bDisableVOIP",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
Properties.Add((PropertyId=702,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=702,name="bDisablePublicTextChat",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
Properties.Add((PropertyId=703,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=703,name="bPartitionSpectators",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1))))
// Kick voting
`def_boolproperty(650, "bDisableKickVote")
`def_floatproperty(654, "KickVotePercentage", 0, 1.0, 0.05)
// Map Voting
`def_boolproperty(600, "bDisableMapVote")
`def_floatproperty(601, "MapVoteDuration", 10, 300, 5)
`def_floatproperty(602, "MapVotePercentage", 0, 100, 5)
}

View File

@ -0,0 +1,10 @@
/**
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class HashLib extends Object abstract;
function string getAlgName();
function string getHash(coerce string inputData);

View File

@ -0,0 +1,44 @@
/**
* Defines the interface for gametype/mutator configuration handlers that can
* not be handled soly by a Settings subclass. By implemented
* IAdvWebAdminSettings the developer has more freedom of configuration items.
* However, using it does create a dependency on the WebAdmin package (an
* optional server side only package).
*
* IMPORTANT! The WebAdmin is an optional server-side only package. Do not
* introduce a dependency on this package from a package that a client needs to
* download in order to play your mod.
*
* Implementers must be a subclass of Settings (or one of it's subclasses).
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface IAdvWebAdminSettings;
/**
* Called when the instance is created.
*/
function advInitSettings(WorldInfo worldinfo, DataStoreCache dscache);
/**
* Called when the instance is queued to be cleanup. It should be used to unset
* all actor references.
*/
function cleanupSettings();
/**
* Called when the settings should be saved. Return true when the settings were
* saved. Use the webadmin reference to addMessage for feedback to the user about
* incorrect values and what not.
*/
function bool advSaveSettings(WebRequest request, WebAdminMessages messages);
/**
* Called to render the settings. This produce the HTML code for all settings
* this implementation should expose. You can use the given SettingsRenderer to
* perform standard rendering.
*/
function advRenderSettings(WebResponse response, SettingsRenderer renderer,
optional string substName = "settings", optional ISettingsPrivileges privileges);

View File

@ -0,0 +1,96 @@
/**
* The query handler interface. The WebAdmin contains a collection of query
* handlers with handle most requests assigned to the WebAdmin application.
* During creating the query handler will receive a couple of set up calls:
* init(...) and registerMenuItems(...). The webadmin has to register all
* URLs it will handle (url without the webapp path prefix). It is also allowed
* to replace an existing menu item. When the WebAdmin is shut down the
* cleanup() method will be called. Use this to perform some clean up and to set
* all Actor references to none (in case the query handler extends Object).
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface IQueryHandler;
struct KeyValuePair
{
var string key;
var string value;
};
/**
* Struct contain current query information. Passed to the QueryHandlers.
*/
struct WebAdminQuery
{
var WebRequest request;
var WebResponse response;
var ISession session;
var IWebAdminUser user;
var array<KeyValuePair> cookies;
/** true if the client will be given an application/xhtml+xml document */
var bool acceptsXhtmlXml;
};
enum EMessageType
{
MT_Information,
MT_Warning,
MT_Error,
};
/**
* Used for the generic message processing in WebAdmin.addMessage();
*/
struct Message
{
var EMessageType type;
var string text;
};
/**
* Called when the WebAdmin creates and initializes this query handler.
*/
function init(WebAdmin webapp);
/**
* Cleanup (prepare for being destroyed). If the implementation extends Object
* it should set all actor references to none.
*/
function cleanup();
/**
* Called by the webadmin to request the query handler to handle this query.
*
* @return true when the query was handled.
*/
function bool handleQuery(WebAdminQuery q);
/**
* Called in case of an unhandled path.
*
* @return true when the query was handled.
*/
function bool unhandledQuery(WebAdminQuery q);
/**
* Called before sendPage is completed. Can be implemented by subclasses to add
* additional information to the rendered page.
*/
function decoratePage(WebAdminQuery q);
/**
* Called by the webadmin to request the query handler to add its menu items to
* the web admin menu. The menu is used to determine what query handler will be
* handle a given path. Paths not registered will be passed to all query handlers
* until one returns true.
*/
function registerMenuItems(WebAdminMenu menu);
/**
* Return true if this handler produces valid XHTML documents. In that case
* the content might be send as "application/xhtml+xml" rather than "text/html"
*/
function bool producesXhtml();

View File

@ -0,0 +1,48 @@
/**
* A session interface
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface ISession;
/**
* Return the session identifier
*/
function string getId();
/**
* Reset the session's data. The ID will stay the same.
*/
function reset();
/**
* Get an object instance from this session.
*/
function Object getObject(string key);
/**
* Add an object to the session
*/
function putObject(string key, Object value);
/**
* Remove the entry with the given key
*/
function removeObject(string key);
/**
* Get a string from this session.
*/
function string getString(string key, optional string defValue = "");
/**
* Add a string value to the session.
*/
function putString(string key, string value);
/**
* Remove the entry with the given key
*/
function removeString(string key);

View File

@ -0,0 +1,28 @@
/**
* Session handler interface
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface ISessionHandler;
/**
* Create a new session
*/
function ISession create();
/**
* Get an existing session. Returns none when there is no session with that id.
*/
function ISession get(string id);
/**
* Destroy the given session.
*/
function bool destroy(ISession session);
/**
* Destroy all sessions
*/
function destroyAll();

View File

@ -0,0 +1,44 @@
/**
* A SettingModifier provides the means to augment the settings as they are
* rendered for output. This can be used to disable certain settings are provide
* additional information.
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
interface ISettingsModifier;
/**
* Called before rendering the settings. Return false if this modifier does not
* intent to augment any settings for the given instance.
*
* @return true if this modifier is going to augement the output.
*/
function bool modifierAppliesTo(WebAdminSettings settings);
/**
* Called at the end of processing the settings. Can be used to clean up local
* state. Any returned content is added to the rendered content.
*/
function string finalizeAugmentation(WebResponse response, String path);
/**
* Called when a setting is being rendered.
* @param renderState
* The rendering state
* @param index
* The index of rendering subelements. This is higher than -1 when the setting
* has multiple render phases for subitems (i.e. a selection list). Note that
* during rendering of the subelements the renderState is not reset.
* @param inContainer
* True when the subelements are within a container, which means that is an
* additional step (with index = -1) for rendering the container.
*/
function augmentSetting(SettingRendererState renderState, optional int index = -1, optional bool inContainer);
/**
* Called after processing submitted values. Should be used to ensure some
* settings have the correct values. This function is called stand-alone.
*/
function ensureSettingValues(WebAdminSettings settings);

View File

@ -0,0 +1,19 @@
/**
* Interface to provide more granural privileges on settings pages.
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
interface ISettingsPrivileges;
/**
* Sets the base uri to check privileges against. This is called by QHDefault
* before settings will be rendered.
*/
function setBasePrivilegeUri(String uri);
/**
* Check if
*/
function bool hasSettingsGroupAccess(class<WebAdminSettings> settings, string groupId);

View File

@ -0,0 +1,63 @@
/**
* WebAdmin authentication interface. An implementation of this interface is
* used to create IWebAdminUser instances.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface IWebAdminAuth;
/**
* Initialize the authentication handler
*/
function init(WorldInfo worldinfo);
/**
* Cleanup (prepare for being destroyed)
*/
function cleanup();
/**
* Try to log in a user with the provided credentials
*
* @param username
* @param password
* @param hashAlg Hash algorithm used to hash the password. Passwords will be hashed as: hash(password+username)
* @param errorMsg can be set to a friendly error message or reason why authentication failed
* @return none when authentication failed, otherwise the created user instance
*/
function IWebAdminUser authenticate(string username, string password, string hashAlg, out string errorMsg);
/**
* Logout the given user. A user does not explicitly log out.
*
* @return true when the user was succesfully logged out.
*/
function bool logout(IWebAdminUser user);
/**
* Like authenticate(...) except that the user is not explicitly logged in (or created).
* This will be used to re-validate an already existing user. For example in the case a
* time out was triggered and the user needs to re-enter his/her password.
*
* @param username
* @param password
* @param hashAlg Hash algorithm used to hash the password. Passwords will be hashed as: hash(password+username)
* @param errorMsg can be set to a friendly error message or reason why authentication failed
*/
function bool validate(string username, string password, string hashAlg, out string errorMsg);
/**
* Validate the given user. This will be used to check if the IWebAdminUser is still valid,
* for example to check if the user wasn't deleted in the mean while.
*
* @param user the user instance to validate
* @param errorMsg can be set to a friendly error message or reason why authentication failed
*/
function bool validateUser(IWebAdminUser user, out string errorMsg);
/**
* Return true if this authentication mechanism supports the given hash algorithm
*/
function bool supportHashAlgorithm(string hashAlg);

View File

@ -0,0 +1,81 @@
/**
* A webadmin user record. Creates by the IWebAdminAuth instance.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
interface IWebAdminUser;
struct MessageEntry
{
var int counter;
/**
* Can be none
*/
var PlayerReplicationInfo sender;
var string senderName;
var string message;
/**
* Say, TeamSay, None
*/
var name type;
var string teamName;
var color teamColor;
/**
* INDEX_NONE if not a member of a team
*/
var int teamId;
};
/**
* Return the name of the user. This is the name used for displaying. It can be
* the same as the result of getUserid();
*/
function string getUsername();
/**
* Return the user id, this was the name used to log in on the webadmin.
*/
function string getUserid();
/**
* Used to check for permissions to perform given actions.
*
* @param path an URL containing the action description. See rfc2396 for more information.
* The scheme part of the URL will be used as identifier for the interface.
* The host is the IP to witch the webserver is bound.
* for example: webadmin://127.0.0.1:8080/current/console
* Note that the webapplication path is not included.
*/
function bool canPerform(string url);
/**
* Returns a list of all urls that were passed to #canPerform(). It does not
* contain information wether the result was true or false.
*/
function array<string> getCheckedPrivileges();
function clearCheckedPrivileges();
/**
* Return a PlayerController associated with this user. This method might return
* none when there is no player controller associated with this user.
*/
function PlayerController getPC();
/**
* Get the message history.
*/
function messageHistory(out array<MessageEntry> history, optional int startingFrom);
/**
* Called when this user is logged out.
*/
function logout();
/**
* Return an instance of the settings privileges class to filter on settings, and/or
* groups. It is not required.
*/
function ISettingsPrivileges getSettingsPrivileges();

View File

@ -0,0 +1,97 @@
/**
* WebApplication that provides static content, even though it is called
* "ImageServer" it also serves other static content like javascript files.
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class KF2ImageServer extends ImageServer config(WebAdmin);
/**
* if true don't try to send gzip files
*/
var config bool bNoGzip;
function string normalizeUri(string uri)
{
local array<string> str;
local int i;
ParseStringIntoArray(repl(uri, "\\", "/"), str, "/", true);
for (i = str.length-1; i >= 0; i--)
{
if (str[i] == "..")
{
i -= 1;
if (i < 0)
{
str.remove(0, 1);
}
else {
str.remove(i, 2);
}
}
}
JoinArray(str, uri, "/");
return "/"$uri;
}
function Query(WebRequest Request, WebResponse Response)
{
local string ext;
local int idx;
if (InStr(Request.URI, "..") != INDEX_NONE)
{
Request.URI = normalizeUri(Request.URI);
}
idx = InStr(Request.URI, ".", true);
if (idx != INDEX_NONE)
{
// get the file extension
ext = Mid(Request.URI, idx+1);
// if ?gzip was present, send the gzip file if the browser supports it.
if (!bNoGzip && Request.GetVariableCount("gzip") > 0)
{
if (InStr(","$Request.GetHeader("accept-encoding")$",", ",gzip,") != INDEX_NONE)
{
if (Response.FileExists(Path $ Request.URI $ ".gz"))
{
response.AddHeader("Content-Encoding: gzip");
Request.URI = Request.URI$".gz";
}
else {
`warn("Tried to serve non existing gzip file: " $ Path $ Request.URI $ ".gz",,'WebAdmin');
}
}
}
}
if( ext ~= "js" )
{
Response.SendStandardHeaders("text/javascript", true);
Response.IncludeBinaryFile( Path $ Request.URI );
return;
}
else if( ext ~= "css" )
{
Response.SendStandardHeaders("text/css", true);
Response.IncludeBinaryFile( Path $ Request.URI );
return;
}
else if( ext ~= "ico" )
{
Response.SendStandardHeaders("image/x-icon", true);
Response.IncludeBinaryFile( Path $ Request.URI );
return;
}
else if( ext ~= "gz" )
{
Response.SendStandardHeaders("application/x-gzip", true);
Response.IncludeBinaryFile( Path $ Request.URI );
return;
}
super.query(Request, Response);
}

View File

@ -0,0 +1,6 @@
/**
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class KF2ServerAdmin extends WebAdmin;

View File

@ -0,0 +1,101 @@
/**
* Generic settings for all builtin gametypes
*
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class KFGameInfoSettings extends WebAdminSettings;
`include(WebAdmin.uci)
`include(SettingsMacros.uci)
`define GIV_CLASS KFGameInfo
`define GIV_VAR KFGameInfoClass
var class<`{GIV_CLASS}> `{GIV_VAR};
var GameInfo gameinfo;
function setCurrentGameInfo(GameInfo instance)
{
if (instance != none)
{
if (!ClassIsChildOf(instance.class, `{GIV_VAR}))
{
return;
}
}
super.setCurrentGameInfo(instance);
gameinfo = instance;
// reload values
if (gameinfo != none)
{
initSettings();
}
}
function cleanupSettings()
{
gameinfo = none;
super.cleanupSettings();
}
function initSettings()
{
if (`{GIV_VAR} == none) return;
`GIV_SetIntPropertyByName('GameStartDelay', GameStartDelay);
`GIV_SetIntPropertyByName('ReadyUpDelay', ReadyUpDelay);
`GIV_SetIntPropertyByName('EndOfGameDelay', EndOfGameDelay);
`GIV_SetBoolPropertyByName('bDisablePickups', bDisablePickups);
// `GIV_SetBoolPropertyByName('bEnableObjectives', bEnableObjectives);
// `GIV_SetBoolPropertyByName('bEnableCoopObjectives', bEnableCoopObjectives);
}
function saveSettings()
{
saveInternal();
if (gameinfo != none) {
gameinfo.SaveConfig();
}
`{GIV_VAR}.static.StaticSaveConfig();
}
protected function saveInternal()
{
local int val;
if (`{GIV_VAR} == none) return;
`GIV_GetIntPropertyByName('GameStartDelay', GameStartDelay);
`GIV_GetIntPropertyByName('ReadyUpDelay', ReadyUpDelay);
`GIV_GetIntPropertyByName('EndOfGameDelay', EndOfGameDelay);
`GIV_GetBoolPropertyByName('bDisablePickups', bDisablePickups);
// `GIV_GetBoolPropertyByName('bEnableObjectives', bEnableObjectives);
// `GIV_GetBoolPropertyByName('bEnableCoopObjectives', bEnableCoopObjectives);
}
defaultproperties
{
SettingsGroups.Add((groupId="Rules",pMin=0,pMax=100))
SettingsGroups.Add((groupId="Game",pMin=100,pMax=200))
SettingsGroups.Add((groupId="Advanced",pMin=1000,pMax=1100))
KFGameInfoClass=class'KFGameInfo'
Properties[0]=(PropertyId=1,Data=(Type=SDT_Int32))
PropertyMappings[0]=(Id=1,name="EndOfGameDelay",MappingType=PVMT_Ranged,MinVal=0,MaxVal=9999,RangeIncrement=5)
Properties[1]=(PropertyId=2,Data=(Type=SDT_Int32))
PropertyMappings[1]=(Id=2,name="GameStartDelay",MappingType=PVMT_Ranged,MinVal=0,MaxVal=9999,RangeIncrement=5)
Properties[2]=(PropertyId=3,Data=(Type=SDT_Int32))
PropertyMappings[2]=(Id=3,name="ReadyUpDelay",MappingType=PVMT_Ranged,MinVal=0,MaxVal=9999,RangeIncrement=5)
Properties[3]=(PropertyId=4,Data=(Type=SDT_Int32))
PropertyMappings[3]=(Id=4,name="bDisablePickups",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1)))
// Properties[4]=(PropertyId=5,Data=(Type=SDT_Int32))
// PropertyMappings[4]=(Id=5,name="bEnableObjectives",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1)))
// Properties[5]=(PropertyId=6,Data=(Type=SDT_Int32))
// PropertyMappings[5]=(Id=6,name="bEnableCoopObjectives",MappingType=PVMT_IdMapped,ValueMappings=((Id=0),(Id=1)))
}

View File

@ -0,0 +1,8 @@
/**
* Settings for KFGameInfo_Survival
*
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class KFGameInfo_SurvivalSettings extends KFGameInfoSettings;

View File

@ -0,0 +1,149 @@
/**
* MessagingSpectator. Spectator base class for game helper spectators which receive messages
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks, Josh Markiewicz
*/
class MessagingSpectator extends Admin;
// Used to clean up these actors during seamless traveling
var protected bool bSeamlessDelete;
var array< delegate<ReceiveMessage> > receivers;
/**
* If true this messaging spectator is kept alive when the list or receivers is
* empty;
*/
var bool bKeepAlive;
delegate ReceiveMessage( PlayerReplicationInfo Sender, string Msg, name Type );
function AddReceiver(delegate<ReceiveMessage> ReceiveMessageDelegate)
{
if (receivers.Find(ReceiveMessageDelegate) == INDEX_NONE)
{
receivers[receivers.Length] = ReceiveMessageDelegate;
}
}
function ClearReceiver(delegate<ReceiveMessage> ReceiveMessageDelegate)
{
local int RemoveIndex;
RemoveIndex = receivers.Find(ReceiveMessageDelegate);
if (RemoveIndex != INDEX_NONE)
{
receivers.Remove(RemoveIndex,1);
}
if (receivers.length == 0 && ReceiveMessage == none)
{
Destroy();
}
}
function bool isSeamlessDelete()
{
return bSeamlessDelete;
}
simulated event PostBeginPlay()
{
super.PostBeginPlay();
bSeamlessDelete = true;
if (PlayerReplicationInfo == none)
{
InitPlayerReplicationInfo();
}
}
reliable client event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type, optional float MsgLifeTime )
{
local delegate<ReceiveMessage> rm;
if (type == 'TeamSay') return; // received through TeamChatProxy
if (type != 'Say' && type != 'TeamSay' && type != 'none')
{
`Log("Received message that is not 'say' or 'teamsay'. Type="$type$" Message= "$s);
}
foreach receivers(rm)
{
rm(pri, s, type);
}
if (ReceiveMessage != none)
{
ReceiveMessage(pri, s, type);
}
}
function InitPlayerReplicationInfo()
{
super.InitPlayerReplicationInfo();
PlayerReplicationInfo.bIsInactive = true;
PlayerReplicationInfo.PlayerName = "<<WebAdmin>>";
PlayerReplicationInfo.bIsSpectator = true;
PlayerReplicationInfo.bOnlySpectator = true;
PlayerReplicationInfo.bOutOfLives = true;
PlayerReplicationInfo.bWaitingPlayer = false;
}
auto state NotPlaying
{}
function EnterStartState()
{
GotoState('NotPlaying');
}
function bool IsSpectating()
{
return true;
}
reliable client function ClientGameEnded(Actor EndGameFocus, bool bIsWinner)
{}
function GameHasEnded(optional Actor EndGameFocus, optional bool bIsWinner)
{}
function Reset()
{}
reliable client function ClientReset()
{}
event InitInputSystem()
{
if (PlayerInput == None)
{
Assert(InputClass != None);
PlayerInput = new(Self) InputClass;
}
if ( Interactions.Find(PlayerInput) == -1 )
{
Interactions[Interactions.Length] = PlayerInput;
}
}
event PlayerTick( float DeltaTime )
{
// this is needed because PlayerControllers with no actual player attached
// will leak during seamless traveling.
if (WorldInfo.NextURL != "" || WorldInfo.IsInSeamlessTravel())
{
Destroy();
}
}
function bool CanRestartPlayer()
{
return false;
}
defaultproperties
{
bIsPlayer=False
CameraClass=None
bAlwaysTick=true
bKeepAlive=false
}

View File

@ -0,0 +1,165 @@
/**
* A administrator record
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class MultiAdminData extends Object perobjectconfig config(MultiAdmin);
`include(WebAdmin.uci)
/**
* The name to show. It does not have to be the same as the login name.
*/
var config string DisplayName;
/**
* The password for this user
*/
var private config string Password;
/**
* Which list to process first
*/
enum EAccessOrder
{
DenyAllow,
AllowDeny,
};
/**
* Sets the order in which the allow and deny options should be processed
*/
var config EAccessOrder Order;
/**
* URL segments to allow or deny. An empty list means "all".
*/
var config array<string> Allow, Deny;
/**
* if true this account is enabled
*/
var config bool bEnabled;
var array<string> cacheDenied;
var array<string> cacheAllowed;
/**
* return the display name
*/
function string getDisplayName()
{
if (len(DisplayName) == 0)
{
return string(name);
}
return DisplayName;
}
function setPassword(string pw)
{
if (len(pw) > 0)
{
Password = pw;
}
}
/**
* return true when the password matches
*/
function bool matchesPassword(string pw)
{
if (!bEnabled) return false;
if (len(password) == 40) // sha1 hash
{
return (Caps(Password) == Caps(pw));
}
return (Password == pw) && (len(pw) > 0);
}
/**
* Return true if this user can access this location. This is just the path part
* not the full uri as the IWebAdminUser gets.
*/
function bool canAccess(string loc)
{
local bool retval;
if (!bEnabled) return false;
if (cacheAllowed.find(loc) != INDEX_NONE) {
return true;
}
if (cacheDenied.find(loc) != INDEX_NONE) {
return false;
}
retval = internalCanAccess(loc);
if (retval) {
cacheAllowed.addItem(loc);
}
else {
cacheDenied.addItem(loc);
}
return retval;
}
protected function bool internalCanAccess(string loc)
{
if (Order == DenyAllow)
{
if (matchDenied(loc))
{
if (!matchAllowed(loc)) return false;
}
return true;
}
else if (Order == AllowDeny)
{
if (matchAllowed(loc)) return true;
if (matchDenied(loc)) return false;
return false;
}
return true;
}
/**
* True if the uri matches any entry
*/
function bool matchAllowed(string loc)
{
local string m;
foreach Allow(m)
{
if (class'WebAdminUtils'.static.maskedCompare(loc, m, true))
{
return true;
}
}
return false;
}
/**
* True if the uri matches any denied entry
*/
function bool matchDenied(string loc)
{
local string m;
foreach Deny(m)
{
if (class'WebAdminUtils'.static.maskedCompare(loc, m, true))
{
return true;
}
}
return false;
}
function clearAuthCache()
{
cacheDenied.length = 0;
cacheAllowed.length = 0;
}
defaultproperties
{
}

View File

@ -0,0 +1,275 @@
/**
* Authentication handler that accepts multiple user accounts
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class MultiWebAdminAuth extends Object implements(IWebAdminAuth) config(WebAdmin);
`include(WebAdmin.uci)
var WorldInfo worldinfo;
var array<MultiWebAdminUser> users;
/**
* Contains a list of known names and the associated data (when instantiated)
*/
struct UserRecord
{
var string name;
var MultiAdminData data;
};
var array<UserRecord> records;
//!localization
var localized string InvalidCreds, NoAdmins, msgUnsupportedHash;
var HashLib hashLib;
function init(WorldInfo wi)
{
worldinfo = wi;
hashLib = new class'Sha1HashLib';
loadRecords();
loadQueryHandler();
}
function loadRecords()
{
local array<string> names;
local int i, j;
local int idx;
local string tmp;
GetPerObjectConfigSections(class'MultiAdminData', names);
for (i = 0; i < names.length; i++)
{
idx = InStr(names[i], " ");
if (idx == INDEX_NONE) continue;
tmp = Left(names[i], idx);
for (j = 0; j < records.length; j++)
{
if (caps(records[j].name) > caps(tmp))
{
records.insert(j, 1);
records[j].name = tmp;
break;
}
}
if (j == records.length)
{
records.length = j+1;
records[j].name = tmp;
}
}
if (records.length == 0)
{
records.length = 1;
records[0].name = "Admin";
records[0].data = new(none, records[0].name) class'MultiAdminData';
records[0].data.bEnabled = true;
tmp = worldinfo.game.consolecommand("get engine.accesscontrol adminpassword", false);
if (len(tmp) == 0) tmp = "Admin";
tmp = getPasswordHash(records[0].data, tmp);
records[0].data.setPassword(tmp);
records[0].data.SaveConfig();
`Log("Created initial webadmin administrator account: "$records[0].name,,'WebAdmin');
}
}
function loadQueryHandler()
{
local QHMultiAdmin qh;
local WebAdmin webadmin;
local WebServer ws;
local int i;
if (WebAdmin(Outer) != none)
{
webadmin = WebAdmin(Outer);
}
else {
foreach worldinfo.AllActors(class'WebServer', ws)
{
break;
}
if (ws == none) return;
for (i = 0; i < ArrayCount(ws.ApplicationObjects); i++)
{
if (WebAdmin(ws.ApplicationObjects[i]) != none)
{
webadmin = WebAdmin(ws.ApplicationObjects[i]);
break;
}
}
}
if (webadmin == none) return;
qh = new class'QHMultiAdmin';
qh.authModule = self;
qh.init(webadmin);
qh.registerMenuItems(webadmin.menu);
}
function MultiAdminData getRecord(string username)
{
local int idx;
idx = records.find('name', username);
if (idx == INDEX_NONE)
{
return none;
}
if (records[idx].name != username)
{
// case sensitivity check
return none;
}
if (records[idx].data == none)
{
records[idx].data = new(none, records[idx].name) class'MultiAdminData';
}
return records[idx].data;
}
function bool removeAdminRecord(string username)
{
`if(`WITH_CLEAR_CONFIG)
local int idx;
local MultiAdminData data;
idx = records.find('name', username);
if (idx == INDEX_NONE)
{
return false;
}
if (records[idx].data == none)
{
records[idx].data = new(none, records[idx].name) class'MultiAdminData';
}
data = records[idx].data;
data.ClearConfig();
records.remove(idx, 1);
for (idx = users.length-1; idx >= 0; idx--)
{
if (users[idx].adminData == data)
{
logout(users[idx]);
}
}
return true;
`endif
return false;
}
function cleanup()
{
local MultiWebAdminUser user;
foreach users(user)
{
user.logout();
}
users.remove(0, users.length);
records.length = 0;
worldinfo = none;
}
function IWebAdminUser authenticate(string username, string password, string hashAlg, out string errorMsg)
{
local MultiWebAdminUser user;
local MultiAdminData adminData;
adminData = getRecord(username);
if (adminData == none)
{
errorMsg = InvalidCreds;
if (records.length == 0)
{
errorMsg @= NoAdmins;
}
return none;
}
if (hashLib != none)
{
if (hashAlg == "")
{
password = hashLib.getHash(password$username);
hashAlg = hashLib.getAlgName();
}
}
if (!supportHashAlgorithm(hashAlg))
{
errorMsg = msgUnsupportedHash;
return none;
}
if (adminData.matchesPassword(password))
{
user = worldinfo.spawn(class'MultiWebAdminUser');
user.adminData = adminData;
user.init();
user.setUsername(adminData.getDisplayName());
users.AddItem(user);
return user;
}
errorMsg = InvalidCreds;
return none;
}
function bool logout(IWebAdminUser user)
{
user.logout();
users.RemoveItem(MultiWebAdminUser(user));
return true;
}
function bool validate(string username, string password, string hashAlg, out string errorMsg)
{
local MultiAdminData adminData;
adminData = getRecord(username);
if (adminData == none)
{
errorMsg = InvalidCreds;
return false;
}
if (hashLib != none)
{
if (hashAlg == "")
{
password = hashLib.getHash(password$username);
hashAlg = hashLib.getAlgName();
}
}
if (!supportHashAlgorithm(hashAlg))
{
errorMsg = msgUnsupportedHash;
return false;
}
if (adminData.matchesPassword(password))
{
return true;
}
errorMsg = InvalidCreds;
return false;
}
function bool validateUser(IWebAdminUser user, out string errorMsg)
{
return true;
}
function bool supportHashAlgorithm(string hashAlg)
{
return hashLib.getAlgName() ~= hashAlg;
}
function string getPasswordHash(MultiAdminData adminData, coerce string newpw)
{
if (hashLib == none) return newpw;
return hashLib.getHash(newpw$adminData.name);
}

View File

@ -0,0 +1,62 @@
/**
* The webadmin user used for the multi admin authentication
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class MultiWebAdminUser extends BasicWebAdminUser implements(ISettingsPrivileges);
var MultiAdminData adminData;
var string settingsPrivBaseUri;
function string getUserid()
{
return string(adminData.name);
}
function logout()
{
adminData = none;
super.logout();
}
function bool canPerform(string uri)
{
local int idx;
addCheckedPrivileges(uri);
if (adminData != none)
{
if (left(uri, 11) ~= "webadmin://")
{
idx = InStr(uri, "/",,, 11);
if (idx != INDEX_NONE)
{
uri = Mid(uri, idx);
if (uri == "/") return true; // always allow root
return adminData.canAccess(uri);
}
}
}
return false;
}
function ISettingsPrivileges getSettingsPrivileges()
{
return self;
}
function setBasePrivilegeUri(String uri)
{
settingsPrivBaseUri = uri;
}
function bool hasSettingsGroupAccess(class<WebAdminSettings> settings, string groupId)
{
return canPerform(settingsPrivBaseUri$"#"$groupId);
}
defaultproperties
{
}

View File

@ -0,0 +1,113 @@
/**
* Retrieves/cached news as reported by the news interface
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class NewsDesk extends Object config(WebAdmin) dependson(WebAdminUtils) dependson(OnlineSubsystem);
`include(WebAdmin.uci)
var config array<string> GameNews;
var config array<string> ContentNews;
var config string LastUpdate;
var OnlineNewsInterface newsIface;
function cleanup()
{
if (newsIface != none)
{
newsIface.ClearReadNewsCompletedDelegate(OnReadNewsCompleted);
}
newsIface = none;
}
/**
* Get the news when needed. Updated once a day
*/
function getNews(optional bool forceUpdate)
{
local DateTime last, now;
if (len(lastUpdate) > 0 && !forceUpdate)
{
class'WebAdminUtils'.static.getDateTime(now, timestamp());
class'WebAdminUtils'.static.getDateTime(last, LastUpdate);
// YY,YYM,MDD
// 20,081,231
if (last.year*10000+last.month*100+last.day >= now.year*10000+now.month*100+now.day)
{
return;
}
}
`log("Updating news...",,'WebAdmin');
if (class'GameEngine'.static.GetOnlineSubsystem() != none)
{
newsIface = class'GameEngine'.static.GetOnlineSubsystem().NewsInterface;
if (newsIface == none)
{
`log("No OnlineNewsInterface; news desk is unavailable",,'WebAdmin');
return;
}
newsIface.AddReadNewsCompletedDelegate(OnReadNewsCompleted);
newsIface.ReadNews(0, ONT_GameNews);
newsIface.ReadNews(0, ONT_ContentAnnouncements);
}
}
function OnReadNewsCompleted(bool bWasSuccessful, EOnlineNewsType NewsType)
{
local array<string> data, parsedData;
local string ln;
local int i,j;
if (bWasSuccessful)
{
ParseStringIntoArray(newsIface.GetNews(0, NewsType), data, chr(10), false);
parsedData.length = data.length;
j = 0;
for (i = 0; i < data.length; i++)
{
ln = `Trim(data[i]);
if (len(ln) > 0 || j > 0)
{
parsedData[j] = ln;
++j;
}
}
parsedData.length = j;
if (NewsType == ONT_GameNews) {
gameNews = parsedData;
}
else if(NewsType == ONT_ContentAnnouncements) {
contentNews = parsedData;
}
LastUpdate = TimeStamp();
SaveConfig();
}
}
function string renderNews(WebAdmin webadmin, WebAdminQuery q)
{
local int i;
local string tmp;
if (gameNews.length > 0 || contentNews.length > 0)
{
for (i = 0; i < gameNews.length; i++)
{
if (i > 0) tmp $= "<br />";
tmp $= `HTMLEscape(gameNews[i]);
}
q.response.subst("news.game", tmp);
tmp = "";
for (i = 0; i < contentNews.length; i++)
{
if (i > 0) tmp $= "<br />";
tmp $= `HTMLEscape(contentNews[i]);
}
q.response.subst("news.content", tmp);
q.response.subst("news.timestamp", `HTMLEscape(LastUpdate));
}
return webadmin.include(q, "news.inc");
}

View File

@ -0,0 +1,44 @@
/**
* Provides a mechanism to clean up the messaging spectators. These are kept
* in memory because they are a PlayerController subclass, and not cleaned up
* because they are not associated with players.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class PCCleanUp extends Info;
var array<MessagingSpectator> cleanupControllers;
event PreBeginPlay()
{
local MessagingSpectator specs;
super.PreBeginPlay();
foreach WorldInfo.AllControllers(class'MessagingSpectator', specs)
{
if (specs.isSeamlessDelete())
{
cleanupControllers[cleanupControllers.length] = specs;
}
}
if (cleanupControllers.length > 0)
{
`Log("Cleaning up "$cleanupControllers.length$" MessagingSpectator instances");
SetTimer(1, false, 'cleanup');
}
else {
Destroy();
}
}
function cleanup()
{
local MessagingSpectator specs;
foreach cleanupControllers(specs)
{
specs.Destroy();
}
Destroy();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/**
* Adds Killing Floor 2 specific information to current Query handler.
*
* Copyright 2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class QHCurrentKF extends QHCurrent;
`include(WebAdmin.uci)
var int DefaultPerkLevel;
function registerMenuItems(WebAdminMenu menu)
{
super.registerMenuItems(menu);
menu.addMenu("/current+gamesummary", "", self);
menu.addMenu("/current/chat+frame", "", self);
menu.addMenu("/current/chat+frame+data", "", self);
menu.setVisibility("/current/chat", false);
}
function bool handleQuery(WebAdminQuery q)
{
switch (q.request.URI)
{
case "/current+gamesummary":
handleAjaxGamesummary(q);
return true;
case "/current/chat+frame":
q.response.subst("page.css.class", "chatframe");
handleCurrentChat(q, "current_chat_frame.html");
return true;
case "/current/chat+frame+data":
handleCurrentChatData(q);
return true;
}
return super.handleQuery(q);
}
function decoratePage(WebAdminQuery q)
{
if (q.user == none)
{
q.response.subst("gamesummary", "");
q.response.subst("chatwindow", "");
return;
}
decorateGameSummary(q);
decorateChatWindow(q);
}
function substGameInfo(WebAdminQuery q)
{
local KFGameInfo kfGameInfo;
local string str;
local int i;
super.substGameInfo(q);
i = int(webadmin.WorldInfo.Game.GameDifficulty);
if (i == webadmin.WorldInfo.Game.GameDifficulty) {
str = Localize("KFCommon_LocalizedStrings", "DifficultyStrings["$i$"]", "KFGame");
}
else {
str = "";
}
if (len(str) == 0) {
str = string(webadmin.WorldInfo.Game.GameDifficulty);
}
q.response.subst("rules.difficulty.text", str);
kfGameInfo = KFGameInfo(webadmin.WorldInfo.Game);
if (kfGameInfo != none)
{
q.response.subst("rules.minnetplayers", kfGameInfo.MinNetPlayers);
q.response.subst("rules.mapvote", `HTMLEscape(kfGameInfo.bDisableMapVote?default.msgOff:default.msgOn));
q.response.subst("rules.kickvote", `HTMLEscape(kfGameInfo.bDisableKickVote?default.msgOff:default.msgOn));
}
if (KFGameInfo_Survival(webadmin.WorldInfo.Game) != none)
{
substGameInfoSurvival(q);
}
}
function substGameInfoSurvival(WebAdminQuery q)
{
local KFGameInfo_Survival gameinfo;
local KFGameReplicationInfo gri;
local int deadMonsters;
gameinfo = KFGameInfo_Survival(webadmin.WorldInfo.Game);
gri = gameinfo.MyKFGRI;
q.response.subst("wave.num", gameinfo.WaveNum);
q.response.subst("wave.max", gameinfo.WaveMax-1);
// total number spawned so far minus living monsters
deadMonsters = gameinfo.NumAISpawnsQueued - gameinfo.GetMonsterAliveCount();
q.response.subst("wave.monsters.pending", gri.WaveTotalAICount - deadMonsters);
q.response.subst("wave.monsters.dead", deadMonsters);
q.response.subst("wave.monsters.total", gri.WaveTotalAICount);
}
function substPri(WebAdminQuery q, PlayerReplicationInfo pri)
{
local KFPlayerReplicationInfo ropri;
super.substPri(q, pri);
ropri = KFPlayerReplicationInfo(pri);
if (ropri != none)
{
q.response.subst("player.perk.class", `HTMLEscape(ropri.CurrentPerkClass));
if (ropri.CurrentPerkClass != none)
{
q.response.subst("player.perk.name", `HTMLEscape(ropri.CurrentPerkClass.default.PerkName));
}
else {
q.response.subst("player.perk.name", "");
}
q.response.subst("player.perk.level", DefaultPerkLevel);
}
}
function bool comparePRI(PlayerReplicationInfo PRI1, PlayerReplicationInfo PRI2, string key)
{
local KFPlayerReplicationInfo kpri1, kpri2;
kpri1 = KFPlayerReplicationInfo(pri1);
kpri2 = KFPlayerReplicationInfo(pri2);
if (kpri1 != none && kpri2 != none)
{
if (key ~= "perk")
{
return caps(kpri1.CurrentPerkClass.default.PerkName) > caps(kpri2.CurrentPerkClass.default.PerkName);
}
else if (key != "perklevel")
{
return kpri1.GetActivePerkLevel() > kpri2.GetActivePerkLevel();
}
}
return super.comparePRI(PRI1, PRI2, key);
}
function handleAjaxGamesummary(WebAdminQuery q)
{
q.response.AddHeader("Content-Type: text/xml");
q.response.SendText("<response>");
q.response.SendText("<gamesummary><![CDATA[");
q.response.SendText(renderGameSummary(q));
q.response.SendText("]]></gamesummary>");
q.response.SendText("</response>");
}
function decorateGameSummary(WebAdminQuery q)
{
q.response.subst("gamesummary.details", renderGameSummary(q));
q.response.subst("gamesummary", webadmin.include(q, "gamesummary_base.inc"));
}
function string renderGameSummary(WebAdminQuery q)
{
substGameInfo(q);
return webadmin.include(q, getGameTypeIncFile(q, "gamesummary"));
}
function decorateChatWindow(WebAdminQuery q)
{
if (InStr(q.request.URI, "/current/chat") == 0) {
q.response.subst("chatwindow", "");
return;
}
q.response.subst("chatwindow", webadmin.include(q, "current_chat_frame.inc"));
}
defaultproperties
{
separateSpectators=true
DefaultPerkLevel=25
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/**
* Adds Killing Floor 2 specific information to current defaults handler.
*
* Copyright (C) 2015 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class QHDefaultsKF extends QHDefaults;
`include(WebAdmin.uci)
function string rewriteSettingsClassname(string pkgName, string clsName)
{
// rewrite standard game classes to WebAdmin
if (pkgName ~= "KFGame") pkgName = string(class.getPackageName());
else if (pkgName ~= "KFGameContent") pkgName = string(class.getPackageName());
return super.rewriteSettingsClassname(pkgName, clsName);
}

View File

@ -0,0 +1,374 @@
/**
* Query handler for the multi admin access control.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class QHMultiAdmin extends Object implements(IQueryHandler) config(WebAdmin)
dependson(WebAdminUtils);
`include(WebAdmin.uci)
var MultiWebAdminAuth authModule;
var WebAdmin webadmin;
/**
* A list of administrator names that are protected. These can not be deleted
* at all. And can not be modified by themselves.
*/
var config array<string> protectedAdmins;
var string fullMenu;
//!localization
var localized string menuAdmins, menuAdminsDesc, msgCreatedAdmin, msgDupName,
msgCantRemoveLast, msgCantDelete, msgRemovedAdmin, msgUnableToRemove,
msgSavedAdmin, msgPassError, msgCannotDisableSelf, msgPrivLogEnabled,
msgPrivLogDisabled;
function init(WebAdmin webapp)
{
local int i;
local string clsname;
webadmin = webapp;
clsname = class.getPackageName()$"."$class.name;
if (authModule == none)
{
`Log("Authentication module is not MultiWebAdminAuth, unregistering QHMultiAdmin",,'WebAdmin');
for (i = 0; i < webadmin.QueryHandlers.Length; i++)
{
if (webadmin.QueryHandlers[i] ~= clsname)
{
webadmin.QueryHandlers.Remove(i, 1);
webadmin.SaveConfig();
break;
}
}
cleanup();
return;
}
else {
for (i = 0; i < webadmin.handlers.Length; i++)
{
if (webadmin.handlers[i] == self)
{
break;
}
}
if (i == webadmin.handlers.Length)
{
webadmin.handlers[i] = self;
}
}
}
function cleanup()
{
authModule = none;
webadmin = none;
}
function bool producesXhtml()
{
return true;
}
function bool handleQuery(WebAdminQuery q)
{
if (authModule == none) return false;
switch (q.request.URI)
{
case "/multiadmin":
handleAdmins(q);
return true;
}
}
function bool unhandledQuery(WebAdminQuery q)
{
return false;
}
function decoratePage(WebAdminQuery q);
function registerMenuItems(WebAdminMenu menu)
{
if (authModule == none) return;
menu.addMenu("/multiadmin", menuAdmins, self, menuAdminsDesc, 1000);
}
function handleAdmins(WebAdminQuery q)
{
local string editAdmin;
local string tmp;
local array<string> tmpa;
local int i;
local MultiAdminData adminData;
editAdmin = q.request.getVariable("adminid");
if ((q.request.getVariable("action") ~= "create") || (q.request.getVariable("action") ~= "create administrator"))
{
if (len(editAdmin) > 0)
{
if (authModule.records.find('name', editAdmin) == INDEX_NONE)
{
adminData = new(none, editAdmin) class'MultiAdminData';
editAdmin = string(adminData.name);
adminData.SaveConfig();
for (i = 0; i < authModule.records.length; i++)
{
if (caps(authModule.records[i].name) > caps(editAdmin))
{
authModule.records.insert(i, 1);
authModule.records[i].name = editAdmin;
authModule.records[i].data = adminData;
break;
}
}
if (i == authModule.records.length)
{
authModule.records.length = i+1;
authModule.records[i].name = editAdmin;
authModule.records[i].data = adminData;
}
webadmin.addMessage(q, repl(msgCreatedAdmin, "%s", editAdmin));
}
else {
webadmin.addMessage(q, repl(msgDupName, "%s", editAdmin), MT_Error);
editAdmin = "";
}
}
else {
webadmin.addMessage(q, "No name given.", MT_Error);
}
}
if (q.request.getVariable("action") ~= "delete")
{
if (authModule.records.length <= 1)
{
webadmin.addMessage(q, msgCantRemoveLast, MT_Error);
}
else if (len(editAdmin) > 0)
{
if (!canDeleteAdmin(editAdmin, q.user))
{
webadmin.addMessage(q, repl(msgCantDelete, "%s", editAdmin), MT_Error);
}
else if (authModule.removeAdminRecord(editAdmin))
{
webadmin.addMessage(q, repl(msgRemovedAdmin, "%s", editAdmin));
}
else {
webadmin.addMessage(q, repl(msgUnableToRemove, "%s", editAdmin), MT_Error);
}
}
}
tmp = "";
for (i = 0; i < authModule.records.length; i++)
{
if (!canEditAdmin(authModule.records[i].name, q.user)) continue;
q.response.subst("multiadmin.name", `HTMLEscape(authModule.records[i].name));
if (authModule.records[i].name ~= editAdmin)
{
q.response.subst("multiadmin.selected", "selected=\"selected\"");
}
else {
q.response.subst("multiadmin.selected", "");
}
// ensure loaded
authModule.getRecord(authModule.records[i].name);
if (authModule.records[i].data.bEnabled) {
q.response.subst("multiadmin.class", "adminEnabled");
}
else {
q.response.subst("multiadmin.class", "adminDisabled");
}
tmp $= webadmin.include(q, "multiadmin_admin_select.inc");
}
q.response.subst("admins", tmp);
if (len(editAdmin) > 0)
{
adminData = authModule.getRecord(editAdmin);
}
q.response.subst("editor", "");
if (adminData != none)
{
if (q.request.getVariable("privlog") ~= "privlog")
{
tmp = q.session.getString("privilege.log");
if (tmp == "")
{
q.session.putString("privilege.log", "true");
webadmin.addMessage(q, msgPrivLogEnabled);
}
else {
q.session.putString("privilege.log", "");
webadmin.addMessage(q, msgPrivLogDisabled);
}
}
if (q.request.getVariable("action") ~= "save" && canEditAdmin(editAdmin, q.user))
{
tmp = q.request.getVariable("password1");
if (tmp == q.request.getVariable("password2"))
{
if (len(tmp) > 0)
{
tmp = authModule.getPasswordHash(adminData, tmp);
adminData.setPassword(tmp);
}
adminData.displayName = q.request.getVariable("displayname");
tmp = q.request.getVariable("enabled");
if (tmp ~= "1") {
adminData.bEnabled = true;
}
else {
if (q.user.getUserId() == editAdmin) {
webadmin.addMessage(q, repl(msgCannotDisableSelf, "%s", editAdmin), MT_Warning);
}
else {
// TODO: count enabled accounts
adminData.bEnabled = false;
}
}
ParseStringIntoArray(q.request.getVariable("allow"), tmpa, chr(10), true);
adminData.allow.length = 0;
for (i = 0; i < tmpa.length; i++)
{
tmp = `Trim(tmpa[i]);
if (len(tmp) == 0) continue;
adminData.allow[adminData.allow.length] = tmp;
}
ParseStringIntoArray(q.request.getVariable("deny"), tmpa, chr(10), true);
adminData.deny.length = 0;
for (i = 0; i < tmpa.length; i++)
{
tmp = `Trim(tmpa[i]);
if (len(tmp) == 0) continue;
adminData.deny[adminData.deny.length] = tmp;
}
if (q.request.getVariable("order") ~= "AllowDeny") {
adminData.order = AllowDeny;
}
else {
adminData.order = DenyAllow;
}
adminData.saveconfig();
adminData.clearAuthCache();
webadmin.addMessage(q, msgSavedAdmin);
}
else {
webadmin.addMessage(q, msgPassError, MT_Error);
}
}
q.response.subst("adminid", `HTMLEscape(adminData.name));
q.response.subst("displayname", `HTMLEscape(adminData.displayName));
if (adminData.bEnabled)
{
q.response.subst("enabled.true", "checked=\"checked\"");
q.response.subst("enabled.false", "");
}
else {
q.response.subst("enabled.true", "");
q.response.subst("enabled.false", "checked=\"checked\"");
}
if (adminData.order == DenyAllow)
{
q.response.subst("order.denyallow", "checked=\"checked\"");
q.response.subst("order.allowdeny", "");
}
else if (adminData.order == AllowDeny)
{
q.response.subst("order.allowdeny", "checked=\"checked\"");
q.response.subst("order.denyallow", "");
}
else {
q.response.subst("order.denyallow", "");
q.response.subst("order.allowdeny", "");
}
tmp = "";
for (i = 0; i < adminData.allow.length; i++)
{
if (len(tmp) > 0) tmp $= chr(10);
tmp $= adminData.allow[i];
}
q.response.subst("allow", tmp);
tmp = "";
for (i = 0; i < adminData.deny.length; i++)
{
if (len(tmp) > 0) tmp $= chr(10);
tmp $= adminData.deny[i];
}
q.response.subst("deny", tmp);
if (!canDeleteAdmin(string(adminData.name), q.user))
{
q.response.subst("allowdelete", "disabled=\"disabled\"");
}
else {
q.response.subst("allowdelete", "");
}
if (len(fullMenu) == 0)
{
fullMenu = webadmin.menu.render("/multiadmin_editor_menu.inc", "/multiadmin_editor_menuitem.inc");
}
q.response.subst("menueditor", fullMenu);
q.response.subst("editor", webadmin.include(q, "multiadmin_editor.inc"));
}
webadmin.sendPage(q, "multiadmin.html");
}
/**
* True if the user can be deleted. Users can not delete themselves.
*/
function bool canDeleteAdmin(string adminName, IWebAdminUser me)
{
`if(`WITH_CLEAR_CONFIG)
if (protectedAdmins.find(adminName) != INDEX_NONE)
{
return false;
}
if (me.getUserid() ~= adminName)
{
return false;
}
return true;
`else
return false;
`endif
}
/**
* True if the user can be edited. Users can edit themselves if they are protected.
*/
function bool canEditAdmin(string adminName, IWebAdminUser me)
{
if (protectedAdmins.find(adminName) != INDEX_NONE)
{
if (me.getUserid() ~= adminName)
{
return true;
}
return false;
}
return true;
}
defaultproperties
{
}

115
WebAdmin/Classes/Session.uc Normal file
View File

@ -0,0 +1,115 @@
/**
* A session implementation
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class Session extends Object implements(ISession);
struct ObjectKV
{
var string key;
var Object value;
};
struct StringKV
{
var string key;
var string value;
};
var protected string id;
var protected array<ObjectKV> objects;
var protected array<StringKV> strings;
function string getId()
{
local int i;
if (id == "")
{
for (i = 0; i < 8; i++)
{
id $= Right(ToHex(rand(0xFFFF)), 4);
}
}
return id;
}
function reset()
{
objects.remove(0, objects.length);
strings.remove(0, strings.length);
}
function Object getObject(string key)
{
local int idx;
idx = objects.Find('key', key);
if (idx > INDEX_NONE)
{
return objects[idx].value;
}
return none;
}
function putObject(string key, Object value)
{
local int idx;
idx = objects.Find('key', key);
if (idx > INDEX_NONE)
{
objects[idx].value = value;
return;
}
objects.add(1);
objects[objects.length - 1].key = key;
objects[objects.length - 1].value = value;
}
function removeObject(string key)
{
local int idx;
idx = objects.Find('key', key);
if (idx > INDEX_NONE)
{
objects.remove(idx, 1);
return;
}
}
function string getString(string key, optional string defValue = "")
{
local int idx;
idx = strings.Find('key', key);
if (idx > INDEX_NONE)
{
return strings[idx].value;
}
return defValue;
}
function putString(string key, string value)
{
local int idx;
idx = strings.Find('key', key);
if (idx > INDEX_NONE)
{
strings[idx].value = value;
return;
}
strings.add(1);
strings[strings.length - 1].key = key;
strings[strings.length - 1].value = value;
}
function removeString(string key)
{
local int idx;
idx = strings.Find('key', key);
if (idx > INDEX_NONE)
{
strings.remove(idx, 1);
return;
}
}

View File

@ -0,0 +1,78 @@
/**
* Default session handler implementation
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class SessionHandler extends Object implements(ISessionHandler) dependson(WebAdminUtils);
`define WITH_BROKEN_RAND
struct SessionKV
{
var string id;
var Session s;
};
var protected array<SessionKV> sessions;
function ISession create()
{
local SessionKV skv;
`if(`WITH_BROKEN_RAND)
local WebAdminUtils.DateTime dt;
local int i;
`endif
`if(`WITH_BROKEN_RAND)
// seed the broken rand function
class'WebAdminUtils'.static.getDateTime(dt, timestamp());
for (i = 0; i < dt.second+dt.minute+dt.day; i++)
{
rand(0xffff);
}
`endif
skv.s = new(Self) class'Session';
skv.id = skv.s.getId();
sessions.AddItem(skv);
//`Log("Created a new session with id: "$skv.id,,'WebAdmin');
return skv.s;
}
function ISession get(string id)
{
local int idx;
idx = sessions.Find('id', id);
if (idx > -1)
{
//`Log("Found session with id: "$id,,'WebAdmin');
return sessions[idx].s;
}
return none;
}
function bool destroy(ISession session)
{
local int idx;
idx = sessions.Find('s', Session(session));
if (idx > -1)
{
session.reset();
//`Log("Destroyed session with id: "$sessions[idx].id,,'WebAdmin');
sessions.remove(idx, 1);
return true;
}
return false;
}
function destroyAll()
{
local int i;
for (i = 0; i < sessions.length; i++)
{
sessions[i].s.reset();
}
sessions.Remove(0, sessions.Length);
}

View File

@ -0,0 +1,134 @@
/**
* Keeps the state of a setting being rendered until it is rendered
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class SettingRendererState extends object;
/**
* The ID of the setting. Should not be changed.
*/
var int settingId;
/**
* The setting name. Should not be changed.
*/
var name settingName;
/**
* Setting type. Set by the renderer as an indication. Changing it has no effect.
*/
var string settingType;
/**
* The mapping type of the value
*/
var EPropertyValueMappingType mappingType;
/**
* The setting's data type
*/
var ESettingsDataType dataType;
/**
* The name (and id) of the HTML form element. Should not be changed.
*/
var string formName;
/**
* CSS class names used in the setting container
*/
var string cssClass;
/**
* The description/label of the setting
*/
var string label;
/**
* Tooltip for the setting
*/
var string tooltip;
/**
* If false, the setting's form element is rendered disabled
*/
var bool bEnabled;
/**
* If false, the setting will not be rendered at all.
*/
var bool bVisible;
struct KeyValue
{
var string Key;
var string Value;
};
/**
* Substitution variables used for rendering the settings.
*/
var array<KeyValue> substitutions;
/**
* Extra HTML to add after the setting. ISettingsModifier can add their
* custom data here.
*/
var string extra;
/**
* The current webresponse instance;
*/
var WebResponse WebResponse;
/**
* The base path to load files from (for the WebResponse)
*/
var string path;
/**
* Reset to the base values
*/
function reset()
{
settingId = 0;
settingName = '';
settingType = "";
formName = "";
cssClass = "";
label = "";
tooltip = "";
bEnabled = true;
bVisible = true;
substitutions.length = 0;
extra = "";
dataType = SDT_Empty;
mappingType = PVMT_RawValue;
}
function subst(string key, coerce string value)
{
local int idx;
idx = substitutions.find('Key', key);
if (idx == INDEX_NONE)
{
idx = substitutions.length;
substitutions.length = idx+1;
substitutions[idx].Key = key;
}
substitutions[idx].Value = value;
}
function string getSubst(string key)
{
local int idx;
idx = substitutions.find('Key', key);
if (idx == INDEX_NONE)
{
return "";
}
return substitutions[idx].Value;
}

View File

@ -0,0 +1,76 @@
/**
* A bit of magic to fake settings classes for gametypes who don't have any.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
* Copyright (C) 2011,2014 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class SettingsMagic extends Object config(WebAdmin);
`include(WebAdmin.uci)
struct MagicCacheEntry
{
var class<GameInfo> cls;
var WebAdminSettings inst;
};
var array<MagicCacheEntry> magicCache;
function cleanup()
{
local int i;
for (i = 0; i < magicCache.length; i++)
{
magicCache[i].inst.cleanupSettings();
magicCache[i].inst = none;
}
magicCache.length = 0;
}
function WebAdminSettings find(class<GameInfo> GameClass)
{
local WebAdminSettings result;
local int idx;
idx = magicCache.find('cls', GameClass);
if (idx != INDEX_NONE)
{
return magicCache[idx].inst;
}
if (class<KFGameInfo_Survival>(GameClass) != none)
{
result = _KFGameInfo_Survival(class<KFGameInfo_Survival>(GameClass));
}
else if (class<KFGameInfo>(GameClass) != none)
{
result = _KFGameInfo(class<KFGameInfo>(GameClass));
}
if (result != none)
{
result.initSettings();
magicCache.Length = magicCache.Length+1;
magicCache[magicCache.Length-1].cls = GameClass;
magicCache[magicCache.Length-1].inst = result;
}
return result;
}
function KFGameInfoSettings _KFGameInfo(class<KFGameInfo> cls)
{
local KFGameInfoSettings r;
r = new class'KFGameInfoSettings';
r.KFGameInfoClass=cls;
return r;
}
function KFGameInfoSettings _KFGameInfo_Survival(class<KFGameInfo_Survival> cls)
{
local KFGameInfoSettings r;
r = new class'KFGameInfo_SurvivalSettings';
r.KFGameInfoClass=cls;
return r;
}

View File

@ -0,0 +1,738 @@
/**
* This class provides the functionality to render a HTML page of a Settings
* instance.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class SettingsRenderer extends Object dependsOn(WebAdminSettings);
`include(WebAdmin.uci)
/**
* Prefix of all include files.
*/
var protected string prefix;
/**
* Prefix for variable names
*/
var protected string namePrefix;
/**
* The base path to load the include files from
*/
var protected string path;
/**
* Minimum number of options in a idMapped setting before switching to a listbox
*/
var int minOptionListSize;
struct SortedSetting
{
var string txt;
/** index of this item in one of the whole lists */
var int idx;
/** if true it's a localized setting rather than a property */
var bool isLocalized;
};
/**
* Settings group containg a sort list of settings.
*/
struct SettingsGroup
{
var WebAdminSettings.SettingsGroupSpec spec;
var array<SortedSetting> settings;
};
var array<SettingsGroup> groups;
var protected WebAdminSettings curSettings;
var protected WebResponse curResponse;
/**
* Modifiers that will be polled to augment the settings
*/
var array<ISettingsModifier> modifiers;
/**
* Currently active modifiers, only filled during rendering
*/
var protected array<ISettingsModifier> activeModifiers;
/**
* Initialization when the instance is created.
*/
function init(string basePath, optional string namePre="settings_", optional string filePrefix="settings_")
{
prefix = filePrefix;
path = basePath;
namePrefix = namePre;
minOptionListSize=4;
}
function string getPath()
{
return path;
}
function string getFilePrefix()
{
return prefix;
}
function string getNamePrefix()
{
return namePrefix;
}
function cleanup()
{
curSettings = none;
curResponse = none;
}
/**
* Used to initialize the rendered for an IAdvWebAdminSettings instance
*/
function initEx(WebAdminSettings settings, WebResponse response)
{
curSettings = settings;
curResponse = response;
}
/**
* Called after processing submitted values. Should be used to ensure some
* settings have the correct values. This function is called stand-alone.
*/
function ensureSettingValues(WebAdminSettings settings)
{
local int i;
for (i = 0; i < modifiers.Length; ++i)
{
modifiers[i].ensureSettingValues(settings);
}
}
/**
* Sort all settings based on their name
*/
function sortSettings(int groupId)
{
local int i, j;
local SortedSetting sortset;
groups[groupId].settings.length = 0; // clear old
for (i = 0; i < curSettings.LocalizedSettingsMappings.length; i++)
{
if (curSettings.LocalizedSettingsMappings[i].Id < groups[groupId].spec.lMin) continue;
if (curSettings.LocalizedSettingsMappings[i].Id >= groups[groupId].spec.lMax) continue;
if (curSettings.LocalizedSettingsMappings[i].Name == '') continue;
sortset.idx = i;
sortset.isLocalized = true;
sortset.txt = getLocalizedSettingText(curSettings.LocalizedSettingsMappings[i].Id);
for (j = 0; j < groups[groupId].settings.length; j++)
{
if (Caps(groups[groupId].settings[j].txt) > Caps(sortset.txt))
{
groups[groupId].settings.Insert(j, 1);
groups[groupId].settings[j] = sortset;
break;
}
}
if (j == groups[groupId].settings.length)
{
groups[groupId].settings[j] = sortset;
}
}
for (i = 0; i < curSettings.PropertyMappings.length; i++)
{
if (curSettings.PropertyMappings[i].Id < groups[groupId].spec.pMin) continue;
if (curSettings.PropertyMappings[i].Id >= groups[groupId].spec.pMax) continue;
if (curSettings.PropertyMappings[i].Name == '') continue;
sortset.idx = i;
sortset.isLocalized = false;
sortset.txt = getSettingText(curSettings.PropertyMappings[i].Id);
for (j = 0; j < groups[groupId].settings.length; j++)
{
if (Caps(groups[groupId].settings[j].txt) > Caps(sortset.txt))
{
groups[groupId].settings.Insert(j, 1);
groups[groupId].settings[j] = sortset;
break;
}
}
if (j == groups[groupId].settings.length)
{
groups[groupId].settings[j] = sortset;
}
}
}
/**
* Creates the settings groups
*/
function createGroups()
{
local SettingsGroup group;
local array<SettingsGroupSpec> specs;
local SettingsGroupSpec spec;
groups.length = 0;
specs = curSettings.settingGroups();
// copy the spec to our format
foreach specs(spec)
{
group.spec = spec;
group.settings.Length = 0;
groups.AddItem(group);
}
// add a dummy group
if (groups.length == 0)
{
group.spec.groupId = "0";
group.spec.pMin = 0;
group.spec.pMax = curSettings.PropertyMappings.Length;
group.spec.lMin = 0;
group.spec.lMax = curSettings.LocalizedSettingsMappings.length;
group.settings.Length = 0;
groups.AddItem(group);
}
}
/**
* Render all properties of the given settings instance
*/
function render(WebAdminSettings settings, WebResponse response,
optional string substName = "settings", optional ISettingsPrivileges privileges)
{
local string result, entry;
local int i;
curSettings = settings;
curResponse = response;
activeModifiers.length = 0;
for (i = 0; i < modifiers.Length; ++i)
{
if (modifiers[i].modifierAppliesTo(settings))
{
activeModifiers[activeModifiers.Length] = modifiers[i];
}
}
createGroups();
for (i = 0; i < groups.length; i++)
{
sortSettings(i);
}
if (groups.length == 1)
{
curResponse.Subst("settings", renderGroup(groups[0]));
result = curResponse.LoadParsedUHTM(path $ "/" $ prefix $ "wrapper_single.inc");
}
else {
result= "";
for (i = 0; i < groups.length; i++)
{
if (groups[i].settings.length == 0) continue;
if (string(int(groups[i].spec.groupId)) != groups[i].spec.groupId)
{
if (privileges != none)
{
if (!privileges.hasSettingsGroupAccess(settings.class, groups[i].spec.groupId))
{
continue;
}
}
}
curResponse.Subst("group.id", groups[i].spec.groupId);
curResponse.Subst("group.title", `HTMLEscape(groups[i].spec.DisplayName));
curResponse.Subst("group.settings", renderGroup(groups[i]));
entry = curResponse.LoadParsedUHTM(path $ "/" $ prefix $ "group.inc");
result $= entry;
}
curResponse.Subst("settings", result);
result = curResponse.LoadParsedUHTM(path $ "/" $ prefix $ "wrapper_group.inc");
}
for (i = 0; i < activeModifiers.Length; ++i)
{
result $= activeModifiers[i].finalizeAugmentation(curResponse, path);
}
activeModifiers.length = 0;
curResponse.subst(substName, result);
}
/**
* Render a selection of settings
*/
function string renderGroup(SettingsGroup group)
{
local string result, entry;
local int i, j;
local EPropertyValueMappingType mtype;
local SettingRendererState renderState;
renderState = new class'SettingRendererState';
renderState.WebResponse = curResponse;
renderState.path = path;
for (i = 0; i < group.settings.length; i++)
{
renderState.reset();
if (group.settings[i].isLocalized)
{
entry = renderLocalizedSetting(curSettings.LocalizedSettingsMappings[group.settings[i].idx].Id, renderState);
}
else {
j = group.settings[i].idx;
curSettings.GetPropertyMappingType(curSettings.PropertyMappings[j].Id, mtype);
renderState.mappingType = mtype;
defaultSubst(curSettings.PropertyMappings[j].Id, renderState);
switch (mtype)
{
case PVMT_PredefinedValues:
entry = renderPredefinedValues(curSettings.PropertyMappings[j].Id, j, renderState);
break;
case PVMT_Ranged:
entry = renderRanged(curSettings.PropertyMappings[j].Id, renderState);
break;
case PVMT_IdMapped:
entry = renderIdMapped(curSettings.PropertyMappings[j].Id, j, renderState);
break;
default:
entry = renderRaw(curSettings.PropertyMappings[j].Id, j, renderState);
}
}
if (len(entry) > 0 && renderState.bVisible)
{
curResponse.subst("setting.html", entry);
result $= curResponse.LoadParsedUHTM(path $ "/" $ prefix $ "entry.inc");
}
}
return result;
}
/**
* Get a readable name for the current localized property
*/
function string getLocalizedSettingText(int settingId)
{
local string val, res, elm;
local int i;
val = curSettings.GetStringSettingColumnHeader(settingId);
if (len(val) > 0) return val;
val = string(curSettings.GetStringSettingName(settingId));
// FooBarQuux -> Foo Bar Quux
res = "";
for (i = 0; i < len(val); i++) {
elm = Mid(val, i, 1);
if (Caps(elm) == elm)
{
elm = " "$elm;
}
else if (i == 0 && Locs(elm) == elm && elm == "b") {
// skip the 'b' in 'bDoSomething'
continue;
}
res = res$elm;
}
return res;
}
/**
* Render a localized property
*/
function string renderLocalizedSetting(int settingId, SettingRendererState renderState)
{
local name propname;
local string options;
local array<IdToStringMapping> values;
local int selectedValue;
local int i;
propname = curSettings.GetStringSettingName(settingId);
renderState.settingType = "localizedSetting";
renderState.settingId = settingId;
renderState.settingName = propname;
renderState.formName = namePrefix$propname;
renderState.label = getLocalizedSettingText(settingId);
renderState.tooltip = Localize(string(curSettings.class.name)$" Tooltips", string(propname), string(curSettings.class.getPackageName()));
curSettings.GetStringSettingValue(settingId, selectedValue);
curSettings.GetStringSettingValueNames(settingId, values);
options = "";
if (values.length >= minOptionListSize)
{
for (i = 0; i < values.Length; i++)
{
renderState.subst("setting.option.value", values[i].id);
renderState.subst("setting.option.text", `HTMLEscape(values[i].name));
if (values[i].id == selectedValue)
{
renderState.subst("setting.option.selected", "selected=\"selected\"");
}
else {
renderState.subst("setting.option.selected", "");
}
options $= renderSetting(renderState, "option.inc", i, true);
}
renderState.subst("setting.options", options);
return renderSetting(renderState, "select.inc");
}
else {
for (i = 0; i < values.Length; i++)
{
renderState.subst("setting.radio.index", i);
renderState.subst("setting.radio.value", values[i].id);
renderState.subst("setting.radio.text", `HTMLEscape(values[i].name));
if (values[i].id == selectedValue)
{
renderState.subst("setting.radio.selected", "checked=\"checked\"");
}
else {
renderState.subst("setting.radio.selected", "");
}
options $= renderSetting(renderState, "radio.inc", i);
}
return options;
}
}
/**
* Get a name for the current setting property.
*/
function string getSettingText(int settingId)
{
local string val, res, elm;
local int i;
val = curSettings.GetPropertyColumnHeader(settingId);
if (len(val) > 0) return val;
val = string(curSettings.GetPropertyName(settingId));
// FooBarQuux -> Foo Bar Quux
res = "";
for (i = 0; i < len(val); i++) {
elm = Mid(val, i, 1);
if (Caps(elm) == elm && string(int(elm)) != elm)
{
elm = " "$elm;
}
else if (i == 0 && Locs(elm) == elm && elm == "b") {
// skip the 'b' in 'bDoSomething'
continue;
}
res = res$elm;
}
return res;
}
/**
* Set the default substitution parts for the current property
* @return true when the setting should be rendered
*/
function defaultSubst(int settingId, SettingRendererState renderState)
{
local name propname;
propname = curSettings.GetPropertyName(settingId);
renderState.settingId = settingId;
renderState.settingName = propname;
renderState.formName = namePrefix$propname;
renderState.label = getSettingText(settingId);
renderState.tooltip = Localize(string(curSettings.class.name)$" Tooltips", string(propname), string(curSettings.class.getPackageName()));
renderState.dataType = curSettings.GetPropertyType(settingId);
}
function string renderPredefinedValues(int settingId, int idx, SettingRendererState renderState)
{
local string options, selectedValue, part1, part2, valDesc;
local int i, j;
local array<SettingsData> values;
local bool usedPreDef, selected;
local string svalue;
local int ivalue;
local float fvalue;
renderState.settingType = "predefinedValues";
selectedValue = curSettings.GetPropertyAsString(settingId);
values = curSettings.PropertyMappings[idx].PredefinedValues;
usedPreDef = false;
for (i = 0; i < values.Length; i++)
{
valDesc = "";
for (j = 0; j < curSettings.PropertyMappings[idx].ValueMappings.Length; j++) {
if (curSettings.PropertyMappings[idx].ValueMappings[j].Id == i) {
valDesc = string(curSettings.PropertyMappings[idx].ValueMappings[j].name);
}
}
switch (values[i].Type)
{
case SDT_Int32:
case SDT_Int64:
ivalue = curSettings.GetSettingsDataInt(values[i]);
renderState.subst("setting.option.value", string(ivalue));
if (len(valDesc) == 0) {
renderState.subst("setting.option.text", string(ivalue));
}
else {
renderState.subst("setting.option.text", valDesc);
}
selected = (ivalue == int(selectedValue));
break;
case SDT_Double:
case SDT_Float:
fvalue = curSettings.GetFloatPredefinedValues(curSettings.PropertyMappings[idx].Id, i);
renderState.subst("setting.option.value", string(fvalue));
if (len(valDesc) == 0) {
renderState.subst("setting.option.text", string(fvalue));
}
else {
renderState.subst("setting.option.text", valDesc);
}
selected = (fvalue ~= float(selectedValue));
break;
case SDT_String:
svalue = curSettings.GetStringPredefinedValues(curSettings.PropertyMappings[idx].Id, i);
renderState.subst("setting.option.value", `HTMLEscape(svalue));
if (len(valDesc) == 0) {
renderState.subst("setting.option.text", `HTMLEscape(svalue));
}
else {
renderState.subst("setting.option.text", valDesc);
}
selected = (svalue ~= selectedValue);
break;
default:
`Log("Unsupported data type "$values[i].Type$" for setting id "$settingId,,'WebAdmin');
return "";
}
if (selected)
{
usedPreDef = true;
renderState.subst("setting.option.selected", "selected=\"selected\"");
}
else {
renderState.subst("setting.option.selected", "");
}
options $= renderSetting(renderState, "option.inc", i, true);
}
curResponse.subst("setting.options", options);
if (!usedPreDef) {
renderState.formName = namePrefix$curSettings.GetPropertyName(settingId)$"_pre";
}
part1 = renderSetting(renderState, "select.inc");
if (usedPreDef) {
renderState.formName = namePrefix$curSettings.GetPropertyName(settingId)$"_raw";
}
else {
renderState.formName = namePrefix$curSettings.GetPropertyName(settingId);
}
part2 = renderRaw(settingId, idx, renderState);
renderState.subst("multisetting.predef", part1);
renderState.subst("multisetting.raw", part2);
renderState.formName = namePrefix$curSettings.GetPropertyName(settingId);
renderState.settingType = "predefinedValuesContainer";
renderState.subst("multisetting.predef.class", usedPreDef?"":"settingsraw");
renderState.subst("multisetting.rawval.class", usedPreDef?"settingsraw":"");
return renderSetting(renderState, "multisetting.inc");
}
function string renderRanged(int settingId, SettingRendererState renderState)
{
local float value, min, max, incr;
local byte asInt;
renderState.settingType = "ranged";
curSettings.GetRangedPropertyValue(settingId, value);
curSettings.GetPropertyRange(settingId, min, max, incr, asInt);
if (asInt != 1)
{
renderState.subst("setting.value", string(value));
renderState.subst("setting.minval", string(min));
renderState.subst("setting.maxval", string(max));
renderState.subst("setting.increment", string(incr));
renderState.subst("setting.asint", "false");
}
else {
renderState.subst("setting.value", string(int(value)));
renderState.subst("setting.minval", string(int(min)));
renderState.subst("setting.maxval", string(int(max)));
renderState.subst("setting.increment", string(int(incr)));
renderState.subst("setting.asint", "true");
}
return renderSetting(renderState, "ranged.inc");
}
function string renderIdMapped(int settingId, int idx, SettingRendererState renderState)
{
local string options;
local array<IdToStringMapping> values;
local int selectedValue;
local int i;
renderState.settingType = "idMapped";
curSettings.GetIntProperty(settingId, selectedValue);
values = curSettings.PropertyMappings[idx].ValueMappings;
if (values.length >= minOptionListSize)
{
for (i = 0; i < values.Length; i++)
{
renderState.subst("setting.option.value", values[i].id);
renderState.subst("setting.option.text", `HTMLEscape(values[i].name));
if (values[i].id == selectedValue)
{
renderState.subst("setting.option.selected", "selected=\"selected\"");
}
else {
renderState.subst("setting.option.selected", "");
}
options $= renderSetting(renderState, "option.inc", i, true);
}
renderState.subst("setting.options", options);
return renderSetting(renderState, "select.inc");
}
else {
for (i = 0; i < values.Length; i++)
{
renderState.subst("setting.radio.index", i);
renderState.subst("setting.radio.value", values[i].id);
renderState.subst("setting.radio.text", `HTMLEscape(values[i].name));
if (values[i].id == selectedValue)
{
renderState.subst("setting.radio.selected", "checked=\"checked\"");
}
else {
renderState.subst("setting.radio.selected", "");
}
options $= renderSetting(renderState, "radio.inc", i);
}
return options;
}
}
function string renderRaw(int settingId, int idx, SettingRendererState renderState)
{
local float min, max, incr;
renderState.settingType = "raw";
renderState.subst("setting.value", `HTMLEscape(curSettings.GetPropertyAsString(settingId)));
min = curSettings.PropertyMappings[idx].MinVal;
max = curSettings.PropertyMappings[idx].MaxVal;
incr = curSettings.PropertyMappings[idx].RangeIncrement;
switch(curSettings.GetPropertyType(settingId))
{
case SDT_Empty:
return "";
case SDT_Int32:
case SDT_Int64:
if (max != 0)
{
renderState.subst("setting.maxval", int(max));
renderState.subst("setting.minval", int(min));
}
else {
renderState.subst("setting.maxval", "Number.NaN");
renderState.subst("setting.minval", "Number.NaN");
}
if (incr > 0)
{
renderState.subst("setting.increment", string(int(incr)));
}
renderState.subst("setting.asint", "true");
return renderSetting(renderState, "int.inc");
case SDT_Double:
case SDT_Float:
if (max != 0)
{
renderState.subst("setting.maxval", max);
renderState.subst("setting.minval", min);
}
else {
renderState.subst("setting.maxval", "Number.NaN");
renderState.subst("setting.minval", "Number.NaN");
}
if (incr > 0)
{
renderState.subst("setting.increment", string(incr));
}
renderState.subst("setting.asint", "false");
return renderSetting(renderState, "float.inc");
default:
if (max != 0)
{
renderState.subst("setting.maxval", max);
renderState.subst("setting.minval", min);
}
else {
renderState.subst("setting.maxval", "Number.NaN");
renderState.subst("setting.minval", "Number.NaN");
}
if (max > 0 && max > min)
{
renderState.subst("setting.maxlength", int(max));
}
else {
renderState.subst("setting.maxlength", "");
}
if (max > 256)
{
return renderSetting(renderState, "textarea.inc");
}
else {
return renderSetting(renderState, "string.inc");
}
}
}
function string renderSetting(SettingRendererState renderState, string filename,
optional int index = -1, optional bool inContainer)
{
local int i;
for (i = 0; i < activeModifiers.length; ++i)
{
activeModifiers[i].augmentSetting(renderState, index, inContainer);
}
for (i = 0; i < renderState.substitutions.length; ++i)
{
curResponse.subst(renderState.substitutions[i].Key, renderState.substitutions[i].Value);
}
curResponse.subst("setting.type", renderState.settingType);
curResponse.subst("setting.id", string(renderState.settingId));
curResponse.subst("setting.name", renderState.settingName);
curResponse.subst("setting.formname", renderState.formName);
curResponse.subst("setting.text", `HTMLEscape(renderState.label));
curResponse.subst("setting.tooltip", `HTMLEscape(renderState.tooltip));
curResponse.subst("setting.enabled", renderState.bEnabled?"":"disabled=\"disabled\"");
curResponse.subst("setting.augmented", renderState.extra);
curResponse.subst("setting.css", renderState.cssClass);
return curResponse.LoadParsedUHTM(path $ "/" $ prefix $ filename);
}
defaultproperties
{
minOptionListSize=4
}

View File

@ -0,0 +1,202 @@
/**
* Utility class to calculate SHA1 hash of some input. This is not thread-safe
*/
class Sha1HashLib extends HashLib;
/**
* Stores the last result
*/
var private int hash[5];
var private string hashString;
var private array<byte> data;
function string getAlgName()
{
return "sha1";
}
function string getHash(coerce string inputData)
{
local int strlen, char, i;
hashString = "";
// convert the input string
data.length = 0;
strlen = Len(inputData);
for (i = 0; i < strlen; ++i)
{
char = Asc(Mid(inputData, i, 1));
do {
data[data.length] = byte(char & 0xFF);
char = char >>> 8;
} until (char == 0);
}
calcHash();
data.length = 0;
return hashString;
}
private final function calcHash()
{
local int i, chunk, tmp;
local int a, b, c, d, e;
local int w[80];
// initialize the result
hash[0] = 0x67452301;
hash[1] = 0xEFCDAB89;
hash[2] = 0x98BADCFE;
hash[3] = 0x10325476;
hash[4] = 0xC3D2E1F0;
// initialize the data
i = data.length;
if (i % 64 < 56)
{
data.length = data.length + 64 - i % 64;
}
else {
data.length = data.length + 128 - i % 64;
}
data[i] = 0x80;
data[data.length - 5] = i >>> 29;
data[data.length - 4] = i >>> 21;
data[data.length - 3] = i >>> 13;
data[data.length - 2] = i >>> 5;
data[data.length - 1] = i << 3;
// the transformation stuff
while (chunk * 64 < data.length) {
for (i = 0; i < 16; i++) {
w[i] = (data[chunk * 64 + i * 4] << 24)
| (data[chunk * 64 + i * 4 + 1] << 16)
| (data[chunk * 64 + i * 4 + 2] << 8)
| data[chunk * 64 + i * 4 + 3];
}
for (i = 16; i < 80; i++) {
tmp = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
w[i] = (tmp << 1) | (tmp >>> 31);
}
a = hash[0];
b = hash[1];
c = hash[2];
d = hash[3];
e = hash[4];
// Round 1
e += ((a << 5) | (a >>> -5)) + (d ^ (b & (c ^ d))) + w[ 0] + 0x5A827999; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (c ^ (a & (b ^ c))) + w[ 1] + 0x5A827999; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (b ^ (e & (a ^ b))) + w[ 2] + 0x5A827999; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (a ^ (d & (e ^ a))) + w[ 3] + 0x5A827999; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (e ^ (c & (d ^ e))) + w[ 4] + 0x5A827999; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (d ^ (b & (c ^ d))) + w[ 5] + 0x5A827999; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (c ^ (a & (b ^ c))) + w[ 6] + 0x5A827999; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (b ^ (e & (a ^ b))) + w[ 7] + 0x5A827999; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (a ^ (d & (e ^ a))) + w[ 8] + 0x5A827999; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (e ^ (c & (d ^ e))) + w[ 9] + 0x5A827999; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (d ^ (b & (c ^ d))) + w[10] + 0x5A827999; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (c ^ (a & (b ^ c))) + w[11] + 0x5A827999; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (b ^ (e & (a ^ b))) + w[12] + 0x5A827999; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (a ^ (d & (e ^ a))) + w[13] + 0x5A827999; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (e ^ (c & (d ^ e))) + w[14] + 0x5A827999; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (d ^ (b & (c ^ d))) + w[15] + 0x5A827999; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (c ^ (a & (b ^ c))) + w[16] + 0x5A827999; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (b ^ (e & (a ^ b))) + w[17] + 0x5A827999; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (a ^ (d & (e ^ a))) + w[18] + 0x5A827999; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (e ^ (c & (d ^ e))) + w[19] + 0x5A827999; c = (c << 30) | (c >>> -30);
// Round 2
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[20] + 0x6ED9EBA1; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[21] + 0x6ED9EBA1; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[22] + 0x6ED9EBA1; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[23] + 0x6ED9EBA1; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[24] + 0x6ED9EBA1; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[25] + 0x6ED9EBA1; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[26] + 0x6ED9EBA1; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[27] + 0x6ED9EBA1; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[28] + 0x6ED9EBA1; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[29] + 0x6ED9EBA1; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[30] + 0x6ED9EBA1; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[31] + 0x6ED9EBA1; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[32] + 0x6ED9EBA1; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[33] + 0x6ED9EBA1; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[34] + 0x6ED9EBA1; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[35] + 0x6ED9EBA1; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[36] + 0x6ED9EBA1; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[37] + 0x6ED9EBA1; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[38] + 0x6ED9EBA1; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[39] + 0x6ED9EBA1; c = (c << 30) | (c >>> -30);
// Round 3
e += ((a << 5) | (a >>> -5)) + ((b & c) | (d & (b | c))) + w[40] + 0x8F1BBCDC; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + ((a & b) | (c & (a | b))) + w[41] + 0x8F1BBCDC; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + ((e & a) | (b & (e | a))) + w[42] + 0x8F1BBCDC; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + ((d & e) | (a & (d | e))) + w[43] + 0x8F1BBCDC; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + ((c & d) | (e & (c | d))) + w[44] + 0x8F1BBCDC; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + ((b & c) | (d & (b | c))) + w[45] + 0x8F1BBCDC; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + ((a & b) | (c & (a | b))) + w[46] + 0x8F1BBCDC; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + ((e & a) | (b & (e | a))) + w[47] + 0x8F1BBCDC; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + ((d & e) | (a & (d | e))) + w[48] + 0x8F1BBCDC; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + ((c & d) | (e & (c | d))) + w[49] + 0x8F1BBCDC; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + ((b & c) | (d & (b | c))) + w[50] + 0x8F1BBCDC; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + ((a & b) | (c & (a | b))) + w[51] + 0x8F1BBCDC; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + ((e & a) | (b & (e | a))) + w[52] + 0x8F1BBCDC; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + ((d & e) | (a & (d | e))) + w[53] + 0x8F1BBCDC; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + ((c & d) | (e & (c | d))) + w[54] + 0x8F1BBCDC; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + ((b & c) | (d & (b | c))) + w[55] + 0x8F1BBCDC; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + ((a & b) | (c & (a | b))) + w[56] + 0x8F1BBCDC; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + ((e & a) | (b & (e | a))) + w[57] + 0x8F1BBCDC; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + ((d & e) | (a & (d | e))) + w[58] + 0x8F1BBCDC; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + ((c & d) | (e & (c | d))) + w[59] + 0x8F1BBCDC; c = (c << 30) | (c >>> -30);
// Round 4
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[60] + 0xCA62C1D6; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[61] + 0xCA62C1D6; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[62] + 0xCA62C1D6; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[63] + 0xCA62C1D6; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[64] + 0xCA62C1D6; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[65] + 0xCA62C1D6; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[66] + 0xCA62C1D6; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[67] + 0xCA62C1D6; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[68] + 0xCA62C1D6; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[69] + 0xCA62C1D6; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[70] + 0xCA62C1D6; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[71] + 0xCA62C1D6; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[72] + 0xCA62C1D6; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[73] + 0xCA62C1D6; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[74] + 0xCA62C1D6; c = (c << 30) | (c >>> -30);
e += ((a << 5) | (a >>> -5)) + (b ^ c ^ d) + w[75] + 0xCA62C1D6; b = (b << 30) | (b >>> -30);
d += ((e << 5) | (e >>> -5)) + (a ^ b ^ c) + w[76] + 0xCA62C1D6; a = (a << 30) | (a >>> -30);
c += ((d << 5) | (d >>> -5)) + (e ^ a ^ b) + w[77] + 0xCA62C1D6; e = (e << 30) | (e >>> -30);
b += ((c << 5) | (c >>> -5)) + (d ^ e ^ a) + w[78] + 0xCA62C1D6; d = (d << 30) | (d >>> -30);
a += ((b << 5) | (b >>> -5)) + (c ^ d ^ e) + w[79] + 0xCA62C1D6; c = (c << 30) | (c >>> -30);
hash[0] += A;
hash[1] += B;
hash[2] += C;
hash[3] += D;
hash[4] += E;
chunk++;
}
hashString = ToHex(hash[0])$ToHex(hash[1])$ToHex(hash[2])$ToHex(hash[3])$ToHex(hash[4]);
}

View File

@ -0,0 +1,30 @@
/**
* Provides a proxy for team chat
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class TeamChatProxy extends MessagingSpectator;
reliable client event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type, optional float MsgLifeTime )
{
local delegate<ReceiveMessage> rm;
if (type != 'TeamSay') return;
foreach receivers(rm)
{
rm(pri, s, type);
}
}
function InitPlayerReplicationInfo()
{
super.InitPlayerReplicationInfo();
PlayerReplicationInfo.PlayerName = "<<TeamChatProxy>>";
}
defaultProperties
{
bKeepAlive=true
}

1313
WebAdmin/Classes/WebAdmin.uc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,333 @@
/**
* Menu manager for the webadmin
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminMenu extends Object;
struct MenuItem
{
/**
* The absolute path (from the webapp path). Examples:
* foo
* foo/bar/quux
* bar
* bar/quux
* bar/quux2
* bar/quux2+data
*
* The bar/quux2+data item will be considered as a sub page of bar/quux2
* When checking for permissions bar/quux2 will be used instead of
* bar/quux2+data
*/
var string path;
/**
* The path to use for permission checking. Will default to path
*/
var string permPath;
/**
* Title of the menu item. If empty the menu item is not shown in the
* rendered menu
*/
var string title;
/**
* A short description
*/
var string description;
/**
* The weight of this item. A low number means it will be higher in the list.
*/
var int weight;
/**
* If true, the menu item is not rendered. Can be used to hide previously
* visible menu items from the navigation menu.
*/
var bool hidden;
/**
* The handler responsible for handling this menu item.
*/
var IQueryHandler handler;
};
var array<MenuItem> menu;
var WebAdmin webadmin;
struct TreeItem
{
/**
* Index to the item in the menu list;
*/
var int cur;
/**
* Path element
*/
var string elm;
var array<int> children;
};
var array<TreeItem> tree;
/**
* Add an item to the menu, or if the path already exist, update an existing
* item. To create a hidden item simply leave out the title.
*/
function addMenuItem(MenuItem item)
{
local int idx;
idx = menu.find('path', item.path);
if (len(item.permPath) == 0)
{
item.permPath = item.path;
}
if (idx > -1)
{
menu[idx].title = item.title;
menu[idx].description = item.description;
menu[idx].weight = item.weight;
menu[idx].handler = item.handler;
}
else {
menu.addItem(item);
}
}
/**
* Add a new menu item (or overwrite the previous for the given path).
*/
function addMenu(string path, string title, IQueryHandler handler,
optional string description = "", optional int weight = 0,
optional string permPath = "")
{
local MenuItem item;
item.path = path;
item.title = title;
item.description = description;
item.weight = weight;
item.handler = handler;
item.permPath = permPath;
item.hidden = len(title) == 0;
addMenuItem(item);
}
/**
* Get the menu handler for a given path
*/
function IQueryHandler getHandlerFor(string path, out string title, out string desc)
{
local int idx;
idx = menu.find('path', path);
if (idx > -1)
{
title = menu[idx].title;
desc = menu[idx].description;
return menu[idx].handler;
}
return none;
}
function setVisibility(string path, bool isVisible)
{
local int idx;
idx = menu.find('path', path);
if (idx > -1)
{
menu[idx].hidden = !isVisible;
}
}
/**
* return the menu instance of the given user. All paths to which the user has
* no access will be filtered from the list.
*
* @return none when the user has absolutely no access, otherwise an instance is
* returned that only contains the paths the user has access to.
*/
function WebAdminMenu getUserMenu(IWebAdminUser forUser)
{
local WebAdminMenu result;
local MenuItem entry, dummy;
if (!forUser.canPerform(webadmin.getAuthURL("/")))
{
return none;
}
result = new(webadmin) class; // create a new instance this class
result.webadmin = webadmin;
foreach menu(entry)
{
if (forUser.canPerform(webadmin.getAuthURL(entry.permPath)))
{
result.addSortedItem(entry);
}
else {
dummy.path = entry.path;
dummy.weight = entry.weight;
result.addSortedItem(dummy);
}
}
result.createTree();
return result;
}
/**
* Add a menu item to the list sorting on the full path.
*/
protected function addSortedItem(MenuItem item)
{
local MenuItem entry;
local int idx;
foreach menu(entry, idx)
{
if (entry.path > item.path)
{
menu.InsertItem(idx, item);
return;
}
}
menu.AddItem(item);
}
/**
* Parses the sorted list of menu items and creates the tree.
*/
protected function createTree()
{
local MenuItem entry;
local int idx;
local int i, idx2, parent, child;
local array<string> parts;
local bool found;
tree.Length = 1;
tree[0].cur = -1;
foreach menu(entry, idx)
{
ParseStringIntoArray(entry.path, parts, "/", true);
parent = 0;
i = 0;
// find the parent item
while (i < parts.length-1)
{
found = false;
foreach tree[parent].children(child)
{
if (tree[child].elm == parts[i])
{
i++;
parent = child;
found = true;
break;
}
}
if (!found)
{
// create a dummy item
tree.Add(1);
tree[tree.length-1].cur = -1;
tree[tree.length-1].elm = parts[i];
tree[parent].children.AddItem(tree.length-1);
parent = tree.length-1;
i++;
}
}
// add the item
found = false;
foreach tree[parent].children(child, idx2)
{
if (menu[tree[child].cur].weight > entry.weight)
{
tree[parent].children.Insert(idx2, 1);
tree[parent].children[idx2] = tree.length;
tree.Add(1);
tree[tree.length-1].cur = idx;
tree[tree.length-1].elm = parts[parts.length-1];
found = true;
break;
}
}
if (!found)
{
idx2 = tree[parent].children.length;
tree[parent].children.add(1);
tree[parent].children[idx2] = tree.length;
tree.Add(1);
tree[tree.length-1].cur = idx;
tree[tree.length-1].elm = parts[parts.length-1];
}
}
}
/**
* Render the current menu tree to a navigation menu
*/
function string render(optional string menu_template = "/navigation_menu.inc",
optional string item_template = "/navigation_item.inc")
{
local string result;
local WebResponse wr;
local MenuItem entry;
local array<MenuItem> menuCopy;
wr = new class'WebResponse';
if (tree.Length == 0)
{
menuCopy = menu;
menu.length = 0;
foreach menuCopy(entry)
{
addSortedItem(entry);
}
createTree();
}
result = renderChilds(tree[0].children, wr, menu_template, item_template);
wr.subst("navigation.items", result);
return wr.LoadParsedUHTM(webadmin.path$menu_template);
}
protected function string renderChilds(array<int> childs, WebResponse wr,
optional string menu_template = "/navigation_menu.inc",
optional string item_template = "/navigation_item.inc")
{
local int child, menuid;
local string result, subitems;
foreach childs(child)
{
menuid = tree[child].cur;
if ((menuid > -1) && !menu[menuid].hidden && (Len(menu[menuid].title) > 0))
{
if (tree[child].children.length > 0)
{
subitems = renderChilds(tree[child].children, wr, menu_template, item_template);
if (len(subitems) > 0)
{
wr.subst("navigation.items", subitems, true);
subitems = wr.LoadParsedUHTM(webadmin.path$menu_template);
}
}
else {
subitems = "";
}
wr.subst("item.submenu", subitems, true);
wr.subst("item.type", tree[child].children.length > 0?"with-submenu":"no-submenu");
wr.subst("item.path", webadmin.path$menu[menuid].path);
wr.subst("item.menupath", menu[menuid].path);
wr.subst("item.title", menu[menuid].title);
wr.subst("item.description", menu[menuid].description);
result $= wr.LoadParsedUHTM(webadmin.path$item_template);
}
else if (tree[child].children.length > 0)
{
result $= renderChilds(tree[child].children, wr, menu_template, item_template);
}
}
return result;
}

View File

@ -0,0 +1,45 @@
/**
* Holds a list of messages for the current session.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminMessages extends Object dependson(IQueryHandler);
var array<Message> messages;
function addMessage(string msg, optional EMessageType type = MT_Information)
{
local Message newmsg;
if (len(msg) == 0) return;
newmsg.text = msg;
newmsg.type = type;
messages.AddItem(newmsg);
}
function string renderMessages(WebAdmin wa, WebAdminQuery q)
{
local string result;
local Message msg;
foreach messages(msg)
{
q.response.subst("message", msg.text);
switch(msg.type)
{
case MT_Information:
result $= wa.include(q, "message_info.inc");
break;
case MT_Warning:
result $= wa.include(q, "message_warn.inc");
break;
case MT_Error:
result $= wa.include(q, "message_error.inc");
break;
}
}
q.response.subst("messages", result);
messages.length = 0;
return wa.include(q, "messages.inc");;
}

View File

@ -0,0 +1,246 @@
/**
* Base class for the pluggable settings mechanism for the WebAdmin.
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminSettings extends Settings abstract;
/**
* Specification of a settings group.
*/
struct SettingsGroupSpec
{
/**
* An unique id for this group
*/
var string groupId;
/**
* Name to show as title for the group.
*/
var localized string DisplayName;
/**
* range in the properties that belongs to this group
*/
var int pMin, pMax;
/**
* range in the localized properties that belongs to this group
*/
var int lMin, lMax;
};
struct FloatPredefinedValue
{
var int PropertyId;
var array<float> Values;
};
struct StringPredefinedValue
{
var int PropertyId;
var array<string> Values;
};
/**
* Returned by default by the settingGroups function
*/
var protected array<SettingsGroupSpec> SettingsGroups;
var protected array<FloatPredefinedValue> FloatPredefinedValues;
var protected array<StringPredefinedValue> StringPredefinedValues;
/**
* Called by the webadmin to intialize the values for the settings.
*/
function initSettings();
/**
* Called to provide the current gameinfo instance. If you use this (i.e. store
* it) make sure to set it to none in cleanupSettings().
*/
function setCurrentGameInfo(GameInfo instance);
/**
* Called by the webadmin to save the settings.
*/
function saveSettings()
{
saveInternal();
}
/**
* Called by the webadmin when it is being destroyed. Implement this function
* to clean references to objects and/or actors when needed.
*/
function cleanupSettings();
/**
* Return the groups specification. When an empty array is returned settings are
* not grouped in any way.
*/
function array<SettingsGroupSpec> settingGroups()
{
return SettingsGroups;
}
/**
* Get the predefined values for a given property which uses floats
*/
function float GetFloatPredefinedValues(int PropertyId, int Index, float DefValue = 0)
{
local int idx;
idx = FloatPredefinedValues.Find('PropertyId', PropertyId);
if (idx == INDEX_NONE)
{
`warn("GetFloatPredefinedValues("$PropertyId$", "$Index$"): no property");
return DefValue;
}
if (FloatPredefinedValues[idx].Values.Length <= Index || Index < 0)
{
`warn("GetFloatPredefinedValues("$PropertyId$", "$Index$"): index out of bounds");
return DefValue;
}
return FloatPredefinedValues[idx].values[Index];
}
/**
* Get the predefined values for a given property which uses strings
*/
function string GetStringPredefinedValues(int PropertyId, int Index, string DefValue = "")
{
local int idx;
idx = StringPredefinedValues.Find('PropertyId', PropertyId);
if (idx == INDEX_NONE)
{
`warn("GetStringPredefinedValues("$PropertyId$", "$Index$"): no property");
return DefValue;
}
if (StringPredefinedValues[idx].Values.Length <= Index || Index < 0)
{
`warn("GetStringPredefinedValues("$PropertyId$", "$Index$"): index out of bounds");
return DefValue;
}
return StringPredefinedValues[idx].values[Index];
}
/**
* Called by saveSettings. This function should perform the updating of the
* configuration
*/
protected function saveInternal();
// helper functions
protected function bool SetFloatPropertyByName(name prop, float value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
SetFloatProperty(PropertyId, value);
return true;
}
return false;
}
protected function bool SetIntPropertyByName(name prop, int value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
SetIntProperty(PropertyId, value);
return true;
}
return false;
}
protected function bool SetStringPropertyByName(name prop, string value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
SetStringProperty(PropertyId, value);
return true;
}
return false;
}
protected function bool SetStringArrayPropertyByName(name prop, array<string> value, optional string delim = ";")
{
local int PropertyId;
local string realval;
local int i;
if (GetPropertyId(prop, PropertyId))
{
for (i = 0; i < value.length; i++)
{
if (i > 0) realval $= delim;
realval $= value[i];
}
SetStringProperty(PropertyId, realval);
return true;
}
return false;
}
protected function bool GetFloatPropertyByName(name prop, out float value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
return GetFloatProperty(PropertyId, value);
}
return false;
}
protected function bool GetIntPropertyByName(name prop, out int value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
return GetIntProperty(PropertyId, value);
}
return false;
}
protected function bool GetStringPropertyByName(name prop, out string value)
{
local int PropertyId;
if (GetPropertyId(prop, PropertyId))
{
return GetStringProperty(PropertyId, value);
}
return false;
}
protected function bool GetStringArrayPropertyByName(name prop, out array<string> value, optional string delim = ";")
{
local int PropertyId;
local string realval;
local int i;
if (GetPropertyId(prop, PropertyId))
{
if (GetStringProperty(PropertyId, realval))
{
value.length = 0;
ParseStringIntoArray(realval, value, delim, true);
for (i = 0; i < value.length; i++)
{
value[i] -= chr(10);
value[i] -= chr(13);
}
return true;
}
}
return false;
}
defaultproperties
{
}

View File

@ -0,0 +1,17 @@
/**
* Data resource for webadmin "skins"
*
* Copyright 2009 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminSkin extends UIResourceDataProvider PerObjectConfig;
var config string FriendlyName;
var config string CssFile;
defaultproperties
{
//bSearchAllInis=true
}

View File

@ -0,0 +1,340 @@
/**
* Configuration settings for the WebAdmin
*
* Copyright 2009 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminSystemSettings extends WebAdminSettings implements(IQueryHandler);
`include(WebAdmin.uci)
var WebAdmin webadmin;
var QHCurrent qhcurrent;
var ChatLog chatlog;
var SettingsRenderer settingsRenderer;
//!localized
var localized string menuSystem, menuSystemDesc, msgSettingsSaved;
function init(WebAdmin webapp)
{
webadmin = webapp;
}
function delayedInit()
{
local IQueryHandler qh;
local Object o;
foreach webadmin.handlers(qh)
{
if (qh.IsA('QHCurrent'))
{
o = qh;
qhcurrent = QHCurrent(o);
}
}
foreach webadmin.WorldInfo.AllActors(class'ChatLog', chatlog)
{
break;
}
}
function cleanup()
{
settingsRenderer = none;
qhcurrent = none;
chatlog = none;
webadmin = none;
}
function registerMenuItems(WebAdminMenu menu)
{
menu.addMenu("/webadmin", menuSystem, self, menuSystemDesc, 999);
menu.addMenu("/system/allowancecache", "", self, "Rebuild the mutator allowance cache.");
}
function bool handleQuery(WebAdminQuery q)
{
switch (q.request.URI)
{
case "/webadmin":
handleSettings(q);
return true;
case "/system/allowancecache":
handleRebuildAllowanceCache(q);
return true;
}
return false;
}
function bool unhandledQuery(WebAdminQuery q);
function decoratePage(WebAdminQuery q);
function bool producesXhtml()
{
return true;
}
function handleRebuildAllowanceCache(WebAdminQuery q)
{
local array<DCEGameInfo> gts;
local int i;
if (q.request.getVariable("action") ~= "rebuild")
{
webadmin.dataStoreCache.allowanceCache.length = 0;
gts = webadmin.dataStoreCache.getGameTypes();
for (i = 0; i < gts.length; i++)
{
webadmin.dataStoreCache.getMutators(gts[i].data.ClassName);
}
webadmin.addMessage(q, "Mutator allowance cache has been rebuild.");
}
webadmin.addMessage(q, "<form action=\""$WebAdmin.Path$q.Request.URI$"\" method=\"post\">"
$"<p>Only rebuild the mutator cache when the server is empty. It is strongly adviced to restart the game after rebuilding has been completed.</p>"
$"<p><button type=\"submit\" name=\"action\" value=\"rebuild\">Rebuild cache</button></p></form>", MT_Warning);
q.response.Subst("page.title", "Rebuild Mutator Allowance Cache");
webadmin.sendPage(q, "message.html");
}
function handleSettings(WebAdminQuery q)
{
local ISettingsPrivileges privs;
if (settingsRenderer == none)
{
delayedInit();
loadSettings();
settingsRenderer = new class'SettingsRenderer';
settingsRenderer.init(webadmin.path);
}
if (q.request.getVariable("action") ~= "save")
{
class'QHDefaults'.static.applySettings(self, q.request);
saveSettings();
webadmin.addMessage(q, msgSettingsSaved);
}
privs = q.user.getSettingsPrivileges();
if (privs != none)
{
privs.setBasePrivilegeUri(webadmin.getAuthURL(q.request.uri));
}
settingsRenderer.render(self, q.response,, privs);
q.response.subst("liveAdjustStyle", "style=\"display: none;\"");
webadmin.sendPage(q, "default_settings_general.html");
}
/**
* Load the webadmin settings
*/
protected function loadSettings()
{
// generic
SetStringPropertyByName('AuthenticationClass', webadmin.AuthenticationClass);
SetStringPropertyByName('SessionHandlerClass', webadmin.SessionHandlerClass);
SetIntPropertyByName('bHttpAuth', int(webadmin.bHttpAuth));
SetStringPropertyByName('startpage', webadmin.startpage);
SetIntPropertyByName('bChatLog', int(webadmin.bChatLog));
SetIntPropertyByName('bUseStrictContentType', int(webadmin.bUseStrictContentType));
SetIntPropertyByName('sessionOctetValidation', webadmin.sessionOctetValidation);
SetIntPropertyByName('MaxAuthFails', webadmin.MaxAuthFails);
// qhcurrent
if (qhcurrent != none)
{
SetIntPropertyByName('ChatRefresh', qhcurrent.ChatRefresh);
SetIntPropertyByName('bConsoleEnabled', int(qhcurrent.bConsoleEnabled));
SetStringArrayPropertyByName('denyUrlOptions', qhcurrent.denyUrlOptions, chr(10));
SetStringArrayPropertyByName('denyConsoleCommands', qhcurrent.denyConsoleCommands, chr(10));
SetIntPropertyByName('bAdminConsoleCommandsHack', int(qhcurrent.bAdminConsoleCommandsHack));
SetStringPropertyByName('AdminCommandHandlerClass', qhcurrent.AdminCommandHandlerClass);
SetIntPropertyByName('bEnableTeamChat', int(qhcurrent.bEnableTeamChat));
SetIntPropertyByName('hideNews', int(qhcurrent.hideNews));
}
else {
SetIntPropertyByName('ChatRefresh', class'QHCurrent'.default.ChatRefresh);
SetIntPropertyByName('bConsoleEnabled', int(class'QHCurrent'.default.bConsoleEnabled));
SetStringArrayPropertyByName('denyUrlOptions', class'QHCurrent'.default.denyUrlOptions, chr(10));
SetStringArrayPropertyByName('denyConsoleCommands', class'QHCurrent'.default.denyConsoleCommands, chr(10));
SetIntPropertyByName('bAdminConsoleCommandsHack', int(class'QHCurrent'.default.bAdminConsoleCommandsHack));
SetStringPropertyByName('AdminCommandHandlerClass', class'QHCurrent'.default.AdminCommandHandlerClass);
SetIntPropertyByName('bEnableTeamChat', int(class'QHCurrent'.default.bEnableTeamChat));
SetIntPropertyByName('hideNews', int(class'QHCurrent'.default.hideNews));
}
if (chatlog != none)
{
SetStringPropertyByName('chatLogFilename', chatlog.filename);
SetIntPropertyByName('chatLogUnique', int(chatlog.bUnique));
SetIntPropertyByName('chatLogIncludeTimeStamp', int(chatlog.bIncludeTimeStamp));
}
else {
SetStringPropertyByName('chatLogFilename', class'ChatLog'.default.filename);
SetIntPropertyByName('chatLogUnique', int(class'ChatLog'.default.bUnique));
SetIntPropertyByName('chatLogIncludeTimeStamp', int(class'ChatLog'.default.bIncludeTimeStamp));
}
}
function saveSettings()
{
local int intval;
// generic
GetStringPropertyByName('AuthenticationClass', webadmin.AuthenticationClass);
GetStringPropertyByName('SessionHandlerClass', webadmin.SessionHandlerClass);
if (GetIntPropertyByName('bHttpAuth', intval))
{
webadmin.bHttpAuth = intval != 0;
}
GetStringPropertyByName('startpage', webadmin.startpage);
if (GetIntPropertyByName('bChatLog', intval))
{
webadmin.bChatLog = intval != 0;
}
if (GetIntPropertyByName('bUseStrictContentType', intval))
{
webadmin.bUseStrictContentType = intval != 0;
}
GetIntPropertyByName('sessionOctetValidation', webadmin.sessionOctetValidation);
GetIntPropertyByName('MaxAuthFails', webadmin.MaxAuthFails);
webadmin.SaveConfig();
// qhcurrent
if (qhcurrent != none)
{
GetIntPropertyByName('ChatRefresh', qhcurrent.ChatRefresh);
if (GetIntPropertyByName('bConsoleEnabled', intval))
{
qhcurrent.bConsoleEnabled = intval != 0;
}
GetStringArrayPropertyByName('denyUrlOptions', qhcurrent.denyUrlOptions, chr(10));
GetStringArrayPropertyByName('denyConsoleCommands', qhcurrent.denyConsoleCommands, chr(10));
if (GetIntPropertyByName('bAdminConsoleCommandsHack', intval))
{
qhcurrent.bAdminConsoleCommandsHack = intval != 0;
}
GetStringPropertyByName('AdminCommandHandlerClass', qhcurrent.AdminCommandHandlerClass);
if (GetIntPropertyByName('bEnableTeamChat', intval))
{
qhcurrent.bEnableTeamChat = intval != 0;
}
if (GetIntPropertyByName('hideNews', intval))
{
qhcurrent.hideNews = intval != 0;
}
qhcurrent.SaveConfig();
}
else {
GetIntPropertyByName('ChatRefresh', class'QHCurrent'.default.ChatRefresh);
if (GetIntPropertyByName('bConsoleEnabled', intval))
{
class'QHCurrent'.default.bConsoleEnabled = intval != 0;
}
GetStringArrayPropertyByName('denyUrlOptions', class'QHCurrent'.default.denyUrlOptions, chr(10));
GetStringArrayPropertyByName('denyConsoleCommands', class'QHCurrent'.default.denyConsoleCommands, chr(10));
if (GetIntPropertyByName('bAdminConsoleCommandsHack', intval))
{
class'QHCurrent'.default.bAdminConsoleCommandsHack = intval != 0;
}
GetStringPropertyByName('AdminCommandHandlerClass', class'QHCurrent'.default.AdminCommandHandlerClass);
if (GetIntPropertyByName('bEnableTeamChat', intval))
{
class'QHCurrent'.default.bEnableTeamChat = intval != 0;
}
if (GetIntPropertyByName('hideNews', intval))
{
class'QHCurrent'.default.hideNews = intval != 0;
}
class'QHCurrent'.static.StaticSaveConfig();
}
// chatlog
if (chatlog != none)
{
GetStringPropertyByName('chatLogFilename', chatlog.filename);
if (GetIntPropertyByName('chatLogUnique', intval))
{
chatlog.bUnique = intval != 0;
}
if (GetIntPropertyByName('chatLogIncludeTimeStamp', intval))
{
chatlog.bIncludeTimeStamp = intval != 0;
}
chatlog.SaveConfig();
}
else {
GetStringPropertyByName('chatLogFilename', class'ChatLog'.default.filename);
if (GetIntPropertyByName('chatLogUnique', intval))
{
class'ChatLog'.default.bUnique = intval != 0;
}
if (GetIntPropertyByName('chatLogIncludeTimeStamp', intval))
{
class'ChatLog'.default.bIncludeTimeStamp = intval != 0;
}
class'ChatLog'.static.StaticSaveConfig();
}
}
defaultProperties
{
SettingsGroups.Add((groupId="General",pMin=0,pMax=20))
SettingsGroups.Add((groupId="ChatLogging",pMin=20,pMax=30))
SettingsGroups.Add((groupId="Authentication",pMin=30,pMax=40))
SettingsGroups.Add((groupId="Advanced",pMin=100,pMax=120))
// generic
Properties.Add((PropertyId=100,Data=(Type=SDT_String)))
Properties.Add((PropertyId=101,Data=(Type=SDT_String)))
Properties.Add((PropertyId=30,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=0,Data=(Type=SDT_String)))
Properties.Add((PropertyId=20,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=102,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=100,Name="AuthenticationClass" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
PropertyMappings.Add((Id=101,name="SessionHandlerClass" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
PropertyMappings.Add((Id=30,name="bHttpAuth" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=0,name="startpage" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
PropertyMappings.Add((Id=20,name="bChatLog" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=102,name="bUseStrictContentType" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
// qhcurrent
Properties.Add((PropertyId=1,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=2,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=3,Data=(Type=SDT_String)))
Properties.Add((PropertyId=4,Data=(Type=SDT_String)))
Properties.Add((PropertyId=104,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=103,Data=(Type=SDT_String)))
Properties.Add((PropertyId=5,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=6,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=1,name="ChatRefresh" ,MappingType=PVMT_Ranged,MinVal=0,MaxVal=9999,RangeIncrement=10))
PropertyMappings.Add((Id=2,name="bConsoleEnabled" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=3,name="denyUrlOptions" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=1024))
PropertyMappings.Add((Id=4,name="denyConsoleCommands" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=1024))
PropertyMappings.Add((Id=104,name="bAdminConsoleCommandsHack" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=103,name="AdminCommandHandlerClass" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
PropertyMappings.Add((Id=5,name="bEnableTeamChat" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=6,name="hideNews" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
// chatlog
Properties.Add((PropertyId=21,Data=(Type=SDT_String)))
Properties.Add((PropertyId=22,Data=(Type=SDT_Int32)))
Properties.Add((PropertyId=23,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=21,name="chatLogFilename" ,MappingType=PVMT_RawValue,MinVal=0,MaxVal=256))
PropertyMappings.Add((Id=22,name="chatLogUnique" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
PropertyMappings.Add((Id=23,name="chatLogIncludeTimeStamp" ,MappingType=PVMT_IdMapped,ValueMappings=((Id=0 ),(Id=1 ))))
Properties.Add((PropertyId=31,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=31,name="sessionOctetValidation" ,MappingType=PVMT_PredefinedValues,PredefinedValues=((Value1=0,Type=SDT_Int32),(Value1=1,Type=SDT_Int32),(Value1=2,Type=SDT_Int32),(Value1=3,Type=SDT_Int32),(Value1=4,Type=SDT_Int32)),MinVal=0,MaxVal=4,RangeIncrement=1))
Properties.Add((PropertyId=32,Data=(Type=SDT_Int32)))
PropertyMappings.Add((Id=32,name="MaxAuthFails",MappingType=PVMT_Ranged,MinVal=0,MaxVal=9999,RangeIncrement=1))
}

View File

@ -0,0 +1,487 @@
/**
* Various static utility functions
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebAdminUtils extends Object;
struct TranslitEntry
{
/** from */
var string f;
/** to */
var string t;
};
/** translit characters */
var localized array<TranslitEntry> translit;
var localized string msgUnknownTeam;
var const array<string> monthNames;
var const array<string> dayNames;
var const array<int> dowTable;
struct DateTime
{
var int year;
var int month;
var int day;
var int hour;
var int minute;
var int second;
};
static final function String translitText(coerce string inp)
{
local int i;
for (i = 0; i < default.translit.length; i++)
{
inp = Repl(inp, default.translit[i].f, default.translit[i].t);
}
return inp;
}
/**
* Escape HTML tags in the given string. Expects the input to not contain any
* escaped entities (i.e. &...;)
*/
static final function String HTMLEscape(coerce string inp)
{
inp = translitText(inp);
inp = Repl(inp, "&", "&amp;");
inp = Repl(inp, "<", "&lt;");
inp = Repl(inp, "\"", "&quot;");
return Repl(inp, ">", "&gt;");
}
/**
* Trim everything below ascii 32 from begin and end
*/
static final function String Trim(coerce string inp)
{
local int b,e;
b = 0;
e = Len(inp)-1;
while (b < e)
{
if (Asc(Mid(inp, b, 1)) > 32) break;
b++;
}
while (e >= b)
{
if (Asc(Mid(inp, e, 1)) > 32) break;
e--;
}
return mid(inp, b, e-b+1);
}
/**
* Convert a color to the HTML equivalent
*/
static final function String ColorToHTMLColor(color clr)
{
return "#"$Right(ToHex(clr.R), 2)$Right(ToHex(clr.G), 2)$Right(ToHex(clr.B), 2);
}
/**
* Convert a HTML color to an unreal color
*/
static final function color HTMLColorToColor(string clr)
{
local color res;
local int col;
clr = trim(clr);
if (left(clr, 1) == "#")
{
clr = mid(clr, 1);
}
if (len(clr) != 6)
{
return res;
}
col = FromHex(clr);
if (clr == "<<invalid>>")
{
return res;
}
res.R = (col & 0xff0000) >>> 16;
res.G = (col & 0xff00) >>> 8;
res.B = col & 0xff;
res.A = 255;
return res;
}
static final function int FromHex(out string hx)
{
local int res, i, t, s;
if (len(hx) > 8)
{
hx = "<<invalid>>";
return -1;
}
hx = caps(hx);
s = 0;
for (i = len(hx) - 1; i >= 0; --i)
{
t = asc(mid(hx, i, 1));
if (t >= 48 && t <= 57)
{
t -= 48;
}
else if (t >= 65 && t <= 70)
{
t -= 55;
}
else {
hx = "<<invalid>>";
return -1;
}
if (s > 0) {
t = t << s;
}
res = res | t;
s += 4;
}
return res;
}
/**
* Parse a timestamp to a DateTime structure.
* The format of the timestamp is: YYYY/MM/DD - HH:MM:SS
*/
static final function bool getDateTime(out DateTime record, string ts)
{
local int idx;
local array<string> parts;
ts -= " ";
idx = InStr(ts, "-");
if (idx == INDEX_NONE) return false;
ParseStringIntoArray(Left(ts, idx), parts, "/", false);
if (parts.length != 3) return false;
record.year = int(parts[0]);
record.month = int(parts[1]);
record.day = int(parts[2]);
ParseStringIntoArray(Mid(ts, idx+1), parts, ":", false);
if (parts.length != 3) return false;
record.hour = int(parts[0]);
record.minute = int(parts[1]);
record.second = int(parts[2]);
return true;
}
// From: 07 Oct 2011 22:35:56 GMT
// To: Fri, 07-Oct-2011 22:35:56 GMT
static final function string convertToRfc2109Date(string dateString)
{
local array<string> parts;
local int month, dow;
ParseStringIntoArray(dateString, parts, " ", true);
if (parts.Length != 5)
{
`log("Cannot convert date "$dateString$" to RFC 2019, not the correct number of parts",,'WebAdmin');
return "";
}
month = default.monthNames.find(parts[1]);
if (month == INDEX_NONE)
{
`log("Cannot convert date "$dateString$" to RFC 2019, unknown month: "$parts[1],,'WebAdmin');
return "";
}
dow = getDayOfWeek(int(parts[2]), month, int(parts[0]));
if (dow < 0 || dow > 6)
{
`log("Cannot convert date "$dateString$" to RFC 2019, wrong day of week value: "$dow,,'WebAdmin');
return "";
}
return default.dayNames[dow]$", "$parts[0]$"-"$parts[1]$"-"$parts[2]@parts[3]@parts[4];
}
/**
* return the day of the week. 0 = sunday, 1 = monday
*/
static final function int getDayOfWeek(int year, int month, int day)
{
// Sakamoto's algorithm
if (month < 3) {
--year;
}
return (year + year/4 - year/100 + year/400 + default.dowTable[month-1] + day) % 7;
}
static final function string getLocalized(coerce string data)
{
local array<string> parts;
if (!(Left(data, 9) ~= "<Strings:")) return data;
data = Mid(data, 9, Len(data)-10);
ParseStringIntoArray(data, parts, ".", true);
if (parts.length >= 3)
{
return Localize(parts[1], parts[2], parts[0]);
}
return "";
}
static final function parseUrlOptions(out array<KeyValuePair> options, string url)
{
local string part;
local array<string> parts;
local int idx, i;
local KeyValuePair kv;
ParseStringIntoArray(url, parts, "?", true);
foreach parts(part)
{
i = InStr(part, "=");
if (i == INDEX_NONE)
{
kv.Key = part;
kv.Value = "";
}
else {
kv.Key = Left(part, i);
kv.Value = Mid(part, i+1);
}
for (idx = 0; idx < options.length; idx++)
{
if (kv.key ~= options[idx].key)
{
break;
}
}
`Log("Add "$kv.key$" at "$idx);
options[idx] = kv;
}
}
/**
* Check if the target matches the mask, which can contain * for 0 or more
* matching characters. Use ** for greedy matching. For example:
* 'x*y' matches 'xzy', but not 'xzyy'. 'x**y' will match.
*/
static final function bool maskedCompare(coerce string target, string mask, optional bool caseInsensitive)
{
local int idx;
local string part;
local bool greedy;
if (caseInsensitive)
{
mask = caps(mask);
target = caps(target);
}
do
{
// quick escape
if (mask == "*" || mask == "**") return true;
idx = InStr(mask, "*");
if (idx < 0)
{
// no * thus match whole
return target == mask;
}
if (idx > 0)
{
// check prefix
part = left(mask, idx);
mask = mid(mask, idx);
if (left(target, len(part)) != part)
{
return false;
}
// remove matched part from target
target = mid(target, len(part));
}
else {
// idx can only be 0 at this point
// find the substring between 2 wildcards
greedy = (left(mask, 2) == "**");
idx = InStr(mask, "*",,, greedy?2:1);
if (idx < 0)
{
idx = len(mask);
}
if (greedy)
{
part = mid(mask, 2, idx-1);
mask = mid(mask, idx+1);
}
else {
part = mid(mask, 1, idx-1);
mask = mid(mask, idx);
}
if (len(part) == 0)
{
// in case of ***?
continue;
}
idx = InStr(target, part, greedy);
if (idx == INDEX_NONE)
{
// part was not found in the string
return false;
}
// remove everything up to and including the matched part
target = mid(target, idx+len(part));
}
} until (len(mask) == 0 || len(target) == 0);
return (target == mask) || (mask == "*") || (mask == "**");
}
static final function string removeUrlOption(string url, coerce string option)
{
local array<string> opts;
opts[0] = option;
return removeUrlOptions(url, opts);
}
static final function string removeUrlOptions(string url, array<string> options)
{
local string opt;
local int idx, end;
foreach options(opt)
{
idx = 0;
while (idx != -1)
{
idx = InStr(url,opt,,true,idx);
if (idx == -1) continue;
// doesn't start with ? or nothing; so not an option
if (idx > 0 && Mid(url, idx-1, 1) != "?")
{
idx += Len(opt);
continue;
}
// find end
end = idx+Len(opt);
if (end >= len(url))
{
// nop
}
else if (Mid(url, end, 1) == "=")
{
// start of a value
end = InStr(url, "?",,, idx);
if (end == -1) end = Len(url);
}
else if (Mid(url, end, 1) != "?")
{
// ?foobar?quux was found for option 'foo'
idx = end;
continue;
}
url = Mid(url, 0, idx-1)$Mid(url, end);
}
}
return url;
}
static function string getTeamNameEx(TeamInfo team)
{
if (team == none) return getTeamName(-1);
if (team.GetHumanReadableName() ~= "Team")
{
return getTeamName(team.TeamIndex);
}
return team.GetHumanReadableName();
}
static function string getTeamName(int index)
{
//FIXME
/* if (index == `AXIS_TEAM_INDEX)
{
return Localize("ROLocalMessageGame", "Axis", "ROGame");
}
else if (index == `ALLIES_TEAM_INDEX)
{
return Localize("ROLocalMessageGame", "Allies", "ROGame");
}*/
return repl(default.msgUnknownTeam, "%d", index+1);
}
/**
* Extract the IP from a string. Makes sure that if a ip:port is passed, only ip is returned.
*/
static function string extractIp(string str)
{
// assumes IPv4
local int idx;
if (InStr(str, ".") > 0)
{
idx = InStr(str, ":");
if (idx != INDEX_NONE)
{
str = Left(str, idx);
}
}
// TODO: verify ip-ness?
return str;
}
static function string UniqueNetIdToString(UniqueNetId aId)
{
local UniqueNetId empty;
if (empty == aId)
{
return "";
}
return class'OnlineSubsystem'.static.UniqueNetIdToString(aId);
}
static function string iso8601datetime(int year, int month, int day, int hour,int minute, int second,int msec)
{
return ""$year$"-"$right("0"$month, 2)$"-"$right("0"$day, 2)$"T"$
right("0"$hour, 2)$":"$right("0"$minute, 2)$":"$right("0"$second, 2)$"."$right("00"$msec, 3);
}
defaultproperties
{
monthNames[1]="Jan"
monthNames[2]="Feb"
monthNames[3]="Mar"
monthNames[4]="Apr"
monthNames[5]="May"
monthNames[6]="Jun"
monthNames[7]="Jul"
monthNames[8]="Aug"
monthNames[9]="Sep"
monthNames[10]="Oct"
monthNames[11]="Nov"
monthNames[12]="Dec"
dayNames[0]="Sun"
dayNames[1]="Mon"
dayNames[2]="Tue"
dayNames[3]="Wed"
dayNames[4]="Thu"
dayNames[5]="Fri"
dayNames[6]="Sat"
dowTable[0]=0
dowTable[1]=3
dowTable[2]=2
dowTable[3]=5
dowTable[4]=0
dowTable[5]=3
dowTable[6]=5
dowTable[7]=1
dowTable[8]=4
dowTable[9]=6
dowTable[10]=2
dowTable[11]=4
}

View File

@ -0,0 +1,31 @@
/**
* Quick fix for handling request content type
*
* Copyright 2008 Epic Games, Inc. All Rights Reserved
*
* @author Michiel 'elmuerte' Hendriks
*/
class WebConnectionEx extends WebConnection;
function CheckRawBytes()
{
if(RawBytesExpecting <= 0)
{
if(InStr(Locs(Request.ContentType), "application/x-www-form-urlencoded") != 0)
{
`log("WebConnection: Unknown form data content-type: "$Request.ContentType);
Response.HTTPError(400); // Can't deal with this type of form data
}
else
{
Request.DecodeFormData(ReceivedData);
if (Application.PreQuery(Request, Response))
{
Application.Query(Request, Response);
Application.PostQuery(Request, Response);
}
ReceivedData = "";
}
Cleanup();
}
}

View File

@ -0,0 +1,87 @@
/**
* Settings handled for the server's Welcome Page.
*
* Copyright (C) 2011 Tripwire Interactive LLC
*
* @author Michiel 'elmuerte' Hendriks
*/
class WelcomeSettings extends WebAdminSettings implements(IAdvWebAdminSettings);
`include(WebAdmin.uci)
`if(`WITH_WELCOME_SETTINGS)
var KFGameInfo gameinfo;
function setCurrentGameInfo(GameInfo instance)
{
gameinfo = KFGameInfo(instance);
}
function cleanupSettings()
{
gameinfo = none;
super.cleanupSettings();
}
function advInitSettings(WorldInfo worldinfo, DataStoreCache dscache);
function bool advSaveSettings(WebRequest request, WebAdminMessages messages)
{
class'KFGameInfo'.default.BannerLink = request.GetVariable("BannerLink", "");
class'KFGameInfo'.default.ClanMotto = Repl(Repl(request.GetVariable("ClanMotto", ""), Chr(10), "@nl@"), Chr(13), "");
class'KFGameInfo'.default.ClanMottoColor = class'WebAdminUtils'.static.HTMLColorToColor(request.GetVariable("ClanMottoColor", ""));
class'KFGameInfo'.default.ServerMOTD = Repl(Repl(request.GetVariable("ServerMOTD", ""), Chr(10), "@nl@"), Chr(13), "");
class'KFGameInfo'.default.ServerMOTDColor = class'WebAdminUtils'.static.HTMLColorToColor(request.GetVariable("ServerMOTDColor", ""));
class'KFGameInfo'.default.WebSiteLink = request.GetVariable("WebLink", "");
class'KFGameInfo'.default.WebLinkColor = class'WebAdminUtils'.static.HTMLColorToColor(request.GetVariable("WebLinkColor", ""));
class'KFGameInfo'.static.StaticSaveConfig();
if (gameinfo != none)
{
gameinfo.BannerLink = class'KFGameInfo'.default.BannerLink;
gameinfo.ClanMotto = class'KFGameInfo'.default.ClanMotto;
gameinfo.ClanMottoColor = class'KFGameInfo'.default.ClanMottoColor;
gameinfo.ServerMOTD = class'KFGameInfo'.default.ServerMOTD;
gameinfo.ServerMOTDColor = class'KFGameInfo'.default.ServerMOTDColor;
gameinfo.WebSiteLink = class'KFGameInfo'.default.WebSiteLink;
gameinfo.WebLinkColor = class'KFGameInfo'.default.WebLinkColor;
gameinfo.SaveConfig();
}
return true;
}
function advRenderSettings(WebResponse response, SettingsRenderer renderer,
optional string substName = "settings", optional ISettingsPrivileges privileges)
{
if (gameinfo != none)
{
response.Subst("BannerLink", gameinfo.BannerLink);
response.Subst("ClanMotto", repl(gameinfo.ClanMotto, "@nl@", chr(10)));
response.Subst("ClanMottoColor", class'WebAdminUtils'.static.ColorToHTMLColor(gameinfo.ClanMottoColor));
response.Subst("ServerMOTD", repl(gameinfo.ServerMOTD, "@nl@", chr(10)));
response.Subst("ServerMOTDColor", class'WebAdminUtils'.static.ColorToHTMLColor(gameinfo.ServerMOTDColor));
response.Subst("WebLink", gameinfo.WebSiteLink);
response.Subst("WebLinkColor", class'WebAdminUtils'.static.ColorToHTMLColor(gameinfo.WebLinkColor));
}
else {
response.Subst("BannerLink", class'KFGameInfo'.default.BannerLink);
response.Subst("ClanMotto", repl(class'KFGameInfo'.default.ClanMotto, "@nl@", chr(10)));
response.Subst("ClanMottoColor", class'WebAdminUtils'.static.ColorToHTMLColor(class'KFGameInfo'.default.ClanMottoColor));
response.Subst("ServerMOTD", repl(class'KFGameInfo'.default.ServerMOTD, "@nl@", chr(10)));
response.Subst("ServerMOTDColor", class'WebAdminUtils'.static.ColorToHTMLColor(class'KFGameInfo'.default.ServerMOTDColor));
response.Subst("WebLink", class'KFGameInfo'.default.WebSiteLink);
response.Subst("WebLinkColor", class'WebAdminUtils'.static.ColorToHTMLColor(class'KFGameInfo'.default.WebLinkColor));
}
}
`else
function advInitSettings(WorldInfo worldinfo, DataStoreCache dscache);
function cleanupSettings();
function bool advSaveSettings(WebRequest request, WebAdminMessages messages);
function advRenderSettings(WebResponse response, SettingsRenderer renderer, optional string substName = "settings", optional ISettingsPrivileges privileges);
`endif
defaultProperties
{
}