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

868 lines
20 KiB
Ucode

class UT2K4Browser_ServersList extends ServerBrowserMCList;
// if _RO_
// else
//#exec OBJ LOAD file=..\Textures\Old2k4\ServerIcons.utx
//#exec texture Import File=..\Xinterface\textures\UTClassicIcon.tga Name=S_UTClassic Mips=Off Alpha=1
// end if _RO_
var() array<GameInfo.ServerResponseLine> Servers;
// OutstandingPings keeps track of simultaneous open connections vs. remaining servers awaiting pings
var() array<int> OutstandingPings;
var() int UseSimultaneousPings;
var() int NumReceivedPings;
var() int NumPlayers;
var() int RedialId; // ListID of the server we're redialing
//ifdef _KF_
var() int MaxPingsPerSecond;
var float LastPingTime;
var() int PingStart;
var bool bAutoPinging;
//endif _KF_
var() bool bPresort;
var() bool bInitialized;
var() array<Material> Icons;
var() localized array<String> IconDescriptions;
struct report
{
var int listid;
var string ping, receive;
};
var array<report> reports;
function InitComponent(GUIController InController, GUIComponent InOwner)
{
Super.InitComponent(InController,InOwner);
}
function AddPingReport( int id, string s )
{
local int i;
if ( !bDebugging )
return;
for ( i = 0; i < reports.Length; i++ )
{
if ( reports[i].listid == id )
{
log("Warning, overwriting existing ping report ("$i$") for listid:"$id@s);
reports[i].ping = s;
return;
}
if ( reports[i].listid > id )
break;
}
Reports.insert(i,1);
Reports[i].listid = id;
reports[i].ping = s;
}
function AddReceiveReport( int id, string s )
{
local int i;
if ( !bDebugging )
return;
for ( i = 0; i < reports.Length; i++ )
{
if ( reports[i].listid == id )
{
reports[i].receive = s;
return;
}
if ( reports[i].listid > id )
break;
}
log("Warning, no matching ping report found for listid:"$id@s);
Reports.insert(i,1);
Reports[i].listid = id;
reports[i].receive = s;
}
function string getreportsortstring( int idx )
{
local int i;
local string s,port;
local array<string> parts;
divide(reports[idx].ping, ":", s, port);
split(s, ".", parts);
for ( i = 0; i < 4; i++ )
padleft(parts[i],4,"0");
padleft(port,5,"0");
return JoinArray(parts,".") $ port;
}
function logall()
{
local int i, id;
local string idx, listid, outping, inping;
local GUIMultiColumnList temp;
if ( !bDebugging )
return;
idx = "Index";
listId = "ListID";
outping = "Address Pinged";
inping = "Received";
PadRight(idx, 8);
PadRight(listid, 8);
PadRight(outping, 22);
// Add to a temporary list for sorting
temp = new(None) class'guimulticolumnlist';
temp.SortColumn = 0;
temp.GetSortString = getreportsortstring;
for ( i = 0; i < reports.Length; i++ )
temp.addeditem();
temp.sort();
log(idx$listid$outping$inping);
for ( i = 0; i < reports.Length; i++ )
{
id = temp.sortdata[i].sortitem;
idx = string(id);
listid = string(reports[id].listid);
outping = reports[id].ping;
inping = reports[id].receive;
PadRight(idx, 8);
PadRight(listid, 8);
PadRight(outping, 22);
log(idx$listid$outping$inping);
}
}
event Timer()
{
//ifdef _KF_
if( bAutoPinging )
AutoPingServers();
else if( IsValid() && OutstandingPings.Length <= 5 && bVisible )
tp_MyPage.RefreshCurrentServer();
//else
//if( IsValid() && OutstandingPings.Length <= 5 && bVisible )
// tp_MyPage.RefreshCurrentServer();
//endif _KF_
}
function SetAnchor(UT2K4Browser_ServerListPageBase Anchor)
{
Super.SetAnchor(Anchor);
if ( Anchor != None )
{
UseSimultaneousPings = tp_MyPage.Browser.GetMaxConnections();
//ifdef _KF_
MaxPingsPerSecond = Controller.MaxPingsPerSecond;
//endif _KF_
}
}
function Clear()
{
PingStart = 0;
StopPings();
Servers.Remove(0,Servers.Length);
ItemCount = 0;
Super.Clear();
}
function CopyServerToClipboard()
{
local string URL;
if( IsValid() )
{
URL = PlayerOwner().GetURLProtocol()$"://"$Servers[CurrentListId()].IP$":"$string(Servers[CurrentListId()].Port);
PlayerOwner().CopyToClipboard(URL);
}
}
function Connect(bool Spectator)
{
local string URL;
if( IsValid() )
{
//!!
URL = PlayerOwner().GetURLProtocol()$"://"$Servers[CurrentListId()].IP$":"$string(Servers[SortData[Index].SortItem].Port);
if( Spectator )
URL = URL $ "?SpectatorOnly=1";
if( tp_MyPage.ConnectLAN )
URL = URL $ "?LAN";
Controller.CloseAll(false,True);
PlayerOwner().ClientTravel( URL, TRAVEL_Absolute, False );
}
}
function AddFavorite( UT2K4ServerBrowser Browser )
{
if( IsValid() )
Browser.OnAddFavorite( Servers[CurrentListId()] );
}
function bool MyOnDblClick(GUIComponent Sender)
{
Connect(false);
return true;
}
function bool InternalOnKeyEvent(out byte Key, out byte State, float delta)
{
if( Super.InternalOnKeyEvent(Key, State, delta) )
return true;
if( State==3 )
{
switch( EInputKey(Key) )
{
case IK_Enter:
Connect(false);
return true;
case IK_F5:
tp_MyPage.RefreshList();
return true;
case IK_C:
if(Controller.CtrlPressed)
{
// Ctrl-C on the server list copies selected server to clipboard.
CopyServerToClipboard();
return true;
}
break;
}
}
return false;
}
function MyOnReceivedServer( GameInfo.ServerResponseLine s )
{
local int i;
local UT2K4ServerBrowser SB;
/* if ( bPreSort && bool(s.Flags & 32) && (!bool(s.Flags & 8)) ) // if it's standard, but not listen, add it to the front
i = 0;
else
*/ i = Servers.Length;
Servers.Insert(i,1);
Servers[i] = s;
if( Servers[i].Ping == 0 )
Servers[i].Ping = 9999;
if ( !bPresort )
AddedItem(i);
// Add the item to the cache
SB = UT2K4ServerBrowser(PageOwner);
if ( SB!=None && SB.Verified )
SB.AddToServerCache(s);
}
function MyPingTimeout( int listid, ServerQueryClient.EPingCause PingCause )
{
local int i;
AddReceiveReport(listid,"TIMEOUT");
if(listid < 0 || listid >= Servers.Length)
{
PingStart = 0;
return;
}
// remove from the outstanding ping list
for( i=0;i<OutstandingPings.Length;i++ )
if( OutstandingPings[i] == listid )
{
OutstandingPings.Remove(i,1);
break;
}
if( Servers[listid].Ping == 9999 )
{
Servers[listid].Ping = 20000;
UpdatedItem(listid);
}
// continue pinging
if( PingCause == PC_AutoPing )
{
NumReceivedPings++;
NeedsSorting = True;
AutoPingServers();
}
}
function MyReceivedPingInfo( int listid, ServerQueryClient.EPingCause PingCause, GameInfo.ServerResponseLine s )
{
local int i,Flags;
local bool bFound;
if( listid < 0 || listid >= Servers.Length )
{
PingStart = 0;
AddReceiveReport(listid,s.ip$":"$s.port);
return;
}
else AddReceiveReport(listid,s.ip$":"$s.port);
Flags = Servers[listid].Flags;
Servers[listid] = s;
Servers[listid].Flags = Flags;
// see if we can move the ping start marker down
for( i=PingStart;i<listid && i<Servers.Length;i++ )
if( Servers[i].Ping == 9999 )
break;
if( i < listid )
PingStart = listid;
// remove from the outstanding ping list
for( i=0;i<OutstandingPings.Length;i++ )
if( OutstandingPings[i] == listid )
{
bFound = True;
OutstandingPings.Remove(i,1);
break;
}
UpdatedItem(listid);
// update rules/player info
if( IsValid() && listid == SortData[Index].SortItem )
OnChange(None);
// continue pinging
if( PingCause == PC_AutoPing )
{
NumReceivedPings++;
NumPlayers += s.CurrentPlayers;
NeedsSorting = True;
AutoPingServers();
}
}
function RepingServers()
{
InvalidatePings();
AutoPingServers();
}
function FakeFinished()
{
local int i;
for (i=0;I<Servers.Length;i++)
AddedItem();
RepingServers();
SetTimer(5.0,true);
}
function MyQueryFinished( MasterServerClient.EResponseInfo ResponseInfo, int Info )
{
local int i;
if( ResponseInfo == RI_Success )
{
if ( bPreSort )
{
if ( ItemCount > 0 )
{
log(Name@"RECEIVED QUERYFINISHED NOTICE WITH ITEMCOUNT > 0!!! ITEMCOUNT:"$ItemCount@"Info:"$Info);
return;
}
for ( i = 0; i < Servers.Length; i++ )
AddedItem();
}
// if _RO_
/* Commented out for Steam Master Server Integration
// endif
RepingServers();
// if _RO_
*/
// endif
SetTimer(5.0, True);
}
}
function InvalidatePings()
{
local int i;
reports.Remove(0,reports.length);
StopPings();
PingStart = 0;
NumReceivedPings=0;
NumPlayers=0;
for( i=0;i<Servers.Length;i++ )
{
Servers[i].Ping = 9999;
UpdatedItem(i);
}
}
function AutoPingServers()
{
local int i, j;
//ifdef _KF_
bAutoPinging = true;
if ( MaxPingsPerSecond > 0 )
{
if ( PlayerOwner().Level.TimeSeconds - LastPingTime > 1.0 / MaxPingsPerSecond )
{
UseSimultaneousPings = OutstandingPings.Length + 1;
LastPingTime = PlayerOwner().Level.TimeSeconds;
}
else
{
UseSimultaneousPings = -1;
}
}
//endif _KF_
for( i=PingStart;i<Servers.Length && OutstandingPings.Length < UseSimultaneousPings;i++ )
{
if( Servers[i].Ping == 9999 )
{
// see if we already have an outstanding ping for this server.
for( j=0;j<OutstandingPings.Length;j++ )
if( OutstandingPings[j] == i )
break;
if( j == OutstandingPings.Length )
{
// add out outstanding ping list
OutstandingPings[j] = i;
// ping
AddPingReport(i,Servers[i].IP$":"$Servers[i].Port);
tp_MyPage.PingServer( i, PC_AutoPing, Servers[i] );
}
}
}
// if(OutstandingPings.Length == 0)
// NumReceivedPings = Servers.Length; // debugging
//ifdef _KF_
if ( OutstandingPings.Length == 0 && (MaxPingsPerSecond <= 0 || PlayerOwner().Level.TimeSeconds - LastPingTime > 1.0 / MaxPingsPerSecond) )
{
bAutoPinging = false;
logall();
}
else if ( MaxPingsPerSecond > 0 )
{
SetTimer(1.0 / MaxPingsPerSecond, true);
}
//else
//if ( OutstandingPings.Length == 0 )
// logall();
//endif _KF_
}
function StopPings()
{
// log(Name@"StopPings OutstandingPings:"$OutstandingPings.Length);
OutstandingPings.Remove(0,OutstandingPings.Length);
if ( tp_MyPage != None )
tp_MyPage.CancelPings();
}
function int RemoveServerAt( int Pos )
{
local int i;
if ( !IsValidIndex(pos) )
return -1;
// In order to avoid having to search for the SortData that has SortItem == Pos,
// just do a switcharoo and pull out the last server
i = Servers.Length - 1;
if ( pos < i )
Servers[pos] = Servers[i];
Servers.Remove(i,1);
if ( i == Index )
{
tp_MyPage.li_Rules.Clear();
tp_MyPage.li_Players.Clear();
}
RemovedItem(i);
SetIndex(Index);
NeedsSorting = True;
return pos;
}
function int RemoveCurrentServer()
{
local int OldItem;
if ( IsValid() )
{
OldItem = SortData[Index].SortItem;
if ( RemoveServerAt(Index) != -1 )
return OldItem;
}
return -1;
}
function MyOnDrawItem(Canvas Canvas, int i, float X, float Y, float W, float H, bool bSelected, bool bPending)
{
local float CellLeft, CellWidth, DrawX;
local float IconPosX, IconPosY;
local string Ping;
local int k, flags, checkFlag;
local GUIStyles DStyle;
// Draw the selection border
if( bSelected )
{
SelectedStyle.Draw(Canvas,MenuState,X,Y,W,H+1);
DStyle = SelectedStyle;
}
else
DStyle = Style;
//////////////////////////////////////////////////////////////////////
// Here we get the flags for this server.
// Passworded 1
// Stats 2
// LatestVersion 4
// Listen Server 8
// Vac Secured 16 // Was Instagib 16
// Beginner 32 // Was Standard 32
// Normal 64 // Was UT CLassic 64
// Hard 128
// Suicidal 256
flags = Servers[SortData[i].SortItem].Flags;
//////////////////////////////////////////////////////////////////////
GetCellLeftWidth( 0, CellLeft, CellWidth );
IconPosX = CellLeft;
IconPosY = Y;
// First flag is in the second to most sig bit (dont want to mess with sign bit). Then we work down.
checkFlag = 1;
// While we still have icon, and we can fit another one in.
for(k=0; (k<Icons.Length) && (IconPosX < CellLeft + CellWidth); k++)
{
// if _RO_
if (Icons[k] != none)
{
// end if _RO_
if((flags & checkFlag) != 0)
{
DrawX = Min(14, (CellLeft + CellWidth) - IconPosX);
Canvas.DrawColor = Canvas.MakeColor(255, 255, 255, 255);
Canvas.SetPos(IconPosX, IconPosY);
Canvas.DrawTile(Icons[k], DrawX, 14, 0, 0, DrawX+1.0, 15.0);
IconPosX += 14;
}
// if _RO_
}
// end if _RO_
checkFlag = checkFlag << 1;
}
GetCellLeftWidth( 1, CellLeft, CellWidth );
DStyle.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, Servers[SortData[i].SortItem].ServerName, FontScale );
GetCellLeftWidth( 2, CellLeft, CellWidth );
DStyle.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, Servers[SortData[i].SortItem].MapName, FontScale );
GetCellLeftWidth( 3, CellLeft, CellWidth );
if( Servers[SortData[i].SortItem].CurrentPlayers>0 || Servers[SortData[i].SortItem].MaxPlayers>0 )
DStyle.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, string(Servers[SortData[i].SortItem].CurrentPlayers)$"/"$string((Servers[SortData[i].SortItem].MaxPlayers&255)), FontScale );
GetCellLeftWidth( 4, CellLeft, CellWidth );
if( Servers[SortData[i].SortItem].CurrentWave>0 || Servers[SortData[i].SortItem].FinalWave>0 )
DStyle.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, string(Servers[SortData[i].SortItem].CurrentWave)$"/"$string((Servers[SortData[i].SortItem].FinalWave)), FontScale );
GetCellLeftWidth( 5, CellLeft, CellWidth );
if( Servers[SortData[i].SortItem].Ping == 9999 )
Ping = "?";
else
if( Servers[SortData[i].SortItem].Ping > 9999 )
Ping = "N/A";
else
Ping = string(Servers[SortData[i].SortItem].Ping);
DStyle.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, Ping, FontScale );
}
function string GetSortString( int i )
{
local string s, t;
switch( SortColumn )
{
case 0:
s = BuildFlagString(Servers[i].Flags) $ i;
break;
case 1:
s = Left(caps(Servers[i].ServerName), 8);
break;
case 2:
s = Left(caps(Servers[i].MapName), 8);
break;
case 3:
s = string(Servers[i].CurrentPlayers);
PadLeft(S, 4, "0");
t = string((Servers[i].MaxPlayers&255));
PadLeft(T, 4, "0");
s $= t $ i;
break;
case 4:
s = string(Servers[i].CurrentWave);
PadLeft(s, 4, "0");
t = string(Servers[i].FinalWave);
PadLeft(t, 4, "0");
s $= t $ i;
break;
default:
s = string(Servers[i].Ping);
PadLeft(S,5,"0");
s $= i;
break;
}
return s;
}
function string Get(optional bool bGuarantee)
{
local string s;
if ( IsValid() )
s = Servers[CurrentListId()].IP $ ":" $ Servers[CurrentListId()].Port;
else if ( Servers.Length > 0 && bGuarantee )
s = Servers[SortData[0].SortItem].IP $ ":" $ Servers[SortData[0].SortItem].Port;
return s;
}
function bool GetCurrent( out GameInfo.ServerResponseLine s )
{
if ( !IsValid() )
return false;
s = Servers[CurrentListId()];
return true;
}
function int FindIndex( string IP, optional string Port )
{
local int i;
for ( i = 0; i < Servers.Length; i++ )
if ( Servers[i].IP == IP && (Port == "" || Servers[i].Port == int(Port)) )
return i;
return -1;
}
function bool IsValid()
{
return Index >= 0 && Index < Servers.Length;
}
function bool IsValidIndex(int Test)
{
return Test >= 0 && Test < Servers.Length;
}
event Opened(GUIComponent Sender)
{
Super.Opened(Sender);
if ( bInitialized && tp_MyPage != None )
AutoPingServers();
else bInitialized = True;
}
event Closed(GUIComponent Sender, bool bCancelled)
{
Super.Closed(Sender, bCancelled);
StopPings();
}
function string BuildFlagString( int Flags )
{
// Passworded 1
// Stats 2
// LatestVersion 4
// Listen Server 8
// Instagib 16
// Standard 32
// UT CLassic 64
local int Value;
local string Result;
Value = 9999;
if ( bool(Flags & 256) )
Value -= 1000;
else if ( bool(Flags & 128) )
Value -= 2000;
else if ( bool(Flags & 64) )
Value -= 3000;
else if ( bool(Flags & 32) )
Value -= 4000;
if ( !bool(Flags & 8) )
Value -= 50;
if ( !bool(Flags & 16) )
Value -= 85;
if ( bool(Flags & 2) )
Value -= 100;
Result = string(Value);
PadLeft(Result,4,"0");
return Result;
}
defaultproperties
{
OnDrawItem=MyOnDrawItem
OnDblClick=MyOnDblClick
ColumnHeadings(0)=""
ColumnHeadings(1)="Server Name"
ColumnHeadings(2)="Map"
ColumnHeadings(3)="Players"
ColumnHeadings(4)="Wave"
ColumnHeadings(5)="Ping"
InitColumnPerc(0)=0.10
InitColumnPerc(1)=0.40
InitColumnPerc(2)=0.20
InitColumnPerc(3)=0.10
InitColumnPerc(4)=0.10
InitColumnPerc(5)=0.10
SortColumn=5
SortDescending=False
// if _RO_
/*
// end if _RO_
Icons(0)=material'ServerIcons.Password'
IconDescriptions(0)="Passworded"
Icons(1)=material'ServerIcons.Stats'
IconDescriptions(1)="Stats Enabled"
Icons(2)=material'ServerIcons.LatestVersion'
IconDescriptions(2)="Latest Version"
Icons(3)=material'ServerIcons.ListenServer'
IconDescriptions(3)="Listen Server"
Icons(4)=material'ServerIcons.Instagib'
IconDescriptions(4)="Instagib"
Icons(5)=material'ServerIcons.Standard'
IconDescriptions(5)="Standard Server"
// RO
Icons(6)=material'S_UTClassic'
IconDescriptions(6)="UT Classic"
// if _KF_
*/
Icons(0)=Texture'InterfaceArt_tex.ServerIcons.passworded'
IconDescriptions(0)="Passworded"
Icons(1)=Texture'InterfaceArt_tex.ServerIcons.Stats'
IconDescriptions(1)="Stats Enabled"
Icons(2)=none
//Icons(2)=Texture'InterfaceArt_tex.ServerIcons.latest_version'
//IconDescriptions(2)="Latest Version"
Icons(3)=Texture'InterfaceArt_tex.ServerIcons.listen2'
IconDescriptions(3)="Listen Server"
Icons(4)=Texture'InterfaceArt_tex.ServerIcons.VAC_Servericon' // formerly instagib
IconDescriptions(4)="VAC Secured"
Icons(5)=Texture'InterfaceArt2_tex.Server_Icons.Easy'
IconDescriptions(5)="Beginner Difficulty"
Icons(6)=Texture'InterfaceArt2_tex.Server_Icons.Normal'
IconDescriptions(6)="Normal Difficulty"
Icons(7)=Texture'InterfaceArt2_tex.Server_Icons.Hard'
IconDescriptions(7)="Hard Difficulty"
Icons(8)=Texture'InterfaceArt2_tex.Server_Icons.Suicide'
IconDescriptions(8)="Suicidal Difficulty"
Icons(9)=Texture'InterfaceArt2_tex.Server_Icons.HellonEarth'
IconDescriptions(9)="Hell Difficulty"
// end if _RO_
WinWidth=1
WinHeight=1
WinTop=0
WinLeft=0
}