upload
This commit is contained in:
105
WebAdmin/Classes/AbstractSettingsModifier.uc
Normal file
105
WebAdmin/Classes/AbstractSettingsModifier.uc
Normal 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
|
||||
{
|
||||
}
|
230
WebAdmin/Classes/AdminCommandHandler.uc
Normal file
230
WebAdmin/Classes/AdminCommandHandler.uc
Normal 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";
|
||||
}
|
181
WebAdmin/Classes/BanImporter.uc
Normal file
181
WebAdmin/Classes/BanImporter.uc
Normal 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;
|
||||
}
|
114
WebAdmin/Classes/BasicWebAdminAuth.uc
Normal file
114
WebAdmin/Classes/BasicWebAdminAuth.uc
Normal 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;
|
||||
}
|
175
WebAdmin/Classes/BasicWebAdminUser.uc
Normal file
175
WebAdmin/Classes/BasicWebAdminUser.uc
Normal 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
158
WebAdmin/Classes/ChatLog.uc
Normal 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
|
||||
}
|
25
WebAdmin/Classes/DCEGameInfo.uc
Normal file
25
WebAdmin/Classes/DCEGameInfo.uc
Normal 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;
|
||||
}
|
25
WebAdmin/Classes/DCEMapInfo.uc
Normal file
25
WebAdmin/Classes/DCEMapInfo.uc
Normal 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;
|
||||
}
|
25
WebAdmin/Classes/DCEMutator.uc
Normal file
25
WebAdmin/Classes/DCEMutator.uc
Normal 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;
|
||||
}
|
757
WebAdmin/Classes/DataStoreCache.uc
Normal file
757
WebAdmin/Classes/DataStoreCache.uc
Normal 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;
|
||||
}
|
81
WebAdmin/Classes/DataStoreCacheEntry.uc
Normal file
81
WebAdmin/Classes/DataStoreCacheEntry.uc
Normal 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;
|
||||
*/
|
||||
}
|
41
WebAdmin/Classes/DataStoreCacheKF.uc
Normal file
41
WebAdmin/Classes/DataStoreCacheKF.uc
Normal 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);
|
||||
}
|
||||
*/
|
266
WebAdmin/Classes/GeneralSettings.uc
Normal file
266
WebAdmin/Classes/GeneralSettings.uc
Normal 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)
|
||||
}
|
10
WebAdmin/Classes/HashLib.uc
Normal file
10
WebAdmin/Classes/HashLib.uc
Normal 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);
|
44
WebAdmin/Classes/IAdvWebAdminSettings.uc
Normal file
44
WebAdmin/Classes/IAdvWebAdminSettings.uc
Normal 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);
|
96
WebAdmin/Classes/IQueryHandler.uc
Normal file
96
WebAdmin/Classes/IQueryHandler.uc
Normal 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();
|
48
WebAdmin/Classes/ISession.uc
Normal file
48
WebAdmin/Classes/ISession.uc
Normal 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);
|
28
WebAdmin/Classes/ISessionHandler.uc
Normal file
28
WebAdmin/Classes/ISessionHandler.uc
Normal 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();
|
44
WebAdmin/Classes/ISettingsModifier.uc
Normal file
44
WebAdmin/Classes/ISettingsModifier.uc
Normal 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);
|
19
WebAdmin/Classes/ISettingsPrivileges.uc
Normal file
19
WebAdmin/Classes/ISettingsPrivileges.uc
Normal 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);
|
63
WebAdmin/Classes/IWebAdminAuth.uc
Normal file
63
WebAdmin/Classes/IWebAdminAuth.uc
Normal 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);
|
81
WebAdmin/Classes/IWebAdminUser.uc
Normal file
81
WebAdmin/Classes/IWebAdminUser.uc
Normal 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();
|
97
WebAdmin/Classes/KF2ImageServer.uc
Normal file
97
WebAdmin/Classes/KF2ImageServer.uc
Normal 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);
|
||||
}
|
6
WebAdmin/Classes/KF2ServerAdmin.uc
Normal file
6
WebAdmin/Classes/KF2ServerAdmin.uc
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Tripwire Interactive LLC
|
||||
*
|
||||
* @author Michiel 'elmuerte' Hendriks
|
||||
*/
|
||||
class KF2ServerAdmin extends WebAdmin;
|
101
WebAdmin/Classes/KFGameInfoSettings.uc
Normal file
101
WebAdmin/Classes/KFGameInfoSettings.uc
Normal 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)))
|
||||
}
|
8
WebAdmin/Classes/KFGameInfo_SurvivalSettings.uc
Normal file
8
WebAdmin/Classes/KFGameInfo_SurvivalSettings.uc
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Settings for KFGameInfo_Survival
|
||||
*
|
||||
* Copyright (C) 2015 Tripwire Interactive LLC
|
||||
*
|
||||
* @author Michiel 'elmuerte' Hendriks
|
||||
*/
|
||||
class KFGameInfo_SurvivalSettings extends KFGameInfoSettings;
|
149
WebAdmin/Classes/MessagingSpectator.uc
Normal file
149
WebAdmin/Classes/MessagingSpectator.uc
Normal 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
|
||||
}
|
165
WebAdmin/Classes/MultiAdminData.uc
Normal file
165
WebAdmin/Classes/MultiAdminData.uc
Normal 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
|
||||
{
|
||||
}
|
275
WebAdmin/Classes/MultiWebAdminAuth.uc
Normal file
275
WebAdmin/Classes/MultiWebAdminAuth.uc
Normal 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);
|
||||
}
|
62
WebAdmin/Classes/MultiWebAdminUser.uc
Normal file
62
WebAdmin/Classes/MultiWebAdminUser.uc
Normal 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
|
||||
{
|
||||
}
|
113
WebAdmin/Classes/NewsDesk.uc
Normal file
113
WebAdmin/Classes/NewsDesk.uc
Normal 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");
|
||||
}
|
44
WebAdmin/Classes/PCCleanUp.uc
Normal file
44
WebAdmin/Classes/PCCleanUp.uc
Normal 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();
|
||||
}
|
1739
WebAdmin/Classes/QHCurrent.uc
Normal file
1739
WebAdmin/Classes/QHCurrent.uc
Normal file
File diff suppressed because it is too large
Load Diff
180
WebAdmin/Classes/QHCurrentKF.uc
Normal file
180
WebAdmin/Classes/QHCurrentKF.uc
Normal 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
|
||||
}
|
1610
WebAdmin/Classes/QHDefaults.uc
Normal file
1610
WebAdmin/Classes/QHDefaults.uc
Normal file
File diff suppressed because it is too large
Load Diff
18
WebAdmin/Classes/QHDefaultsKF.uc
Normal file
18
WebAdmin/Classes/QHDefaultsKF.uc
Normal 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);
|
||||
}
|
374
WebAdmin/Classes/QHMultiAdmin.uc
Normal file
374
WebAdmin/Classes/QHMultiAdmin.uc
Normal 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
115
WebAdmin/Classes/Session.uc
Normal 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;
|
||||
}
|
||||
}
|
78
WebAdmin/Classes/SessionHandler.uc
Normal file
78
WebAdmin/Classes/SessionHandler.uc
Normal 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);
|
||||
}
|
134
WebAdmin/Classes/SettingRendererState.uc
Normal file
134
WebAdmin/Classes/SettingRendererState.uc
Normal 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;
|
||||
}
|
76
WebAdmin/Classes/SettingsMagic.uc
Normal file
76
WebAdmin/Classes/SettingsMagic.uc
Normal 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;
|
||||
}
|
738
WebAdmin/Classes/SettingsRenderer.uc
Normal file
738
WebAdmin/Classes/SettingsRenderer.uc
Normal 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
|
||||
}
|
202
WebAdmin/Classes/Sha1HashLib.uc
Normal file
202
WebAdmin/Classes/Sha1HashLib.uc
Normal 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]);
|
||||
}
|
30
WebAdmin/Classes/TeamChatProxy.uc
Normal file
30
WebAdmin/Classes/TeamChatProxy.uc
Normal 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
1313
WebAdmin/Classes/WebAdmin.uc
Normal file
File diff suppressed because it is too large
Load Diff
333
WebAdmin/Classes/WebAdminMenu.uc
Normal file
333
WebAdmin/Classes/WebAdminMenu.uc
Normal 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;
|
||||
}
|
45
WebAdmin/Classes/WebAdminMessages.uc
Normal file
45
WebAdmin/Classes/WebAdminMessages.uc
Normal 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");;
|
||||
}
|
246
WebAdmin/Classes/WebAdminSettings.uc
Normal file
246
WebAdmin/Classes/WebAdminSettings.uc
Normal 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
|
||||
{
|
||||
}
|
17
WebAdmin/Classes/WebAdminSkin.uc
Normal file
17
WebAdmin/Classes/WebAdminSkin.uc
Normal 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
|
||||
}
|
340
WebAdmin/Classes/WebAdminSystemSettings.uc
Normal file
340
WebAdmin/Classes/WebAdminSystemSettings.uc
Normal 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))
|
||||
}
|
487
WebAdmin/Classes/WebAdminUtils.uc
Normal file
487
WebAdmin/Classes/WebAdminUtils.uc
Normal 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, "&", "&");
|
||||
inp = Repl(inp, "<", "<");
|
||||
inp = Repl(inp, "\"", """);
|
||||
return Repl(inp, ">", ">");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
31
WebAdmin/Classes/WebConnectionEx.uc
Normal file
31
WebAdmin/Classes/WebConnectionEx.uc
Normal 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();
|
||||
}
|
||||
}
|
87
WebAdmin/Classes/WelcomeSettings.uc
Normal file
87
WebAdmin/Classes/WelcomeSettings.uc
Normal 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
|
||||
{
|
||||
|
||||
}
|
Reference in New Issue
Block a user