/** * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. */ /** * This class is used to connect to a host mesh beacon in order to * establish a connected mesh network. */ class MeshBeaconClient extends MeshBeacon native; /** * Holds a reference to the data that is used to reach the potential host * while a connection is being established for this client */ var const OnlineGameSearchResult HostPendingRequest; /** Used to send the initial client connection request to the host */ struct native ClientConnectionRequest { /** Net Id of primary player on this client */ var UniqueNetId PlayerNetId; /** NAT Type for this client */ var ENatType NatType; /** TRUE if the client is able to host a vs match */ var bool bCanHostVs; /** Ratio of successful vs unsuccessful matches hosted by this client in the past */ var float GoodHostRatio; /** History of bandwidth results from previous tests. Saved/loaded in the player's profile */ var array BandwidthHistory; /** Elapsed time in minutes since the last bandwidth test */ var int MinutesSinceLastTest; }; /** Active connection request that is pending for this client */ var const ClientConnectionRequest ClientPendingRequest; /** Keeps track of all data needed for the current upstream bandwidth test */ struct native ClientBandwidthTestData { /** Type of test current being done */ var EMeshBeaconBandwidthTestType TestType; /** State of the bandwidth test for the client */ var EMeshBeaconBandwidthTestState CurrentState; /** Total bytes expected to be sent in order to complete this test */ var int NumBytesToSendTotal; /** Tally of bytes that have been sent so far for the test */ var int NumBytesSentTotal; /** Size of last buffer that was sent for the test */ var int NumBytesSentLast; /** Time since test was started */ var float ElapsedTestTime; }; /** The upstream test state for the client */ var ClientBandwidthTestData CurrentBandwidthTest; /** Used to drive the client state machine */ enum EMeshBeaconClientState { // Inactive or unknown MBCS_None, // A connection request is outstanding with host MBCS_Connecting, // Connected to the host and is ready to send MBCS_Connected, // Failed to establish a connection MBCS_ConnectionFailed, // Client has sent to the host and is awaiting for replies MBCS_AwaitingResponse, // The client has closed the connection MBCS_Closed }; /** The state of the client beacon as it establishes a connection to the host */ var EMeshBeaconClientState ClientBeaconState; /** The pending request to be sent */ var EMeshBeaconPacketType ClientBeaconRequestType; /** Indicates how long the client should wait for a connection response before timing out */ var config float ConnectionRequestTimeout; /** Used to track how long we've been waiting for a connection response */ var float ConnectionRequestElapsedTime; /** Name of the class to use for address resolving and registering */ var config string ResolverClassName; /** Class to use for address resolving and registering */ var class ResolverClass; /** Platform specific address resolver for this beacon. Instantiated using the ResolverClass type. */ var ClientBeaconAddressResolver Resolver; /** TRUE if address was registered with the beacon address resolver */ var transient bool bUsingRegisteredAddr; cpptext { /** * Ticks the network layer to see if there are any requests or responses to requests * * @param DeltaTime the amount of time that has elapsed since the last tick */ virtual void Tick(FLOAT DeltaTime); /** * Loads the class specified for the Resolver and constructs it if needed */ void InitResolver(void); /** * Creates a beacon that will send requests to remote hosts * * @param Addr the address that we are connecting to (needs to be resolved) * @return true if the beacon was created successfully, false otherwise */ UBOOL InitClientBeacon(const FInternetIpAddr& Addr); /** * Unregisters the address and zeros the members involved to prevent multiple releases */ void CleanupAddress(void); /** * Handles checking for the transition from connecting to connected (socket established) */ void CheckConnectionStatus(void); /** * Sends all the data for a new client connection on the host. * Client data includes the player net id, cient NAT type, and previous bandwidth history. * Assumes that a connection has successfully been established with the host. */ void SendClientConnectionRequest(void); /** * Checks the socket for a response from the host and processes if present */ void ReadHostData(void); /** * Processes a packet that was received from the host * * @param Packet the packet that the host sent * @param PacketSize the size of the packet to process */ void ProcessHostPacket(BYTE* Packet,INT PacketSize); /** * Routes the response packet received from a host to the correct handler based on its type. * * @param HostPacketType packet ID from EMeshBeaconPacketType that represents a host response to this client * @param FromBuffer the packet serializer to read from * @return TRUE if the data packet type was processed */ UBOOL HandleHostPacketByType(BYTE HostPacketType,FNboSerializeFromBuffer& FromBuffer); /** * Common routine for notifying of a timeout trying to talk to host */ void ProcessHostTimeout(void); /** * Processes a heartbeat update, sends a heartbeat back, and clears the timer */ void ProcessHeartbeat(void); /** * Update a bandwidth test that is currently in progress for this client. * All other host packets are ignored until the current test finishes or timeout occurs. */ void ProcessInProgressBandwidthTest(void); /** * Reads the host response to the client's connection request. * Triggers a delegate. * * @param FromBuffer the packet serializer to read from */ void ProcessHostResponseConnectionRequest(FNboSerializeFromBuffer& FromBuffer); /** * Handles a new bandwidth test request initiated by the host for this client. * Triggers a delegate. * * @param FromBuffer the packet serializer to read from */ void ProcessHostRequestBandwidthTest(FNboSerializeFromBuffer& FromBuffer); /** * Handles a host response that all upstream bandwidth data was received by the host. * Triggers a delegate. * * @param FromBuffer the packet serializer to read from */ void ProcessHostFinishedBandwidthTest(FNboSerializeFromBuffer& FromBuffer); /** * Processes a travel request packet that was received from the host * * @param FromBuffer the packet serializer to read from */ void ProcessHostTravelRequest(FNboSerializeFromBuffer& FromBuffer); /** * Processes a request packet that was received from the host to create a new game session * * @param FromBuffer the packet serializer to read from */ void ProcessHostCreateNewSessionRequest(FNboSerializeFromBuffer& FromBuffer); } /** * Stops listening for requests/responses and releases any allocated memory */ native event DestroyBeacon(); /** * Request a connection to be established to the remote host. As part of the * connection request also send the NAT type and bandwidth history data for the client. * Note this request is async and the results will be sent via the delegate * * @param DesiredHost the server that the connection will be made to * @param ClientRequest the client data that is going to be sendt with the request * @param bRegisterSecureAddress if TRUE then then key exchange is required to connect with the host * @return TRUE if the request async task started ok, false if it failed to send */ native function bool RequestConnection(const out OnlineGameSearchResult DesiredHost,const out ClientConnectionRequest ClientRequest,bool bRegisterSecureAddress); /** * Have this client start a bandwidth test on the connected host by sending a start packet * and then streaming as many dummy packets as possible before timeout (MaxBandwidthTestSendTime). * * @param TestType test to run based on enum of EMeshBeaconBandwidthTestType supported bandwidth test types * @param TestBufferSize size in bytes of total data to be sent for the bandwidth test * @return TRUE if the test was successfully started */ native function bool BeginBandwidthTest(EMeshBeaconBandwidthTestType TestType,INT TestBufferSize); /** * Delegate called by the client mesh beacon when a connection request has been responded to by the destination host * * @param ConnectionResult whether the connection request was successful */ delegate OnConnectionRequestResult(EMeshBeaconConnectionResult ConnectionResult); /** * Delegate called by the client mesh beacon when a new bandwidth test request has been received from the host. * * @param TestType test to run based on enum of EMeshBeaconBandwidthTestType supported bandwidth test types */ delegate OnReceivedBandwidthTestRequest(EMeshBeaconBandwidthTestType TestType); /** * Delegate called by the client mesh beacon when bandwidth testing has completed on the host * and the results have been sent back to this client. * * @param TestType test that completed based on enum of EMeshBeaconBandwidthTestType supported bandwidth test types * @param TestResult overall result from running the test * @param BandwidthStats statistics and timing information from running the test */ delegate OnReceivedBandwidthTestResults( EMeshBeaconBandwidthTestType TestType, EMeshBeaconBandwidthTestResult TestResult, const out ConnectionBandwidthStats BandwidthStats); /** * Delegate called by the client mesh beacon when the host sends a request for all clients to travel to * the destination included in the packet. * * @param SessionName the name of the session to register * @param SearchClass the search that should be populated with the session * @param PlatformSpecificInfo the binary data to place in the platform specific areas */ delegate OnTravelRequestReceived(name SessionName,class SearchClass,const out byte PlatformSpecificInfo[80]); /** * Delegate called by the client mesh beacon when the host sends a request for a client to create a new game session. * Used during game session migration to a new host. * * @param SessionName the name of the session to register * @param SearchClass the search that should be populated with the session * @param Players list of players to register on the newly created session */ delegate OnCreateNewSessionRequestReceived(name SessionName,class SearchClass,const out array Players); /** * Notify host of a newly created game session by this client. Host can decide to use/discard the new game session. * * @param bSuccess TRUE if the session was created successfully * @param SessionName the name of the session that was created * @param SearchClass the search that should be populated with the session * @param PlatformSpecificInfo the binary data to place in the platform specific areas */ native function bool SendHostNewGameSessionResponse(bool bSuccess,name SessionName,class SearchClass,const out byte PlatformSpecificInfo[80]); `if(`notdefined(FINAL_RELEASE)) /** * Render debug info about the client mesh beacon */ function DumpInfo() { local int HistoryIdx; `Log("Debug info for Beacon: "$BeaconName,,'DevBeacon'); `Log("",,'DevBeacon'); `Log("Client entry: ",,'DevBeacon'); `Log(" PlayerNetId: "$class'OnlineSubsystem'.static.UniqueNetIdToString(ClientPendingRequest.PlayerNetId),,'DevBeacon'); `Log(" NatType: "$ClientPendingRequest.NatType,,'DevBeacon'); `Log(" GoodHostRatio: "$ClientPendingRequest.GoodHostRatio,,'DevBeacon'); `Log(" bCanHostVs: "$ClientPendingRequest.bCanHostVs,,'DevBeacon'); `Log(" MinutesSinceLastTest: "$ClientPendingRequest.MinutesSinceLastTest,,'DevBeacon'); `Log(" BandwidthTest.CurrentState: "$CurrentBandwidthTest.CurrentState,,'DevBeacon'); `Log(" BandwidthTest.TestType: "$CurrentBandwidthTest.TestType,,'DevBeacon'); `Log(" Bandwidth History: "$ClientPendingRequest.BandwidthHistory.Length,,'DevBeacon'); for (HistoryIdx=0; HistoryIdx < ClientPendingRequest.BandwidthHistory.Length; HistoryIdx++) { `Log(" " $" Upstream bytes/sec: "$ClientPendingRequest.BandwidthHistory[HistoryIdx].UpstreamRate $" Downstream bytes/sec: "$ClientPendingRequest.BandwidthHistory[HistoryIdx].DownstreamRate $" Roundrtrip msec: "$ClientPendingRequest.BandwidthHistory[HistoryIdx].RoundtripLatency,,'DevBeacon'); } } /** * Render debug info about the client mesh beacon * * @param Canvas canvas object to use for rendering debug info */ function DebugRender(Canvas Canvas) { local int HistoryIdx; local float XL,YL; local float Offset; Offset = 50; Canvas.Font = class'Engine'.Static.GetTinyFont(); Canvas.StrLen("============================================================",XL,YL); Canvas.SetPos(Offset,Offset); Canvas.SetDrawColor(0,0,255,64); Canvas.DrawTile(Canvas.DefaultTexture,XL,Canvas.SizeY-(Offset*2),0,0,1,1); Canvas.SetPos(Offset,Offset); Canvas.SetDrawColor(255,255,255); Canvas.DrawText("Debug info for Beacon: "$BeaconName); Canvas.DrawText(""); Canvas.DrawText("Client entry: "); Canvas.StrLen("============================================================",XL,YL); Canvas.SetPos(Canvas.CurX+10,Canvas.CurY); Canvas.DrawText("PlayerNetId: "$class'OnlineSubsystem'.static.UniqueNetIdToString(ClientPendingRequest.PlayerNetId)); Canvas.DrawText("NatType: "$ClientPendingRequest.NatType); Canvas.DrawText("GoodHostRatio: "$ClientPendingRequest.GoodHostRatio); Canvas.DrawText("bCanHostVs: "$ClientPendingRequest.bCanHostVs); Canvas.DrawText("MinutesSinceLastTest: "$ClientPendingRequest.MinutesSinceLastTest); Canvas.DrawText("Current BandwidthTest: "); Canvas.SetPos(Canvas.CurX+10,Canvas.CurY); Canvas.DrawText("CurrentState: "$CurrentBandwidthTest.CurrentState); Canvas.DrawText("TestType: "$CurrentBandwidthTest.TestType); Canvas.DrawText("NumBytesToSendTotal: "$CurrentBandwidthTest.NumBytesToSendTotal); Canvas.DrawText("NumBytesSentTotal: "$CurrentBandwidthTest.NumBytesSentTotal); Canvas.SetPos(Canvas.CurX-10,Canvas.CurY); Canvas.DrawText("Bandwidth History: "$ClientPendingRequest.BandwidthHistory.Length); Canvas.SetPos(Canvas.CurX+10,Canvas.CurY); for (HistoryIdx=0; HistoryIdx < ClientPendingRequest.BandwidthHistory.Length; HistoryIdx++) { Canvas.DrawText(" Upstream bytes/sec: "$ClientPendingRequest.BandwidthHistory[HistoryIdx].UpstreamRate $" Roundrtrip msec: "$ClientPendingRequest.BandwidthHistory[HistoryIdx].RoundtripLatency); } } `endif