- Регистрация
- 12 Янв 2024
- Сообщения
- 74
- Реакции
- 10
- Баллы
- 8
Написал небольшой мутатор, который заменяет пару функций в паре классов у вебадмина
После этого можно и писать и читать в вебадмине под виндой и линуксом.
Хотя в принципе можно было бы не заменять классы, а сделать мутатор использующий тот же BroadcastHandler, как и здесь
У мутатора 1 переменная в ини файле - isWindows
Если сервер работает под виндой - ставим true, иначе false
Важно!
Настраивал мутатор при следующей настройке в UWeb.int
[WebResponse]
CharSet="windows-1251"
Для другой кодировки возможно потребуется другой сдвиг кода символа. Если у кого-то не будет работать мутатор - сделаю версию, где можно будет руками настраивать сдвиг.
Ссылка
Добавлять в виде:WebAdminCyrillicMut.WebAdminCyrillicMut
Замечание: Тем у кого не работает мутатор.
Убедитесь, что у вас нет мутаторов, которые правят
class'XWebAdmin.UTServerAdmin'.default.QueryHandlerClasses[0]
Если хотите использовать мутатор совместно с AdminControlv2 - проверьте, чтобы в AdminControlv2.ini была следующая строка
bSupportWebAdmin=false
Версия с использованием BroadcastHandler:
Код для загрузки мутатора: WebAdminCyrillicMutv2.WebAdminCyrillicMut.
Ссылка
После этого можно и писать и читать в вебадмине под виндой и линуксом.
Хотя в принципе можно было бы не заменять классы, а сделать мутатор использующий тот же BroadcastHandler, как и здесь
У мутатора 1 переменная в ини файле - isWindows
Если сервер работает под виндой - ставим true, иначе false
Важно!
Настраивал мутатор при следующей настройке в UWeb.int
[WebResponse]
CharSet="windows-1251"
Для другой кодировки возможно потребуется другой сдвиг кода символа. Если у кого-то не будет работать мутатор - сделаю версию, где можно будет руками настраивать сдвиг.
Код:
class WebAdminCyrillicMut extends Mutator config(WebAdminCyrillicMut);
var config bool isWindows;
function PostBeginPlay()
{
SaveConfig();
class'XWebAdmin.UTServerAdmin'.default.QueryHandlerClasses[0]="WebAdminCyrillicMut.newxWebQueryCurrent";
class'XWebAdmin.UTServerAdmin'.default.SpectatorType=Class'WebAdminCyrillicMut.newUTServerAdminSpectator';
class'WebAdminCyrillicMut.newUTServerAdminSpectator'.default.isWindows=isWindows;
}
defaultproperties
{
isWindows=true
}
Код:
class newUTServerAdminSpectator extends UTServerAdminSpectator
config;
var bool isWindows;
function string ConvertString(string msg)
{
local int i;
local string tmp;
local string result;
local int code;
tmp=msg;
for(i=0; i<Len(msg); i++)
{
code=Asc(tmp);
if(code>1000) code-=848;
if(i==0) result=Chr(code);
else result=result$Chr(code);
tmp=Mid(tmp,1);
}
return result;
}
function string FormatMessage(PlayerReplicationInfo PRI, String Text, name Type)
{
local string Message;
local string cPlayerName;
if(PRI!=none)
cPlayerName=PRI.PlayerName;
if(!isWindows)
{
Text=ConvertString(Text);
if(PRI!=none)
cPlayerName=ConvertString(PRI.PlayerName);
}
if (PRI != None)
{
if (Type == 'Say' && PRI == PlayerReplicationInfo)
Message = Text;
else if (Type == 'Say')
Message = cPlayerName$": "$Text;
else if (Type == 'TeamSay')
Message = "["$cPlayerName$"]: "$Text;
else
Message = "("$Type$") "$Text;
}
else if (Type == 'Console')
Message = "WebAdmin:"@Text;
else
Message = "("$Type$") "$Text;
return Message;
}
Код:
class newxWebQueryCurrent extends xWebQueryCurrent
config;
function string ConvertString(string msg)
{
local int i;
local string tmp;
local string result;
local int code;
tmp=msg;
for(i=0; i<Len(msg); i++)
{
code=Asc(tmp);
if(code>191 && code<400) code+=848;
if(i==0) result=Chr(code);
else result=result$Chr(code);
tmp=Mid(tmp,1);
}
return result;
}
function QueryCurrentConsole(WebRequest Request, WebResponse Response)
{
local String SendStr, OutStr;
if (CanPerform("Xc"))
{
SendStr = Request.GetVariable("SendText", "");
SendStr=ConvertString(SendStr);//Flame
if (SendStr != "" && !(Left(SendStr, 6) ~= "debug " || SendStr ~= "debug"))
{
if (Left(SendStr, 4) ~= "say ")
Level.Game.Broadcast(Spectator, Mid(SendStr, 4), 'Say');
else if (SendStr ~= "pause")
{
if (Level.Pauser == None)
Level.Pauser = Spectator.PlayerReplicationInfo;
else Level.Pauser = None;
}
else if (SendStr ~= "dump")
Spectator.Dump();
else if ((Left(SendStr, 4) ~= "get " || Left(SendStr,4) ~= "set ") &&
(InStr(Caps(SendStr), "XADMINCONFIG") != -1 || !CanPerform("Ms")) )
{
if ( InStr(Caps(SendStr), "XADMINCONFIG") != -1 )
{
StatusError(Response, ConsoleUserlist);
ShowMessage(Response, Error, "");
log("User attempted to modify or enumerate admin account information illegally using the webadmin console. User:"$Request.Username$".",'WebAdmin');
}
else if ( !CanPerform("Ms") )
AccessDenied(Response);
}
else
{
OutStr = Level.ConsoleCommand(SendStr);
if (OutStr != "")
Spectator.AddMessage(None, OutStr, 'Console');
}
}
Response.Subst("LogURI", CurrentConsoleLogPage);
Response.Subst("SayURI", CurrentConsoleSendPage);
ShowPage(Response, CurrentConsolePage);
}
else
AccessDenied(Response);
}
function QueryCurrentPlayers(WebRequest Request, WebResponse Response)
{
local string Sort, PlayerListSubst, TempStr, TempTag, TempData;
local string TableHeaders, GameType, Reverse, ColorNames[2], Last;
local StringArray PlayerList;
local Controller P, NextP;
local int i, Cols, mlength;
local string IP, ID;
local bool bCanKick, bCanBan, bCanKickBots;
Response.Subst("Section", CurrentLinks[1]);
Response.Subst("PostAction", CurrentPlayersPage);
ColorNames[0] = class'TeamInfo'.default.ColorNames[0];
ColorNames[1] = class'TeamInfo'.default.ColorNames[1];
MLength = int(Eval(Len(ColorNames[0]) > Len(ColorNames[1]), string(Len(ColorNames[0])), string(Len(ColorNames[1]))));
if (CanPerform("Xp|Kp|Kb|Ko"))
{
PlayerList = new(None) class'SortedStringArray';
Sort = Request.GetVariable("Sort", "Name");
Last = Request.GetVariable("Last");
Response.Subst("Sort", Sort);
Cols = 0;
bCanKick = CanPerform("Kp");
bCanBan = CanPerform("Kb");
bCanKickBots = CanPerform("Ko|Mb");
if (Last == Sort && Request.GetVariable("ReverseSort") == "")
{
PlayerList.ToggleSort();
Reverse = "?ReverseSort=True";
}
else Reverse = "";
// Count the number of Columns allowed
if (bCanKick || bCanBan || bCanKickBots)
{
// Use 'do-while' to avoid access-none when destroying Controllers within the loop
P = Level.ControllerList;
if (P != None)
{
do {
NextP = P.NextController;
if( PlayerController(P) != None
&& P.PlayerReplicationInfo != None
&& NetConnection(PlayerController(P).Player) != None)
{
if ( bCanBan && Request.GetVariable("Ban" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.KickBanPlayer(PlayerController(P));
//if _RO_
else if ( bCanBan && Request.GetVariable("Session" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.BanPlayer(PlayerController(P), true);
//end _RO_
else if ( bCanKick && Request.GetVariable("Kick" $ string(P.PlayerReplicationInfo.PlayerID)) != "" )
Level.Game.AccessControl.KickPlayer(PlayerController(P));
}
else if ( PlayerController(P) == None && bCanKickBots && P.PlayerReplicationInfo != None &&
Request.GetVariable("Kick" $ string(P.PlayerReplicationInfo.PlayerID)) != "")
{ // Kick Bots
P.Destroy();
}
P = NextP;
} until (P == None);
}
if (bCanKick || bCanKickBots) Cols += 1;
if (bCanBan) Cols += 2;
Response.Subst("KickButton", SubmitButton("Kick", KickButtonText[Cols-1]));
// Build of valid TableHeaders
TableHeaders = "";
if (bCanKick || bCanKickBots)
{
Response.Subst("HeadTitle", "Kick");
TableHeaders $= WebInclude(PlayerListHeader);
}
if (bCanBan)
{
//if _RO_
Response.Subst("HeadTitle", "Session");
TableHeaders $= WebInclude(PlayerListHeader);
//end _RO_
Response.Subst("HeadTitle", "Ban");
TableHeaders $= WebInclude(PlayerListHeader);
}
if (Sort ~= "Name") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Name");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
if (Level.Game.GameReplicationInfo.bTeamGame)
{
if (Sort ~= "Team") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Team");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
}
if (Sort ~= "Ping") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Ping");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
if (Sort ~= "Score") Response.Subst("ReverseSort", Reverse);
else Response.Subst("ReverseSort", "");
Response.Subst("HeadTitle", "Score");
TableHeaders $= WebInclude(PlayerListLinkedHeader);
//if _RO_
Response.Subst("HeadTitle", "Team Kills");
TableHeaders $= WebInclude(PlayerListHeader);
//end _RO_
Response.Subst("HeadTitle", "IP");
TableHeaders $= WebInclude(PlayerListHeader);
// evo ---
if (Level.Game.AccessControl.bBanbyID)
{
Response.Subst("HeadTitle", "Global ID");
TableHeaders $= WebInclude(PlayerListHeader);
}
// --- evo
Response.Subst("TableHeaders", TableHeaders);
}
if (CanPerform("Ms"))
{
GameType = Level.GetItemName(SetGamePI(GameType));
if (GamePI != None && GamePI.Settings[GamePI.FindIndex(GameType$".MinPlayers")].SecLevel <= CurAdmin.MaxSecLevel())
{
if ((Request.GetVariable("SetMinPlayers", "") != "") && UnrealMPGameInfo(Level.Game) != None)
{
UnrealMPGameInfo(Level.Game).MinPlayers = Min(Max(int(Request.GetVariable("MinPlayers", String(0))), 0), 32);
Level.Game.SaveConfig();
}
Response.Subst("MinPlayers", string(UnrealMPGameInfo(Level.Game).MinPlayers));
Response.Subst("MinPlayerPart", WebInclude(PlayerListMinPlayers));
}
else
{
Response.Subst("MinPlayers", "");
Response.Subst("MinPlayersPart", "");
}
}
for (P=Level.ControllerList; P!=None; P=P.NextController)
{
TempData = "";
if (!P.bDeleteMe && P.bIsPlayer && P.PlayerReplicationInfo != None)
{
Response.Subst("Content", CheckBox("Kick" $ string(P.PlayerReplicationInfo.PlayerID), False));
if (CanPerform("Kp"))
TempData $= WebInclude(CellCenter);
if (CanPerform("Kb"))
{
//if _RO_
if ( PlayerController(P) != None )
{
Response.Subst("Content", Checkbox("Session" $ string(P.PlayerReplicationInfo.PlayerID), False));
TempData $= WebInclude(CellCenter);
Response.Subst("Content", Checkbox("Ban" $ string(P.PlayerReplicationInfo.PlayerID), False));
TempData $= WebInclude(CellCenter);
}
else
{
Response.Subst("Content", "");
TempData $= WebInclude(CellCenter)$WebInclude(CellCenter);
}
//else
//if ( PlayerController(P) != None )
// Response.Subst("Content", Checkbox("Ban" $ string(P.PlayerReplicationInfo.PlayerID), False));
//else Response.Subst("Content", "");
//TempData $= WebInclude(CellCenter);
//end _RO_
}
TempStr = "";
if (DeathMatch(Level.Game) != None && DeathMatch(Level.Game).bTournament && P.PlayerReplicationInfo.bReadyToPlay)
TempStr = " (Ready) ";
else if (P.PlayerReplicationInfo.bIsSpectator)
TempStr = " (Spectator) ";
else if (PlayerController(P) == None)
TempStr = " (Bot) ";
if( PlayerController(P) != None )
{
IP = PlayerController(P).GetPlayerNetworkAddress();
IP = HtmlEncode(" " $ Left(IP, InStr(IP, ":")));
// evo ---
ID = HtmlEncode(" " $ Eval(Level.Game.AccessControl.bBanbyID, PlayerController(P).GetPlayerIDHash(), " "));
// --- evo
}
else
{
IP = HtmlEncode(" ");
ID = HtmlEncode(" ");
}
Response.Subst("Content", HtmlEncode(ConvertString(P.PlayerReplicationInfo.PlayerName) $ TempStr));
TempData $= WebInclude(NowrapLeft);
if (Level.Game.bTeamGame)
{
if (P.PlayerReplicationInfo.Team != None && P.PlayerReplicationInfo.Team.TeamIndex < 4)
Response.Subst("Content", "<span style='background-color: "$class'TeamInfo'.default.ColorNames[P.PlayerReplicationInfo.Team.TeamIndex]$"'>"$HtmlEncode(" ")$"</span>"$HtmlEncode(P.PlayerReplicationInfo.Team.GetHumanReadableName()));
else if (P.PlayerReplicationInfo.bIsSpectator)
Response.Subst("Content", HtmlEncode(" "));
TempData $= WebInclude(NowrapCenter);
}
Response.Subst("Content", string(P.PlayerReplicationInfo.Ping*4));
TempData $= WebInclude(CellCenter);
Response.Subst("Content", string(int(P.PlayerReplicationInfo.Score)));
TempData $= WebInclude(CellCenter);
//if _RO_
Response.Subst("Content", string(P.PlayerReplicationInfo.FFKills));
TempData $= WebInclude(CellCenter);
//end _RO_
Response.Subst("Content", IP);
TempData $= WebInclude(CellCenter);
if (Level.Game.AccessControl.bBanbyID)
{
Response.Subst("Content", ID);
TempData $= WebInclude(CellCenter);
}
switch (Sort)
{
case "Name":
TempTag = ConvertString(P.PlayerReplicationInfo.PlayerName); break;
case "Team": // Ordered by Team, then subordered by last selected sort method
TempTag = PadRight(class'TeamInfo'.default.ColorNames[P.PlayerReplicationInfo.Team.TeamIndex],MLength,"0");
switch (Last)
{
case "Name":
TempTag $= ConvertString(P.PlayerReplicationInfo.PlayerName); break;
case "Ping":
TempTag $= PadLeft(string(P.PlayerReplicationInfo.Ping*4), 5, "0"); break;
default:
TempTag $= PadLeft(string(int(P.PlayerReplicationInfo.Score)), 4, "0"); break;
}
break;
case "Ping":
TempTag = PadLeft(string(P.PlayerReplicationInfo.Ping*4), 5, "0"); break;
default:
TempTag = PadLeft(string(int(P.PlayerReplicationInfo.Score)), 4, "0"); break;
}
Response.Subst("RowContent", TempData);
PlayerList.Add( WebInclude(RowLeft), TempTag);
}
}
PlayerListSubst = "";
if (PlayerList.Count() > 0)
{
for ( i=0; i<PlayerList.Count(); i++)
{
if (Sort ~= "Score")
PlayerListSubst = PlayerList.GetItem(i) $ PlayerListSubst;
else PlayerListSubst $= PlayerList.GetItem(i);
}
}
else
{
Response.Subst("SpanContent", NoPlayersConnected);
Response.Subst("SpanLength", "6");
Response.Subst("RowContent", WebInclude(CellColSpan));
PlayerListSubst = WebInclude(RowCenter);
}
Response.Subst("PlayerList", PlayerListSubst);
Response.Subst("MinPlayers", string(UnrealMPGameInfo(Level.Game).MinPlayers));
Response.Subst("PageHelp", NotePlayersPage);
MapTitle(Response);
ShowPage(Response, CurrentPlayersPage);
}
else
AccessDenied(Response);
}
function QueryCurrentGame(WebRequest Request, WebResponse Response)
{
local StringArray ExcludeMaps, IncludeMaps, MovedMaps;
local class<GameInfo> GameClass;
local string NewGameType, SwitchButtonName, GameState, NewMap;
local bool bMakeChanges;
local Controller C;
local xPlayer XP;
local TeamPlayerReplicationInfo PRI;
local int MultiKills, Sprees, GameIndex;
if (CanPerform("Mt|Mm"))
{
if (Request.GetVariable("SwitchGameTypeAndMap", "") != "")
{
if (CanPerform("Mt"))
ServerChangeMap(Request, Response, Request.GetVariable("MapSelect"), Request.GetVariable("GameTypeSelect"));
else AccessDenied(Response);
return;
}
else if (Request.GetVariable("SwitchMap", "") != "")
{
if (CanPerform("Mm|Mt"))
{
NewMap = Request.GetVariable("MapSelect");
Level.ServerTravel(NewMap$"?game="$Level.Game.Class$"?mutator="$UsedMutators(), false);
ShowMessage(Response, WaitTitle, Repl(MapChangingTo, "%MapName%", NewMap));
}
else AccessDenied(Response);
return;
}
bMakeChanges = (Request.GetVariable("ApplySettings", "") != "");
if (CanPerform("Mt") && (bMakeChanges || Request.GetVariable("SwitchGameType", "") != ""))
{
NewGameType = Request.GetVariable("GameTypeSelect");
GameClass = class<GameInfo>(DynamicLoadObject(NewGameType, class'Class'));
}
else GameClass = None;
if (GameClass == None)
{
GameClass = Level.Game.Class;
NewGameType = String(GameClass);
}
GameIndex = Level.Game.MaplistHandler.GetGameIndex(NewGameType);
ExcludeMaps = ReloadExcludeMaps(NewGameType);
IncludeMaps = ReloadIncludeMaps(ExcludeMaps, GameIndex, Level.Game.MaplistHandler.GetActiveList(GameIndex));
GameState = "";
// Show game status if admin has necessary privs
if (CanPerform("Ma"))
{
if (Level.Game.NumPlayers > 0)
{
for (C = Level.ControllerList; C != None; C = C.NextController)
{
MultiKills = 0;
Sprees = 0;
PRI = None;
XP = xPlayer(C);
if (XP != None && !XP.bDeleteMe)
{
if (TeamPlayerReplicationInfo(XP.PlayerReplicationInfo) != None)
PRI = TeamPlayerReplicationInfo(XP.PlayerReplicationInfo);
if (PRI != None)
{
Response.Subst("PlayerName", HtmlEncode(ConvertString(PRI.PlayerName)));
Response.Subst("Kills", string(PRI.Kills));
//if _RO_
Response.Subst("FFKills", string(PRI.FFKills));
//end _RO_
Response.Subst("Deaths", string(PRI.Deaths));
Response.Subst("Suicides",string(PRI.Suicides));
//if _RO_
/*
//end _RO_
for (i = 0; i < 7; i++)
MultiKills += PRI.MultiKills[i];
Response.Subst("MultiKills", string(MultiKills));
for (i = 0; i < 6; i++)
Sprees += PRI.Spree[i];
Response.Subst("Sprees", string(Sprees));
//if _RO_
*/
//end _RO_
GameState $= WebInclude(StatTableRow);
}
}
}
}
else
//if _RO_
GameState = "<tr><td colspan=\"5\" align=\"center\">"@NoPlayersConnected@"</td></tr>";
//else
//GameState = "<tr><td colspan=\"6\" align=\"center\">"@NoPlayersConnected@"</td></tr>";
//end _RO_
Response.Subst("StatRows", GameState);
Response.Subst("GameState", WebInclude(StatTable));
}
if (GameClass == Level.Game.Class)
{
SwitchButtonName="SwitchMap";
MovedMaps = New(None) Class'SortedStringArray';
MovedMaps.CopyFromId(IncludeMaps, IncludeMaps.FindTagId(Left(string(Level), InStr(string(Level), "."))));
}
else SwitchButtonName="SwitchGameTypeAndMap";
if (CanPerform("Mt"))
{
Response.Subst("Content", Select("GameTypeSelect", GenerateGameTypeOptions(NewGameType)));
Response.Subst("GameTypeButton", SubmitButton("SwitchGameType", SwitchText));
}
else Response.Subst("Content", Level.Game.Default.GameName);
Response.Subst("GameTypeSelect", WebInclude(CellLeft));
Response.Subst("Content", Select("MapSelect", GenerateMapListSelect(IncludeMaps, MovedMaps)));
Response.Subst("MapSelect", WebInclude(CellLeft));
Response.Subst("MapButton", SubmitButton(SwitchButtonName, SwitchText));
Response.Subst("PostAction", CurrentGamePage);
Response.Subst("Section", CurrentLinks[0]);
Response.Subst("PageHelp", NoteGamePage);
MapTitle(Response);
ShowPage(Response, CurrentGamePage);
}
else AccessDenied(Response);
}
defaultproperties
{
}
Ссылка
Добавлять в виде:WebAdminCyrillicMut.WebAdminCyrillicMut
Замечание: Тем у кого не работает мутатор.
Убедитесь, что у вас нет мутаторов, которые правят
class'XWebAdmin.UTServerAdmin'.default.QueryHandlerClasses[0]
Если хотите использовать мутатор совместно с AdminControlv2 - проверьте, чтобы в AdminControlv2.ini была следующая строка
bSupportWebAdmin=false
Версия с использованием BroadcastHandler:
Код для загрузки мутатора: WebAdminCyrillicMutv2.WebAdminCyrillicMut.
Код:
class WebAdminCyrillicMut extends Mutator config(WebAdminCyrillicMut);
var config bool isWindows;
var WACBroadcastHandler WACBHandler;
simulated function PostBeginPlay()
{
local KFGameType KFGT;
local BroadcastHandler BH;
Super.PostBeginPlay();
KFGT = KFGameType(Level.Game);
BH = KFGT.BroadcastHandler;
while ( BH != none )
{
if ( BH.NextBroadcastHandler == none )
{
WACBHandler = Spawn(class'WACBroadcastHandler',Self);
WACBHandler.isWindows = isWindows;
BH.NextBroadcastHandler = WACBHandler;
break;
}
BH = BH.NextBroadcastHandler;
}
}
defaultproperties
{
isWindows=true
GroupName="KF-WebAdminCyrillicMut"
FriendlyName="WebAdminCyrillicMut"
Description="Fixes appearance of russian text sent from and to WebAdmin."
}
Код:
class WACBroadcastHandler extends BroadcastHandler;
var bool isWindows;
function BroadcastText( PlayerReplicationInfo SenderPRI, PlayerController Receiver, coerce string Msg, optional name Type )
{
if ( SenderPRI != Receiver.PlayerReplicationInfo ) // Не перекодировать сообщения самому себе
{
if ( SenderPRI.PlayerName ~= "WebAdmin" && SenderPRI.PlayerID == 0 )
{
Msg = ConvertStringOut(Msg);
}
else if ( Receiver.PlayerReplicationInfo.PlayerName ~= "WebAdmin" && Receiver.PlayerReplicationInfo.PlayerID == 0 )
{
if ( !isWindows )
{
Msg = ConvertStringIn(Msg);
}
}
}
if ( NextBroadcastHandler != None )
{
NextBroadcastHandler.BroadcastText( SenderPRI, Receiver, Msg, Type );
}
else
{
Receiver.TeamMessage( SenderPRI, Msg, Type );
}
}
Ссылка
Последнее редактирование: