597 lines
15 KiB
Ucode
597 lines
15 KiB
Ucode
//==============================================================================
|
|
// Created on: 09/10/2003
|
|
// Choose a difficulty level and gametype, and the player will be automatically
|
|
// connected to the server with the best ping in that category.
|
|
//
|
|
// Written by Ron Prestenback
|
|
// © 2003, Epic Games, Inc. All Rights Reserved
|
|
//==============================================================================
|
|
class UT2K4QuickPlay extends LargeWindow;
|
|
|
|
var automated GUIImage i_Border;
|
|
var automated GUILabel l_Status, l_ServerCount;
|
|
var automated moComboBox co_Game, co_Difficulty;
|
|
var automated GUIButton b_QuickConnect, b_lClose;
|
|
|
|
var MasterServerClient MSC;
|
|
|
|
var localized array<string> DifficultyOptions;
|
|
|
|
// Whether we are currently conducting a search for a good server
|
|
var bool bPendingSearch;
|
|
|
|
// Number of concurrent ping requests currently active - used to make sure we don't surpass MaxSimultaneousPing
|
|
var int ThreadCount;
|
|
var array<int> PingQueue; // Indexes of servers still waiting to be pinged
|
|
var int QuickConnectSeconds; // Number of seconds since we began pinging for quick connect
|
|
var config int MaxWaitSeconds; // Maximum number of seconds to wait for pinging to be completed
|
|
|
|
// List of servers received from the Master Server
|
|
var array<GameInfo.ServerResponseLine> Servers;
|
|
|
|
// Quick Connect properties
|
|
var localized string PlayText, SearchingText, ReadyText, ConnectText, CancelText, ConnectHint, CancelHint;
|
|
|
|
// Localized status text
|
|
var localized string BeginningSearchText;
|
|
var localized string AuthFailedText;
|
|
var localized string ServerCountText;
|
|
var localized string SearchCancelledText;
|
|
var localized string ConnectionFailedText;
|
|
var localized string TimedOutText;
|
|
var localized string NoServersReceivedText;
|
|
var localized string NoValidText;
|
|
|
|
delegate OnOpenConnection();
|
|
delegate OnCloseConnection();
|
|
|
|
function InitComponent(GUIController MyController, GUIComponent MyComponent)
|
|
{
|
|
local int i;
|
|
|
|
Super.InitComponent(MyController, MyComponent);
|
|
|
|
for ( i = 0; i < DifficultyOptions.Length; i++ )
|
|
co_Difficulty.AddItem(DifficultyOptions[i]);
|
|
|
|
StatusTimer(None);
|
|
ChangeQuickPlayStatus(False);
|
|
}
|
|
|
|
event Opened(GUIComponent Sender)
|
|
{
|
|
local int i;
|
|
local array<CacheManager.GameRecord> Records;
|
|
|
|
Super.Opened(Sender);
|
|
class'CacheManager'.static.GetGameTypeList(Records);
|
|
for ( i = 0; i < Records.Length; i++ )
|
|
co_Game.AddItem(Records[i].GameName,,Records[i].ClassName);
|
|
}
|
|
|
|
event Closed(GUIComponent Sender, bool bCancelled)
|
|
{
|
|
Super.Closed(Sender, bCancelled);
|
|
CloseMSConnection();
|
|
}
|
|
|
|
event Free()
|
|
{
|
|
Super.Free();
|
|
CloseMSConnection();
|
|
}
|
|
|
|
function bool NotifyLevelChange()
|
|
{
|
|
CloseMSConnection();
|
|
return Super.NotifyLevelChange();
|
|
}
|
|
|
|
function CloseMSConnection()
|
|
{
|
|
KillTimer();
|
|
if ( MSC != None )
|
|
{
|
|
MSC.CancelPings();
|
|
MSC.Stop();
|
|
}
|
|
|
|
MSC = None;
|
|
}
|
|
|
|
// Master Server Methods
|
|
function AddQueryTerm(coerce string Key, coerce string Value, MasterServerClient.EQueryType QueryType)
|
|
{
|
|
local MasterServerClient.QueryData QD;
|
|
local int i;
|
|
|
|
for ( i = 0; i < MSC.Query.Length; i++ )
|
|
{
|
|
QD = MSC.Query[i];
|
|
if ( QD.Key ~= Key && QD.Value ~= Value && QD.QueryType == QueryType )
|
|
return;
|
|
}
|
|
|
|
QD.Key = Key;
|
|
QD.Value = Value;
|
|
QD.QueryType = QueryType;
|
|
|
|
MSC.Query[i] = QD;
|
|
}
|
|
|
|
function HandleObject( Object Obj, optional Object OptionalObj_1, optional Object OptionalObj_2 )
|
|
{
|
|
MSC = MasterServerClient(Obj);
|
|
}
|
|
|
|
event Timer()
|
|
{
|
|
if ( Playerowner() != None && PlayerOwner().Level.TimeSeconds - QuickConnectSeconds > MaxWaitSeconds )
|
|
{
|
|
SetStatusCaption( NoValidText );
|
|
CancelQuickPlay();
|
|
}
|
|
|
|
UpdateQueue();
|
|
}
|
|
|
|
function bool InternalOnClick(GUIComponent Sender)
|
|
{
|
|
if ( Sender == b_QuickConnect )
|
|
{
|
|
if ( QuickConnectPending() )
|
|
{
|
|
CancelQuickPlay();
|
|
SetStatusCaption(SearchCancelledText);
|
|
}
|
|
else
|
|
{
|
|
SetStatusCaption( BeginningSearchText @ co_Game.GetText() );
|
|
CreateQuickPlayQuery( co_Game.GetExtra(), co_Difficulty.GetIndex() );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( Sender == b_lClose )
|
|
{
|
|
Controller.CloseMenu(false);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function CreateQuickPlayQuery( string GameType, int Index )
|
|
{
|
|
local int Pos;
|
|
|
|
// Reset any browser query in progress.
|
|
ResetQueryClient(MSC);
|
|
|
|
// Add all query parameters
|
|
ChangeQuickPlayStatus(True);
|
|
|
|
// Remove the package name, if it exists
|
|
Pos = InStr(GameType, ".");
|
|
if ( Pos != -1 )
|
|
GameType = Mid(GameType, Pos + 1);
|
|
|
|
// Desired GameType
|
|
AddQueryTerm( "gametype", GameType, QT_Equals );
|
|
|
|
// Desired Difficulty
|
|
AddQueryTerm( "category", Index, QT_Equals );
|
|
|
|
// Must be standard server
|
|
AddQueryTerm( "standard", "true", QT_Equals );
|
|
|
|
// Must not be passworded
|
|
AddQueryTerm( "password", "false", QT_Equals );
|
|
|
|
// Cannot be empty or full
|
|
AddQueryTerm( "currentplayers", "0", QT_GreaterThan );
|
|
AddQueryTerm( "freespace", "0", QT_GreaterThan );
|
|
|
|
// begin searching for an appropriate server (too bad objects don't support states)
|
|
QuickConnectSeconds = PlayerOwner().Level.TimeSeconds;
|
|
MSC.StartQuery( CTM_Query );
|
|
}
|
|
|
|
function QueryComplete( MasterServerClient.EResponseInfo ResponseInfo, int Info )
|
|
{
|
|
switch ( ResponseInfo )
|
|
{
|
|
case RI_Success:
|
|
SetTimer(0.1, True);
|
|
break;
|
|
|
|
case RI_AuthenticationFailed:
|
|
SetStatusCaption( AuthFailedText );
|
|
CancelQuickPlay();
|
|
break;
|
|
|
|
case RI_ConnectionFailed:
|
|
SetStatusCaption( ConnectionFailedText );
|
|
CancelQuickPlay();
|
|
break;
|
|
|
|
case RI_ConnectionTimeout:
|
|
SetStatusCaption( TimedOutText );
|
|
CancelQuickPlay();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function CancelQuickPlay()
|
|
{
|
|
// Cancel pings
|
|
MSC.CancelPings();
|
|
|
|
ChangeQuickPlayStatus(False);
|
|
}
|
|
|
|
function ChangeQuickPlayStatus(bool bIsSearching)
|
|
{
|
|
bPendingSearch = bIsSearching;
|
|
if ( QuickConnectPending() )
|
|
{
|
|
// Set the button text
|
|
SetStatusCaption(SearchingText);
|
|
b_QuickConnect.Caption = CancelText;
|
|
b_QuickConnect.SetHint(CancelHint);
|
|
}
|
|
|
|
else
|
|
{
|
|
KillTimer();
|
|
l_Status.SetTimer(2.5, False);
|
|
b_QuickConnect.Caption = ConnectText;
|
|
b_QuickConnect.SetHint(ConnectHint);
|
|
}
|
|
}
|
|
|
|
function ResetQueryClient(ServerQueryClient Client)
|
|
{
|
|
if ( Client == None )
|
|
return;
|
|
|
|
ThreadCount = 0;
|
|
if ( PingQueue.Length > 0 )
|
|
PingQueue.Remove(0, PingQueue.Length);
|
|
|
|
if ( Servers.Length > 0 )
|
|
Servers.Remove(0, Servers.Length);
|
|
|
|
UpdateServerCount();
|
|
Client.OnReceivedPingInfo = ReceivedPingInfo;
|
|
Client.OnPingTimeout = ReceivedPingTimeout;
|
|
if ( MasterServerClient(Client) == None )
|
|
return;
|
|
|
|
MasterServerClient(Client).OnReceivedServer = ReceivedNewServer;
|
|
MasterServerClient(Client).OnQueryFinished = QueryComplete;
|
|
MasterServerClient(Client).Query.Length = 0;
|
|
}
|
|
|
|
function ReceivedNewServer( GameInfo.ServerResponseLine NewServer )
|
|
{
|
|
local int i;
|
|
|
|
// log("ReceivedNewServer:"$NewServer.ServerName@"IP:"$NewServer.IP);
|
|
// Just add this entry
|
|
i = Servers.Length;
|
|
Servers[i] = NewServer;
|
|
|
|
// If the response packet didn't include the ping, set it to the highest value
|
|
if ( Servers[i].Ping == 0 )
|
|
Servers[i].Ping = 9999;
|
|
|
|
AddServerToPingQueue(i);
|
|
}
|
|
|
|
function AddServerToPingQueue(int ServerIndex)
|
|
{
|
|
local int i;
|
|
|
|
// log("AddServerToPingQueue ServerIndex:"$ServerIndex);
|
|
i = PingQueue.Length;
|
|
PingQueue[i] = ServerIndex;
|
|
|
|
UpdateServerCount();
|
|
if ( ThreadCount < class'UT2K4ServerBrowser'.static.CalculateMaxConnections() )
|
|
PingServer(i);
|
|
}
|
|
|
|
// go through our list of server received from the master server, and ping each one
|
|
// this search will only ping servers for which we have not received pings from
|
|
// this allows the ping to be stopped and restarted (such as when switching tabs)
|
|
function UpdateQueue()
|
|
{
|
|
local int maxcount;
|
|
|
|
maxcount = class'UT2K4ServerBrowser'.static.CalculateMaxConnections();
|
|
|
|
// log("UpdateQueue cnt:"$maxcount@"ThreadCount:"$ThreadCount@"PingQueue.Length:"$PingQueue.Length);
|
|
while ( ThreadCount < maxcount && PingQueue.Length > 0 )
|
|
PingServer(0);
|
|
|
|
UpdateServerCount();
|
|
if ( PingQueue.Length == 0 && ThreadCount <= 0 )
|
|
PingingComplete();
|
|
}
|
|
|
|
function PingServer(int QueueIndex)
|
|
{
|
|
local int i;
|
|
|
|
if ( QueueIndex < 0 || QueueIndex >= PingQueue.Length )
|
|
return;
|
|
|
|
i = PingQueue[QueueIndex];
|
|
PingQueue.Remove(QueueIndex, 1);
|
|
|
|
ThreadCount++;
|
|
|
|
MSC.PingServer( i, PC_AutoPing, Servers[i].IP, Servers[i].QueryPort, QI_Ping, Servers[i] );
|
|
OnOpenConnection();
|
|
}
|
|
|
|
function ReceivedPingInfo( int ServerIndex, ServerQueryClient.EPingCause PingCause, GameInfo.ServerResponseLine ServerInfo )
|
|
{
|
|
// log("ReceivedPingInfo ServerIndex:"$ServerIndex@"EPingCause:"$GetEnum(enum'EPingCause', PingCause));
|
|
// Validation
|
|
if ( ServerIndex < 0 || ServerIndex >= Servers.Length )
|
|
return;
|
|
|
|
// Replace my copy with this copy
|
|
OnCloseConnection();
|
|
ThreadCount--;
|
|
Servers[ServerIndex] = ServerInfo;
|
|
UpdateServerCount();
|
|
}
|
|
|
|
function ReceivedPingTimeout( int ServerIndex, ServerQueryClient.EPingCause PingCause )
|
|
{
|
|
// log("ReceivedPingTimeout ServerIndex:"$ServerIndex@"Servers.Length:"$Servers.Length);
|
|
// Validation
|
|
if ( ServerIndex < 0 || ServerIndex >= Servers.Length )
|
|
return;
|
|
|
|
OnCloseConnection();
|
|
ThreadCount--;
|
|
UpdateServerCount();
|
|
}
|
|
|
|
function PingingComplete()
|
|
{
|
|
local int i, Best;
|
|
local string URL;
|
|
local PlayerController PC;
|
|
|
|
// log("PingingComplete Servers.Length:"$Servers.Length);
|
|
|
|
if ( Servers.Length == 0 )
|
|
{
|
|
SetStatusCaption(NoServersReceivedText);
|
|
ChangeQuickPlayStatus(False);
|
|
return;
|
|
}
|
|
|
|
Best = -1;
|
|
for ( i = 0; i < Servers.Length; i++ )
|
|
{
|
|
if ( Best == -1 )
|
|
{
|
|
if ( Servers[i].Ping < 9999 )
|
|
Best = i;
|
|
}
|
|
|
|
else if ( Servers[i].Ping <= Servers[Best].Ping )
|
|
{
|
|
if ( Servers[i].Ping < Servers[Best].Ping )
|
|
{
|
|
Best = i;
|
|
continue;
|
|
}
|
|
|
|
if ( Servers[i].CurrentPlayers > Servers[Best].CurrentPlayers )
|
|
{
|
|
Best = i;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// No valid servers found!!!!!
|
|
if ( Best == -1 )
|
|
{
|
|
SetStatusCaption(NoValidText);
|
|
ChangeQuickPlayStatus(False);
|
|
return;
|
|
}
|
|
|
|
PC = PlayerOwner();
|
|
|
|
// TODO: Possibly display chosen server, and offer to accept or reject choice
|
|
URL = PC.GetURLProtocol() $ "://" $ Servers[Best].IP $ ":" $ Servers[Best].Port;
|
|
|
|
log("Performing ClientTravel URL:"$URL);
|
|
KillTimer();
|
|
|
|
CloseMSConnection();
|
|
Controller.CloseAll(False,True);
|
|
PC.ClientTravel( URL, TRAVEL_Relative, False );
|
|
}
|
|
|
|
function bool QuickConnectPending()
|
|
{
|
|
return bPendingSearch;
|
|
}
|
|
|
|
function StatusTimer(GUIComponent Sender)
|
|
{
|
|
SetStatusCaption(ReadyText);
|
|
}
|
|
|
|
function SetStatusCaption(string StatusMessage)
|
|
{
|
|
l_Status.Caption = StatusMessage;
|
|
}
|
|
|
|
function UpdateServerCount()
|
|
{
|
|
local string s;
|
|
|
|
s = Repl(ServerCountText, "%NumReceived%", Servers.Length - PingQueue.Length);
|
|
s = Repl(s, "%TotalServers%", Servers.Length);
|
|
|
|
l_ServerCount.Caption = s;
|
|
}
|
|
|
|
|
|
DefaultProperties
|
|
{
|
|
WinWidth=1.000000
|
|
WinHeight=0.370059
|
|
WinLeft=0.000000
|
|
WinTop=0.250000
|
|
|
|
Begin Object Class=GUILabel Name=ConnectionStatus
|
|
WinWidth=0.849415
|
|
WinHeight=0.411678
|
|
WinLeft=0.073594
|
|
WinTop=0.323438
|
|
TextAlign=TXTA_Center
|
|
FontScale=FNS_Medium
|
|
bBoundToParent=True
|
|
bScaleToParent=True
|
|
RenderWeight=0.1
|
|
OnTimer=StatusTimer
|
|
StyleName="TextLabel"
|
|
End Object
|
|
|
|
Begin Object Class=GUILabel Name=ServerCount
|
|
WinWidth=0.761916
|
|
WinHeight=0.130428
|
|
WinLeft=0.120469
|
|
WinTop=0.604924
|
|
TextAlign=TXTA_Center
|
|
FontScale=FNS_Small
|
|
StyleName="TextLabel"
|
|
bBoundToParent=True
|
|
bScaleToParent=True
|
|
RenderWeight=0.1
|
|
End Object
|
|
|
|
Begin Object Class=moComboBox Name=GameType
|
|
WinWidth=0.488417
|
|
WinHeight=0.087720
|
|
WinLeft=0.059751
|
|
WinTop=0.204219
|
|
Caption="Game Type"
|
|
Hint="Select the game type you would like to play."
|
|
CaptionWidth=0.3
|
|
ComponentWidth=-1
|
|
TabOrder=0
|
|
bAutoSizeCaption=True
|
|
bReadOnly=True
|
|
bBoundToParent=True
|
|
bScaleToParent=True
|
|
bStandardized=true
|
|
End Object
|
|
|
|
Begin Object Class=moComboBox Name=DifficultyLevel
|
|
WinWidth=0.367167
|
|
WinHeight=0.087720
|
|
WinLeft=0.571664
|
|
WinTop=0.200564
|
|
Caption="Difficulty"
|
|
// if _RO
|
|
Hint="Select the desired game difficulty level to search for. If you are new to Killing Floor, it is recommended that you choose the \"Beginner\" setting."
|
|
// else
|
|
// Hint="Select the desired game difficulty level to search for. If you are new to UT2004, it is recommended that you choose the \"Beginner\" setting."
|
|
// end if _RO_
|
|
CaptionWidth=0.3
|
|
ComponentWidth=-1
|
|
TabOrder=1
|
|
bAutoSizeCaption=True
|
|
bReadOnly=True
|
|
bBoundToParent=True
|
|
bScaleToParent=True
|
|
bStandardized=true
|
|
End Object
|
|
|
|
ConnectText="SEARCH"
|
|
ConnectHint="Once you've selected a gametype and difficulty level, click \"Search\" to begin searching for a server in the category that has the lowest ping"
|
|
CancelText="CANCEL"
|
|
CancelHint="Cancel the current \"Quick Join\" search"
|
|
|
|
Begin Object Class=GUIButton Name=QuickPlaySearch
|
|
WinWidth=0.120347
|
|
WinHeight=0.109137
|
|
WinLeft=0.307992
|
|
WinTop=0.767551
|
|
TabOrder=2
|
|
OnClick=InternalOnClick
|
|
bScaleToParent=True
|
|
bBoundToParent=True
|
|
// bAutoSize=True
|
|
bStandardized=true
|
|
End Object
|
|
|
|
|
|
Begin Object Class=GUIButton Name=QuickPlayClose
|
|
Caption="CLOSE"
|
|
WinWidth=0.125690
|
|
WinHeight=0.109137
|
|
WinLeft=0.600883
|
|
WinTop=0.767551
|
|
TabOrder=4
|
|
Hint="Close this window"
|
|
OnClick=InternalOnClick
|
|
bScaleToParent=True
|
|
bBoundToParent=True
|
|
bStandardized=true
|
|
End Object
|
|
|
|
Begin Object class=GUIImage name=IBorder
|
|
WinWidth=0.878815
|
|
WinHeight=0.460699
|
|
WinLeft=0.060258
|
|
WinTop=0.300283
|
|
RenderWeight=0.001
|
|
ImageStyle=ISTY_Stretched
|
|
Image=Texture'InterfaceArt_tex.Menu.changeme_texture' //material'2K4Menus.NewControls.Display99'
|
|
bScaleToParent=True
|
|
bBoundToParent=True
|
|
End Object
|
|
I_Border=iBorder
|
|
|
|
MaxWaitSeconds=20
|
|
b_QuickConnect=QuickPlaySearch
|
|
b_lClose=QuickPlayClose
|
|
co_Difficulty=DifficultyLevel
|
|
co_Game=GameType
|
|
l_ServerCount=ServerCount
|
|
l_Status=ConnectionStatus
|
|
|
|
DifficultyOptions(0)="Beginner"
|
|
DifficultyOptions(1)="Experienced"
|
|
DifficultyOptions(2)="Expert"
|
|
|
|
bCaptureInput=True
|
|
|
|
SearchingText="SEARCHING..."
|
|
|
|
ReadyText="Ready"
|
|
BeginningSearchText="Please wait...requesting servers for gametype:"
|
|
AuthFailedText="Authentication failed! Please try again later..."
|
|
ConnectionFailedText="Connection failed - unable to contact the master server. Please check your network connection and try again!"
|
|
TimedOutText="Master server took too long to respond to query. Please try again later."
|
|
SearchCancelledText="Search cancelled!"
|
|
NoServersReceivedText="Sorry, no valid servers found for this gametype!"
|
|
NoValidText="Unknown error encountered while pinging servers! Please retry query..."
|
|
ServerCountText="Received/Total - %NumReceived%/%TotalServers%"
|
|
WindowName="Quick Play"
|
|
}
|