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 Servers; // OutstandingPings keeps track of simultaneous open connections vs. remaining servers awaiting pings var() array 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 Icons; var() localized array IconDescriptions; struct report { var int listid; var string ping, receive; }; var array 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 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= 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 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 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 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; (k0 || 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 }