//============================================================================= // LocalPlayer // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. //============================================================================= class LocalPlayer extends Player within Engine config(Engine) inherits(FObserverInterface) native transient; /** The controller ID which this player accepts input from. */ var int ControllerId; /** The master viewport containing this player's view. */ var GameViewportClient ViewportClient; /** The coordinates for the upper left corner of the master viewport subregion allocated to this player. 0-1 */ var vector2d Origin; /** The size of the master viewport subregion allocated to this player. 0-1 */ var vector2d Size; /** Chain of post process effects for this player view */ var transient const PostProcessChain PlayerPostProcess; var transient const array PlayerPostProcessChains; /** This gets set when we don't want to use the player chain and use the world/default chain instead */ var transient bool bForceDefaultPostProcessChain; var private native const pointer ViewState{FSceneViewStateInterface}; // WITH_REALD BEGIN var private native const pointer ViewState2{FSceneViewStateInterface}; // WITH_REALD END struct SynchronizedActorVisibilityHistory { var pointer State; var pointer CriticalSection; }; var private native transient const SynchronizedActorVisibilityHistory ActorVisibilityHistory; /** The location of the player's view the previous frame. */ var const transient vector LastViewLocation; struct native CurrentPostProcessVolumeInfo { /** Last pp settings used when blending to the next set of volume values. */ var PostProcessSettings LastSettings; /** The last post process volume that was applied to the scene */ var PostProcessVolume LastVolumeUsed; /** Time when a new post process volume was set */ var float BlendStartTime; /** Time when the settings blend was last updated. */ var float LastBlendTime; }; /** The Post Process value used */ var const noimport transient CurrentPostProcessVolumeInfo CurrentPPInfo; /** Baseline Level Post Process Info */ var const noimport transient CurrentPostProcessVolumeInfo LevelPPInfo; struct native PostProcessSettingsOverride { var PostProcessSettings Settings; var bool bBlendingIn; var bool bBlendingOut; var float CurrentBlendInTime; // blend-in progress, in seconds var float CurrentBlendOutTime; // blend-out progress, in seconds var float BlendInDuration; // total time of current blend-in var float BlendOutDuration; // total time of current blend-out var float BlendStartTime; // time this override became active, for blending internal to Settings var InterpCurveFloat TimeAlphaCurve; // Curve to map time with blend alpha. Optional, will override BlendInDuration/BlendOutDuration }; /** Stack of active overrides. Restricting this to 1 "active" and have all others be fading out, though * it should be fairly easy to extend to allow gameplay to maintain multiple actives at once. */ var protected transient array ActivePPOverrides; /** How to constrain perspective viewport FOV */ var config EAspectRatioAxisConstraint AspectRatioAxisConstraint; /** The last map this player remembers being on. Used to determine if the map has changed and the pp needs to be reset to default*/ var string LastMap; /** Whether or not to use the next map's defaults and ditch the current pp settings */ var bool bWantToResetToMapDefaultPP; /** set when we've sent a split join request */ var const editconst transient bool bSentSplitJoin; /** This Local Player's translation context. See GetTranslationContext() below. */ var TranslationContext TagContext; /** Caches a local reference to the online subsystems auth interface, if it has one set */ var OnlineAuthInterface CachedAuthInt; /** Whether or not we are awaiting server auth results */ var bool bPendingServerAuth; /** Timestamp for when server auth started */ var float ServerAuthTimestamp; /** If this many seconds pass before server auth completes, 'ServerAuthTimedOut' is triggered (from native code) */ var int ServerAuthTimeout; /** The number of times server authentication has failed */ var int ServerAuthRetryCount; /** The maximum number of server auth retries */ var int MaxServerAuthRetryCount; /** Stores the UID of the server currently being authenticated */ var UniqueNetId ServerAuthUID; cpptext { /** Is object propagation currently overriding our view? */ static UBOOL bOverrideView; static FVector OverrideLocation; static FRotator OverrideRotation; // Constructor. ULocalPlayer(); /** * Tick tasks required for auth code * * @param DeltaTime The time passed since the last tick */ virtual void Tick(FLOAT DeltaTime); /** * Rebuilds the PlayerPostProcessChain. * This should be called whenever the chain array has items inserted/removed. */ void RebuildPlayerPostProcessChain(); /** * Updates the post-process settings for the player's view. * @param ViewLocation - The player's current view location. */ virtual void UpdatePostProcessSettings(const FVector& ViewLocation); /** Update a specific CurrentPostProcessVolumeInfo with the settings and volume specified * * @param PPInfo - The CurrentPostProcessVolumeInfo struct to update * @param NewSettings - The PostProcessSettings to apply to PPInfo * @param NewVolume - The PostProcessVolume to apply to PPInfo */ virtual void UpdatePPSetting(FCurrentPostProcessVolumeInfo& PPVolume, FPostProcessSettings& NewSettings, const FLOAT CurrentWorldTime); #if WITH_REALD FSceneView* CalcSceneViewOffs( FSceneViewFamily* ViewFamily, FVector& ViewLocation, FRotator& ViewRotation, FViewport* Viewport, FMatrix* Offset=NULL, FViewElementDrawer* ViewDrawer=NULL ); #endif /** * Calculate the view settings for drawing from this view actor * * @param View - output view struct * @param ViewLocation - output actor location * @param ViewRotation - output actor rotation * @param Viewport - current client viewport * @param ViewDrawer - optional drawing in the view */ FSceneView* CalcSceneView( FSceneViewFamily* ViewFamily, FVector& ViewLocation, FRotator& ViewRotation, FViewport* Viewport, FViewElementDrawer* ViewDrawer=NULL ); // UObject interface. virtual void FinishDestroy(); // FExec interface. virtual UBOOL Exec(const TCHAR* Cmd,FOutputDevice& Ar); void ExecMacro( const TCHAR* Filename, FOutputDevice& Ar ); // FObserverInterface interface virtual FVector GetObserverViewLocation() { return LastViewLocation; } } /** * Creates an actor for this player. * @param URL - The URL the player joined with. * @param OutError - If an error occurred, returns the error description. * @return False if an error occurred, true if the play actor was successfully spawned. */ native final function bool SpawnPlayActor(string URL,out string OutError); /** sends a splitscreen join command to the server to allow a splitscreen player to connect to the game * the client must already be connected to a server for this function to work * @note this happens automatically for all viewports that exist during the initial server connect * so it's only necessary to manually call this for viewports created after that * if the join fails (because the server was full, for example) all viewports on this client will be disconnected */ native final function SendSplitJoin(); /** * Tests the visibility state of an actor in the most recent frame of this player's view to complete rendering. * @param TestActor - The actor to check visibility for. * @return True if the actor was visible in the frame. */ native final function bool GetActorVisibility(Actor TestActor) const; /** * Begins an override of the current post process settings. */ simulated native function OverridePostProcessSettings( PostProcessSettings OverrideSettings, optional float BlendInTime ); /** * Begins an override of the current post process settings with a time/alpha curve. */ simulated native function OverridePostProcessSettingsCurve( PostProcessSettings OverrideSettings, const out InterpCurveFloat Curve); /** * Stop overriding post process settings. * Will only affect active overrides -- in-progress blendouts are unaffected. * @param BlendOutTime - The amount of time you want to take to recover from the override you are clearing. */ simulated native function ClearPostProcessSettingsOverride(optional float BlendOutTime); /** * Changes the ControllerId for this player; if the specified ControllerId is already taken by another player, changes the ControllerId * for the other player to the ControllerId currently in use by this player. * * @param NewControllerId the ControllerId to assign to this player. */ final function SetControllerId( int NewControllerId ) { local LocalPlayer OtherPlayer; local int CurrentControllerId; if ( ControllerId != NewControllerId ) { `log(Name @ "changing ControllerId from" @ ControllerId @ "to" @ NewControllerId,,'PlayerManagement'); // first, unregister the player's data stores if we already have a PlayerController. if ( Actor != None ) { Actor.PreControllerIdChange(); } CurrentControllerId = ControllerId; // set this player's ControllerId to -1 so that if we need to swap controllerIds with another player we don't // re-enter the function for this player. ControllerId = -1; // see if another player is already using this ControllerId; if so, swap controllerIds with them OtherPlayer = ViewportClient.FindPlayerByControllerId(NewControllerId); if ( OtherPlayer != None ) { OtherPlayer.SetControllerId(CurrentControllerId); } ControllerId = NewControllerId; if ( Actor != None ) { Actor.PostControllerIdChange(); } } } /** * A TranslationContext is part of the system for managing translation tags in localization text. * This system handles text with special tags. E.g.: Press to look at point of interest. * A TranslationContext provides information that cannot be deduced from the text alone. * In this case, it is used to differentiate between players 1 and 2. * @return Translation Context for this Local Player. */ native final function TranslationContext GetTranslationContext(); /** * Add the given post process chain to the chain at the given index. * * @param InChain The post process chain to insert. * @param InIndex The position to insert the chain in the complete chain. * If -1, insert it at the end of the chain. * @param bInClone If TRUE, create a deep copy of the chains effects before insertion. * * @return boolean TRUE if the chain was inserted * FALSE if not */ native function bool InsertPostProcessingChain(PostProcessChain InChain, int InIndex, bool bInClone); /** * Remove the post process chain at the given index. * * @param InIndex The position to insert the chain in the complete chain. * * @return boolean TRUE if the chain was removed * FALSE if not */ native function bool RemovePostProcessingChain(int InIndex); /** * Remove all post process chains. * * @return boolean TRUE if the chain array was cleared * FALSE if not */ native function bool RemoveAllPostProcessingChains(); /** * Get the PPChain at the given index. * * @param InIndex The index of the chain to retrieve. * * @return PostProcessChain The post process chain if found; NULL if not. */ native function PostProcessChain GetPostProcessChain(int InIndex); /** * Forces the PlayerPostProcess chain to be rebuilt. * This should be called if a PPChain is retrieved using the GetPostProcessChain, * and is modified directly. */ native function TouchPlayerPostProcessChain(); /** transforms 2D screen coordinates into a 3D world-space origin and direction * @note: use the Canvas version where possible as it already has the necessary information, * whereas this function must gather it and is therefore slower * @param ScreenPos - relative screen coordinates (0 to 1, relative to this player's viewport region) * @param WorldOrigin (out) - world-space origin vector * @param WorldDirection (out) - world-space direction vector */ native final function DeProject(vector2D RelativeScreenPos, out vector WorldOrigin, out vector WorldDirection); /** transforms 3D world coordinates into a 2D screen position (0-1, 0-1) * @note: use the Canvas version where possible as it already has the necessary information, * whereas this function must gather it and is therefore slower * @param WorldLoc - world location to project * @return screen coordinates (0-1, 0-1) */ native final function vector2d Project(vector WorldLoc); /** transforms 2D screen coordinates into a 3D world-space origin and direction * @note: use the Canvas version where possible as it already has the necessary information, * whereas this function must gather it and is therefore slower * @note: this version does not update visibility lists, physics world camera, or anything else * that the normal DeProject does. it doesn't call CalcSceneView. * @param ScreenPos - relative screen coordinates (0 to 1, relative to this player's viewport region) * @param WorldOrigin (out) - world-space origin vector * @param WorldDirection (out) - world-space direction vector */ native final function FastDeProject(vector2D RelativeScreenPos, out vector WorldOrigin, out vector WorldDirection); /** transforms 3D world coordinates into a 2D screen position (0-1, 0-1) * @note: use the Canvas version where possible as it already has the necessary information, * whereas this function must gather it and is therefore slower * @note: this version does not update visibility lists, physics world camera, or anything else * that the normal Project does. it doesn't call CalcSceneView. * @param WorldLoc - world location to project * @return screen coordinates (0-1, 0-1) */ native final function vector2d FastProject(vector WorldLoc); /** retrieves this player's unique net ID from the online subsystem */ final event UniqueNetId GetUniqueNetId() { local UniqueNetId Result; local GameEngine TheEngine; TheEngine = GameEngine(Outer); if (TheEngine != None && TheEngine.OnlineSubsystem != None && TheEngine.OnlineSubsystem.PlayerInterface != None) { TheEngine.OnlineSubsystem.PlayerInterface.GetUniquePlayerId(ControllerId, Result); } return Result; } /** retrieves this player's name/tag from the online subsytem * if this function returns a non-empty string, the returned name will replace the "Name" URL parameter * passed around in the level loading and connection code, which normally comes from DefaultEngine.ini */ event string GetNickname() { local GameEngine TheEngine; TheEngine = GameEngine(Outer); if (TheEngine != None && TheEngine.OnlineSubsystem != None && TheEngine.OnlineSubsystem.PlayerInterface != None) { return TheEngine.OnlineSubsystem .PlayerInterface.GetPlayerNickname(ControllerId); } else { return ""; } } /** * Authentication handling */ /** * Triggered when the client opens a connection to a server, in order to setup authentication delegates */ event NotifyServerConnectionOpen() { local WorldInfo WI; // Currently, only the primary local player supports auth code if (GamePlayers[0] == self) { // Setup the online subsystem delegates CachedAuthInt = Class'GameEngine'.static.GetOnlineSubsystem().AuthInterface; if (CachedAuthInt != none) { CachedAuthInt.AddClientAuthRequestDelegate(ProcessClientAuthRequest); CachedAuthInt.AddServerAuthResponseDelegate(ProcessServerAuthResponse); CachedAuthInt.AddServerAuthCompleteDelegate(OnServerAuthComplete); CachedAuthInt.AddClientAuthEndSessionRequestDelegate(ProcessClientAuthEndSessionRequest); CachedAuthInt.AddServerConnectionCloseDelegate(OnServerConnectionClose); } // Execute server auth tracking, no matter what the server auth configuration is // (just so the clientside code can implement strict checks, if desired) bPendingServerAuth = true; WI = Class'WorldInfo'.static.GetWorldInfo(); if (WI != none) { ServerAuthTimestamp = WI.RealTimeSeconds; } else { // If WorldInfo does not exist, the native code will set ServerAuthTimestamp when it is created (if > RealTimeSeconds) ServerAuthTimestamp = 10.0; } } } /** * Client authentication handling */ /** * Called when the client receives a message from the server, requesting a client auth session * * @param ServerUID The UID of the game server * @param ServerIP The public (external) IP of the game server * @param ServerPort The port of the game server * @param bSecure whether or not the server has anticheat enabled (relevant to OnlineSubsystemSteamworks and VAC) */ //@HSL_BEGIN_XBOX function ProcessClientAuthRequest(UniqueNetId ServerUID, IpAddr ServerIP, int ServerPort, bool bSecure) //@HSL_END_XBOX { local UniqueNetId NullId; local int AuthTicketUID; if (ServerUID != NullId) { if (CachedAuthInt.CreateClientAuthSession(ServerUID, ServerIP, ServerPort, bSecure, AuthTicketUID)) { if (!class'WorldInfo'.static.isEOSBuild() && !CachedAuthInt.SendClientAuthResponse(AuthTicketUID)) { `log("LocalPlayer::ProcessClientAuthRequest: WARNING!!! Failed to send auth ticket to server"); } } else { `log("LocalPlayer::ProcessClientAuthRequest: WARNING!!! Failed to create client auth session"); } } } /** * Called when the client receives a request from the server, to end an active auth session * * @param ServerConnection The server NetConnection */ function ProcessClientAuthEndSessionRequest(Player ServerConnection) { local LocalAuthSession CurClientSession; local AuthSession CurServerSession; if (CachedAuthInt.FindLocalClientAuthSession(ServerConnection, CurClientSession)) { CachedAuthInt.EndLocalClientAuthSession(CurClientSession.EndPointUID, CurClientSession.EndPointIP, CurClientSession.EndPointPort); } else { `log("LocalPlayer::ProcessClientAuthEndSessionRequest: Couldn't find local client auth session"); } if (CachedAuthInt.FindServerAuthSession(ServerConnection, CurServerSession)) { CachedAuthInt.EndRemoteServerAuthSession(CurServerSession.EndPointUID, CurServerSession.EndPointIP); } else { `log("LocalPlayer::ProcessClientAuthEndSessionRequest: Couldn't find server auth session"); } } /** * Server authentication handling */ /** * Called when the client receives auth data from the server, needed for authentication * * @param ServerUID The UID of the server * @param ServerIP The IP of the server * @param AuthTicketUID The UID used to reference the auth data */ //@HSL_BEGIN_XBOX function ProcessServerAuthResponse(UniqueNetId ServerUID, IpAddr ServerIP, int AuthTicketUID) //@HSL_END_XBOX { local WorldInfo WI; if (CachedAuthInt.VerifyServerAuthSession(ServerUID, ServerIP, AuthTicketUID)) { bPendingServerAuth = True; ServerAuthUID = ServerUID; WI = Class'WorldInfo'.static.GetWorldInfo(); if (WI != none) { ServerAuthTimestamp = WI.RealTimeSeconds; } else { // If WorldInfo does not exist, the native code will set ServerAuthTimestamp when it is created (if > RealTimeSeconds) ServerAuthTimestamp = 10.0; } `log("Kicked off game server auth"); } else { `log("Failed to kickoff game server auth"); } } /** * Called on the client, when the authentication result for the server has returned * * @param bSuccess whether or not authentication was successful * @param ServerUID The UID of the server * @param ServerConnection The connection associated with the server (for retrieving auth session data) * @param ExtraInfo Extra information about authentication, e.g. failure reasons */ function OnServerAuthComplete(bool bSuccess, UniqueNetId ServerUID, Player ServerConnection, string ExtraInfo) { if (bSuccess) { `log("Server auth success"); bPendingServerAuth = False; } else { // Retry auth ServerAuthTimedOut(); } } /** * Triggered by native code, if server auth times out * NOTE: Determined by: (WorldInfo.RealTimeSeconds - ServerAuthTimestamp) > ServerAuthTimeout */ event ServerAuthTimedOut() { local WorldInfo WI; local AuthSession CurServerSession; if (CachedAuthInt != none) { if (ServerAuthRetryCount < MaxServerAuthRetryCount) { // End the current server auth session, before retrying foreach CachedAuthInt.AllServerAuthSessions(CurServerSession) { if (CurServerSession.EndPointUID == ServerAuthUID) { CachedAuthInt.EndRemoteServerAuthSession(CurServerSession.EndPointUID, CurServerSession.EndPointIP); break; } } `log("Sending server auth retry request"); // Send a retry request CachedAuthInt.SendServerAuthRetryRequest(); ServerAuthRetryCount++; // Update the auth timestamp WI = Class'WorldInfo'.static.GetWorldInfo(); if (WI != none) { ServerAuthTimestamp = WI.RealTimeSeconds; } else { ServerAuthTimestamp = 10.0; } } else { ServerAuthFailure(); } } } /** * Called when server authentication fails completely, after several retries. * If strict handling of server auth is required (e.g. disconnecting the client), implement it here */ function ServerAuthFailure() { `log("Server authentication failed after"@MaxServerAuthRetryCount@"tries"); // Uncomment to enable strict authentication checks //Actor.ConsoleCommand("Disconnect"); } /** * Server disconnect cleanup */ /** * Called on the client when a server net connection is closing (so auth sessions can be ended) * NOTE: Triggered >before< NotifyServerConnectionClose * * @param ServerConnection The server NetConnection that is closing */ function OnServerConnectionClose(Player ServerConnection) { // Pass on to the static function, to keep code in one place StaticOnServerConnectionClose(ServerConnection); } /** * Static version of the above, to be called when there is no valid LocalPlayer instance (e.g. at exit) * * @param ServerConnection The server NetConnection that is closing */ static final function StaticOnServerConnectionClose(Player ServerConnection) { local OnlineAuthInterface CurAuthInt; local LocalAuthSession CurClientSession; local AuthSession CurServerSession; CurAuthInt = Class'GameEngine'.static.GetOnlineSubsystem().AuthInterface; if (CurAuthInt != none) { if (CurAuthInt.FindLocalClientAuthSession(ServerConnection, CurClientSession)) { CurAuthInt.EndLocalClientAuthSession(CurClientSession.EndPointUID, CurClientSession.EndPointIP, CurClientSession.EndPointPort); } else { `log("LocalPlayer::StaticOnServerConnectionClose: Couldn't find local client auth session"); } if (CurAuthInt.FindServerAuthSession(ServerConnection, CurServerSession)) { CurAuthInt.EndRemoteServerAuthSession(CurServerSession.EndPointUID, CurServerSession.EndPointIP); } else { `log("LocalPlayer::StaticOnServerConnectionClose: Couldn't find server auth session"); } } } /** * Exit cleanup */ /** * Triggered when the viewport associated with this LocalPlayer has been closed, and the LocalPlayer is about to become invalid * NOTE: This usually happens just before game exit, by clicking the window close button for the game */ event ViewportClosed() { Cleanup(True); } /** * Triggered when the client closes the connection to the server, for cleaning up authentication delegates */ event NotifyServerConnectionClose() { Cleanup(); } /** * Trigered by the game engine upon exit, for online subsystem cleanup */ event Exit() { // Trigger regular cleanup Cleanup(True); } /** * Cleans up online subsystem delegates upon server disconnect, or game exit * * @param bExit whether or not cleanup was triggered due to game exit */ function Cleanup(optional bool bExit) { // Currently, only the primary local player supports auth code if (GamePlayers[0] == self) { // Clear online subsystem delegates if (CachedAuthInt != none) { CachedAuthInt.ClearClientAuthRequestDelegate(ProcessClientAuthRequest); CachedAuthInt.ClearServerAuthResponseDelegate(ProcessServerAuthResponse); CachedAuthInt.ClearServerAuthCompleteDelegate(OnServerAuthComplete); CachedAuthInt.ClearClientAuthEndSessionRequestDelegate(ProcessClientAuthEndSessionRequest); CachedAuthInt.ClearServerConnectionCloseDelegate(OnServerConnectionClose); if (bExit) { // End local client auth sessions CachedAuthInt.EndAllLocalClientAuthSessions(); // End server auth sessions CachedAuthInt.EndAllRemoteServerAuthSessions(); } } CachedAuthInt = none; bPendingServerAuth = False; } } defaultproperties { // bOverridePostProcessSettings=false // inital postprocess setting should be set, not faded in bWantToResetToMapDefaultPP=true bForceDefaultPostProcessChain=false ServerAuthTimeout=10 MaxServerAuthRetryCount=3 }