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

499 lines
13 KiB
Ucode

class Browser_ServersList extends GUIMultiColumnList;
//#exec OBJ LOAD file=..\Textures\Old2k4\ServerIcons.utx
//#exec Texture Import File=textures\UTClassicIcon.tga Name=S_UTClassic Mips=Off Alpha=1
var() array<GameInfo.ServerResponseLine> Servers;
var Browser_ServerListPageBase MyPage;
var Browser_RulesList MyRulesList;
var Browser_PlayersList MyPlayersList;
var GUIStyles SelStyle;
var array<int> OutstandingPings;
var int UseSimultaneousPings;
var int PingStart;
var int NumReceivedPings;
var int NumPlayers;
//ifdef _KF_
var int MaxPingsPerSecond;
//endif _KF_
var() array<Material> Icons;
var() localized array<String> IconDescriptions;
function InitComponent(GUIController MyController, GUIComponent MyOwner)
{
local int i;
// let rules/players know about us
MyPlayersList.MyServersList = Self;
MyRulesList.MyServersList = Self;
// set delegates
OnDrawItem = MyOnDrawItem;
OnChange = MyOnChange;
OnDblClick = MyOnDblClick;
Super.Initcomponent(MyController, MyOwner);
SelStyle = Controller.GetStyle("SquareButton",FontScale);
if( Controller.MaxSimultaneousPings == 0 )
{
i = class'Player'.default.ConfiguredInternetSpeed;
if (i<=2600)
UseSimultaneousPings = 10;
else if (i<=5000)
UseSimultaneousPings = 15;
else if (i<=10000)
UseSimultaneousPings = 20;
else
UseSimultaneousPings = 35;
}
else
UseSimultaneousPings = Controller.MaxSimultaneousPings;
//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( Index >= 0 )
{
URL = PlayerOwner().GetURLProtocol()$"://"$Servers[SortData[Index].SortItem].IP$":"$string(Servers[SortData[Index].SortItem].Port);
PlayerOwner().CopyToClipboard(URL);
}
}
function Connect(bool Spectator)
{
local string URL;
if( Index >= 0 )
{
URL = PlayerOwner().GetURLProtocol()$"://"$Servers[SortData[Index].SortItem].IP$":"$string(Servers[SortData[Index].SortItem].Port);
if( Spectator )
URL = URL $ "?SpectatorOnly=1";
if( MyPage.ConnectLAN )
URL = URL $ "?LAN";
Controller.CloseAll(false);
PlayerOwner().ClientTravel( URL, TRAVEL_Absolute, false );
}
}
function AddFavorite( ServerBrowser Browser )
{
if( Index >= 0 )
Browser.OnAddFavorite( Servers[SortData[Index].SortItem] );
}
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==1 )
{
switch(Key)
{
case 0x0D: //IK_Enter
Connect(false);
return true;
break;
case 0x74: //IK_F5
MyPage.RefreshList();
return true;
break;
case 0x43: // 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;
i = Servers.Length;
Servers.Length=i+1;
Servers[i] = s;
if( Servers[i].Ping == 0 )
Servers[i].Ping = 9999;
ItemCount++;
AddedItem();
}
function MyPingTimeout( int listid, ServerQueryClient.EPingCause PingCause )
{
local int i;
if(listid >= Servers.Length)
return;
if( Servers[listid].Ping == 9999 )
{
Servers[listid].Ping = 10000;
UpdatedItem(listid);
}
// remove from the outstanding ping list
for( i=0;i<OutstandingPings.Length;i++ )
if( OutstandingPings[i] == listid )
{
OutstandingPings.Remove(i,1);
break;
}
// continue pinging
if( PingCause == PC_AutoPing )
{
NumReceivedPings++;
NeedsSorting = True;
AutoPingServers();
}
}
function MyReceivedPingInfo( int listid, ServerQueryClient.EPingCause PingCause, GameInfo.ServerResponseLine s )
{
local int i;
if( listid < 0 )
{
PingStart = 0;
return;
}
Servers[listid] = s;
// 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 )
{
OutstandingPings.Remove(i,1);
break;
}
UpdatedItem(listid);
// update rules/player info
if( Index>=0 && listid == SortData[Index].SortItem )
MyOnChange(None);
// continue pinging
if( PingCause == PC_AutoPing )
{
NumReceivedPings++;
NumPlayers += s.CurrentPlayers;
NeedsSorting = True;
AutoPingServers();
}
}
function RePingServers()
{
InvalidatePings();
AutoPingServers();
}
function MyQueryFinished( MasterServerClient.EResponseInfo ResponseInfo, int Info )
{
if( ResponseInfo == RI_Success )
{
RePingServers();
}
}
function InvalidatePings()
{
local int i;
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;
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 )
{
// ping
MyPage.PingServer( i, PC_AutoPing, Servers[i] );
// add out outstanding ping list
j = OutstandingPings.Length;
OutstandingPings.Length = j+1;
OutstandingPings[j] = i;
}
}
}
if(OutstandingPings.Length == 0)
NumReceivedPings = Servers.Length;
}
function StopPings()
{
OutstandingPings.Remove(0,OutstandingPings.Length);
MyPage.CancelPings();
}
event Timer()
{
if( Index >= 0 )
MyOnChange( Self );
}
function MyOnChange(GUIComponent Sender)
{
MyRulesList.Clear();
MyPlayersList.Clear();
if( Index >= 0 )
{
// when changing selected servers, get their rules
if( Sender != None )
MyPage.PingServer( SortData[Index].SortItem, PC_Clicked, Servers[SortData[Index].SortItem] );
MyRulesList.ItemCount = Servers[SortData[Index].SortItem].ServerInfo.Length;
MyRulesList.ListItem = SortData[Index].SortItem;
MyPlayersList.ItemCount = Servers[SortData[Index].SortItem].PlayerInfo.Length;
MyPlayersList.ListItem = SortData[Index].SortItem;
SetTimer( 5, false );
}
else
{
MyRulesList.ItemCount = 0;
MyPlayersList.ItemCount = 0;
}
}
function int RemoveCurrentServer()
{
local int OldItem;
if( Index >= 0 )
{
OldItem = SortData[Index].SortItem;
Servers.Remove( OldItem, 1 );
ItemCount--;
MyRulesList.ItemCount = 0;
MyPlayersList.ItemCount = 0;
// tell sort lists.
RemovedCurrent();
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;
// Draw the selection border
if( bSelected )
SelStyle.Draw(Canvas,MSAT_Pressed, X, Y-2, W, H+2 );
//////////////////////////////////////////////////////////////////////
// Here we get the flags for this server.
// Passworded 1 << 30
// Stats 1 << 29
// LatestVersion 1 << 28
// Listen Server 1 << 27
// Instagib 1 << 26
// Standard 1 << 25
// UT CLassic 1 << 24
// flags are encoded at the top of MaxPlayers
flags = Servers[SortData[i].SortItem].MaxPlayers;
//flags = (1 << 30) | (1 << 29) | (1 << 28) | (1 << 27) | (1 << 26) | (1 << 25); // For testing
//////////////////////////////////////////////////////////////////////
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 << 30;
// While we still have icon, and we can fit another one in.
for(k=0; (k<Icons.Length) && (IconPosX < CellLeft + CellWidth); k++)
{
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;
}
checkFlag = checkFlag >> 1;
}
GetCellLeftWidth( 1, CellLeft, CellWidth );
Style.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, Servers[SortData[i].SortItem].ServerName, FontScale );
GetCellLeftWidth( 2, CellLeft, CellWidth );
Style.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 )
Style.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].Ping == 9999 )
Ping = "?";
else
if( Servers[SortData[i].SortItem].Ping == 10000 )
Ping = "N/A";
else
Ping = string(Servers[SortData[i].SortItem].Ping);
Style.DrawText( Canvas, MenuState, CellLeft, Y, CellWidth, H, TXTA_Left, Ping, FontScale );
// GetCellLeftWidth( 4, CellLeft, CellWidth );
// Style.DrawText( Canvas, MenuState, X+CellLeft, Y, CellWidth, H, TXTA_Left, SortData[i].SortString, FontScale );
}
function string GetSortString( int i )
{
local string s, t;
switch( SortColumn )
{
case 0:
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);
while(Len(s) < 4 )
s = "0"$s;
t = string((Servers[i].MaxPlayers&255));
while(Len(t) < 4 )
t = "0"$t;
s = s $ t;
break;
case 4:
s = string(Servers[i].Ping);
while(Len(s) < 5 )
s = "0"$s;
break;
default:
s = string(Servers[i].Ping);
while(Len(s) < 5 )
s = "0"$s;
break;
}
return s;
}
defaultproperties
{
ColumnHeadings(0)=""
ColumnHeadings(1)="Server Name"
ColumnHeadings(2)="Map"
ColumnHeadings(3)="Players"
ColumnHeadings(4)="Ping"
InitColumnPerc(0)=0.10
InitColumnPerc(1)=0.37
InitColumnPerc(2)=0.25
InitColumnPerc(3)=0.13
InitColumnPerc(4)=0.15
SortColumn=4
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"
Icons(6)=material'xInterface.S_UTClassic'
IconDescriptions(6)="UT Classic"
// if _RO_
*/
// end if _RO_
}