2024-12-15 01:39:39 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Text ;
using PlayFab.Public ;
using PlayFab.SharedModels ;
using UnityEngine ;
namespace PlayFab.Internal
{
/// <summary>
/// This is a wrapper for Http So we can better separate the functionaity of Http Requests delegated to WWW or HttpWebRequest
/// </summary>
public class PlayFabHttp : SingletonMonoBehaviour < PlayFabHttp >
{
private static List < CallRequestContainer > _apiCallQueue = new List < CallRequestContainer > ( ) ; // Starts initialized, and is nulled when it's flushed
public delegate void ApiProcessingEvent < in TEventArgs > ( TEventArgs e ) ;
public delegate void ApiProcessErrorEvent ( PlayFabRequestCommon request , PlayFabError error ) ;
public static event ApiProcessingEvent < ApiProcessingEventArgs > ApiProcessingEventHandler ;
public static event ApiProcessErrorEvent ApiProcessingErrorEventHandler ;
public static readonly Dictionary < string , string > GlobalHeaderInjection = new Dictionary < string , string > ( ) ;
private static IPlayFabLogger _logger ;
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
private static IScreenTimeTracker screenTimeTracker = new ScreenTimeTracker ( ) ;
private const float delayBetweenBatches = 5.0f ;
#endif
#if PLAYFAB_REQUEST_TIMING
public struct RequestTiming
{
public DateTime StartTimeUtc ;
public string ApiEndpoint ;
public int WorkerRequestMs ;
public int MainThreadRequestMs ;
}
public delegate void ApiRequestTimingEvent ( RequestTiming time ) ;
public static event ApiRequestTimingEvent ApiRequestTimingEventHandler ;
#endif
/// <summary>
/// Return the number of api calls that are waiting for results from the server
/// </summary>
/// <returns></returns>
public static int GetPendingMessages ( )
{
var transport = PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) ;
return transport . IsInitialized ? transport . GetPendingMessages ( ) : 0 ;
}
/// <summary>
/// This initializes the GameObject and ensures it is in the scene.
/// </summary>
public static void InitializeHttp ( )
{
if ( string . IsNullOrEmpty ( PlayFabSettings . TitleId ) )
throw new PlayFabException ( PlayFabExceptionCode . TitleNotSet , "You must set PlayFabSettings.TitleId before making API Calls." ) ;
var transport = PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) ;
if ( transport . IsInitialized )
return ;
transport . Initialize ( ) ;
CreateInstance ( ) ; // Invoke the SingletonMonoBehaviour
}
/// <summary>
/// This initializes the GameObject and ensures it is in the scene.
/// </summary>
public static void InitializeLogger ( IPlayFabLogger setLogger = null )
{
if ( _logger ! = null )
throw new InvalidOperationException ( "Once initialized, the logger cannot be reset." ) ;
if ( setLogger = = null )
setLogger = new PlayFabLogger ( ) ;
_logger = setLogger ;
}
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
/// <summary>
/// This initializes ScreenTimeTracker object and notifying it to start sending info.
/// </summary>
/// <param name="playFabUserId">Result of the user's login, represent user ID</param>
public static void InitializeScreenTimeTracker ( string entityId , string entityType , string playFabUserId )
{
screenTimeTracker . ClientSessionStart ( entityId , entityType , playFabUserId ) ;
instance . StartCoroutine ( SendScreenTimeEvents ( delayBetweenBatches ) ) ;
}
/// <summary>
/// This function will send Screen Time events on a periodic basis.
/// </summary>
/// <param name="secondsBetweenBatches">Delay between batches, in seconds</param>
private static IEnumerator SendScreenTimeEvents ( float secondsBetweenBatches )
{
WaitForSeconds delay = new WaitForSeconds ( secondsBetweenBatches ) ;
while ( ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . Send ( ) ;
yield return delay ;
}
}
#endif
public static void SimpleGetCall ( string fullUrl , Action < byte [ ] > successCallback , Action < string > errorCallback )
{
InitializeHttp ( ) ;
PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) . SimpleGetCall ( fullUrl , successCallback , errorCallback ) ;
}
public static void SimplePutCall ( string fullUrl , byte [ ] payload , Action < byte [ ] > successCallback , Action < string > errorCallback )
{
InitializeHttp ( ) ;
PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) . SimplePutCall ( fullUrl , payload , successCallback , errorCallback ) ;
}
public static void SimplePostCall ( string fullUrl , byte [ ] payload , Action < byte [ ] > successCallback , Action < string > errorCallback )
{
InitializeHttp ( ) ;
PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) . SimplePostCall ( fullUrl , payload , successCallback , errorCallback ) ;
}
protected internal static void MakeApiCall < TResult > ( string apiEndpoint ,
PlayFabRequestCommon request , AuthType authType , Action < TResult > resultCallback ,
Action < PlayFabError > errorCallback , object customData = null , Dictionary < string , string > extraHeaders = null , PlayFabAuthenticationContext authenticationContext = null , PlayFabApiSettings apiSettings = null , IPlayFabInstanceApi instanceApi = null )
where TResult : PlayFabResultCommon
{
apiSettings = apiSettings ? ? PlayFabSettings . staticSettings ;
var fullUrl = apiSettings . GetFullUrl ( apiEndpoint , apiSettings . RequestGetParams ) ;
_MakeApiCall ( apiEndpoint , fullUrl , request , authType , resultCallback , errorCallback , customData , extraHeaders , false , authenticationContext , apiSettings , instanceApi ) ;
}
protected internal static void MakeApiCallWithFullUri < TResult > ( string fullUri ,
PlayFabRequestCommon request , AuthType authType , Action < TResult > resultCallback ,
Action < PlayFabError > errorCallback , object customData = null , Dictionary < string , string > extraHeaders = null , PlayFabAuthenticationContext authenticationContext = null , PlayFabApiSettings apiSettings = null , IPlayFabInstanceApi instanceApi = null )
where TResult : PlayFabResultCommon
{
apiSettings = apiSettings ? ? PlayFabSettings . staticSettings ;
// This will not be called if environment file does not exist or does not contain property the debugging URI
_MakeApiCall ( null , fullUri , request , authType , resultCallback , errorCallback , customData , extraHeaders , false , authenticationContext , apiSettings , instanceApi ) ;
}
/// <summary>
/// Internal method for Make API Calls
/// </summary>
private static void _MakeApiCall < TResult > ( string apiEndpoint , string fullUrl ,
PlayFabRequestCommon request , AuthType authType , Action < TResult > resultCallback ,
Action < PlayFabError > errorCallback , object customData , Dictionary < string , string > extraHeaders , bool allowQueueing , PlayFabAuthenticationContext authenticationContext , PlayFabApiSettings apiSettings , IPlayFabInstanceApi instanceApi )
where TResult : PlayFabResultCommon
{
InitializeHttp ( ) ;
SendEvent ( apiEndpoint , request , null , ApiProcessingEventType . Pre ) ;
var serializer = PluginManager . GetPlugin < ISerializerPlugin > ( PluginContract . PlayFab_Serializer ) ;
var reqContainer = new CallRequestContainer
{
ApiEndpoint = apiEndpoint ,
FullUrl = fullUrl ,
settings = apiSettings ,
context = authenticationContext ,
CustomData = customData ,
Payload = Encoding . UTF8 . GetBytes ( serializer . SerializeObject ( request ) ) ,
ApiRequest = request ,
ErrorCallback = errorCallback ,
RequestHeaders = extraHeaders ? ? new Dictionary < string , string > ( ) , // Use any headers provided by the customer
instanceApi = instanceApi
} ;
// Append any additional headers
foreach ( var pair in GlobalHeaderInjection )
if ( ! reqContainer . RequestHeaders . ContainsKey ( pair . Key ) )
reqContainer . RequestHeaders [ pair . Key ] = pair . Value ;
#if PLAYFAB_REQUEST_TIMING
reqContainer . Timing . StartTimeUtc = DateTime . UtcNow ;
reqContainer . Timing . ApiEndpoint = apiEndpoint ;
#endif
// Add PlayFab Headers
var transport = PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) ;
reqContainer . RequestHeaders [ "X-ReportErrorAsSuccess" ] = "true" ; // Makes processing PlayFab errors a little easier
reqContainer . RequestHeaders [ "X-PlayFabSDK" ] = PlayFabSettings . VersionString ; // Tell PlayFab which SDK this is
switch ( authType )
{
#if ENABLE_PLAYFABSERVER_API | | ENABLE_PLAYFABADMIN_API | | UNITY_EDITOR | | ENABLE_PLAYFAB_SECRETKEY
case AuthType . DevSecretKey :
if ( apiSettings . DeveloperSecretKey = = null ) throw new PlayFabException ( PlayFabExceptionCode . DeveloperKeyNotSet , "DeveloperSecretKey is not found in Request, Server Instance or PlayFabSettings" ) ;
reqContainer . RequestHeaders [ "X-SecretKey" ] = apiSettings . DeveloperSecretKey ; break ;
#endif
#if ! DISABLE_PLAYFABCLIENT_API
case AuthType . LoginSession :
if ( authenticationContext ! = null )
reqContainer . RequestHeaders [ "X-Authorization" ] = authenticationContext . ClientSessionTicket ;
break ;
#endif
#if ! DISABLE_PLAYFABENTITY_API
case AuthType . EntityToken :
if ( authenticationContext ! = null )
reqContainer . RequestHeaders [ "X-EntityToken" ] = authenticationContext . EntityToken ;
break ;
#endif
2025-01-24 21:36:28 +00:00
case AuthType . TelemetryKey :
if ( authenticationContext ! = null )
reqContainer . RequestHeaders [ "X-TelemetryKey" ] = authenticationContext . TelemetryKey ;
break ;
2024-12-15 01:39:39 +00:00
}
// These closures preserve the TResult generic information in a way that's safe for all the devices
reqContainer . DeserializeResultJson = ( ) = >
{
reqContainer . ApiResult = serializer . DeserializeObject < TResult > ( reqContainer . JsonResponse ) ;
} ;
reqContainer . InvokeSuccessCallback = ( ) = >
{
if ( resultCallback ! = null )
{
resultCallback ( ( TResult ) reqContainer . ApiResult ) ;
}
} ;
if ( allowQueueing & & _apiCallQueue ! = null )
{
for ( var i = _apiCallQueue . Count - 1 ; i > = 0 ; i - - )
if ( _apiCallQueue [ i ] . ApiEndpoint = = apiEndpoint )
_apiCallQueue . RemoveAt ( i ) ;
_apiCallQueue . Add ( reqContainer ) ;
}
else
{
transport . MakeApiCall ( reqContainer ) ;
}
}
/// <summary>
/// Internal code shared by IPlayFabHTTP implementations
/// </summary>
internal void OnPlayFabApiResult ( CallRequestContainer reqContainer )
{
var result = reqContainer . ApiResult ;
#if ! DISABLE_PLAYFABENTITY_API
var entRes = result as AuthenticationModels . GetEntityTokenResponse ;
if ( entRes ! = null )
{
PlayFabSettings . staticPlayer . EntityToken = entRes . EntityToken ;
}
#endif
#if ! DISABLE_PLAYFABCLIENT_API
var logRes = result as ClientModels . LoginResult ;
var regRes = result as ClientModels . RegisterPlayFabUserResult ;
if ( logRes ! = null )
{
logRes . AuthenticationContext = new PlayFabAuthenticationContext ( logRes . SessionTicket , logRes . EntityToken . EntityToken , logRes . PlayFabId , logRes . EntityToken . Entity . Id , logRes . EntityToken . Entity . Type ) ;
if ( reqContainer . context ! = null )
reqContainer . context . CopyFrom ( logRes . AuthenticationContext ) ;
}
else if ( regRes ! = null )
{
regRes . AuthenticationContext = new PlayFabAuthenticationContext ( regRes . SessionTicket , regRes . EntityToken . EntityToken , regRes . PlayFabId , regRes . EntityToken . Entity . Id , regRes . EntityToken . Entity . Type ) ;
if ( reqContainer . context ! = null )
reqContainer . context . CopyFrom ( regRes . AuthenticationContext ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour OnEnable Method
/// </summary>
private void OnEnable ( )
{
if ( _logger ! = null )
{
_logger . OnEnable ( ) ;
}
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
if ( ( screenTimeTracker ! = null ) & & ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . OnEnable ( ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour OnDisable
/// </summary>
private void OnDisable ( )
{
if ( _logger ! = null )
{
_logger . OnDisable ( ) ;
}
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
if ( ( screenTimeTracker ! = null ) & & ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . OnDisable ( ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour OnDestroy
/// </summary>
private void OnDestroy ( )
{
var transport = PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) ;
if ( transport . IsInitialized )
{
transport . OnDestroy ( ) ;
}
if ( _logger ! = null )
{
_logger . OnDestroy ( ) ;
}
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
if ( ( screenTimeTracker ! = null ) & & ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . OnDestroy ( ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour OnApplicationFocus
/// </summary>
public void OnApplicationFocus ( bool isFocused )
{
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
if ( ( screenTimeTracker ! = null ) & & ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . OnApplicationFocus ( isFocused ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour OnApplicationQuit
/// </summary>
public void OnApplicationQuit ( )
{
#if ! DISABLE_PLAYFABENTITY_API & & ! DISABLE_PLAYFABCLIENT_API
if ( ( screenTimeTracker ! = null ) & & ! PlayFabSettings . DisableFocusTimeCollection )
{
screenTimeTracker . OnApplicationQuit ( ) ;
}
#endif
}
/// <summary>
/// MonoBehaviour Update
/// </summary>
private void Update ( )
{
var transport = PluginManager . GetPlugin < ITransportPlugin > ( PluginContract . PlayFab_Transport ) ;
if ( transport . IsInitialized )
{
if ( _apiCallQueue ! = null )
{
foreach ( var eachRequest in _apiCallQueue )
transport . MakeApiCall ( eachRequest ) ; // Flush the queue
_apiCallQueue = null ; // null this after it's flushed
}
transport . Update ( ) ;
}
while ( _injectedCoroutines . Count > 0 )
StartCoroutine ( _injectedCoroutines . Dequeue ( ) ) ;
while ( _injectedAction . Count > 0 )
{
var action = _injectedAction . Dequeue ( ) ;
if ( action ! = null )
{
action . Invoke ( ) ;
}
}
}
#region Helpers
protected internal static PlayFabError GeneratePlayFabError ( string apiEndpoint , string json , object customData )
{
Dictionary < string , object > errorDict = null ;
Dictionary < string , List < string > > errorDetails = null ;
var serializer = PluginManager . GetPlugin < ISerializerPlugin > ( PluginContract . PlayFab_Serializer ) ;
try
{
// Deserialize the error
errorDict = serializer . DeserializeObject < Dictionary < string , object > > ( json ) ;
}
catch ( Exception ) { /* Unusual, but shouldn't actually matter */ }
try
{
object errorDetailsString ;
if ( errorDict ! = null & & errorDict . TryGetValue ( "errorDetails" , out errorDetailsString ) )
errorDetails = serializer . DeserializeObject < Dictionary < string , List < string > > > ( errorDetailsString . ToString ( ) ) ;
}
catch ( Exception ) { /* Unusual, but shouldn't actually matter */ }
return new PlayFabError
{
ApiEndpoint = apiEndpoint ,
HttpCode = errorDict ! = null & & errorDict . ContainsKey ( "code" ) ? Convert . ToInt32 ( errorDict [ "code" ] ) : 400 ,
HttpStatus = errorDict ! = null & & errorDict . ContainsKey ( "status" ) ? ( string ) errorDict [ "status" ] : "BadRequest" ,
Error = errorDict ! = null & & errorDict . ContainsKey ( "errorCode" ) ? ( PlayFabErrorCode ) Convert . ToInt32 ( errorDict [ "errorCode" ] ) : PlayFabErrorCode . ServiceUnavailable ,
ErrorMessage = errorDict ! = null & & errorDict . ContainsKey ( "errorMessage" ) ? ( string ) errorDict [ "errorMessage" ] : json ,
ErrorDetails = errorDetails ,
CustomData = customData ,
RetryAfterSeconds = errorDict ! = null & & errorDict . ContainsKey ( "retryAfterSeconds" ) ? Convert . ToUInt32 ( errorDict [ "retryAfterSeconds" ] ) : ( uint? ) null ,
} ;
}
protected internal static void SendErrorEvent ( PlayFabRequestCommon request , PlayFabError error )
{
if ( ApiProcessingErrorEventHandler = = null )
return ;
try
{
ApiProcessingErrorEventHandler ( request , error ) ;
}
catch ( Exception e )
{
Debug . LogException ( e ) ;
}
}
protected internal static void SendEvent ( string apiEndpoint , PlayFabRequestCommon request , PlayFabResultCommon result , ApiProcessingEventType eventType )
{
if ( ApiProcessingEventHandler = = null )
return ;
try
{
ApiProcessingEventHandler ( new ApiProcessingEventArgs
{
ApiEndpoint = apiEndpoint ,
EventType = eventType ,
Request = request ,
Result = result
} ) ;
}
catch ( Exception e )
{
Debug . LogException ( e ) ;
}
}
public static void ClearAllEvents ( )
{
ApiProcessingEventHandler = null ;
ApiProcessingErrorEventHandler = null ;
}
#if PLAYFAB_REQUEST_TIMING
protected internal static void SendRequestTiming ( RequestTiming rt )
{
if ( ApiRequestTimingEventHandler ! = null )
{
ApiRequestTimingEventHandler ( rt ) ;
}
}
#endif
#endregion
private readonly Queue < IEnumerator > _injectedCoroutines = new Queue < IEnumerator > ( ) ;
private readonly Queue < Action > _injectedAction = new Queue < Action > ( ) ;
public void InjectInUnityThread ( IEnumerator x )
{
_injectedCoroutines . Enqueue ( x ) ;
}
public void InjectInUnityThread ( Action action )
{
_injectedAction . Enqueue ( action ) ;
}
}
#region Event Classes
public enum ApiProcessingEventType
{
Pre ,
Post
}
public class ApiProcessingEventArgs
{
public string ApiEndpoint ;
public ApiProcessingEventType EventType ;
public PlayFabRequestCommon Request ;
public PlayFabResultCommon Result ;
public TRequest GetRequest < TRequest > ( ) where TRequest : PlayFabRequestCommon
{
return Request as TRequest ;
}
}
#endregion
}