upload
This commit is contained in:
53
IpDrv/Classes/ClientBeaconAddressResolver.uc
Normal file
53
IpDrv/Classes/ClientBeaconAddressResolver.uc
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows a client to register and resolve the address for a host that it wants to connect to.
|
||||
* The platform specific implementation for this resolver will handle the specifics of generating
|
||||
* a secure key to allow for a connection.
|
||||
*/
|
||||
class ClientBeaconAddressResolver extends Object
|
||||
native
|
||||
config(Engine);
|
||||
|
||||
/** The port that the beacon will listen on */
|
||||
var int BeaconPort;
|
||||
|
||||
/** The name to use when logging (helps debugging) */
|
||||
var name BeaconName;
|
||||
|
||||
cpptext
|
||||
{
|
||||
/**
|
||||
* Performs platform specific resolution of the address
|
||||
*
|
||||
* @param DesiredHost the host to resolve the IP address for
|
||||
* @param Addr out param having it's address set
|
||||
*
|
||||
* @return true if the address could be resolved, false otherwise
|
||||
*/
|
||||
virtual UBOOL ResolveAddress(const FOnlineGameSearchResult& DesiredHost,FInternetIpAddr& Addr);
|
||||
|
||||
/**
|
||||
* Allows for per platform registration of secure keys, so that a secure connection
|
||||
* can be opened and used for sending/receiving data.
|
||||
*
|
||||
* @param DesiredHost the host that is being registered
|
||||
*/
|
||||
virtual UBOOL RegisterAddress(const FOnlineGameSearchResult& DesiredHost)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for per platform unregistration of secure keys, which breaks the link between
|
||||
* a client and server. This also releases any memory associated with the keys.
|
||||
*
|
||||
* @param DesiredHost the host that is being registered
|
||||
*/
|
||||
virtual UBOOL UnregisterAddress(const FOnlineGameSearchResult& DesiredHost)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
73
IpDrv/Classes/HelloWeb.uc
Normal file
73
IpDrv/Classes/HelloWeb.uc
Normal file
@ -0,0 +1,73 @@
|
||||
/*=============================================================================
|
||||
HelloWeb.uc - example web server
|
||||
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
class HelloWeb extends WebApplication;
|
||||
|
||||
/* Usage:
|
||||
This is a sample web application, to demonstrate how to program for the web server.
|
||||
|
||||
|
||||
[IpDrv.WebServer]
|
||||
Applications[0]="IpDrv.HelloWeb"
|
||||
ApplicationPaths[0]="/hello"
|
||||
bEnabled=True
|
||||
|
||||
http://server.ip.address/hello
|
||||
|
||||
*/
|
||||
|
||||
function Init()
|
||||
{
|
||||
`log("HelloWeb INIT");
|
||||
}
|
||||
|
||||
event Query(WebRequest Request, WebResponse Response)
|
||||
{
|
||||
local int i;
|
||||
|
||||
`log("Query received"@Request@Response);
|
||||
if(Request.Username != "test" || Request.Password != "test")
|
||||
{
|
||||
Response.FailAuthentication("HelloWeb");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(Request.URI)
|
||||
{
|
||||
case "/form.html":
|
||||
Response.SendText("<form method=post action=submit.html>");
|
||||
Response.SendText("<input type=edit name=TestEdit>");
|
||||
Response.SendText("<p><select multiple name=selecter>");
|
||||
Response.SendText("<option value=\"one\">Number One");
|
||||
Response.SendText("<option value=\"two\">Number Two");
|
||||
Response.SendText("<option value=\"three\">Number Three");
|
||||
Response.SendText("<option value=\"four\">Number Four");
|
||||
Response.SendText("</select><p>");
|
||||
Response.SendText("<input type=submit name=Submit value=Submit>");
|
||||
Response.SendText("</form>");
|
||||
break;
|
||||
case "/submit.html":
|
||||
Response.SendText("Thanks for submitting the form.<br>");
|
||||
Response.SendText("TestEdit was \""$Request.GetVariable("TestEdit")$"\"<p>");
|
||||
Response.SendText("You selected these items:<br>");
|
||||
for(i=Request.GetVariableCount("selecter")-1;i>=0;i--)
|
||||
Response.SendText("\""$Request.GetVariableNumber("selecter", i)$"\"<br>");
|
||||
break;
|
||||
case "/include.html":
|
||||
Response.Subst("variable1", "This is variable 1");
|
||||
Response.Subst("variable2", "This is variable 2");
|
||||
Response.Subst("variable3", "This is variable 3");
|
||||
Response.IncludeUHTM("testinclude.html");
|
||||
break;
|
||||
default:
|
||||
Response.SendText("Hello web! The current level is "$WorldInfo.Title);
|
||||
Response.SendText("<br>Click <a href=\"form.html\">this link</a> to go to a test form");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
48
IpDrv/Classes/ImageServer.uc
Normal file
48
IpDrv/Classes/ImageServer.uc
Normal file
@ -0,0 +1,48 @@
|
||||
/*=============================================================================
|
||||
ImageServer.uc - example image server
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
class ImageServer extends WebApplication;
|
||||
|
||||
/* Usage:
|
||||
[IpDrv.WebServer]
|
||||
Applications[0]="IpDrv.ImageServer"
|
||||
ApplicationPaths[0]="/images"
|
||||
bEnabled=True
|
||||
|
||||
http://server.ip.address/images/test.jpg
|
||||
*/
|
||||
|
||||
event Query(WebRequest Request, WebResponse Response)
|
||||
{
|
||||
local string Image;
|
||||
|
||||
Image = Request.URI;
|
||||
if (!Response.FileExists(Path $ Image))
|
||||
{
|
||||
Response.HTTPError(404);
|
||||
return;
|
||||
}
|
||||
else if( Right(Caps(Image), 4) == ".JPG" || Right(Caps(Image), 5) == ".JPEG" )
|
||||
{
|
||||
Response.SendStandardHeaders("image/jpeg", true);
|
||||
}
|
||||
else if( Right(Caps(Image), 4) == ".GIF" )
|
||||
{
|
||||
Response.SendStandardHeaders("image/gif", true);
|
||||
}
|
||||
else if( Right(Caps(Image), 4) == ".BMP" )
|
||||
{
|
||||
Response.SendStandardHeaders("image/bmp", true);
|
||||
}
|
||||
else if( Right(Caps(Image), 4) == ".PNG" )
|
||||
{
|
||||
Response.SendStandardHeaders("image/png", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.SendStandardHeaders("application/octet-stream", true);
|
||||
}
|
||||
Response.IncludeBinaryFile( Path $ Image );
|
||||
}
|
||||
|
113
IpDrv/Classes/InternetLink.uc
Normal file
113
IpDrv/Classes/InternetLink.uc
Normal file
@ -0,0 +1,113 @@
|
||||
/*=============================================================================
|
||||
// InternetLink: Parent class for Internet connection classes
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
class InternetLink extends Info
|
||||
native
|
||||
transient;
|
||||
|
||||
cpptext
|
||||
{
|
||||
AInternetLink();
|
||||
void BeginDestroy();
|
||||
UBOOL Tick( FLOAT DeltaTime, enum ELevelTick TickType );
|
||||
FSocket* GetSocket()
|
||||
{
|
||||
return Socket;
|
||||
}
|
||||
FSocket* GetRemoteSocket()
|
||||
{
|
||||
return RemoteSocket;
|
||||
}
|
||||
FResolveInfo*& GetResolveInfo()
|
||||
{
|
||||
return *(FResolveInfo**)&PrivateResolveInfo;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Types & Variables.
|
||||
|
||||
// Data receive mode.
|
||||
// Cannot be set in default properties.
|
||||
var enum ELinkMode
|
||||
{
|
||||
MODE_Text,
|
||||
MODE_Line,
|
||||
MODE_Binary
|
||||
} LinkMode;
|
||||
|
||||
// MODE_Line behavior, how to receive/send lines
|
||||
var enum ELineMode
|
||||
{
|
||||
LMODE_auto,
|
||||
LMODE_DOS, // CRLF
|
||||
LMODE_UNIX, // LF
|
||||
LMODE_MAC, // LFCR
|
||||
} InLineMode, OutLineMode; // OutLineMode: LMODE_auto == LMODE_DOS
|
||||
|
||||
// Internal
|
||||
var const pointer Socket{FSocket}; // (sockets are 64-bit on AMD64, so use "pointer").
|
||||
var const int Port;
|
||||
var const pointer RemoteSocket{FSocket};
|
||||
var private native const pointer PrivateResolveInfo;
|
||||
var const int DataPending;
|
||||
|
||||
// Receive mode.
|
||||
// If mode is MODE_Manual, received events will not be called.
|
||||
// This means it is your responsibility to check the DataPending
|
||||
// var and receive the data.
|
||||
// Cannot be set in default properties.
|
||||
var enum EReceiveMode
|
||||
{
|
||||
RMODE_Manual,
|
||||
RMODE_Event
|
||||
} ReceiveMode;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Natives.
|
||||
|
||||
// Returns true if data is pending on the socket.
|
||||
native function bool IsDataPending();
|
||||
|
||||
// Parses an Unreal URL into its component elements.
|
||||
// Returns false if the URL was invalid.
|
||||
native function bool ParseURL
|
||||
(
|
||||
coerce string URL,
|
||||
out string Addr,
|
||||
out int PortNum,
|
||||
out string LevelName,
|
||||
out string EntryName
|
||||
);
|
||||
|
||||
// Resolve a domain or dotted IP.
|
||||
// Nonblocking operation.
|
||||
// Triggers Resolved event if successful.
|
||||
// Triggers ResolveFailed event if unsuccessful.
|
||||
native function Resolve( coerce string Domain );
|
||||
|
||||
// Returns most recent winsock error.
|
||||
native function int GetLastError();
|
||||
|
||||
// Convert an IP address to a string.
|
||||
native function string IpAddrToString( IpAddr Arg );
|
||||
|
||||
// Convert a string to an IP
|
||||
native function bool StringToIpAddr( string Str, out IpAddr Addr );
|
||||
|
||||
native function GetLocalIP(out IpAddr Arg );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Events.
|
||||
|
||||
// Called when domain resolution is successful.
|
||||
// The IpAddr struct Addr contains the valid address.
|
||||
event Resolved( IpAddr Addr );
|
||||
|
||||
// Called when domain resolution fails.
|
||||
event ResolveFailed();
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
49
IpDrv/Classes/MCPBase.uc
Normal file
49
IpDrv/Classes/MCPBase.uc
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a base class for commonly needed MCP functions
|
||||
*/
|
||||
class MCPBase extends McpServiceBase
|
||||
native
|
||||
abstract
|
||||
inherits(FTickableObject)
|
||||
config(Engine);
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
|
||||
/**
|
||||
* Returns whether it is okay to tick this object. E.g. objects being loaded in the background shouldn't be ticked
|
||||
* till they are finalized and unreachable objects cannot be ticked either.
|
||||
*
|
||||
* @return TRUE if tickable, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL IsTickable() const
|
||||
{
|
||||
// We cannot tick objects that are unreachable or are in the process of being loaded in the background.
|
||||
return !HasAnyFlags( RF_Unreachable | RF_AsyncLoading );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if an object should be ticked when the game is paused.
|
||||
*
|
||||
* @return always TRUE as networking needs to be ticked even when paused
|
||||
*/
|
||||
virtual UBOOL IsTickableWhenPaused() const
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs to be overridden by child classes
|
||||
*
|
||||
* @param ignored
|
||||
*/
|
||||
virtual void Tick(FLOAT)
|
||||
{
|
||||
check(0 && "Implement this in child classes");
|
||||
}
|
||||
}
|
295
IpDrv/Classes/McpClashMobBase.uc
Normal file
295
IpDrv/Classes/McpClashMobBase.uc
Normal file
@ -0,0 +1,295 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for ClashMob Mcp services
|
||||
*/
|
||||
class McpClashMobBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String McpClashMobClassName;
|
||||
|
||||
enum McpChallengeFileStatus
|
||||
{
|
||||
/** No action yet */
|
||||
MCFS_NotStarted,
|
||||
/** Cache load or web download pending */
|
||||
MCFS_Pending,
|
||||
/** Data was successfully loaded */
|
||||
MCFS_Success,
|
||||
/** Data was not successfully loaded */
|
||||
MCFS_Failed
|
||||
};
|
||||
|
||||
/** Single file entry that can be downloaded for a challenge */
|
||||
struct McpClashMobChallengeFile
|
||||
{
|
||||
// JSON imported properties
|
||||
var bool should_keep_post_challenge;
|
||||
var string title_id;
|
||||
var string file_name;
|
||||
var string dl_name;
|
||||
var string hash_code;
|
||||
var string type;
|
||||
|
||||
/** Status of file load/download */
|
||||
var McpChallengeFileStatus Status;
|
||||
};
|
||||
|
||||
/** Parameters of a push notification */
|
||||
struct McpClashMobPushNotificationParams
|
||||
{
|
||||
// JSON imported properties
|
||||
var int bah;
|
||||
};
|
||||
|
||||
/* Info about notifications that will be triggered for a challenge */
|
||||
struct McpClashMobPushNotification
|
||||
{
|
||||
// JSON imported properties
|
||||
var array<string> device_tokens;
|
||||
var string badge_type;
|
||||
var string sound;
|
||||
var string message;
|
||||
var McpClashMobPushNotificationParams params;
|
||||
};
|
||||
|
||||
/** Single challenge event that is queried from the server */
|
||||
struct McpClashMobChallengeEvent
|
||||
{
|
||||
// JSON imported properties
|
||||
var string unique_challenge_id;
|
||||
var string visible_date;
|
||||
var string start_date;
|
||||
var string end_date;
|
||||
var string completed_date;
|
||||
var string purge_date;
|
||||
var string challenge_type;
|
||||
var int num_attempts;
|
||||
var int num_successful_attempts;
|
||||
var int goal_value;
|
||||
var int goal_start_value;
|
||||
var int goal_current_value;
|
||||
var bool has_started;
|
||||
var bool is_visible;
|
||||
var bool has_completed;
|
||||
var bool was_successful;
|
||||
var array<McpClashMobChallengeFile> file_list;
|
||||
var int facebook_likes;
|
||||
var int facebook_comments;
|
||||
var float facebook_like_scaler;
|
||||
var float facebook_comment_scaler;
|
||||
var int facebook_like_goal_progress;
|
||||
var int facebook_comment_goal_progress;
|
||||
var string facebook_id;
|
||||
var int twitter_retweets;
|
||||
var float twitter_retweets_scaler;
|
||||
var int twitter_goal_progress;
|
||||
var string twitter_id;
|
||||
};
|
||||
|
||||
/** Current user status with respect to a single challenge event that is queried from the server */
|
||||
struct McpClashMobChallengeUserStatus
|
||||
{
|
||||
// JSON imported properties
|
||||
var string unique_challenge_id;
|
||||
var string unique_user_id;
|
||||
var int num_attempts;
|
||||
var int num_successful_attempts;
|
||||
var int goal_progress;
|
||||
var bool did_complete;
|
||||
var string last_update_time;
|
||||
var int user_award_given;
|
||||
var string accept_time;
|
||||
var bool did_preregister;
|
||||
var string facebook_like_time;
|
||||
var bool enrolled_via_facebook;
|
||||
var bool liked_via_facebook;
|
||||
var bool commented_via_facebook;
|
||||
var string twitter_retweet_time;
|
||||
var bool enrolled_via_twitter;
|
||||
var bool retweeted;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpClashMobBase CreateInstance()
|
||||
{
|
||||
local class<McpClashMobBase> McpClashMobBaseClass;
|
||||
local McpClashMobBase NewInstance;
|
||||
|
||||
McpClashMobBaseClass = class<McpClashMobBase>(DynamicLoadObject(default.McpClashMobClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpClashMobBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpClashMobBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate called when the challenge list query has completed
|
||||
*
|
||||
* @param bWasSuccessful true if the challenge list was retrieved from the server
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnQueryChallengeListComplete(bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Initiates a web request to retrieve the list of available challenge events from the server.
|
||||
*/
|
||||
function QueryChallengeList();
|
||||
|
||||
/**
|
||||
* Access the currently cached challenge list that was downloaded. Use QueryChallengeList first
|
||||
*
|
||||
* @param OutChallengeEvents the list of events that should be filled in
|
||||
*/
|
||||
function GetChallengeList(out array<McpClashMobChallengeEvent> OutChallengeEvents);
|
||||
|
||||
/**
|
||||
* Delegate called when a single challenge file is loaded/downloaded
|
||||
*
|
||||
* @param bWasSuccessful true if the file was loaded from cache or downloaded from server
|
||||
* @param UniqueChallengeId id of challenge that owns the file
|
||||
* @param DlName download name of the file
|
||||
* @param FileName logical name of the file
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnDownloadChallengeFileComplete(bool bWasSuccessful, string UniqueChallengeId, string DlName, string FileName, string Error);
|
||||
|
||||
/**
|
||||
* Get the list of files for a given challenge
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge that may have files
|
||||
* @param OutChallengeFiles list of files that should be filled in
|
||||
*/
|
||||
function GetChallengeFileList(string UniqueChallengeId, out array<McpClashMobChallengeFile> OutChallengeFiles);
|
||||
|
||||
/**
|
||||
* Starts the load/download of a challenge file
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge that owns the file
|
||||
* @param DlName download name of the file
|
||||
*/
|
||||
function DownloadChallengeFile(string UniqueChallengeId, string DlName);
|
||||
|
||||
/**
|
||||
* Access the cached copy of the file data
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge that owns the file
|
||||
* @param DlName download name of the file
|
||||
* @param OutFileContents byte array filled in with the file contents
|
||||
*/
|
||||
function GetChallengeFileContents(string UniqueChallengeId, string DlName, out array<byte> OutFileContents);
|
||||
|
||||
/**
|
||||
* Clear the cached memory copy of a challenge file
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge that owns the file
|
||||
* @param DlName download name of the file
|
||||
*/
|
||||
function ClearCachedChallengeFile(string UniqueChallengeId, string DlName);
|
||||
|
||||
/**
|
||||
* Clear the cached memory copy of a challenge file
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge that owns the file
|
||||
* @param DlName download name of the file
|
||||
*/
|
||||
function DeleteCachedChallengeFile(string UniqueChallengeId, string DlName);
|
||||
|
||||
/**
|
||||
* Called when the web request to have user accept a challenge completes
|
||||
*
|
||||
* @param bWasSuccessful true if request completed successfully
|
||||
* @param UniqueChallengeId id of challenge to accept
|
||||
* @param UniqueUserId id of user that wants to accept challenge
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnAcceptChallengeComplete(bool bWasSuccessful, string UniqueChallengeId, string UniqueUserId, String Error);
|
||||
|
||||
/**
|
||||
* Initiates a web request to have user accept a challenge
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to accept
|
||||
* @param UniqueUserId id of user that wants to accept challenge
|
||||
*/
|
||||
function AcceptChallenge(string UniqueChallengeId, string UniqueUserId);
|
||||
|
||||
/**
|
||||
* Called when the web request to retrieve the current status of a challenge for a user completes
|
||||
*
|
||||
* @param bWasSuccessful true if request completed successfully
|
||||
* @param UniqueChallengeId id of challenge to query
|
||||
* @param UniqueUserId id of user to retrieve challenge status for
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnQueryChallengeUserStatusComplete(bool bWasSuccessful, string UniqueChallengeId, string UniqueUserId, String Error);
|
||||
|
||||
/**
|
||||
* Initiates a web request to retrieve the current status of a challenge for a user
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to query
|
||||
* @param UniqueUserId id of user to retrieve challenge status for
|
||||
*/
|
||||
function QueryChallengeUserStatus(string UniqueChallengeId, string UniqueUserId);
|
||||
|
||||
/**
|
||||
* Initiates a web request to retrieve the current status of a challenge for a list of users user
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to query
|
||||
* @param UniqueUserId id of user that is initiating the request
|
||||
* @param UserIdsToRead list of ids to read status for
|
||||
*/
|
||||
function QueryChallengeMultiUserStatus(string UniqueChallengeId, string UniqueUserId, const out array<string> UserIdsToRead);
|
||||
|
||||
/**
|
||||
* Get the cached status of a user for a challenge. Use QueryChallengeUserStatus first
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to retrieve
|
||||
* @param UniqueUserId id of user to retrieve challenge status for
|
||||
* @param OutChallengeUserStatus user status values to be filled in
|
||||
*/
|
||||
function GetChallengeUserStatus(string UniqueChallengeId, string UniqueUserId, out McpClashMobChallengeUserStatus OutChallengeUserStatus);
|
||||
|
||||
/**
|
||||
* Called when the web request to update the current progress of a challenge for a user completes
|
||||
*
|
||||
* @param bWasSuccessful true if request completed successfully
|
||||
* @param UniqueChallengeId id of challenge to update
|
||||
* @param UniqueUserId id of user to update challenge progress for
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnUpdateChallengeUserProgressComplete(bool bWasSuccessful, string UniqueChallengeId, string UniqueUserId, String Error);
|
||||
|
||||
/**
|
||||
* Initiates a web request to update the current progress of a challenge for a user
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to update
|
||||
* @param UniqueUserId id of user to update challenge progress for
|
||||
*/
|
||||
function UpdateChallengeUserProgress(string UniqueChallengeId, string UniqueUserId, bool bDidComplete, int GoalProgress);
|
||||
|
||||
/**
|
||||
* Called when the web request to update the current reward of a challenge for a user completes
|
||||
*
|
||||
* @param bWasSuccessful true if request completed successfully
|
||||
* @param UniqueChallengeId id of challenge to update
|
||||
* @param UniqueUserId id of user to update challenge progress for
|
||||
* @param Error info string about why the error occurred
|
||||
*/
|
||||
delegate OnUpdateChallengeUserRewardComplete(bool bWasSuccessful, string UniqueChallengeId, string UniqueUserId, String Error);
|
||||
|
||||
/**
|
||||
* Initiates a web request to update the current reward given to a user for a challenge
|
||||
*
|
||||
* @param UniqueChallengeId id of challenge to update
|
||||
* @param UniqueUserId id of user to update challenge progress for
|
||||
*/
|
||||
function UpdateChallengeUserReward(string UniqueChallengeId, string UniqueUserId, int UserReward);
|
||||
|
25
IpDrv/Classes/McpClashMobFileDownload.uc
Normal file
25
IpDrv/Classes/McpClashMobFileDownload.uc
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for ClashMob Mcp services
|
||||
*/
|
||||
class McpClashMobFileDownload extends OnlineTitleFileDownloadWeb
|
||||
config(Engine);
|
||||
|
||||
/**
|
||||
* Build the clashmob specific Url for downloading a given file
|
||||
*
|
||||
* @param FileName the file to search the table for
|
||||
*
|
||||
* @param the URL to use to request the file or BaseURL if no special mapping is present
|
||||
*/
|
||||
function string GetUrlForFile(string FileName)
|
||||
{
|
||||
local string Url;
|
||||
|
||||
Url = GetBaseURL() $ RequestFileURL $
|
||||
"?appKey=" $ McpConfig.AppKey $ "&appSecret=" $ McpConfig.AppSecret $
|
||||
"&dlName=";
|
||||
|
||||
return Url;
|
||||
}
|
1279
IpDrv/Classes/McpClashMobManager.uc
Normal file
1279
IpDrv/Classes/McpClashMobManager.uc
Normal file
File diff suppressed because it is too large
Load Diff
298
IpDrv/Classes/McpGroupsBase.uc
Normal file
298
IpDrv/Classes/McpGroupsBase.uc
Normal file
@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for user groups and the factory method
|
||||
* for creating the registered implementing object
|
||||
*/
|
||||
class McpGroupsBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config string McpGroupsManagerClassName;
|
||||
|
||||
/**
|
||||
* Enum for whether or not a member has accepted a group invite
|
||||
*/
|
||||
enum EMcpGroupAcceptState
|
||||
{
|
||||
/** Member has rejected the invite to the group, or there has been an error. */
|
||||
MGAS_Error,
|
||||
|
||||
/** Member has not yet responded to the group invite. */
|
||||
MGAS_Pending,
|
||||
|
||||
/** Member has accepted the group invite. */
|
||||
MGAS_Accepted
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum for the Access Level of the group
|
||||
*/
|
||||
enum EMcpGroupAccessLevel
|
||||
{
|
||||
/** Only Owners have permission to make changes to this group */
|
||||
MGAL_Owner,
|
||||
|
||||
/** Members and Owners have permission to make changes to this group */
|
||||
MGAL_Member,
|
||||
|
||||
/** Everyone has permission to make changes to this group */
|
||||
MGAL_Public
|
||||
};
|
||||
|
||||
/**
|
||||
* Group Member
|
||||
*/
|
||||
struct McpGroupMember
|
||||
{
|
||||
/** Member Id */
|
||||
var String MemberId;
|
||||
|
||||
/** Member's Accept State to the group */
|
||||
var EMcpGroupAcceptState AcceptState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
struct McpGroup
|
||||
{
|
||||
/** The User Id of the owner of the group */
|
||||
var String OwnerId;
|
||||
|
||||
/** Unique Group Id */
|
||||
var String GroupId;
|
||||
|
||||
/** Human Readable Name of the group.*/
|
||||
var String GroupName;
|
||||
|
||||
/**
|
||||
* Access level defining who can make modifications to the group.
|
||||
* Note: Access is not restricted on the server, it is up to those using the API to enforce these permissions.
|
||||
*/
|
||||
var EMcpGroupAccessLevel AccessLevel;
|
||||
|
||||
/** Collection of users who are members of the group */
|
||||
var array<McpGroupMember> Members;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* List of Groups belonging to one user
|
||||
*/
|
||||
struct McpGroupList
|
||||
{
|
||||
/** User that requested the list of groups */
|
||||
var string RequesterId;
|
||||
|
||||
/** Collection of groups that the user owns OR belongs to */
|
||||
var array<McpGroup> Groups;
|
||||
|
||||
};
|
||||
|
||||
/** Holds the groups for each user in memory */
|
||||
var array<McpGroupList> GroupLists;
|
||||
|
||||
/**
|
||||
* Create Instance of an McpGroup
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
static final function McpGroupsBase CreateInstance()
|
||||
{
|
||||
local class<McpGroupsBase> McpGroupsManagerClass;
|
||||
local McpGroupsBase NewInstance;
|
||||
|
||||
McpGroupsManagerClass = class<McpGroupsBase>(DynamicLoadObject(default.McpGroupsManagerClassName,class'Class'));
|
||||
if (McpGroupsManagerClass != None)
|
||||
{
|
||||
NewInstance = new McpGroupsManagerClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the URL and sends the request to create a group.
|
||||
*
|
||||
* @param UniqueUserId the UserId that will own the group
|
||||
* @param GroupName the name the group will be created with
|
||||
*/
|
||||
function CreateGroup(String OwnerId, String GroupName);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param GroupName the name of the group
|
||||
* @param GroupId the group id of the group that was created
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnCreateGroupComplete(McpGroup Group, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Deletes a Group by GroupId
|
||||
*
|
||||
* @param UniqueUserId UserId of the owner of the Group
|
||||
* @param GroupId Id of the group
|
||||
*/
|
||||
function DeleteGroup(String UniqueUserId, String GroupId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param GroupId the group id of the group that was Deleted
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteGroupComplete(String GroupId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Queries the backend for the Groups belonging to the supplied UserId
|
||||
*
|
||||
* @param UniqueUserId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryGroups(String RequesterId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param UserId the user id of the groups that were queried
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryGroupsComplete(string UserId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of groups that belonging to the specified UserId
|
||||
* Called after QueryGroups.
|
||||
*
|
||||
* @param UserId the request object that was used
|
||||
* @param GroupList the response object that was generated
|
||||
*/
|
||||
function GetGroupList(string UserId, out McpGroupList GroupList);
|
||||
|
||||
/**
|
||||
* Queries the back-end for the Groups Members belonging to the specified group
|
||||
*
|
||||
* @param UniqueUserId the id of the owner of the group being queried
|
||||
* @param GroupId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryGroupMembers(String UniqueUserId, String GroupId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param GroupId the id of the group from which the members were queried
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryGroupMembersComplete(String GroupId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of Group Members that belong to the specified GroupId
|
||||
* Called after QueryGroupMembers.
|
||||
*
|
||||
* @param GroupId the request object that was used
|
||||
* @param GroupList the response object that was generated
|
||||
*/
|
||||
function GetGroupMembers(String GroupId, out array<McpGroupMember> GroupMembers);
|
||||
|
||||
/**
|
||||
* Adds an Group Members to the specified Group. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param UniqueUserId the user that owns the group
|
||||
* @param GroupId the group id
|
||||
* @param MemberIds list of member ids to add to the group
|
||||
* @param bRequiresAcceptance whether or not members need to accept an invitation to a group
|
||||
*/
|
||||
function AddGroupMembers(String OwnerId, String GroupId, const out array<String> MemberIds, bool bRequiresAcceptance);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param GroupId the id of the group to which the members were added
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnAddGroupMembersComplete(String GroupId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Remove Group Members from the specified Group. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param UniqueUserId the user that owns the group
|
||||
* @param GroupId the group id
|
||||
* @param MemberIds list of member ids to add to the group
|
||||
*/
|
||||
function RemoveGroupMembers(String OwnerId, String GroupId, const out array<String> MemberIds);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param GroupId the id of the group from which the members were removed
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnRemoveGroupMembersComplete(String GroupId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Deletes all Groups that belong to UniqueUserId
|
||||
*
|
||||
* @param UniqueUserId UserId of the owner of the Group
|
||||
*/
|
||||
function DeleteAllGroups(String OwnerId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteAllGroupsComplete(String RequesterId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Queries the backend for the Pending Group Invites belonging to the supplied UserId
|
||||
*
|
||||
* @param UniqueUserId the id of the user to query invites for
|
||||
*/
|
||||
function QueryGroupInvites(String UniqueUserId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryGroupInvitesComplete(bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of Pending Group Invites that belong to the specified UserId
|
||||
* Called after QueryGroupInvites.
|
||||
*
|
||||
* @param UserId the request object that was used
|
||||
* @param GroupList the response object that was generated
|
||||
*/
|
||||
function GetGroupInviteList(string UserId, out McpGroupList InviteList);
|
||||
|
||||
/**
|
||||
* Set's the a Member's membership status to Accept or Reject
|
||||
* based on the value of bShouldAccept
|
||||
*
|
||||
* @param UniqueUserId User who's status is to be update
|
||||
* @param GroupId
|
||||
* @param bShouldAccept 1 = accepted 0 = rejected
|
||||
*/
|
||||
function AcceptGroupInvite(String UniqueUserId, String GroupId, bool bShouldAccept);
|
||||
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the add mapping result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
delegate OnAcceptGroupInviteComplete(String GroupId, bool bWasSuccessful, String Error);
|
943
IpDrv/Classes/McpGroupsManager.uc
Normal file
943
IpDrv/Classes/McpGroupsManager.uc
Normal file
@ -0,0 +1,943 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Concrete implementation for managing groups through the groups web API
|
||||
*/
|
||||
class McpGroupsManager extends McpGroupsBase
|
||||
config(Engine);
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci)
|
||||
|
||||
/** The URL of CreateGroup function on the server */
|
||||
var config string CreateGroupUrl;
|
||||
|
||||
/** The URL of DeleteGroup function on the server */
|
||||
var config string DeleteGroupUrl;
|
||||
|
||||
/** The URL of ListGroups function on the server */
|
||||
var config string QueryGroupsUrl;
|
||||
|
||||
/** The URL of ListGroupMembers function on the server */
|
||||
var config string QueryGroupMembersUrl;
|
||||
|
||||
/** The URL of AddGroupMembers function on the server */
|
||||
var config string AddGroupMembersUrl;
|
||||
|
||||
/** The URL of RemoveGroupMembers function on the server */
|
||||
var config string RemoveGroupMembersUrl;
|
||||
|
||||
/** The URL of DeleteAllGroups function on the server */
|
||||
var config string DeleteAllGroupsUrl;
|
||||
|
||||
/** The URL of AcceptGroupInvite function on the server */
|
||||
var config string AcceptGroupInviteUrl;
|
||||
|
||||
/** The URL of RejectGroupInvite function on the server */
|
||||
var config string RejectGroupInviteUrl;
|
||||
|
||||
/**
|
||||
* Creates the URL and sends the request to create a group.
|
||||
* - This updates the group on the server QueryGroups will need to be
|
||||
* - run again before GetGroups will reflect the this new group.
|
||||
* @param UniqueUserId the UserId that will own the group
|
||||
* @param GroupName The name the group will be created with
|
||||
*/
|
||||
function CreateGroup(String UniqueUserId, String GroupName)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface CreateGroupRequest;
|
||||
local McpGroup FailedGroup;
|
||||
|
||||
//Ensure that UniqueUserId and GroupName both have values
|
||||
if(Len(UniqueUserId) > 0 && Len(GroupName) > 0)
|
||||
{
|
||||
// Create HttpRequest
|
||||
CreateGroupRequest = class'HttpFactory'.static.CreateRequest();
|
||||
|
||||
if(CreateGroupRequest != none)
|
||||
{
|
||||
// Fill url out using parameters
|
||||
Url = GetBaseURL() $ CreateGroupUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ UniqueUserId $
|
||||
"&groupName=" $ GroupName $
|
||||
"&accessLevel=" $ "OWNER";
|
||||
|
||||
// Build our web request with the above URL
|
||||
CreateGroupRequest.SetURL(URL);
|
||||
CreateGroupRequest.SetVerb("POST");
|
||||
|
||||
CreateGroupRequest.OnProcessRequestComplete = OnCreateGroupRequestComplete;
|
||||
|
||||
// Call Web Request
|
||||
if (!CreateGroupRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log(`Location $ " URL is " $ Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnCreateGroupComplete(FailedGroup, false, "HttpRequest was not be created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnCreateGroupComplete(FailedGroup, false, "UserId or GroupName wasn't specified");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed.
|
||||
* Used to return any errors and notify any registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnCreateGroupRequestComplete(HttpRequestInterface CreateGroupRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local McpGroup CreatedGroup;
|
||||
local String JsonString;
|
||||
local JsonObject ParsedJson;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none && CreateGroupRequest != none)
|
||||
{
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_CREATED;
|
||||
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
|
||||
//Set default parameters in case create was not successful
|
||||
CreatedGroup.OwnerId = CreateGroupRequest.GetURLParameter("uniqueUserId");
|
||||
CreatedGroup.GroupName = CreateGroupRequest.GetURLParameter("groupName");
|
||||
JsonString = HttpResponse.GetContentAsString();
|
||||
if (JsonString != "" && bWasSuccessful)
|
||||
{
|
||||
// Parse the json
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
CreatedGroup.GroupId = ParsedJson.GetStringValue("group_id");
|
||||
CreatedGroup.OwnerId = ParsedJson.GetStringValue("unique_user_id");
|
||||
CreatedGroup.GroupName = ParsedJson.GetStringValue("group_name");
|
||||
CreatedGroup.AccessLevel = EMcpGroupAccessLevel(ParsedJson.GetIntValue("access_level") );
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`Location@" CreateGroup query did not return a group.");
|
||||
}
|
||||
}
|
||||
OnCreateGroupComplete(CreatedGroup, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a Group by GroupId
|
||||
*
|
||||
* @param UniqueUserId UserId of the owner of the Group
|
||||
* @param GroupId Id of the group
|
||||
*/
|
||||
function DeleteGroup(String UniqueUserId, String GroupId)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface DeleteGroupRequest;
|
||||
|
||||
//Ensure that UniqueUserId and GroupId both have values
|
||||
if(Len(UniqueUserId) > 0 && Len(GroupId) > 0)
|
||||
{
|
||||
// Delete HttpRequest
|
||||
DeleteGroupRequest = class'HttpFactory'.static.CreateRequest();
|
||||
|
||||
if(DeleteGroupRequest != none)
|
||||
{
|
||||
// Fill url out using parameters
|
||||
Url = GetBaseURL() $ DeleteGroupUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ UniqueUserId $
|
||||
"&groupId=" $ GroupId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
DeleteGroupRequest.SetVerb("DELETE");
|
||||
DeleteGroupRequest.SetURL(URL);
|
||||
DeleteGroupRequest.OnProcessRequestComplete = OnDeleteGroupRequestComplete;
|
||||
|
||||
// call WebRequest
|
||||
if(!DeleteGroupRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log(`Location $ "URL is " $ Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDeleteGroupComplete(GroupId, false, "HttpRequest could not be completed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDeleteGroupComplete(GroupId, false, "UniqueUserId and/or GroupId was not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed.
|
||||
* Used to process the response and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnDeleteGroupRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local String GroupId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none)
|
||||
{
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
GroupId = HttpResponse.GetURLParameter("GroupId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnDeleteGroupComplete(GroupId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queries the backend for the Groups belonging to the supplied UserId
|
||||
*
|
||||
* @param UniqueUserId the id of the owner of the groups to return
|
||||
*/
|
||||
//Change to be not just owner but all groups userId belongs to
|
||||
function QueryGroups(String RequesterId)
|
||||
{
|
||||
// Cache one group result set per user, instead of only having one array<McpGroup> that gets over
|
||||
local string Url;
|
||||
local HttpRequestInterface QueryGroupsRequest;
|
||||
|
||||
//Ensure that UniqueUserId and GroupName both have values
|
||||
if(Len(RequesterId) > 0 )
|
||||
{
|
||||
// List HttpRequest
|
||||
QueryGroupsRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (QueryGroupsRequest != none)
|
||||
{
|
||||
// Fill it out using parameters
|
||||
// The server takes one of two parameters
|
||||
// - uniqueUserId will return only groups owned by the user
|
||||
// - memberUniqueUserId will return all groups that have the user as a member (including groups owned by that user)
|
||||
Url = GetBaseURL() $ QueryGroupsUrl $ GetAppAccessURL() $
|
||||
"&memberUniqueUserId=" $ RequesterId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
QueryGroupsRequest.SetURL(URL);
|
||||
QueryGroupsRequest.SetVerb("GET");
|
||||
QueryGroupsRequest.OnProcessRequestComplete = OnQueryGroupsRequestComplete;
|
||||
|
||||
// Call Web Request
|
||||
if(!QueryGroupsRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryGroupsComplete(RequesterId, false, "Http Request could not be created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryGroupsComplete(RequesterId, false, "RequesterId was not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
delegate OnQueryGroupsRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Error;
|
||||
local McpGroup Group;
|
||||
local string JsonString;
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local string RequesterId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
// Both HttpResponse and OriginalRequest need to be present
|
||||
if (HttpResponse != none && OriginalRequest != none)
|
||||
{
|
||||
RequesterId = OriginalRequest.GetURLParameter("memberUniqueUserId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
JsonString = HttpResponse.GetContentAsString();
|
||||
if (JsonString != "")
|
||||
{
|
||||
// Parse the json
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
// Add each mapping in the json packet if missing
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
Group.OwnerId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("unique_user_id");
|
||||
Group.GroupId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("group_id");
|
||||
Group.GroupName = ParsedJson.ObjectArray[JsonIndex].GetStringValue("group_name");
|
||||
Group.AccessLevel = EMcpGroupAccessLevel(ParsedJson.ObjectArray[JsonIndex].GetIntValue("access_level") );
|
||||
|
||||
CacheGroup(RequesterId, Group);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = "Query did not return any content in it's response.";
|
||||
`log(`Location $ Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = HttpResponse.GetContentAsString();
|
||||
}
|
||||
}
|
||||
OnQueryGroupsComplete(RequesterId, bWasSuccessful, Error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of groups that related to the specified UserId
|
||||
* Called after QueryGroups, which fills/updates the GroupLists variable with the response from the server.
|
||||
* To check for ownership compare the RequesterId to the OwnerId of the group.
|
||||
*
|
||||
* @param UserId the request object that was used
|
||||
* @param GroupList the response object that was generated
|
||||
*/
|
||||
function GetGroupList(string UserId, out McpGroupList GroupList)
|
||||
{
|
||||
local int GroupListIndex;
|
||||
|
||||
// Make sure that userId is valid
|
||||
if( Len(UserId) > 0 )
|
||||
{
|
||||
// Check Cache Variable GroupLists
|
||||
GroupListIndex = GroupLists.Find('RequesterId', UserId);
|
||||
if(GroupListIndex != INDEX_NONE)
|
||||
{
|
||||
GroupList = GroupLists[GroupListIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location $ " Requester Id not found or GroupLists is empty. Using UserId: " $ UserId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location $ "UserId not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the backend for the Groups Members belonging to the specified group
|
||||
*
|
||||
* After the Query is returned from the server the data is stored in a local variable GroupLists
|
||||
* In order to fully fill this variable for a given user you need to run QueryGroups AND QueryGroupMembers.
|
||||
* Since there are potentially many more GroupMembers than Groups it saves bandwidth to only Query what you need
|
||||
*
|
||||
* @param UniqueUserId the id of the owner of the group being queried
|
||||
* @param GroupId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryGroupMembers(String UniqueUserId, String GroupId)
|
||||
{
|
||||
// Cache one group result set per user, instead of only having one array<McpGroup> that gets over
|
||||
local string Url;
|
||||
local HttpRequestInterface QueryGroupMembersRequest;
|
||||
|
||||
|
||||
// Make sure that UniqueUserId and GroupId have been given
|
||||
if( Len(UniqueUserId) > 0 && Len(GroupId) > 0)
|
||||
{
|
||||
// List HttpRequest
|
||||
QueryGroupMembersRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (QueryGroupMembersRequest != none)
|
||||
{
|
||||
//Create URL parameters
|
||||
Url = GetBaseURL() $ QueryGroupMembersUrl $ GetAppAccessURL() $
|
||||
"&groupId=" $ GroupId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
QueryGroupMembersRequest.SetURL(URL);
|
||||
QueryGroupMembersRequest.SetVerb("GET");
|
||||
QueryGroupMembersRequest.OnProcessRequestComplete = OnQueryGroupMembersRequestComplete;
|
||||
|
||||
// Call WebRequest
|
||||
if(!QueryGroupMembersRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryGroupMembersComplete(GroupId, false, "HttpRequest not created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryGroupMembersComplete(GroupId, false, "UserId and/or GroupId not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
delegate OnQueryGroupMembersRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Error;
|
||||
local string JsonString;
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local EMcpGroupAcceptState AcceptState;
|
||||
local string MemberId;
|
||||
local string GroupId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
|
||||
// Both HttpResponse and OriginalRequest need to be present
|
||||
if (HttpResponse != none && OriginalRequest != none)
|
||||
{
|
||||
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
JsonString = HttpResponse.GetContentAsString();
|
||||
if (JsonString != "")
|
||||
{
|
||||
// @todo joeg - Replace with Wes' ImportJson() once it's implemented
|
||||
// Parse the json
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
// Add each mapping in the json packet if missing
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
MemberId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("unique_user_id");
|
||||
GroupId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("group_id");
|
||||
AcceptState = EMcpGroupAcceptState(ParsedJson.ObjectArray[JsonIndex].GetIntValue("status"));
|
||||
|
||||
CacheGroupMember(MemberId, GroupId, AcceptState);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = "Query did not return any content in it's response.";
|
||||
`log(`Location $ Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = HttpResponse.GetContentAsString();
|
||||
}
|
||||
}
|
||||
OnQueryGroupMembersComplete(GroupId, bWasSuccessful, Error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of Group Members that belong to the specified GroupId
|
||||
* Called after QueryGroupMembers.
|
||||
*
|
||||
* @param GroupId the request object that was used
|
||||
* @param GroupList the response object that was generated
|
||||
*/
|
||||
function GetGroupMembers(String GroupId, out array<McpGroupMember> GroupMembers)
|
||||
{
|
||||
local int GroupIndex;
|
||||
local McpGroupList GroupList;
|
||||
local McpGroup GroupTemp;
|
||||
|
||||
foreach GroupLists(GroupList)
|
||||
{
|
||||
foreach GroupList.Groups(GroupTemp, GroupIndex)
|
||||
{
|
||||
if(GroupTemp.GroupId == GroupId)
|
||||
{
|
||||
GroupMembers = GroupTemp.Members;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Group Members to the specified Group. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param UniqueUserId the user that owns the group
|
||||
* @param GroupId the group id
|
||||
* @param MemberIds list of member ids to add to the group
|
||||
* @param bRequiresAcceptance whether or not members need to accept an invitation to a group
|
||||
*/
|
||||
function AddGroupMembers(String UniqueUserId, String GroupId, const out array<String> MemberIds, bool bRequiresAcceptance)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface AddGroupMembersRequest;
|
||||
local string JsonPayload;
|
||||
local int Index;
|
||||
|
||||
|
||||
// Make sure that UniqueUserId and GroupId have been given
|
||||
if( Len(UniqueUserId) > 0 && Len(GroupId) > 0)
|
||||
{
|
||||
// Create HttpRequest
|
||||
AddGroupMembersRequest = class'HttpFactory'.static.CreateRequest();
|
||||
AddGroupMembersRequest.OnProcessRequestComplete = OnAddGroupMembersRequestComplete;
|
||||
if(AddGroupMembersRequest != none)
|
||||
{
|
||||
Url = GetBaseURL() $ AddGroupMembersUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ UniqueUserId $
|
||||
"&groupId=" $ GroupId $
|
||||
"&requiresAcceptance=" $ bRequiresAcceptance ? "true" : "false";
|
||||
// If bRequiresAcceptance is set to false then the group member will be created with a status of Accepted by default
|
||||
// If it is set to true then the status will be set to Pending and the user will need to accept the invite to officially be part of the group
|
||||
|
||||
if(MemberIds.Length > 0)
|
||||
{
|
||||
// Make a json string from our list of ids
|
||||
JsonPayload = "[ ";
|
||||
for (Index = 0; Index < MemberIds.Length; Index++)
|
||||
{
|
||||
JsonPayload $= "\"" $ MemberIds[Index] $ "\"";
|
||||
// Only add the comma to the string if this isn't the last item
|
||||
if (Index + 1 < MemberIds.Length)
|
||||
{
|
||||
JsonPayload $= ",";
|
||||
}
|
||||
}
|
||||
JsonPayload $= " ]";
|
||||
|
||||
// Fill it out using parameters
|
||||
AddGroupMembersRequest.SetVerb("POST");
|
||||
AddGroupMembersRequest.SetContentAsString(JsonPayload);
|
||||
AddGroupMembersRequest.SetURL(URL);
|
||||
|
||||
// Call WebRequest
|
||||
if (!AddGroupMembersRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log(`Location@"URL(" $ Url $ ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location@" No MemberIds given.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAddGroupMembersComplete(GroupId, false, "HttpRequest was not created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAddGroupMembersComplete(GroupId, false, "UserId and/or GroupId not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the add mapping result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
delegate OnAddGroupMembersRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local String GroupId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none)
|
||||
{
|
||||
GroupId = HttpResponse.GetURLParameter("GroupId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnAddGroupMembersComplete(GroupId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Group Members from the specified Group. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param UniqueUserId the user that owns the group
|
||||
* @param GroupId the group id
|
||||
* @param MemberIds list of member ids to add to the group
|
||||
*/
|
||||
function RemoveGroupMembers(String UniqueUserId, String GroupId, const out array<String> MemberIds)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface RemoveGroupMembersRequest;
|
||||
local string JsonPayload;
|
||||
local int Index;
|
||||
|
||||
// Make sure that UniqueUserId and GroupId have been given
|
||||
if( Len(UniqueUserId) > 0 && Len(GroupId) > 0)
|
||||
{
|
||||
RemoveGroupMembersRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if(RemoveGroupMembersRequest != none)
|
||||
{
|
||||
|
||||
Url = GetBaseURL() $ RemoveGroupMembersUrl $ GetAppAccessURL() $
|
||||
"&groupId=" $ GroupId;
|
||||
|
||||
if(MemberIds.Length > 0)
|
||||
{
|
||||
// Make a json string from our list of ids
|
||||
JsonPayload = "[ ";
|
||||
for (Index = 0; Index < MemberIds.Length; Index++)
|
||||
{
|
||||
JsonPayload $= "\"" $ MemberIds[Index] $ "\"";
|
||||
// Only add the comma to the string if this isn't the last item
|
||||
if (Index + 1 < MemberIds.Length)
|
||||
{
|
||||
JsonPayload $= ",";
|
||||
}
|
||||
}
|
||||
JsonPayload $= " ]";
|
||||
|
||||
RemoveGroupMembersRequest.SetURL(URL);
|
||||
RemoveGroupMembersRequest.SetContentAsString(JsonPayload);
|
||||
RemoveGroupMembersRequest.SetVerb("DELETE");
|
||||
RemoveGroupMembersRequest.OnProcessRequestComplete = OnRemoveGroupMembersRequestComplete;
|
||||
|
||||
// Call the request
|
||||
if (!RemoveGroupMembersRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location@" No MemberIds given.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnRemoveGroupMembersComplete(GroupId, false, "Http request was not created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnRemoveGroupMembersComplete(GroupId, false, "UniqueUserId and/or GroupId was not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the add mapping result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnRemoveGroupMembersRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local String GroupId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none)
|
||||
{
|
||||
GroupId = HttpResponse.GetURLParameter("GroupId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnRemoveGroupMembersComplete(GroupId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all Groups that belong to UniqueUserId
|
||||
*
|
||||
* @param UniqueUserId UserId of the owner of the Group
|
||||
*/
|
||||
function DeleteAllGroups(String UniqueUserId)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface DeleteGroupRequest;
|
||||
|
||||
// Make sure that UniqueUserId and GroupId have been given
|
||||
if( Len(UniqueUserId) > 0 )
|
||||
{
|
||||
// Delete HttpRequest
|
||||
DeleteGroupRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if(DeleteGroupRequest != none)
|
||||
{
|
||||
|
||||
// Fill it out using parameters
|
||||
DeleteGroupRequest.SetVerb("DELETE");
|
||||
|
||||
Url = GetBaseURL() $ DeleteAllGroupsUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ UniqueUserId;
|
||||
|
||||
DeleteGroupRequest.SetURL(URL);
|
||||
|
||||
`log("URL="@DeleteGroupRequest.GetURL());
|
||||
|
||||
DeleteGroupRequest.OnProcessRequestComplete = OnDeleteGroupRequestComplete;
|
||||
|
||||
// call WebRequest
|
||||
if(!DeleteGroupRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDeleteAllGroupsComplete(UniqueUserId, false, "HttpRequest was not created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDeleteAllGroupsComplete(UniqueUserId, false, "UniqueUserId was not specified");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
function OnDeleteAllGroupsRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local string RequesterId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
// Both HttpResponse and OriginalRequest need to be present
|
||||
if (HttpResponse != none && OriginalRequest != none)
|
||||
{
|
||||
RequesterId = OriginalRequest.GetURLParameter("uniqueUserId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnDeleteAllGroupsComplete(RequesterId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the a Member's membership status to Accept or Reject
|
||||
* based on the value of bShouldAccept
|
||||
*
|
||||
* @param UniqueUserId User who's status is to be update
|
||||
* @param GroupId
|
||||
* @param bShouldAccept 1 = accepted 0 = rejected
|
||||
*/
|
||||
function AcceptGroupInvite(String UniqueUserId, String GroupId, bool bShouldAccept)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface AcceptGroupInviteRequest;
|
||||
|
||||
// Make sure that UniqueUserId and GroupId have been given
|
||||
if( Len(UniqueUserId) > 0 && Len(GroupId) > 0)
|
||||
{
|
||||
// Create HttpRequest
|
||||
AcceptGroupInviteRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if(AcceptGroupInviteRequest != none)
|
||||
{
|
||||
Url = GetBaseURL() $ AcceptGroupInviteUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ UniqueUserId $
|
||||
"&groupId=" $ GroupId $
|
||||
"&status=" $ bShouldAccept ? "accepted" : "rejected";
|
||||
|
||||
// Fill it out using parameters
|
||||
AcceptGroupInviteRequest.SetVerb("POST");
|
||||
AcceptGroupInviteRequest.SetURL(URL);
|
||||
AcceptGroupInviteRequest.OnProcessRequestComplete = OnAcceptGroupInviteRequestComplete;
|
||||
|
||||
// Call WebRequest
|
||||
`log("Calling Process Request");
|
||||
if(!AcceptGroupInviteRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAcceptGroupInviteComplete(GroupId, false, "HttpRequest not created");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAcceptGroupInviteComplete(GroupId, false, "UniqueUserId or GroupId was not specified");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the add mapping result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
delegate OnAcceptGroupInviteRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local String GroupId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none)
|
||||
{
|
||||
GroupId = HttpResponse.GetURLParameter("GroupId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnAcceptGroupInviteComplete(GroupId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*Store group in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param Group to be placed in the cache
|
||||
*/
|
||||
function CacheGroup(string RequesterId, McpGroup Group)
|
||||
{
|
||||
local int AddAt;
|
||||
local int GroupIndex;
|
||||
local int GroupListIndex;
|
||||
local McpGroupList UserGroupList;
|
||||
local bool bWasFound;
|
||||
|
||||
//Find the user's cached group list in collection of users group lists, GroupLists
|
||||
//TODO Is there a better way to do this? Have to rely on the response not being empty, though if it is then we don't do anything so that's fine.
|
||||
// - More importantly we're querying the GroupLists for every entry returned which isn't as efficient.
|
||||
// - Could just set GroupListIndex and then check if it's been set ...
|
||||
bWasFound = false;
|
||||
GroupListIndex = GroupLists.Find('RequesterId', RequesterId);
|
||||
if(GroupListIndex != INDEX_NONE)
|
||||
{
|
||||
UserGroupList = GroupLists[GroupListIndex];
|
||||
|
||||
// Search the array for any existing adding only when missing
|
||||
for (GroupIndex = 0; GroupIndex < UserGroupList.Groups.Length && !bWasFound; GroupIndex++)
|
||||
{
|
||||
bWasFound = Group.GroupId == UserGroupList.Groups[GroupIndex].GroupId;
|
||||
}
|
||||
// Add this one since it wasn't found
|
||||
if (!bWasFound)
|
||||
{
|
||||
AddAt = UserGroupList.Groups.Length;
|
||||
UserGroupList.Groups.Length = AddAt + 1;
|
||||
UserGroupList.Groups[AddAt] = Group;
|
||||
GroupLists[GroupListIndex] = UserGroupList;
|
||||
}
|
||||
|
||||
`log(`Location $ " GroupName: " $ UserGroupList.Groups[AddAt].GroupName);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add User with this first returned group since it wasn't found
|
||||
AddAt = GroupLists.Length;
|
||||
GroupLists.Length = AddAt +1;
|
||||
GroupLists[AddAt].RequesterId = Group.OwnerId;
|
||||
GroupLists[AddAt].Groups[0]=Group;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*Store group member in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param MemberId to be placed in the cache
|
||||
* @param GroupId to be placed in the cache
|
||||
* @param intAcceptState whether the group member's status is accepted, pending or rejected
|
||||
*/
|
||||
function CacheGroupMember(String MemberId, String GroupId, EMcpGroupAcceptState AcceptState)
|
||||
{
|
||||
local int MemberIndex;
|
||||
local McpGroupList GroupList;
|
||||
local int GroupListIndex;
|
||||
local McpGroup GroupTemp;
|
||||
local int GroupIndex;
|
||||
local int AddAt;
|
||||
|
||||
// Have the variables been passed in properly
|
||||
if(Len(MemberId) > 0 && Len(GroupId) > 0 && Len(AcceptState) > 0)
|
||||
{
|
||||
// Look at each GroupList (userId:<groups>) mapping to see where to add/update this member
|
||||
foreach GroupLists(GroupList, GroupListIndex)
|
||||
{
|
||||
// For each group related to the user see if it's the GroupId specified
|
||||
foreach GroupList.Groups(GroupTemp, GroupIndex)
|
||||
{
|
||||
// If the group is found Update the Member field
|
||||
// This will potential run multiple times as it will be stored under both owners and members in GroupLists
|
||||
if(GroupTemp.GroupId == GroupId)
|
||||
{
|
||||
// Locate the proper place to update or add the member
|
||||
MemberIndex = GroupTemp.Members.find('MemberId', MemberId);
|
||||
if(MemberIndex == INDEX_NONE)
|
||||
{
|
||||
// No MemberId found so add the member
|
||||
AddAt = GroupTemp.Members.Length;
|
||||
GroupTemp.Members.Length = AddAt +1;
|
||||
GroupTemp.Members[AddAt].MemberId = MemberId;
|
||||
GroupTemp.Members[AddAt].AcceptState = AcceptState;
|
||||
}
|
||||
else
|
||||
{
|
||||
// GroupId and MemberId have been confirmed so just update the accept state if it's changed
|
||||
if(GroupTemp.Members[MemberIndex].AcceptState != AcceptState)
|
||||
{
|
||||
GroupTemp.Members[MemberIndex].AcceptState = AcceptState;
|
||||
}
|
||||
}
|
||||
// Set the group at the location to the updated (or unchanged) group
|
||||
GroupList.Groups[GroupIndex] = GroupTemp;
|
||||
}
|
||||
}
|
||||
// Set the GroupList at the location to the updated (or unchanged) GroupList
|
||||
GroupLists[GroupListIndex] = GroupList;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location@" Either the MemberId, GroupId, or AcceptState was not Specified.");
|
||||
}
|
||||
}
|
89
IpDrv/Classes/McpIdMappingBase.uc
Normal file
89
IpDrv/Classes/McpIdMappingBase.uc
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for mapping account ids and the factory method
|
||||
* for creating the registered implementing object
|
||||
*/
|
||||
class McpIdMappingBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String McpIdMappingClassName;
|
||||
|
||||
/**
|
||||
* Maps an McpId to an external account (Facebook, G+, Twitter, etc.)
|
||||
*/
|
||||
struct McpIdMapping
|
||||
{
|
||||
/** The McpId that owns this mapping */
|
||||
var String McpId;
|
||||
/** The external account id that is being mapped to the McpId */
|
||||
var String ExternalId;
|
||||
/** The type of account (Facebook, G+, Twitter, GameCenter, etc.) this is to avoid name collisions */
|
||||
var String ExternalType;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpIdMappingBase CreateInstance()
|
||||
{
|
||||
local class<McpIdMappingBase> McpIdMappingBaseClass;
|
||||
local McpIdMappingBase NewInstance;
|
||||
|
||||
McpIdMappingBaseClass = class<McpIdMappingBase>(DynamicLoadObject(default.McpIdMappingClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpIdMappingBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpIdMappingBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an external account mapping. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param McpId the account to add the mapping to
|
||||
* @param ExternalId the external account that is being mapped to this account
|
||||
* @param ExternalType the type of account for disambiguation
|
||||
*/
|
||||
function AddMapping(String McpId, String ExternalId, String ExternalType);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the MCP account that this external account is being added to
|
||||
* @param ExternalId the account id that was being mapped
|
||||
* @param ExternalType the external account type that was being mapped
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnAddMappingComplete(String McpId, String ExternalId, String ExternalType, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Queries the backend for the McpIds of the list of external ids of a specific type
|
||||
*
|
||||
* @param ExternalIds the set of ids to get McpIds for
|
||||
* @param ExternalType the type of account that is being mapped to McpIds
|
||||
*/
|
||||
function QueryMappings(const out array<String> ExternalIds, String ExternalType);
|
||||
|
||||
/**
|
||||
* Called once the query results come back from the server to indicate success/failure of the request
|
||||
*
|
||||
* @param ExternalType the external account type that was being queried for
|
||||
* @param bWasSuccessful whether the query succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryMappingsComplete(String ExternalType, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of id mappings that match the requested account type
|
||||
*
|
||||
* @param ExternalType the account type that we want the mappings for
|
||||
* @param IdMappins the out array that gets the copied data
|
||||
*/
|
||||
function GetIdMappings(String ExternalType, out array<McpIdMapping> IdMappings);
|
301
IpDrv/Classes/McpIdMappingManager.uc
Normal file
301
IpDrv/Classes/McpIdMappingManager.uc
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Concrete implementation for mapping McpIds to external account ids
|
||||
*/
|
||||
class McpIdMappingManager extends McpIdMappingBase;
|
||||
|
||||
/**
|
||||
* Holds the set of mapped accounts that we've downloaded
|
||||
*/
|
||||
var array<McpIdMapping> AccountMappings;
|
||||
|
||||
/** The URL to use when making add mapping requests */
|
||||
var config String AddMappingUrl;
|
||||
|
||||
/** The URL to use when making query mapping requests */
|
||||
var config String QueryMappingUrl;
|
||||
|
||||
/** Holds the state information for an outstanding add mapping request */
|
||||
struct AddMappingRequest
|
||||
{
|
||||
/** The McpId the add is happening to */
|
||||
var string McpId;
|
||||
/** The external account id that is being mapped to a MCP id */
|
||||
var string ExternalId;
|
||||
/** The account type that is being mapped */
|
||||
var string ExternalType;
|
||||
/** The request object for this request */
|
||||
var HttpRequestInterface Request;
|
||||
};
|
||||
|
||||
/** The set of add mapping requests that are pending */
|
||||
var array<AddMappingRequest> AddMappingRequests;
|
||||
|
||||
/** Holds the state information for an outstanding query mapping request */
|
||||
struct QueryMappingRequest
|
||||
{
|
||||
/** The account type that is being mapped */
|
||||
var string ExternalType;
|
||||
/** The request object for this request */
|
||||
var HttpRequestInterface Request;
|
||||
};
|
||||
|
||||
/** The set of query mapping requests that are pending */
|
||||
var array<QueryMappingRequest> QueryMappingRequests;
|
||||
|
||||
/**
|
||||
* Adds an external account mapping. Sends this request to MCP to be processed
|
||||
*
|
||||
* @param McpId the account to add the mapping to
|
||||
* @param ExternalId the external account that is being mapped to this account
|
||||
* @param ExternalType the type of account for disambiguation
|
||||
*/
|
||||
function AddMapping(String McpId, String ExternalId, String ExternalType)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ AddMappingUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&externalAccountId=" $ ExternalId $
|
||||
"&externalAccountType=" $ ExternalType;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnAddMappingRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = AddMappingRequests.Length;
|
||||
AddMappingRequests.Length = AddAt + 1;
|
||||
AddMappingRequests[AddAt].McpId = McpId;
|
||||
AddMappingRequests[AddAt].ExternalId = ExternalId;
|
||||
AddMappingRequests[AddAt].ExternalType = ExternalType;
|
||||
AddMappingRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start AddMapping web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the add mapping result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnAddMappingRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int AddAt;
|
||||
local int ResponseCode;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = AddMappingRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
`Log("Account mapping McpId(" $ AddMappingRequests[Index].McpId $ "), ExternalId(" $
|
||||
AddMappingRequests[Index].ExternalId $ "), ExternalType(" $
|
||||
AddMappingRequests[Index].ExternalType $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
// Add this item to our list
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
AddAt = AccountMappings.Length;
|
||||
AccountMappings.Length = AddAt + 1;
|
||||
AccountMappings[AddAt].McpId = AddMappingRequests[Index].McpId;
|
||||
AccountMappings[AddAt].ExternalId = AddMappingRequests[Index].ExternalId;
|
||||
AccountMappings[AddAt].ExternalType = AddMappingRequests[Index].ExternalType;
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnAddMappingComplete(AddMappingRequests[Index].McpId,
|
||||
AddMappingRequests[Index].ExternalId,
|
||||
AddMappingRequests[Index].ExternalType,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
// Done with the pending request
|
||||
AddMappingRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the backend for the McpIds of the list of external ids of a specific type
|
||||
*
|
||||
* @param ExternalIds the set of ids to get McpIds for
|
||||
* @param ExternalType the type of account that is being mapped to McpIds
|
||||
*/
|
||||
function QueryMappings(const out array<String> ExternalIds, String ExternalType)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
local string JsonPayload;
|
||||
local int Index;
|
||||
local bool bFirst;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ QueryMappingUrl $ GetAppAccessURL() $
|
||||
"&externalAccountType=" $ ExternalType;
|
||||
|
||||
// Make a json string from our list of ids
|
||||
JsonPayload = "[ ";
|
||||
bFirst = true;
|
||||
for (Index = 0; Index < ExternalIds.Length; Index++)
|
||||
{
|
||||
if (Len(ExternalIds[Index]) > 0)
|
||||
{
|
||||
if (!bFirst)
|
||||
JsonPayload $= ",";
|
||||
bFirst = false;
|
||||
JsonPayload $= "\"" $ ExternalIds[Index] $ "\"";
|
||||
}
|
||||
}
|
||||
JsonPayload $= " ]";
|
||||
|
||||
`log("QueryMappings.JsonPayload:\n" $ JsonPayload);
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetContentAsString(JsonPayload);
|
||||
Request.SetVerb("POST");
|
||||
Request.SetHeader("Content-Type","multipart/form-data");
|
||||
Request.OnProcessRequestComplete = OnQueryMappingsRequestComplete;
|
||||
|
||||
// Store off the data for reporting later
|
||||
AddAt = QueryMappingRequests.Length;
|
||||
QueryMappingRequests.Length = AddAt + 1;
|
||||
QueryMappingRequests[AddAt].ExternalType = ExternalType;
|
||||
QueryMappingRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start QueryMappings web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnQueryMappingsRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int AddAt;
|
||||
local int ResponseCode;
|
||||
local string JsonString;
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local int AccountIndex;
|
||||
local bool bWasFound;
|
||||
local string McpId;
|
||||
local string ExternalId;
|
||||
local string ExternalType;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = QueryMappingRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
`Log("Account mapping query for ExternalType(" $
|
||||
QueryMappingRequests[Index].ExternalType $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
// Add this item to our list
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
JsonString = Response.GetContentAsString();
|
||||
if (JsonString != "")
|
||||
{
|
||||
`Log("JSON for account query = \r\n" $ JsonString);
|
||||
// @todo joeg - Replace with Wes' ImportJson() once it's implemented
|
||||
// Parse the json
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
// Add each mapping in the json packet if missing
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
McpId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("unique_user_id");
|
||||
ExternalId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("external_account_id");
|
||||
ExternalType = ParsedJson.ObjectArray[JsonIndex].GetStringValue("external_account_type");
|
||||
bWasFound = false;
|
||||
// Search the array for any existing adding only when missing
|
||||
for (AccountIndex = 0; AccountIndex < AccountMappings.Length && !bWasFound; AccountIndex++)
|
||||
{
|
||||
bWasFound = McpId == AccountMappings[AccountIndex].McpId &&
|
||||
ExternalId == AccountMappings[AccountIndex].ExternalId &&
|
||||
ExternalType == AccountMappings[AccountIndex].ExternalType;
|
||||
}
|
||||
// Add this one since it wasn't found
|
||||
if (!bWasFound)
|
||||
{
|
||||
AddAt = AccountMappings.Length;
|
||||
AccountMappings.Length = AddAt + 1;
|
||||
AccountMappings[AddAt].McpId = McpId;
|
||||
AccountMappings[AddAt].ExternalId = ExternalId;
|
||||
AccountMappings[AddAt].ExternalType = ExternalType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnQueryMappingsComplete(QueryMappingRequests[Index].ExternalType,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
// Done with the pending request
|
||||
QueryMappingRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of id mappings that match the requested account type
|
||||
*
|
||||
* @param ExternalType the account type that we want the mappings for
|
||||
* @param IdMappings the out array that gets the copied data
|
||||
*/
|
||||
function GetIdMappings(String ExternalType, out array<McpIdMapping> IdMappings)
|
||||
{
|
||||
local int Index;
|
||||
local int AddAt;
|
||||
|
||||
IdMappings.Length = 0;
|
||||
|
||||
// Search through for all mappings that match the desired type
|
||||
for (Index = 0; Index < AccountMappings.Length; Index++)
|
||||
{
|
||||
if (AccountMappings[Index].ExternalType == ExternalType)
|
||||
{
|
||||
AddAt = IdMappings.Length;
|
||||
IdMappings.Length = AddAt + 1;
|
||||
IdMappings[AddAt] = AccountMappings[Index];
|
||||
}
|
||||
}
|
||||
}
|
524
IpDrv/Classes/McpManagedValueManager.uc
Normal file
524
IpDrv/Classes/McpManagedValueManager.uc
Normal file
@ -0,0 +1,524 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* This is the concrete implementation
|
||||
*/
|
||||
class McpManagedValueManager extends McpManagedValueManagerBase;
|
||||
|
||||
/** The URL to use when making save slot */
|
||||
var config String CreateSaveSlotUrl;
|
||||
|
||||
/** The URL to use when reading save slot */
|
||||
var config String ReadSaveSlotUrl;
|
||||
|
||||
/** The URL to use when updating a value */
|
||||
var config String UpdateValueUrl;
|
||||
|
||||
/** The URL to use when deleting a value */
|
||||
var config String DeleteValueUrl;
|
||||
|
||||
/** The list of all save slots that are being managed */
|
||||
var array<ManagedValueSaveSlot> SaveSlots;
|
||||
|
||||
/** Holds the state information for an outstanding save slot request */
|
||||
struct SaveSlotRequestState
|
||||
{
|
||||
/** The MCP id that was returned by the backend */
|
||||
var string McpId;
|
||||
/** The save slot involved */
|
||||
var string SaveSlot;
|
||||
/** The request object for this request */
|
||||
var HttpRequestInterface Request;
|
||||
};
|
||||
|
||||
/** Holds the state information used by requests that act on a value id */
|
||||
struct ValueRequestState extends SaveSlotRequestState
|
||||
{
|
||||
var Name ValueId;
|
||||
};
|
||||
|
||||
/** The set of create save slot requests that are pending */
|
||||
var array<SaveSlotRequestState> CreateSaveSlotRequests;
|
||||
|
||||
/** The set of read save slot requests that are pending */
|
||||
var array<SaveSlotRequestState> ReadSaveSlotRequests;
|
||||
|
||||
/** The set of update value requests that are pending */
|
||||
var array<ValueRequestState> UpdateValueRequests;
|
||||
|
||||
/** The set of update value requests that are pending */
|
||||
var array<ValueRequestState> DeleteValueRequests;
|
||||
|
||||
/**
|
||||
* Creates the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the create
|
||||
* @param SaveSlot the save slot that is being create
|
||||
*/
|
||||
function CreateSaveSlot(String McpId, String SaveSlot)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ CreateSaveSlotUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&saveSlotId=" $ SaveSlot;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnCreateSaveSlotRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = CreateSaveSlotRequests.Length;
|
||||
CreateSaveSlotRequests.Length = AddAt + 1;
|
||||
CreateSaveSlotRequests[AddAt].McpId = McpId;
|
||||
CreateSaveSlotRequests[AddAt].SaveSlot = SaveSlot;
|
||||
CreateSaveSlotRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start CreateSaveSlot web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("Create save slot URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the create save slot result
|
||||
* and notify any registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnCreateSaveSlotRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local string ResponseString;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = CreateSaveSlotRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Parse the JSON payload of default values for this save slot
|
||||
ParseValuesForSaveSlot(CreateSaveSlotRequests[Index].McpId,
|
||||
CreateSaveSlotRequests[Index].SaveSlot,
|
||||
ResponseString);
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnCreateSaveSlotComplete(CreateSaveSlotRequests[Index].McpId,
|
||||
CreateSaveSlotRequests[Index].SaveSlot,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
`Log("Create save slot McpId(" $ CreateSaveSlotRequests[Index].McpId $ "), SaveSlot(" $
|
||||
CreateSaveSlotRequests[Index].SaveSlot $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
CreateSaveSlotRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the stored save slots for the ones for this user
|
||||
*
|
||||
* @param McpId the user being searched for
|
||||
* @param SaveSlot the save slot being searched for
|
||||
*
|
||||
* @return the index of the save slot if found
|
||||
*/
|
||||
function int FindSaveSlotIndex(String McpId, String SaveSlot)
|
||||
{
|
||||
local int SaveSlotIndex;
|
||||
|
||||
// Search all of the save slots for one that matches the user and slot id
|
||||
for (SaveSlotIndex = 0; SaveSlotIndex < SaveSlots.Length; SaveSlotIndex++)
|
||||
{
|
||||
if (SaveSlots[SaveSlotIndex].OwningMcpId == McpId &&
|
||||
SaveSlots[SaveSlotIndex].SaveSlot == SaveSlot)
|
||||
{
|
||||
return SaveSlotIndex;
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the json of managed values for a user's save slot
|
||||
*
|
||||
* @param McpId the user that owns the data
|
||||
* @param SaveSlot the save slot where the data is stored
|
||||
* @param JsonPayload the json data to parse
|
||||
*/
|
||||
function ParseValuesForSaveSlot(String McpId, String SaveSlot, String JsonPayload)
|
||||
{
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local int SaveSlotIndex;
|
||||
local int ManagedValueIndex;
|
||||
local name ValueId;
|
||||
local int Value;
|
||||
|
||||
// Find the save slot for the user
|
||||
SaveSlotIndex = FindSaveSlotIndex(McpId, SaveSlot);
|
||||
if (SaveSlotIndex == INDEX_NONE)
|
||||
{
|
||||
// Add the save slot since it is missing
|
||||
SaveSlotIndex = SaveSlots.Length;
|
||||
SaveSlots.Length = SaveSlotIndex + 1;
|
||||
SaveSlots[SaveSlotIndex].OwningMcpId = McpId;
|
||||
SaveSlots[SaveSlotIndex].SaveSlot = SaveSlot;
|
||||
}
|
||||
// @todo joeg - Replace with Wes' ImportJson() once it's implemented with proper variable naming support
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonPayload);
|
||||
// Add/update each managed value to the user's save slot
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
// Grab the value and id we need to store
|
||||
ValueId = name(ParsedJson.ObjectArray[JsonIndex].GetStringValue("value_id"));
|
||||
Value = ParsedJson.ObjectArray[JsonIndex].GetIntValue("value");
|
||||
// Find the existing managed value
|
||||
ManagedValueIndex = SaveSlots[SaveSlotIndex].Values.Find('ValueId', ValueId);
|
||||
if (ManagedValueIndex == INDEX_NONE)
|
||||
{
|
||||
// Not stored yet, so add one
|
||||
ManagedValueIndex = SaveSlots[SaveSlotIndex].Values.Length;
|
||||
SaveSlots[SaveSlotIndex].Values.Length = ManagedValueIndex + 1;
|
||||
SaveSlots[SaveSlotIndex].Values[ManagedValueIndex].ValueId = ValueId;
|
||||
}
|
||||
// Store the updated value
|
||||
SaveSlots[SaveSlotIndex].Values[ManagedValueIndex].Value = Value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all of the values in the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the read
|
||||
* @param SaveSlot the save slot that is being read
|
||||
*/
|
||||
function ReadSaveSlot(String McpId, String SaveSlot)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ ReadSaveSlotUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&saveSlotId=" $ SaveSlot;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("GET");
|
||||
Request.OnProcessRequestComplete = OnReadSaveSlotRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = ReadSaveSlotRequests.Length;
|
||||
ReadSaveSlotRequests.Length = AddAt + 1;
|
||||
ReadSaveSlotRequests[AddAt].McpId = McpId;
|
||||
ReadSaveSlotRequests[AddAt].SaveSlot = SaveSlot;
|
||||
ReadSaveSlotRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start ReadSaveSlot web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("Read save slot URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the read save slot result
|
||||
* and notify any registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnReadSaveSlotRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local string ResponseString;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = ReadSaveSlotRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Parse the JSON payload of default values for this save slot
|
||||
ParseValuesForSaveSlot(ReadSaveSlotRequests[Index].McpId,
|
||||
ReadSaveSlotRequests[Index].SaveSlot,
|
||||
ResponseString);
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnReadSaveSlotComplete(ReadSaveSlotRequests[Index].McpId,
|
||||
ReadSaveSlotRequests[Index].SaveSlot,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
`Log("Read save slot McpId(" $ ReadSaveSlotRequests[Index].McpId $ "), SaveSlot(" $
|
||||
ReadSaveSlotRequests[Index].SaveSlot $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
ReadSaveSlotRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of values for the requested user's specified save slot
|
||||
*/
|
||||
function array<ManagedValue> GetValues(String McpId, String SaveSlot)
|
||||
{
|
||||
local int SaveSlotIndex;
|
||||
local array<ManagedValue> EmptyArray;
|
||||
|
||||
// Find the slot and then return the array of values stored there
|
||||
SaveSlotIndex = FindSaveSlotIndex(McpId, SaveSlot);
|
||||
if (SaveSlotIndex != INDEX_NONE)
|
||||
{
|
||||
return SaveSlots[SaveSlotIndex].Values;
|
||||
}
|
||||
// To avoid the script warning
|
||||
EmptyArray.Length = 0;
|
||||
return EmptyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The value the server returned for the requested value id from the user's specific save slot
|
||||
*/
|
||||
function int GetValue(String McpId, String SaveSlot, Name ValueId)
|
||||
{
|
||||
local int SaveSlotIndex;
|
||||
local int ValueIndex;
|
||||
local int Value;
|
||||
|
||||
// Find the slot first
|
||||
SaveSlotIndex = FindSaveSlotIndex(McpId, SaveSlot);
|
||||
if (SaveSlotIndex != INDEX_NONE)
|
||||
{
|
||||
// Find the requested value
|
||||
ValueIndex = SaveSlots[SaveSlotIndex].Values.Find('ValueId', ValueId);
|
||||
if (ValueIndex != INDEX_NONE)
|
||||
{
|
||||
Value = SaveSlots[SaveSlotIndex].Values[ValueIndex].Value;
|
||||
}
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a specific value in the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the update
|
||||
* @param SaveSlot the save slot that was being updated
|
||||
* @param ValueId the value that the server should update
|
||||
* @param Value the value to apply as the update (delta or absolute determined by the server)
|
||||
*/
|
||||
function UpdateValue(String McpId, String SaveSlot, Name ValueId, int Value)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ UpdateValueUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&saveSlotId=" $ SaveSlot $
|
||||
"&valueId=" $ ValueId $
|
||||
"&value=" $ Value;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnUpdateValueRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = UpdateValueRequests.Length;
|
||||
UpdateValueRequests.Length = AddAt + 1;
|
||||
UpdateValueRequests[AddAt].McpId = McpId;
|
||||
UpdateValueRequests[AddAt].SaveSlot = SaveSlot;
|
||||
UpdateValueRequests[AddAt].ValueId = ValueId;
|
||||
UpdateValueRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start UpdateValue web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("Update value URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the update value result
|
||||
* and notify any registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnUpdateValueRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local string ResponseString;
|
||||
local int UpdatedValue;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = UpdateValueRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
ResponseString = "[" $ Response.GetContentAsString() $ "]";
|
||||
// Parse the JSON payload of default values for this save slot
|
||||
ParseValuesForSaveSlot(UpdateValueRequests[Index].McpId,
|
||||
UpdateValueRequests[Index].SaveSlot,
|
||||
ResponseString);
|
||||
}
|
||||
UpdatedValue = GetValue(UpdateValueRequests[Index].McpId, UpdateValueRequests[Index].SaveSlot, UpdateValueRequests[Index].ValueId);
|
||||
// Notify anyone waiting on this
|
||||
OnUpdateValueComplete(UpdateValueRequests[Index].McpId,
|
||||
UpdateValueRequests[Index].SaveSlot,
|
||||
UpdateValueRequests[Index].ValueId,
|
||||
UpdatedValue,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
`Log("Update value McpId(" $ UpdateValueRequests[Index].McpId $ "), SaveSlot(" $
|
||||
UpdateValueRequests[Index].SaveSlot $ "), ValueId(" $
|
||||
UpdateValueRequests[Index].ValueId $ "), Value(" $
|
||||
UpdatedValue $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
UpdateValueRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the delete
|
||||
* @param SaveSlot the save slot that is having the value deleted from
|
||||
* @param ValueId the value id for the server to delete
|
||||
*/
|
||||
function DeleteValue(String McpId, String SaveSlot, Name ValueId)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ DeleteValueUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&saveSlotId=" $ SaveSlot $
|
||||
"&valueId=" $ ValueId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("DELETE");
|
||||
Request.OnProcessRequestComplete = OnDeleteValueRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = DeleteValueRequests.Length;
|
||||
DeleteValueRequests.Length = AddAt + 1;
|
||||
DeleteValueRequests[AddAt].McpId = McpId;
|
||||
DeleteValueRequests[AddAt].SaveSlot = SaveSlot;
|
||||
DeleteValueRequests[AddAt].ValueId = ValueId;
|
||||
DeleteValueRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start DeleteValue web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("Delete value URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the delete value result
|
||||
* and notify any registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnDeleteValueRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local int SaveSlotIndex;
|
||||
local int ValueIndex;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = DeleteValueRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Find the slot first
|
||||
SaveSlotIndex = FindSaveSlotIndex(DeleteValueRequests[Index].McpId, DeleteValueRequests[Index].SaveSlot);
|
||||
if (SaveSlotIndex != INDEX_NONE)
|
||||
{
|
||||
// Find the requested value
|
||||
ValueIndex = SaveSlots[SaveSlotIndex].Values.Find('ValueId', DeleteValueRequests[Index].ValueId);
|
||||
if (ValueIndex != INDEX_NONE)
|
||||
{
|
||||
// Remove it from the array
|
||||
SaveSlots[SaveSlotIndex].Values.Remove(ValueIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnDeleteValueComplete(DeleteValueRequests[Index].McpId,
|
||||
DeleteValueRequests[Index].SaveSlot,
|
||||
DeleteValueRequests[Index].ValueId,
|
||||
bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
`Log("Delete value McpId(" $ DeleteValueRequests[Index].McpId $ "), SaveSlot(" $
|
||||
DeleteValueRequests[Index].SaveSlot $ "), ValueId(" $
|
||||
DeleteValueRequests[Index].ValueId $ ") was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
DeleteValueRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
142
IpDrv/Classes/McpManagedValueManagerBase.uc
Normal file
142
IpDrv/Classes/McpManagedValueManagerBase.uc
Normal file
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* This is the base interface for manipulating a user's managed values
|
||||
*/
|
||||
class McpManagedValueManagerBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String McpManagedValueManagerClassName;
|
||||
|
||||
/**
|
||||
* Name to value mapping for a value managed on the MCP server
|
||||
*/
|
||||
struct ManagedValue
|
||||
{
|
||||
/** The ID of the value */
|
||||
var Name ValueId;
|
||||
/** The value the server from the server */
|
||||
var int Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds a single user's save slot information for managed values
|
||||
*/
|
||||
struct ManagedValueSaveSlot
|
||||
{
|
||||
/** The owner of this save slot */
|
||||
var String OwningMcpId;
|
||||
/** The save slot id */
|
||||
var String SaveSlot;
|
||||
/** The list of managed values in this save slot */
|
||||
var array<ManagedValue> Values;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpManagedValueManagerBase CreateInstance()
|
||||
{
|
||||
local class<McpManagedValueManagerBase> McpManagedValueManagerBaseClass;
|
||||
local McpManagedValueManagerBase NewInstance;
|
||||
|
||||
McpManagedValueManagerBaseClass = class<McpManagedValueManagerBase>(DynamicLoadObject(default.McpManagedValueManagerClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpManagedValueManagerBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpManagedValueManagerBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the create
|
||||
* @param SaveSlot the save slot that is being create
|
||||
*/
|
||||
function CreateSaveSlot(String McpId, String SaveSlot);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that requested the save slot create
|
||||
* @param SaveSlot the save slot that was created
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnCreateSaveSlotComplete(string McpId, string SaveSlot, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Reads all of the values in the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the read
|
||||
* @param SaveSlot the save slot that is being read
|
||||
*/
|
||||
function ReadSaveSlot(String McpId, String SaveSlot);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that requested the read
|
||||
* @param SaveSlot the save slot that was being read
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnReadSaveSlotComplete(string McpId, string SaveSlot, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* @return The list of values for the requested user's specified save slot
|
||||
*/
|
||||
function array<ManagedValue> GetValues(String McpId, String SaveSlot);
|
||||
|
||||
/**
|
||||
* @return The value the server returned for the requested value id from the user's specific save slot
|
||||
*/
|
||||
function int GetValue(String McpId, String SaveSlot, Name ValueId);
|
||||
|
||||
/**
|
||||
* Updates a specific value in the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the update
|
||||
* @param SaveSlot the save slot that was being updated
|
||||
* @param ValueId the value that the server should update
|
||||
* @param Value the value to apply as the update (delta or absolute determined by the server)
|
||||
*/
|
||||
function UpdateValue(String McpId, String SaveSlot, Name ValueId, int Value);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that requested the update
|
||||
* @param SaveSlot the save slot that was being updated
|
||||
* @param ValueId the value id that was updated
|
||||
* @param Value the value that the server returned as part of the update (in case the server overrides it)
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnUpdateValueComplete(string McpId, string SaveSlot, Name ValueId, int Value, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Deletes a value from the user's specified save slot
|
||||
*
|
||||
* @param McpId the id of the user that requested the delete
|
||||
* @param SaveSlot the save slot that is having the value deleted from
|
||||
* @param ValueId the value id for the server to delete
|
||||
*/
|
||||
function DeleteValue(String McpId, String SaveSlot, Name ValueId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that requested the delete
|
||||
* @param SaveSlot the save slot that was having the value deleted from
|
||||
* @param ValueId the value id that the server deleted
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteValueComplete(string McpId, string SaveSlot, Name ValueId, bool bWasSuccessful, String Error);
|
223
IpDrv/Classes/McpMessageBase.uc
Normal file
223
IpDrv/Classes/McpMessageBase.uc
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for McpMessages and the factory method
|
||||
* for creating the registered implementing object
|
||||
*/
|
||||
class McpMessageBase extends McpServiceBase
|
||||
native
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config string McpMessageManagerClassName;
|
||||
|
||||
/** Compression types supported */
|
||||
enum EMcpMessageCompressionType
|
||||
{
|
||||
MMCT_NONE,
|
||||
MMCT_LZO,
|
||||
MMCT_ZLIB
|
||||
};
|
||||
|
||||
/** Default Compression Type */
|
||||
var config EMcpMessageCompressionType CompressionType;
|
||||
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
struct native McpMessage
|
||||
{
|
||||
/**
|
||||
* Unique id to use as a key for this message
|
||||
*/
|
||||
var String MessageId;
|
||||
/**
|
||||
* The user id to deliver this message to
|
||||
*/
|
||||
var String ToUniqueUserId;
|
||||
/**
|
||||
* The user id this message is from
|
||||
*/
|
||||
var String FromUniqueUserId;
|
||||
/**
|
||||
* The friendly name of the user sending the data
|
||||
*/
|
||||
var String FromFriendlyName;
|
||||
/**
|
||||
* The application specific message type
|
||||
*/
|
||||
var String MessageType;
|
||||
/**
|
||||
* The date until this message is no longer valid (should be deleted)
|
||||
*/
|
||||
var String ValidUntil;
|
||||
/**
|
||||
* The compression type of this message so the client knows how to de-compress the payload
|
||||
*/
|
||||
var EMcpMessageCompressionType MessageCompressionType;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of Messages belonging to one user
|
||||
*/
|
||||
struct native McpMessageList
|
||||
{
|
||||
/** User that User the messages were Sent To */
|
||||
var string ToUniqueUserId;
|
||||
|
||||
/** Collection of groups that the user owns OR belongs to */
|
||||
var array<McpMessage> Messages;
|
||||
};
|
||||
|
||||
/**
|
||||
* Message Contents
|
||||
*/
|
||||
struct native McpMessageContents
|
||||
{
|
||||
/**
|
||||
* Message Id
|
||||
*/
|
||||
var string MessageId;
|
||||
|
||||
/**
|
||||
* Payload holding the contents of the message
|
||||
*/
|
||||
var array<byte> MessageContents;
|
||||
};
|
||||
|
||||
/** Holds the MessageContents for each user in memory */
|
||||
var array<McpMessageContents> MessageContentsList;
|
||||
|
||||
/** Holds the members for each user in memory */
|
||||
var array<McpMessageList> MessageLists;
|
||||
|
||||
/**
|
||||
* Create Instance of an McpMessage
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
static final function McpMessageBase CreateInstance()
|
||||
{
|
||||
local class<McpMessageBase> McpMessageBaseClass;
|
||||
local McpMessageBase NewInstance;
|
||||
|
||||
McpMessageBaseClass = class<McpMessageBase>(DynamicLoadObject(default.McpMessageManagerClassName,class'Class'));
|
||||
if (McpMessageBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpMessageBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the URL and sends the request to create a Message.
|
||||
* - This updates the message on the server. QueryMessages will need to be
|
||||
* - run again before GetMessages will reflect the this new message.
|
||||
* @param ToUniqueUserIds the ids of users to send a message to
|
||||
* @param FromUniqueUserId of the user who sent the message
|
||||
* @param FromFriendlyName The friendly name of the user sending the data
|
||||
* @param MessageType The application specific message type
|
||||
* @param PushMessage to be sent to user via push notifications
|
||||
* @param ValidUntil The date until this message is no longer valid (should be deleted)
|
||||
* @param MessageContents payload of the message
|
||||
*/
|
||||
function CreateMessage(
|
||||
const out array<String> ToUniqueUserIds,
|
||||
String FromUniqueUserId,
|
||||
String FromFriendlyName,
|
||||
String MessageType,
|
||||
String PushMessage,
|
||||
String ValidUntil,
|
||||
const out array<byte> MessageContents);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param Message the group id of the group that was created
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnCreateMessageComplete(McpMessage Message, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Deletes a Message by MessageId
|
||||
*
|
||||
* @param MessageId Id of the group
|
||||
*/
|
||||
function DeleteMessage(String MessageId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param MessageId the group id of the group that was Deleted
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteMessageComplete(String MessageId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Queries the backend for the Messages belonging to the supplied UserId
|
||||
*
|
||||
* @param UniqueUserId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryMessages(String ToUniqueUserId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param UserId the user id of the groups that were queried
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryMessagesComplete(string UserId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of messages that sent to the specified UserId
|
||||
* Called after QueryMessages.
|
||||
*
|
||||
* @param ToUniqueUserId the user the messages were sent to
|
||||
* @param MessageList collection of messages
|
||||
*/
|
||||
function GetMessageList(string ToUniqueUserId, out McpMessageList MessageList);
|
||||
|
||||
/**
|
||||
* Queries the back-end for the Messages Contents belonging to the specified group
|
||||
*
|
||||
* @param MessageId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryMessageContents(String MessageId);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param MessageId the id of the group from which the members were queried
|
||||
* @param bWasSuccessful whether the operation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryMessageContentsComplete(String MessageId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns a copy of the Message Contents that belong to the specified MessageId
|
||||
* Called after QueryMessageContents.
|
||||
*
|
||||
* @param MessageId
|
||||
* @param MessageContents
|
||||
*/
|
||||
function bool GetMessageContents(String MessageId, out array<byte> MessageContents);
|
||||
|
||||
/**
|
||||
*Store group in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param Message to be placed in the cache
|
||||
*/
|
||||
function CacheMessage( McpMessage Message);
|
||||
|
||||
/**
|
||||
*Store group member in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param MessageContents Message payload
|
||||
* @param MessageId to be placed in the cache
|
||||
*/
|
||||
function bool CacheMessageContents(const out array<byte> MessageContents, String MessageId);
|
691
IpDrv/Classes/McpMessageManager.uc
Normal file
691
IpDrv/Classes/McpMessageManager.uc
Normal file
@ -0,0 +1,691 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for user messages and the factory method
|
||||
* for creating the registered implementing object
|
||||
*/
|
||||
class McpMessageManager extends McpMessageBase
|
||||
native
|
||||
config(Engine)
|
||||
inherits(FTickableObject);
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci)
|
||||
|
||||
/** The URL of CreateMessage function on the server */
|
||||
var config string CreateMessageUrl;
|
||||
|
||||
/** The URL of DeleteMessage function on the server */
|
||||
var config string DeleteMessageUrl;
|
||||
|
||||
/** The URL of ListMessages function on the server */
|
||||
var config string QueryMessagesUrl;
|
||||
|
||||
/** The URL of ListMessageContents function on the server */
|
||||
var config string QueryMessageContentsUrl;
|
||||
|
||||
/** The URL of DeleteAllMessages function on the server */
|
||||
var config string DeleteAllMessagesUrl;
|
||||
|
||||
/**
|
||||
*CompressMessageRequest holds the information needed by the async compression function
|
||||
*/
|
||||
struct native McpCompressMessageRequest
|
||||
{
|
||||
/** Uncompressed Source Buffer */
|
||||
var array<byte> SourceBuffer;
|
||||
/** Bufffer to hold the compressed data; set after compression is finished */
|
||||
var array<byte> DestBuffer;
|
||||
/** Size of the final compressed data */
|
||||
var int OutCompressedSize;
|
||||
/** Http Request */
|
||||
var HttpRequestInterface Request;
|
||||
/** The compression worker that will do the compression */
|
||||
var native pointer CompressionWorker{FAsyncTask<FCompressAsyncWorker>};
|
||||
};
|
||||
|
||||
/**
|
||||
*UncompressMessageRequest holds the information needed by async uncompression
|
||||
*/
|
||||
struct native McpUncompressMessageRequest
|
||||
{
|
||||
/** Id of message that the compressed payload belongs to */
|
||||
var string MessageId;
|
||||
/** Compressed Source Buffer */
|
||||
var array<byte> SourceBuffer;
|
||||
/** Bufffer to hold the uncompressed data; set after uncompression is finished */
|
||||
var array<byte> DestBuffer;
|
||||
/** Size of the final uncompressed data; passed in before compression from the header of the compressed buffer */
|
||||
var int OutUncompressedSize;
|
||||
/** The uncompression worker that will do the uncompression */
|
||||
var native pointer UncompressionWorker{FAsyncTask<FUncompressAsyncWorker>};
|
||||
};
|
||||
|
||||
/** List of McpCompressMessageRequests to be compressed */
|
||||
var native array<McpCompressMessageRequest> CompressMessageRequests;
|
||||
/** List of McpUncompressMessageRequests to be compressed */
|
||||
var native array<McpUncompressMessageRequest> UncompressMessageRequests;
|
||||
|
||||
/**
|
||||
* Called to start the compression process.
|
||||
* Adds a compression task to the compression queue
|
||||
*
|
||||
* @param MessageCompressionType
|
||||
* @param MessageContent
|
||||
* @param Request
|
||||
*/
|
||||
native function bool StartAsyncCompression(EMcpMessageCompressionType MessageCompressionType, const out array<byte> MessageContent, HttpRequestInterface Request);
|
||||
|
||||
/**
|
||||
* Called to start the uncompression process.
|
||||
* Adds a compression task to the uncompression queue
|
||||
*
|
||||
* @param MessageId
|
||||
* @param MessageCompressionType
|
||||
* @param MessageContent
|
||||
*/
|
||||
native function bool StartAsyncUncompression(string MessageId, EMcpMessageCompressionType MessageCompressionType, const out array<byte> MessageContent);
|
||||
|
||||
/**
|
||||
* Called once the uncompression job has finished
|
||||
* - Ensures that uncompressed message contents are cached and the query's final delegate is called
|
||||
*
|
||||
* @param bWasSuccessful passes whether or not the uncompression task completed successfully
|
||||
* @param UncompressedMessageContents
|
||||
* @param MessageId
|
||||
*/
|
||||
event FinishedAsyncUncompression(bool bWasSuccessful, const out array<byte> UncompressedMessageContents, string MessageId)
|
||||
{
|
||||
if(!CacheMessageContents(UncompressedMessageContents, MessageId))
|
||||
{
|
||||
`Log(`Location("Error Caching Message Contents"));
|
||||
}
|
||||
OnQueryMessageContentsComplete(MessageId, bWasSuccessful, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the URL and sends the request to create a Message.
|
||||
* - This updates the message on the server. QueryMessages will need to be
|
||||
* - run again before GetMessages will reflect the this new message.
|
||||
* @param ToUniqueUserIds the ids of users to send a message to
|
||||
* @param FromUniqueUserId of the user who sent the message
|
||||
* @param FromFriendlyName The friendly name of the user sending the data
|
||||
* @param MessageType The application specific message type
|
||||
* @param PushMessage to be sent to user via push notifications
|
||||
* @param ValidUntil The date until this message is no longer valid (should be deleted)
|
||||
* @param MessageContents payload of the message
|
||||
*/
|
||||
function CreateMessage(
|
||||
const out array<String> ToUniqueUserIds,
|
||||
String FromUniqueUserId,
|
||||
String FromFriendlyName,
|
||||
String MessageType,
|
||||
String PushMessage,
|
||||
String ValidUntil,
|
||||
const out array<byte> MessageContents)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface CreateMessageRequest;
|
||||
local McpMessage Message;
|
||||
local string ToUniqueUserIdsStr;
|
||||
local int Idx;
|
||||
|
||||
// Create HttpRequest
|
||||
CreateMessageRequest = class'HttpFactory'.static.CreateRequest();
|
||||
|
||||
if(CreateMessageRequest != none)
|
||||
{
|
||||
// create comma delimited list of recipient ids
|
||||
for (Idx=0; Idx < ToUniqueUserIds.Length; Idx++)
|
||||
{
|
||||
ToUniqueUserIdsStr $= ToUniqueUserIds[Idx];
|
||||
if ((ToUniqueUserIds.Length - Idx) > 1)
|
||||
{
|
||||
ToUniqueUserIdsStr $= ",";
|
||||
}
|
||||
}
|
||||
// Fill url out using parameters
|
||||
Url = GetBaseURL() $ CreateMessageUrl $ GetAppAccessURL() $
|
||||
"&toUniqueUserIds=" $ ToUniqueUserIdsStr $
|
||||
"&fromUniqueUserId=" $ FromUniqueUserId $
|
||||
"&fromFriendlyName=" $ FromFriendlyName $
|
||||
"&messageType=" $ MessageType $
|
||||
"&pushMessage=" $ PushMessage $
|
||||
"&messageCompressionType=" $ CompressionType $
|
||||
// JS uses DateTo Param
|
||||
"&validUntil=" $ ValidUntil
|
||||
;
|
||||
|
||||
// Build our web request with the above URL
|
||||
CreateMessageRequest.SetURL(URL);
|
||||
CreateMessageRequest.SetHeader("Content-Type","multipart/form-data");
|
||||
CreateMessageRequest.SetVerb("POST");
|
||||
|
||||
CreateMessageRequest.OnProcessRequestComplete = OnCreateMessageRequestComplete;
|
||||
|
||||
//check to see if we need compression
|
||||
if (CompressionType == MMCT_LZO ||
|
||||
CompressionType == MMCT_ZLIB)
|
||||
{
|
||||
if( !StartAsyncCompression(CompressionType, MessageContents, CreateMessageRequest))
|
||||
{
|
||||
OnCreateMessageComplete(Message, false, "Failed to Start Async Compression.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateMessageRequest.SetContent(MessageContents);
|
||||
// Call Web Request
|
||||
if (!CreateMessageRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log(`Location $ " URL is " $ Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed.
|
||||
* Used to return any errors and notify any registered delegate
|
||||
*
|
||||
* @param CreateMessageRequest the request object that was used
|
||||
* @param HttpResponse the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnCreateMessageRequestComplete(HttpRequestInterface CreateMessageRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local McpMessage CreatedMessage;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none && CreateMessageRequest != none)
|
||||
{
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_CREATED;
|
||||
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
|
||||
if ( bWasSuccessful)
|
||||
{
|
||||
`log(`Location@"Message created.");
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`Location@" CreateMessage query did not return a message.");
|
||||
}
|
||||
}
|
||||
OnCreateMessageComplete(CreatedMessage, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a Message by MessageId
|
||||
*
|
||||
* @param MessageId Id of the group
|
||||
*/
|
||||
function DeleteMessage(String MessageId)
|
||||
{
|
||||
local string Url;
|
||||
local HttpRequestInterface DeleteMessageRequest;
|
||||
|
||||
// Delete HttpRequest
|
||||
DeleteMessageRequest = class'HttpFactory'.static.CreateRequest();
|
||||
|
||||
if(DeleteMessageRequest != none)
|
||||
{
|
||||
// Fill url out using parameters
|
||||
Url = GetBaseURL() $ DeleteMessageUrl $ GetAppAccessURL() $
|
||||
"&messageId=" $ MessageId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
DeleteMessageRequest.SetVerb("DELETE");
|
||||
DeleteMessageRequest.SetURL(URL);
|
||||
DeleteMessageRequest.OnProcessRequestComplete = OnDeleteMessageRequestComplete;
|
||||
|
||||
// call WebRequest
|
||||
if(!DeleteMessageRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log(`Location $ "URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed.
|
||||
* Used to process the response and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param OriginalRequest the request object that was used
|
||||
* @param HttpResponse the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnDeleteMessageRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Content;
|
||||
local String MessageId;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
if (HttpResponse != none)
|
||||
{
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
MessageId = HttpResponse.GetURLParameter("MessageId");
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
Content = HttpResponse.GetContentAsString();
|
||||
}
|
||||
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
|
||||
OnDeleteMessageComplete(MessageId, bWasSuccessful, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the backend for the Messages belonging to the supplied UserId
|
||||
*
|
||||
* @param ToUniqueUserId the id of the owner of the groups to return
|
||||
*/
|
||||
function QueryMessages(String ToUniqueUserId)
|
||||
{
|
||||
// Cache one message result set per user
|
||||
local string Url;
|
||||
local HttpRequestInterface QueryMessagesRequest;
|
||||
|
||||
// List HttpRequest
|
||||
QueryMessagesRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (QueryMessagesRequest != none)
|
||||
{
|
||||
//Create URL parameters
|
||||
Url = GetBaseURL() $ QueryMessagesUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ ToUniqueUserId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
QueryMessagesRequest.SetURL(URL);
|
||||
QueryMessagesRequest.SetVerb("GET");
|
||||
QueryMessagesRequest.OnProcessRequestComplete = OnQueryMessagesRequestComplete;
|
||||
`Log(`Location@"URL: " $ URL);
|
||||
// Call WebRequest
|
||||
if(!QueryMessagesRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param OriginalRequest the request object that was used
|
||||
* @param HttpResponse the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnQueryMessagesRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local string Error;
|
||||
local string JsonString;
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local McpMessage Message;
|
||||
local string MessageCompressionTypeString;
|
||||
|
||||
// Set default response code
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
|
||||
// Both HttpResponse and OriginalRequest need to be present
|
||||
if (HttpResponse != none && OriginalRequest != none)
|
||||
{
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
JsonString = HttpResponse.GetContentAsString();
|
||||
if (JsonString != "")
|
||||
{
|
||||
// @todo joeg - Replace with Wes' ImportJson() once it's implemented
|
||||
// Parse the json
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
// Add each mapping in the json packet if missing
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
|
||||
Message.MessageId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("message_id");
|
||||
Message.ToUniqueUserId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("to_unique_user_id");
|
||||
Message.FromUniqueUserId = ParsedJson.ObjectArray[JsonIndex].GetStringValue("from_unique_user_id");
|
||||
Message.FromFriendlyName = ParsedJson.ObjectArray[JsonIndex].GetStringValue("from_friendly_name");
|
||||
Message.MessageType = ParsedJson.ObjectArray[JsonIndex].GetStringValue("message_type");
|
||||
MessageCompressionTypeString = ParsedJson.ObjectArray[JsonIndex].GetStringValue("message_compression_type");
|
||||
Message.ValidUntil = ParsedJson.ObjectArray[JsonIndex].GetStringValue("valid_until");
|
||||
|
||||
// Convert CompressionTypeString stored in the datastore to the enum used on the client
|
||||
switch(MessageCompressionTypeString)
|
||||
{
|
||||
case "MMCT_LZO":
|
||||
Message.MessageCompressionType = MMCT_LZO;
|
||||
break;
|
||||
case "MMCT_ZLIB":
|
||||
Message.MessageCompressionType = MMCT_ZLIB;
|
||||
break;
|
||||
default:
|
||||
Message.MessageCompressionType = MMCT_NONE;
|
||||
}
|
||||
|
||||
CacheMessage(Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = "Query did not return any content in it's response.";
|
||||
`log(`Location $ Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = HttpResponse.GetContentAsString();
|
||||
}
|
||||
}
|
||||
OnQueryMessagesComplete(Message.ToUniqueUserId, bWasSuccessful, Error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of messages that sent to the specified UserId
|
||||
* Called after QueryMessages.
|
||||
*
|
||||
* @param ToUniqueUserId the user the messages were sent to
|
||||
* @param MessageList collection of messages
|
||||
*/
|
||||
function GetMessageList(string ToUniqueUserId, out McpMessageList MessageList)
|
||||
{
|
||||
local int MessageListIndex;
|
||||
|
||||
// Check Cache Variable MessageLists
|
||||
MessageListIndex = MessageLists.Find('ToUniqueUserId', ToUniqueUserId);
|
||||
if(MessageListIndex != INDEX_NONE)
|
||||
{
|
||||
MessageList = MessageLists[MessageListIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location $ " Requester Id not found or MessageLists is empty. Using ToUniqueUserId: " $ ToUniqueUserId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the back-end for the Messages Contents belonging to the specified group
|
||||
*
|
||||
* @param MessageId the id of the message to return
|
||||
*/
|
||||
function QueryMessageContents(String MessageId)
|
||||
{
|
||||
// Cache one message result set per user
|
||||
local string Url;
|
||||
local HttpRequestInterface QueryMessageContentsRequest;
|
||||
|
||||
// List HttpRequest
|
||||
QueryMessageContentsRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (QueryMessageContentsRequest != none)
|
||||
{
|
||||
//Create URL parameters
|
||||
Url = GetBaseURL() $ QueryMessageContentsUrl $ GetAppAccessURL() $
|
||||
"&messageId=" $ MessageId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
QueryMessageContentsRequest.SetURL(URL);
|
||||
QueryMessageContentsRequest.SetVerb("GET");
|
||||
QueryMessageContentsRequest.OnProcessRequestComplete = OnQueryMessageContentsRequestComplete;
|
||||
`Log(`Location@"URL: " $ URL);
|
||||
// Call WebRequest
|
||||
if(!QueryMessageContentsRequest.ProcessRequest())
|
||||
{
|
||||
`Log(`Location@"Failed to process web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param OriginalRequest the request object that was used
|
||||
* @param HttpResponse the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnQueryMessageContentsRequestComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface HttpResponse, bool bWasSuccessful)
|
||||
{
|
||||
local int ResponseCode;
|
||||
local array<byte> MessageContents;
|
||||
local string MessageId;
|
||||
local McpMessage Message;
|
||||
|
||||
ResponseCode = `HTTP_STATUS_SERVER_ERROR;
|
||||
|
||||
// Both HttpResponse and OriginalRequest need to be present
|
||||
if (HttpResponse != none && OriginalRequest != none)
|
||||
{
|
||||
MessageId = OriginalRequest.GetURLParameter("messageId");
|
||||
|
||||
ResponseCode = HttpResponse.GetResponseCode();
|
||||
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == `HTTP_STATUS_OK;
|
||||
if (bWasSuccessful && Len(MessageId) > 0 )
|
||||
{
|
||||
HttpResponse.GetContent(MessageContents);
|
||||
`log("MessageId:" $ MessageId $" Compressed Message Contents Length:" $ MessageContents.Length);
|
||||
if (MessageContents.Length > 0)
|
||||
{
|
||||
GetMessageById(MessageId, Message);
|
||||
|
||||
if( Message.MessageCompressionType == MMCT_NONE)
|
||||
{
|
||||
CacheMessageContents(MessageContents, MessageId);
|
||||
OnQueryMessageContentsComplete(MessageId, bWasSuccessful, HttpResponse.GetContentAsString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !StartAsyncUncompression(MessageId, Message.MessageCompressionType, MessageContents ))
|
||||
{
|
||||
OnQueryMessageContentsComplete(MessageId, false, "Could not Start AsyncDecompression");
|
||||
}
|
||||
// this will call OnQueryMessageContentsComplete
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryMessageContentsComplete(MessageId, false, "Query did not return any content in it's response.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryMessageContentsComplete(MessageId, false, HttpResponse.GetContentAsString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnQueryMessageContentsComplete(MessageId, false, "There was No HttpResponse or Request");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the Message Contents that belong to the specified MessageId
|
||||
* Called after QueryMessageContents.
|
||||
*
|
||||
* @param MessageId
|
||||
* @param MessageContents
|
||||
*/
|
||||
function bool GetMessageContents(String MessageId, out array<byte> MessageContents)
|
||||
{
|
||||
local bool bWasSuccessful;
|
||||
local int MessageContentsIndex;
|
||||
|
||||
MessageContentsIndex = MessageContentsList.Find('MessageId', MessageId);
|
||||
|
||||
if (MessageContentsIndex != INDEX_NONE)
|
||||
{
|
||||
MessageContents = MessageContentsList[MessageContentsIndex].MessageContents;
|
||||
bWasSuccessful = true;
|
||||
}else
|
||||
{
|
||||
bWasSuccessful = false;
|
||||
}
|
||||
return bWasSuccessful;
|
||||
}
|
||||
|
||||
/**
|
||||
*Store group in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param Message to be placed in the cache
|
||||
*/
|
||||
function CacheMessage( McpMessage Message)
|
||||
{
|
||||
local int AddAt;
|
||||
local int MessageIndex;
|
||||
local int MessageListIndex;
|
||||
local McpMessageList UserMessageList;
|
||||
local bool bWasFound;
|
||||
|
||||
//Find the user's cached message list in collection of user's message lists, MessageLists
|
||||
bWasFound = false;
|
||||
MessageListIndex = MessageLists.Find('ToUniqueUserId', Message.ToUniqueUserId);
|
||||
if(MessageListIndex != INDEX_NONE)
|
||||
{
|
||||
UserMessageList = MessageLists[MessageListIndex];
|
||||
|
||||
// Search the array for any existing adding only when missing
|
||||
for (MessageIndex = 0; MessageIndex < UserMessageList.Messages.Length && !bWasFound; MessageIndex++)
|
||||
{
|
||||
bWasFound = Message.MessageId == UserMessageList.Messages[MessageIndex].MessageId;
|
||||
}
|
||||
// Add this one since it wasn't found
|
||||
if (!bWasFound)
|
||||
{
|
||||
AddAt = UserMessageList.Messages.Length;
|
||||
UserMessageList.Messages.Length = AddAt + 1;
|
||||
UserMessageList.Messages[AddAt] = Message;
|
||||
MessageLists[MessageListIndex] = UserMessageList;
|
||||
}
|
||||
|
||||
`log(`Location $ " MessageId: " $ UserMessageList.Messages[AddAt].MessageId);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add User with this first returned group since it wasn't found
|
||||
AddAt = MessageLists.Length;
|
||||
MessageLists.Length = AddAt +1;
|
||||
MessageLists[AddAt].ToUniqueUserId = Message.ToUniqueUserId;
|
||||
MessageLists[AddAt].Messages[0]=Message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Message By Id
|
||||
*
|
||||
* @param MessageId
|
||||
* @param Message
|
||||
*/
|
||||
function bool GetMessageById(string MessageId, out McpMessage Message)
|
||||
{
|
||||
local int MessageListsSize;
|
||||
local int MessageListsItr;
|
||||
local int MessageItr;
|
||||
|
||||
MessageListsSize = MessageLists.Length;
|
||||
|
||||
// Look at each MessageList (userId:array<McpMessage>) mapping
|
||||
for(MessageListsItr = 0; MessageListsItr < MessageListsSize; MessageListsItr++)
|
||||
{
|
||||
// Look to see if the MessageID Inside each MessageList
|
||||
for(MessageItr = 0; MessageItr < MessageLists[MessageListsItr].Messages.Length; MessageItr++)
|
||||
{
|
||||
if ( MessageLists[MessageListsItr].Messages[MessageItr].MessageId == MessageId )
|
||||
{
|
||||
// If it is in the message list set the out variable
|
||||
Message = MessageLists[MessageListsItr].Messages[MessageItr];
|
||||
// Return here because messageIds are unique so continuing the loop is pointless
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*Store group member in an object in memory instead of having to query the server again for it
|
||||
*
|
||||
* @param MessageContents Message payload
|
||||
* @param MessageId to be placed in the cache
|
||||
*/
|
||||
function bool CacheMessageContents(const out array<byte> MessageContents, String MessageId)
|
||||
{
|
||||
local int MessageContentsIndex;
|
||||
local bool bWasSuccessful;
|
||||
|
||||
bWasSuccessful = false;
|
||||
|
||||
// Have the variables been passed in properly
|
||||
if(MessageContents.Length > 0 && Len(MessageId) > 0)
|
||||
{
|
||||
MessageContentsIndex = MessageContentsList.Find('MessageId', MessageId);
|
||||
|
||||
if (MessageContentsIndex != INDEX_NONE)
|
||||
{
|
||||
MessageContentsList[MessageContentsIndex].MessageContents = MessageContents;
|
||||
bWasSuccessful = true;
|
||||
}else
|
||||
{
|
||||
MessageContentsIndex = MessageContentsList.Length;
|
||||
MessageContentsList.Length = MessageContentsList.Length+1;
|
||||
MessageContentsList[MessageContentsIndex].MessageId = MessageId;
|
||||
MessageContentsList[MessageContentsIndex]. MessageContents = MessageContents;
|
||||
|
||||
bWasSuccessful = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`Location@" Either the MessageContents or MessageId were not Specified.");
|
||||
}
|
||||
return bWasSuccessful;
|
||||
}
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
|
||||
/**
|
||||
* Returns whether it is okay to tick this object. E.g. objects being loaded in the background shouldn't be ticked
|
||||
* till they are finalized and unreachable objects cannot be ticked either.
|
||||
*
|
||||
* @return TRUE if tickable, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL IsTickable() const
|
||||
{
|
||||
// We cannot tick objects that are unreachable or are in the process of being loaded in the background.
|
||||
return !HasAnyFlags( RF_Unreachable | RF_AsyncLoading );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if an object should be ticked when the game is paused.
|
||||
*
|
||||
* @return always TRUE as networking needs to be ticked even when paused
|
||||
*/
|
||||
virtual UBOOL IsTickableWhenPaused() const
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs to be overridden by child classes
|
||||
*
|
||||
* @param ignored
|
||||
*/
|
||||
virtual void Tick(FLOAT);
|
||||
}
|
51
IpDrv/Classes/McpServerTimeBase.uc
Normal file
51
IpDrv/Classes/McpServerTimeBase.uc
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for requesting UTC time from the server
|
||||
*/
|
||||
class McpServerTimeBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String McpServerTimeClassName;
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpServerTimeBase CreateInstance()
|
||||
{
|
||||
local class<McpServerTimeBase> McpServerTimeBaseClass;
|
||||
local McpServerTimeBase NewInstance;
|
||||
|
||||
McpServerTimeBaseClass = class<McpServerTimeBase>(DynamicLoadObject(default.McpServerTimeClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpServerTimeBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpServerTimeBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request current UTC time from the server
|
||||
*/
|
||||
function QueryServerTime();
|
||||
|
||||
/**
|
||||
* Called when the time request from the server is complete
|
||||
*
|
||||
* @param bWasSuccessful true if server was contacted and a valid result received
|
||||
* @param DateTimeStr string representing UTC server time (yyyy.MM.dd-HH.mm.ss)
|
||||
* @param Error string representing the error condition
|
||||
*/
|
||||
delegate OnQueryServerTimeComplete(bool bWasSuccessful, string DateTimeStr, string Error);
|
||||
|
||||
/**
|
||||
* Retrieve cached timestamp from last server time query
|
||||
*
|
||||
* @return string representation of time (yyyy.MM.dd-HH.mm.ss)
|
||||
*/
|
||||
function String GetLastServerTime();
|
115
IpDrv/Classes/McpServerTimeManager.uc
Normal file
115
IpDrv/Classes/McpServerTimeManager.uc
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Implementation of interface for requesting UTC time from the server
|
||||
*/
|
||||
class McpServerTimeManager extends McpServerTimeBase;
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci)
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String TimeStampUrl;
|
||||
|
||||
/** String for the last valid server time response */
|
||||
var String LastTimeStamp;
|
||||
|
||||
/** HTTP request object that is used for the server time query. None when no request is in flight */
|
||||
var HttpRequestInterface HTTPRequestServerTime;
|
||||
|
||||
/**
|
||||
* Request current UTC time from the server
|
||||
*/
|
||||
function QueryServerTime()
|
||||
{
|
||||
local string Url,ErrorStr;
|
||||
local bool bPending;
|
||||
|
||||
if (HTTPRequestServerTime == None)
|
||||
{
|
||||
HTTPRequestServerTime = class'HttpFactory'.static.CreateRequest();
|
||||
if (HTTPRequestServerTime != None)
|
||||
{
|
||||
Url = GetBaseURL() $ TimeStampUrl $ GetAppAccessURL();
|
||||
HTTPRequestServerTime.SetURL(Url);
|
||||
HTTPRequestServerTime.SetVerb("GET");
|
||||
HTTPRequestServerTime.OnProcessRequestComplete = OnQueryServerTimeHTTPRequestComplete;
|
||||
if (HTTPRequestServerTime.ProcessRequest())
|
||||
{
|
||||
bPending = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorStr = "failed to start request, Url="$Url;
|
||||
`log(`StaticLocation@ErrorStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorStr = "last request is still being processed";
|
||||
`log(`StaticLocation@ErrorStr);
|
||||
}
|
||||
if (!bPending)
|
||||
{
|
||||
OnQueryServerTimeComplete(false,"",ErrorStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed for getting server time from Mcp
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnQueryServerTimeHTTPRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local string TimeStr,ResponseStr,ErrorStr;
|
||||
local int Idx;
|
||||
local bool bResult;
|
||||
|
||||
HTTPRequestServerTime = None;
|
||||
|
||||
if (bWasSuccessful &&
|
||||
Response != None)
|
||||
{
|
||||
if (Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
ResponseStr = Response.GetContentAsString();
|
||||
Idx = InStr(ResponseStr, "Timestamp=");
|
||||
if (Idx != INDEX_NONE)
|
||||
{
|
||||
// Example : TimeFormat="yyyy.MM.dd-HH.mm.ss" Timestamp=2011.10.29-03.19.49
|
||||
TimeStr = Mid(ResponseStr, Idx);
|
||||
Idx = InStr(ResponseStr, "=");
|
||||
TimeStr = Mid(TimeStr, Idx);
|
||||
|
||||
// cache last valid time stamp
|
||||
LastTimeStamp = TimeStr;
|
||||
|
||||
bResult = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorStr = "invalid server response code, status="$Response.GetResponseCode();
|
||||
`log(`StaticLocation@ErrorStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorStr = "no response";
|
||||
`log(`StaticLocation@ErrorStr);
|
||||
}
|
||||
OnQueryServerTimeComplete(bResult,TimeStr,ErrorStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve cached timestamp from last server time query
|
||||
*
|
||||
* @return string representation of time (yyyy.MM.dd-HH.mm.ss)
|
||||
*/
|
||||
function String GetLastServerTime()
|
||||
{
|
||||
return LastTimeStamp;
|
||||
}
|
62
IpDrv/Classes/McpServiceBase.uc
Normal file
62
IpDrv/Classes/McpServiceBase.uc
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Common functionality for all MCP web service interfaces
|
||||
*/
|
||||
class McpServiceBase extends Object
|
||||
native
|
||||
config(Engine);
|
||||
|
||||
/** Class to load and instantiate for the handling configuration options for MCP services */
|
||||
var config string McpConfigClassName;
|
||||
/** Contains all the configuration options common to MCP services. Protocol, URL, access key, etc */
|
||||
var McpServiceConfig McpConfig;
|
||||
|
||||
/* Initialize always called after constructing a new MCP service subclass instance via its factory method */
|
||||
event Init()
|
||||
{
|
||||
local class<McpServiceConfig> McpConfigClass;
|
||||
|
||||
`log("Loading McpServerBase McpConfigClass:" $ default.McpConfigClassName);
|
||||
// load the configuration class and instantiate it
|
||||
McpConfigClass = class<McpServiceConfig>(DynamicLoadObject(default.McpConfigClassName,class'Class'));
|
||||
if (McpConfigClass != None)
|
||||
{
|
||||
McpConfig = new McpConfigClass;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Base protocol and domain for communicating with MCP server
|
||||
*/
|
||||
function string GetBaseURL()
|
||||
{
|
||||
return McpConfig.Protocol $ "://" $ McpConfig.Domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Access rights for app including title id
|
||||
*/
|
||||
function string GetAppAccessURL()
|
||||
{
|
||||
return "?appKey=" $ McpConfig.AppKey $ "&appSecret=" $ McpConfig.AppSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the URL for auth ticket access
|
||||
*
|
||||
* @param McpId user id to build auth access for
|
||||
*
|
||||
* @return URL parameters needed for user auth ticket usage
|
||||
*/
|
||||
function string GetUserAuthURL(string McpId)
|
||||
{
|
||||
local string AuthTicket;
|
||||
|
||||
AuthTicket = McpConfig.GetUserAuthTicket(McpId);
|
||||
if (Len(AuthTicket) > 0)
|
||||
{
|
||||
return "&authTicket=" $ AuthTicket;
|
||||
}
|
||||
return "";
|
||||
}
|
28
IpDrv/Classes/McpServiceConfig.uc
Normal file
28
IpDrv/Classes/McpServiceConfig.uc
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Base configuration for all MCP web services
|
||||
*/
|
||||
class McpServiceConfig extends Object
|
||||
config(Engine);
|
||||
|
||||
/** The protocol information to prefix when building the url (e.g. https) */
|
||||
var config string Protocol;
|
||||
|
||||
/** The domain to prefix when building the url (e.g. localhost:8080) */
|
||||
var config string Domain;
|
||||
|
||||
/** AppKey for access privileges to the online services for the current title. Not config so it can be hidden */
|
||||
var string AppKey;
|
||||
|
||||
/** AppSecret for access privileges to the online services for the current title. Not config so it can be hidden */
|
||||
var string AppSecret;
|
||||
|
||||
/**
|
||||
* Get the auth ticket for a user
|
||||
*
|
||||
* @param McpId user id to get auth access for
|
||||
*
|
||||
* @return auth ticket string
|
||||
*/
|
||||
function string GetUserAuthTicket(string McpId);
|
946
IpDrv/Classes/McpUserCloudFileDownload.uc
Normal file
946
IpDrv/Classes/McpUserCloudFileDownload.uc
Normal file
@ -0,0 +1,946 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* User file downloading implementation via web service requests to MCP servers
|
||||
*/
|
||||
class McpUserCloudFileDownload extends McpServiceBase
|
||||
native
|
||||
config(Engine)
|
||||
implements(UserCloudFileInterface)
|
||||
dependson(OnlineSubsystem);
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci);
|
||||
|
||||
/** The URL to use when requesting enumeration for list of a user's files */
|
||||
var config String EnumerateCloudFilesUrl;
|
||||
|
||||
/** The URL to use when reading the contents of a file */
|
||||
var config String ReadCloudFileUrl;
|
||||
|
||||
/** The URL to use when writing the contents of a file */
|
||||
var config String WriteCloudFileUrl;
|
||||
|
||||
/** The URL to use when deleting a user cloud file */
|
||||
var config String DeleteCloudFileUrl;
|
||||
|
||||
/** Info about a user file entry as enumerated from Mcp */
|
||||
struct native McpUserCloudFileInfo extends OnlineSubsystem.EmsFile
|
||||
{
|
||||
/** Date/time when file was created on server */
|
||||
var string CreationDate;
|
||||
/** Date/time when file was updated on server */
|
||||
var string LastUpdateDate;
|
||||
/** Compression type used to encode file. Ie. LZO,GZIP,etc */
|
||||
var string CompressionType;
|
||||
};
|
||||
|
||||
/** Info for a single user's cloud files */
|
||||
struct native McpUserCloudFilesEntry
|
||||
{
|
||||
/** Id for user owning cloud files */
|
||||
var string UserId;
|
||||
/** list of files that have started downloads */
|
||||
var array<TitleFileWeb> DownloadedFiles;
|
||||
/** list of files available to download for this user */
|
||||
var array<McpUserCloudFileInfo> EnumeratedFiles;
|
||||
/** HTTP request that is in flight for enumerating files */
|
||||
var HttpRequestInterface HTTPRequestEnumerateFiles;
|
||||
};
|
||||
/** List of cloud file requests for all known users */
|
||||
var private array<McpUserCloudFilesEntry> UserCloudFileRequests;
|
||||
|
||||
/** The list of delegates to notify when the user file list enumeration is complete */
|
||||
var private array<delegate<OnEnumerateUserFilesComplete> > EnumerateUserFilesCompleteDelegates;
|
||||
/** The list of delegates to notify when a user file read is complete */
|
||||
var private array<delegate<OnReadUserFileComplete> > ReadUserFileCompleteDelegates;
|
||||
/** The list of delegates to notify when a user file write is complete */
|
||||
var private array<delegate<OnWriteUserFileComplete> > WriteUserFileCompleteDelegates;
|
||||
/** The list of delegates to notify when a user file delete is complete */
|
||||
var private array<delegate<OnDeleteUserFileComplete> > DeleteUserFileCompleteDelegates;
|
||||
|
||||
/**
|
||||
* Copies the file data into the specified buffer for the specified file
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param FileName the name of the file to read
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the data was copied, false otherwise
|
||||
*/
|
||||
function bool GetFileContents(string UserId,string FileName,out array<byte> FileContents)
|
||||
{
|
||||
local bool bResult;
|
||||
local int EntryIdx,FileIdx;
|
||||
|
||||
// Entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
if (EntryIdx != INDEX_NONE)
|
||||
{
|
||||
// Check to see if file has been downloaded
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Find('Filename',FileName);
|
||||
if (FileIdx != INDEX_NONE &&
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState == OERS_Done)
|
||||
{
|
||||
// Copy contents
|
||||
FileContents = UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Data;
|
||||
bResult = true;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the set of downloaded files if possible (no async tasks outstanding)
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
*
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
function bool ClearFiles(string UserId)
|
||||
{
|
||||
local bool bResult;
|
||||
local int EntryIdx,FileIdx;
|
||||
|
||||
// Entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
if (EntryIdx != INDEX_NONE)
|
||||
{
|
||||
// Check to see if there files still pending download
|
||||
for (FileIdx=0; FileIdx < UserCloudFileRequests[EntryIdx].DownloadedFiles.Length; FileIdx++)
|
||||
{
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState == OERS_InProgress)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Length = 0;
|
||||
bResult = true;
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the cached data for this file if it is not being downloaded currently
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param FileName the name of the file to remove from the cache
|
||||
*
|
||||
* @return true if it could be deleted, false if it could not
|
||||
*/
|
||||
function bool ClearFile(string UserId,string FileName)
|
||||
{
|
||||
local bool bResult;
|
||||
local int EntryIdx,FileIdx;
|
||||
|
||||
// Entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
if (EntryIdx != INDEX_NONE)
|
||||
{
|
||||
// Check to see if file is still pending download
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Find('Filename',FileName);
|
||||
if (FileIdx != INDEX_NONE &&
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState != OERS_InProgress)
|
||||
{
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Remove(FileIdx,1);
|
||||
bResult = true;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a list of available User files from the network store
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
*
|
||||
*/
|
||||
function EnumerateUserFiles(string UserId)
|
||||
{
|
||||
local int EntryIdx;
|
||||
local string Url;
|
||||
local bool bPending;
|
||||
|
||||
// Find entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
// Add an entry for the user if one doesn't exist
|
||||
if (EntryIdx == INDEX_NONE)
|
||||
{
|
||||
EntryIdx = UserCloudFileRequests.Length;
|
||||
UserCloudFileRequests.Length = EntryIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].UserId = UserId;
|
||||
}
|
||||
|
||||
// If HTTPRequestEnumerateFiles already exists then don't need to do anything
|
||||
if (UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles == None)
|
||||
{
|
||||
// Create a new HTTP request
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles = class'HttpFactory'.static.CreateRequest();
|
||||
if (UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles != None)
|
||||
{
|
||||
// build URL for requesting list of files from MCP
|
||||
Url = GetBaseURL() $ EnumerateCloudFilesUrl $ GetAppAccessURL()
|
||||
$"&uniqueUserId=" $ UserId;
|
||||
// Configure the HTTP request and start it
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles.SetURL(Url);
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles.SetVerb("GET");
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles.OnProcessRequestComplete = OnHTTPRequestEnumerateUserFilesComplete;
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles.ProcessRequest();
|
||||
// kicked off successfully
|
||||
bPending = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"User files already being enumerated for"
|
||||
$" UserId="$UserId);
|
||||
}
|
||||
// failed to kick off the request, always call the completion delegate
|
||||
if (!bPending)
|
||||
{
|
||||
CallEnumerateUserFileCompleteDelegates(false,UserId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the HTTP request/response has completed for retrieving a list of ems files for a user
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnHTTPRequestEnumerateUserFilesComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int EntryIdx,JsonIdx;
|
||||
local string JsonString,UserId;
|
||||
local JsonObject ParsedJson;
|
||||
local bool bResult;
|
||||
|
||||
// Find entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('HTTPRequestEnumerateFiles',Request);
|
||||
if (EntryIdx != INDEX_NONE)
|
||||
{
|
||||
UserId = UserCloudFileRequests[EntryIdx].UserId;
|
||||
if (bWasSuccessful &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
// Clear out any existing entries
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles.Length = 0;
|
||||
JsonString = Response.GetContentAsString();
|
||||
if (JsonString != "")
|
||||
{
|
||||
`log(`location@""
|
||||
$" JsonString="$JsonString);
|
||||
|
||||
// Parse JSON response
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonString);
|
||||
// Fill in the list of enumerated user files
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles.Length = ParsedJson.ObjectArray.Length;
|
||||
for (JsonIdx=0; JsonIdx < ParsedJson.ObjectArray.Length; JsonIdx++)
|
||||
{
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].Filename = ParsedJson.ObjectArray[JsonIdx].GetStringValue("file_name");
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].FileSize = int(ParsedJson.ObjectArray[JsonIdx].GetStringValue("file_size"));
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].DlName = ParsedJson.ObjectArray[JsonIdx].GetStringValue("file_name");
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].CreationDate = ParsedJson.ObjectArray[JsonIdx].GetStringValue("creation_date");
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].LastUpdateDate = ParsedJson.ObjectArray[JsonIdx].GetStringValue("last_update_time");
|
||||
UserCloudFileRequests[EntryIdx].EnumeratedFiles[JsonIdx].CompressionType = ParsedJson.ObjectArray[JsonIdx].GetStringValue("compression_type");
|
||||
}
|
||||
}
|
||||
bResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Failed to enumerate files for"
|
||||
$" UserId="$UserCloudFileRequests[EntryIdx].UserId
|
||||
$" URL="$Request.GetURL());
|
||||
}
|
||||
// done with the HTTP request so clear it out
|
||||
UserCloudFileRequests[EntryIdx].HTTPRequestEnumerateFiles = None;
|
||||
}
|
||||
// call the completion delegate since the HTTP request completed
|
||||
CallEnumerateUserFileCompleteDelegates(bResult,UserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate fired when the list of files has been returned from the network store
|
||||
*
|
||||
* @param bWasSuccessful whether the file list was successful or not
|
||||
* @param UserId User owning the storage
|
||||
*
|
||||
*/
|
||||
delegate OnEnumerateUserFilesComplete(bool bWasSuccessful,string UserId);
|
||||
|
||||
/**
|
||||
* Calls delegates on file enumeration completion
|
||||
*/
|
||||
private function CallEnumerateUserFileCompleteDelegates(bool bWasSuccessful,string UserId)
|
||||
{
|
||||
local int Index;
|
||||
local delegate<OnEnumerateUserFilesComplete> CallDelegate;
|
||||
|
||||
// Call the completion delegate for receiving the file list
|
||||
for (Index=0; Index < EnumerateUserFilesCompleteDelegates.Length; Index++)
|
||||
{
|
||||
CallDelegate = EnumerateUserFilesCompleteDelegates[Index];
|
||||
if (CallDelegate != None)
|
||||
{
|
||||
CallDelegate(bWasSuccessful,UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when all files have been enumerated
|
||||
*
|
||||
* @param EnumerateUserFileCompleteDelegate the delegate to add
|
||||
*
|
||||
*/
|
||||
function AddEnumerateUserFileCompleteDelegate(delegate<OnEnumerateUserFilesComplete> EnumerateUserFileCompleteDelegate)
|
||||
{
|
||||
if (EnumerateUserFilesCompleteDelegates.Find(EnumerateUserFileCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
EnumerateUserFilesCompleteDelegates.AddItem(EnumerateUserFileCompleteDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param EnumerateUserFileCompleteDelegate the delegate to remove
|
||||
*
|
||||
*/
|
||||
function ClearEnumerateUserFileCompleteDelegate(delegate<OnEnumerateUserFilesComplete> EnumerateUserFileCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = EnumerateUserFilesCompleteDelegates.Find(EnumerateUserFileCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
EnumerateUserFilesCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of User files that was returned by the network store
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param UserFiles out array of file metadata
|
||||
*
|
||||
*/
|
||||
function GetUserFileList(string UserId,out array<EmsFile> UserFiles)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
if (EntryIdx != INDEX_NONE)
|
||||
{
|
||||
UserFiles.Length = UserCloudFileRequests[EntryIdx].EnumeratedFiles.Length;
|
||||
for (FileIdx=0; FileIdx < UserCloudFileRequests[EntryIdx].EnumeratedFiles.Length; FileIdx++)
|
||||
{
|
||||
UserFiles[FileIdx].DLName = UserCloudFileRequests[EntryIdx].EnumeratedFiles[FileIdx].DLName;
|
||||
UserFiles[FileIdx].Filename = UserCloudFileRequests[EntryIdx].EnumeratedFiles[FileIdx].Filename;
|
||||
UserFiles[FileIdx].FileSize = UserCloudFileRequests[EntryIdx].EnumeratedFiles[FileIdx].FileSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UserFiles.Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous read of the specified user file from the network platform's file store
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param FileToRead the name of the file to read
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
function bool ReadUserFile(string UserId,string FileName)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string Url;
|
||||
local bool bPending;
|
||||
|
||||
if (Len(UserId) > 0 &&
|
||||
Len(FileName) > 0)
|
||||
{
|
||||
// Find entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
// Add an entry for the user if one doesn't exist
|
||||
if (EntryIdx == INDEX_NONE)
|
||||
{
|
||||
EntryIdx = UserCloudFileRequests.Length;
|
||||
UserCloudFileRequests.Length = EntryIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].UserId = UserId;
|
||||
}
|
||||
// Find existing file entry
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Find('Filename',FileName);
|
||||
// Add file entry if one doesn't exist
|
||||
if (FileIdx == INDEX_NONE)
|
||||
{
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Length;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Length = FileIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename = FileName;
|
||||
}
|
||||
// Make sure there is no operation already occurring for the file
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState != OERS_InProgress &&
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest == None)
|
||||
{
|
||||
// Update entry for the file being read
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_InProgress;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Data.Length = 0;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest != None)
|
||||
{
|
||||
// build URL for writing a single file to user's cloud storage
|
||||
Url = GetBaseURL() $ ReadCloudFileUrl $ GetAppAccessURL()
|
||||
$"&uniqueUserId=" $ UserId
|
||||
$"&fileName=" $ FileName;
|
||||
|
||||
// Configure the HTTP request and start it
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetURL(Url);
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetVerb("GET");
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.OnProcessRequestComplete = OnHTTPRequestReadUserFileComplete;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.ProcessRequest();
|
||||
|
||||
// kicked off successfully
|
||||
bPending = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"File operation already in progress"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Invalid parameters"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName);
|
||||
}
|
||||
// failed to kick off the request, always call the completion delegate
|
||||
if (!bPending)
|
||||
{
|
||||
CallReadUserFileCompleteDelegates(false,UserId,FileName);
|
||||
}
|
||||
return bPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the HTTP request/response has completed for reading a user cloud file
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnHTTPRequestReadUserFileComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string FileName,UserId;
|
||||
local bool bResult;
|
||||
local array<byte> FileContents;
|
||||
|
||||
// Find entry for the user/file
|
||||
GetUserFileIndexForRequest(Request,EntryIdx,FileIdx);
|
||||
if (EntryIdx != INDEX_NONE &&
|
||||
FileIdx != INDEX_NONE)
|
||||
{
|
||||
UserId = UserCloudFileRequests[EntryIdx].UserId;
|
||||
FileName = UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename;
|
||||
// check for valid response for the request
|
||||
if (bWasSuccessful &&
|
||||
Response != None &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
// copy contents
|
||||
Response.GetContent(FileContents);
|
||||
// and, copy again
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Data = FileContents;
|
||||
// mark file entry as successfully completed
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_Done;
|
||||
bResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// mark file entry as failed
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_Failed;
|
||||
}
|
||||
// clear out the request as it's done
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest = None;
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Couldn't find entry index");
|
||||
}
|
||||
// call the completion delegate since the HTTP request completed
|
||||
CallReadUserFileCompleteDelegates(bResult,UserId,FileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate fired when a user file read from the network platform's storage is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file read was successful or not
|
||||
* @param UserId User owning the storage
|
||||
* @param FileName the name of the file this was for
|
||||
*
|
||||
*/
|
||||
delegate OnReadUserFileComplete(bool bWasSuccessful,string UserId,string FileName);
|
||||
|
||||
/**
|
||||
* Calls delegates on file read completion
|
||||
*/
|
||||
private function CallReadUserFileCompleteDelegates(bool bWasSuccessful,string UserId,string FileName)
|
||||
{
|
||||
local int Index;
|
||||
local delegate<OnReadUserFileComplete> CallDelegate;
|
||||
|
||||
// Call the completion delegate for receiving the file list
|
||||
for (Index=0; Index < ReadUserFileCompleteDelegates.Length; Index++)
|
||||
{
|
||||
CallDelegate = ReadUserFileCompleteDelegates[Index];
|
||||
if (CallDelegate != None)
|
||||
{
|
||||
CallDelegate(bWasSuccessful,UserId,FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been read
|
||||
*
|
||||
* @param ReadUserFileCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddReadUserFileCompleteDelegate(delegate<OnReadUserFileComplete> ReadUserFileCompleteDelegate)
|
||||
{
|
||||
if (ReadUserFileCompleteDelegates.Find(ReadUserFileCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
ReadUserFileCompleteDelegates.AddItem(ReadUserFileCompleteDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param ReadUserFileCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearReadUserFileCompleteDelegate(delegate<OnReadUserFileComplete> ReadUserFileCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = ReadUserFileCompleteDelegates.Find(ReadUserFileCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
ReadUserFileCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous write of the specified user file to the network platform's file store
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param FileToWrite the name of the file to write
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
function bool WriteUserFile(string UserId,string FileName,const out array<byte> FileContents)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string Url;
|
||||
local bool bPending;
|
||||
|
||||
if (Len(UserId) > 0 &&
|
||||
Len(FileName) > 0 &&
|
||||
FileContents.Length > 0)
|
||||
{
|
||||
// Find entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
// Add an entry for the user if one doesn't exist
|
||||
if (EntryIdx == INDEX_NONE)
|
||||
{
|
||||
EntryIdx = UserCloudFileRequests.Length;
|
||||
UserCloudFileRequests.Length = EntryIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].UserId = UserId;
|
||||
}
|
||||
// Find existing file entry
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Find('Filename',FileName);
|
||||
// Add file entry if one doesn't exist
|
||||
if (FileIdx == INDEX_NONE)
|
||||
{
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Length;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Length = FileIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename = FileName;
|
||||
}
|
||||
// Make sure there is no operation already occurring for the file
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState != OERS_InProgress &&
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest == None)
|
||||
{
|
||||
// Update entry for the file being written
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_InProgress;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Data = FileContents;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest != None)
|
||||
{
|
||||
// build URL for writing a single file to user's cloud storage
|
||||
Url = GetBaseURL() $ WriteCloudFileUrl $ GetAppAccessURL()
|
||||
$"&uniqueUserId=" $ UserId
|
||||
$"&fileName=" $ FileName;
|
||||
|
||||
// Configure the HTTP request and start it
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetURL(Url);
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetVerb("POST");
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetHeader("Content-Type","multipart/form-data");
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetContent(FileContents);
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.OnProcessRequestComplete = OnHTTPRequestWriteUserFileComplete;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.ProcessRequest();
|
||||
|
||||
// kicked off successfully
|
||||
bPending = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"File operation already in progress"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Invalid parameters"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName
|
||||
$" FileContents="$FileContents.Length);
|
||||
}
|
||||
// failed to kick off the request, always call the completion delegate
|
||||
if (!bPending)
|
||||
{
|
||||
CallWriteUserFileCompleteDelegates(false,UserId,FileName);
|
||||
}
|
||||
return bPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to retrieve the user index and file index corresponding to an HTTP file download request
|
||||
*
|
||||
* @param UserIdx [out] entry for the user, -1 if not found
|
||||
* @param FileIdx [out] entry for the user file, -1 if not found
|
||||
*/
|
||||
private function GetUserFileIndexForRequest(HttpRequestInterface Request, out int UserIdx, out int FileIdx)
|
||||
{
|
||||
for (UserIdx=0; UserIdx < UserCloudFileRequests.Length; UserIdx++)
|
||||
{
|
||||
FileIdx = UserCloudFileRequests[UserIdx].DownloadedFiles.Find('HTTPRequest',Request);
|
||||
if (FileIdx != INDEX_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FileIdx == INDEX_NONE)
|
||||
{
|
||||
UserIdx = INDEX_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the HTTP request/response has completed for writing a user cloud file
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnHTTPRequestWriteUserFileComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string FileName,UserId;
|
||||
local bool bResult;
|
||||
|
||||
// Find entry for the user/file
|
||||
GetUserFileIndexForRequest(Request,EntryIdx,FileIdx);
|
||||
if (EntryIdx != INDEX_NONE &&
|
||||
FileIdx != INDEX_NONE)
|
||||
{
|
||||
UserId = UserCloudFileRequests[EntryIdx].UserId;
|
||||
FileName = UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename;
|
||||
// check for valid response for the request
|
||||
if (bWasSuccessful &&
|
||||
Response != None &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
// mark file entry as successfully completed
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_Done;
|
||||
bResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// mark file entry as failed
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_Failed;
|
||||
}
|
||||
// clear out the request as it's done
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest = None;
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Couldn't find entry index");
|
||||
}
|
||||
// call the completion delegate since the HTTP request completed
|
||||
CallWriteUserFileCompleteDelegates(bResult,UserId,FileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate fired when a user file write to the network platform's storage is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file Write was successful or not
|
||||
* @param UserId User owning the storage
|
||||
* @param FileName the name of the file this was for
|
||||
*
|
||||
*/
|
||||
delegate OnWriteUserFileComplete(bool bWasSuccessful,string UserId,string FileName);
|
||||
|
||||
/**
|
||||
* Calls delegates on file write completion
|
||||
*/
|
||||
private function CallWriteUserFileCompleteDelegates(bool bWasSuccessful,string UserId,string FileName)
|
||||
{
|
||||
local int Index;
|
||||
local delegate<OnWriteUserFileComplete> CallDelegate;
|
||||
|
||||
// Call the completion delegate for receiving the file list
|
||||
for (Index=0; Index < WriteUserFileCompleteDelegates.Length; Index++)
|
||||
{
|
||||
CallDelegate = WriteUserFileCompleteDelegates[Index];
|
||||
if (CallDelegate != None)
|
||||
{
|
||||
CallDelegate(bWasSuccessful,UserId,FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been written
|
||||
*
|
||||
* @param WriteUserFileCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddWriteUserFileCompleteDelegate(delegate<OnWriteUserFileComplete> WriteUserFileCompleteDelegate)
|
||||
{
|
||||
if (WriteUserFileCompleteDelegates.Find(WriteUserFileCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
WriteUserFileCompleteDelegates.AddItem(WriteUserFileCompleteDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param WriteUserFileCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearWriteUserFileCompleteDelegate(delegate<OnWriteUserFileComplete> WriteUserFileCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = WriteUserFileCompleteDelegates.Find(WriteUserFileCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
WriteUserFileCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous delete of the specified user file from the network platform's file store
|
||||
*
|
||||
* @param UserId User owning the storage
|
||||
* @param FileToRead the name of the file to read
|
||||
* @param bShouldCloudDelete whether to delete the file from the cloud
|
||||
* @param bShouldLocallyDelete whether to delete the file locally
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
function bool DeleteUserFile(string UserId,string FileName,bool bShouldCloudDelete,bool bShouldLocallyDelete)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string Url;
|
||||
local bool bPending;
|
||||
|
||||
if (Len(UserId) > 0 &&
|
||||
Len(FileName) > 0 &&
|
||||
bShouldCloudDelete)
|
||||
{
|
||||
// Find entry for the user
|
||||
EntryIdx = UserCloudFileRequests.Find('UserId',UserId);
|
||||
// Add an entry for the user if one doesn't exist
|
||||
if (EntryIdx == INDEX_NONE)
|
||||
{
|
||||
EntryIdx = UserCloudFileRequests.Length;
|
||||
UserCloudFileRequests.Length = EntryIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].UserId = UserId;
|
||||
}
|
||||
// Find existing file entry
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Find('Filename',FileName);
|
||||
// Add file entry if one doesn't exist
|
||||
if (FileIdx == INDEX_NONE)
|
||||
{
|
||||
FileIdx = UserCloudFileRequests[EntryIdx].DownloadedFiles.Length;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Length = FileIdx+1;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename = FileName;
|
||||
}
|
||||
// Make sure there is no operation already occurring for the file
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState != OERS_InProgress &&
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest == None)
|
||||
{
|
||||
// Update entry for the file being written
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].AsyncState = OERS_InProgress;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Data.Length = 0;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest != None)
|
||||
{
|
||||
// build URL for writing a single file to user's cloud storage
|
||||
Url = GetBaseURL() $ DeleteCloudFileUrl $ GetAppAccessURL()
|
||||
$"&uniqueUserId=" $ UserId
|
||||
$"&fileName=" $ FileName;
|
||||
|
||||
// Configure the HTTP request and start it
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetURL(Url);
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.SetVerb("DELETE");
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.OnProcessRequestComplete = OnHTTPRequestDeleteUserFileComplete;
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].HTTPRequest.ProcessRequest();
|
||||
|
||||
// kicked off successfully
|
||||
bPending = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"File operation already in progress"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Invalid parameters"
|
||||
$" UserId="$UserId
|
||||
$" FileName="$FileName);
|
||||
}
|
||||
// failed to kick off the request, always call the completion delegate
|
||||
if (!bPending)
|
||||
{
|
||||
if (bShouldCloudDelete)
|
||||
{
|
||||
CallDeleteUserFileCompleteDelegates(false,UserId,FileName);
|
||||
}
|
||||
else if (bShouldLocallyDelete)
|
||||
{
|
||||
//@todo - handle local cache deletion
|
||||
CallDeleteUserFileCompleteDelegates(true,UserId,FileName);
|
||||
}
|
||||
}
|
||||
return bPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the HTTP request/response has completed for deleting a user cloud file
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
private function OnHTTPRequestDeleteUserFileComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int EntryIdx,FileIdx;
|
||||
local string FileName,UserId;
|
||||
local bool bResult;
|
||||
|
||||
// Find entry for the user/file
|
||||
GetUserFileIndexForRequest(Request,EntryIdx,FileIdx);
|
||||
if (EntryIdx != INDEX_NONE &&
|
||||
FileIdx != INDEX_NONE)
|
||||
{
|
||||
UserId = UserCloudFileRequests[EntryIdx].UserId;
|
||||
FileName = UserCloudFileRequests[EntryIdx].DownloadedFiles[FileIdx].Filename;
|
||||
// check for valid response for the request
|
||||
if (bWasSuccessful &&
|
||||
Response != None &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
bResult = true;
|
||||
}
|
||||
// clear out the file entry
|
||||
UserCloudFileRequests[EntryIdx].DownloadedFiles.Remove(FileIdx,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Couldn't find entry index");
|
||||
}
|
||||
// call the completion delegate since the HTTP request completed
|
||||
CallDeleteUserFileCompleteDelegates(bResult,UserId,FileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate fired when a user file delete from the network platform's storage is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file read was successful or not
|
||||
* @param UserId User owning the storage
|
||||
* @param FileName the name of the file this was for
|
||||
*/
|
||||
delegate OnDeleteUserFileComplete(bool bWasSuccessful,string UserId,string FileName);
|
||||
|
||||
/**
|
||||
* Calls delegates on file delete completion
|
||||
*/
|
||||
private function CallDeleteUserFileCompleteDelegates(bool bWasSuccessful,string UserId,string FileName)
|
||||
{
|
||||
local int Index;
|
||||
local delegate<OnDeleteUserFileComplete> CallDelegate;
|
||||
|
||||
// Call the completion delegate for receiving the file list
|
||||
for (Index=0; Index < DeleteUserFileCompleteDelegates.Length; Index++)
|
||||
{
|
||||
CallDelegate = DeleteUserFileCompleteDelegates[Index];
|
||||
if (CallDelegate != None)
|
||||
{
|
||||
CallDelegate(bWasSuccessful,UserId,FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been deleted
|
||||
*
|
||||
* @param DeleteUserFileCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddDeleteUserFileCompleteDelegate(delegate<OnDeleteUserFileComplete> DeleteUserFileCompleteDelegate)
|
||||
{
|
||||
if (DeleteUserFileCompleteDelegates.Find(DeleteUserFileCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
DeleteUserFileCompleteDelegates.AddItem(DeleteUserFileCompleteDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param DeleteUserFileCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearDeleteUserFileCompleteDelegate(delegate<OnDeleteUserFileComplete> DeleteUserFileCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = DeleteUserFileCompleteDelegates.Find(DeleteUserFileCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
DeleteUserFileCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/** clears all delegates for e.g. end of level cleanup */
|
||||
function ClearAllDelegates()
|
||||
{
|
||||
EnumerateUserFilesCompleteDelegates.length = 0;
|
||||
ReadUserFileCompleteDelegates.length = 0;
|
||||
WriteUserFileCompleteDelegates.length = 0;
|
||||
DeleteUserFileCompleteDelegates.length = 0;
|
||||
}
|
327
IpDrv/Classes/McpUserInventoryBase.uc
Normal file
327
IpDrv/Classes/McpUserInventoryBase.uc
Normal file
@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* This is the base interface for manipulating a user's inventory
|
||||
*/
|
||||
class McpUserInventoryBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config string McpUserInventoryClassName;
|
||||
|
||||
/**
|
||||
* Item property which was generated from attribute tables for the item when it was instanced
|
||||
*/
|
||||
struct McpInventoryItemAttribute
|
||||
{
|
||||
/** The unique id for the attribute */
|
||||
var string AttributeId;
|
||||
/** The instanced value for this attribute */
|
||||
var int Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instanced inventory item aquired via (purchase,earn,etc)
|
||||
*/
|
||||
struct McpInventoryItem
|
||||
{
|
||||
/** The unique id of the item instance */
|
||||
var string InstanceItemId;
|
||||
/** The id of the original template this item was instanced from */
|
||||
var string GlobalItemId;
|
||||
/** Total earned amount of this item */
|
||||
var int Quantity;
|
||||
/** Total IAP aquired amount of this item */
|
||||
var int QuantityIAP;
|
||||
/** Scalar that persists from when the item was instanced */
|
||||
var float Scalar;
|
||||
/** The last time (UTC) when this item was modified */
|
||||
var string LastUpdateTime;
|
||||
/** Attributes that were generated when instanced to define the item properties */
|
||||
var array<McpInventoryItemAttribute> Attributes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows for specifying list of global items and quantities
|
||||
*/
|
||||
struct McpInventoryItemContainer
|
||||
{
|
||||
/** The id of the template item */
|
||||
var string GlobalItemId;
|
||||
/** Total amount of this item */
|
||||
var int Quantity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds a single user's save slot information for inventory items
|
||||
*/
|
||||
struct McpInventorySaveSlot
|
||||
{
|
||||
/** The owner of this save slot */
|
||||
var string OwningMcpId;
|
||||
/** The save slot id */
|
||||
var string SaveSlotId;
|
||||
/** The list of inventory items in this save slot */
|
||||
var array<McpInventoryItem> Items;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpUserInventoryBase CreateInstance()
|
||||
{
|
||||
local class<McpUserInventoryBase> McpUserInventoryBaseClass;
|
||||
local McpUserInventoryBase NewInstance;
|
||||
|
||||
McpUserInventoryBaseClass = class<McpUserInventoryBase>(DynamicLoadObject(default.McpUserInventoryClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpUserInventoryBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpUserInventoryBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new save slot for the user
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that is being create
|
||||
* @param ParentSaveSlotId [optional] if specified then all existing IAP purchases
|
||||
* from the parent save slot carry over to the newly created save slot
|
||||
*/
|
||||
function CreateSaveSlot(string McpId, string SaveSlotId, optional string ParentSaveSlotId);
|
||||
|
||||
/**
|
||||
* Called once save slot creation completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that was created
|
||||
* @param bWasSuccessful whether the creation succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnCreateSaveSlotComplete(string McpId, string SaveSlotId, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Deletes an existing user save slot and all the inventory items that belong to it
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that is being deleted
|
||||
*/
|
||||
function DeleteSaveSlot(string McpId, string SaveSlotId);
|
||||
|
||||
/**
|
||||
* Called once save slot deletion completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that was deleted
|
||||
* @param bWasSuccessful whether the deletion succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteSaveSlotComplete(string McpId, string SaveSlotId, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Query for the list of save slots that exist for the user
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
*/
|
||||
function QuerySaveSlotList(string McpId);
|
||||
|
||||
/**
|
||||
* Get the cached list of save slot ids for a user
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
*
|
||||
* @return the list of save slot ids for the user
|
||||
*/
|
||||
function array<string> GetSaveSlotList(string McpId);
|
||||
|
||||
/**
|
||||
* Called once save slot enumeration query completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param bWasSuccessful whether the deletion succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQuerySaveSlotListComplete(string McpId, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Query for the list of current inventory items that exist for the user within a save slot
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot to find items for
|
||||
*/
|
||||
function QueryInventoryItems(string McpId, string SaveSlotId);
|
||||
|
||||
/**
|
||||
* Called once inventory items enumeration query completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot to find items for
|
||||
* @param bWasSuccessful whether the deletion succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryInventoryItemsComplete(string McpId, string SaveSlotId, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Access the currently cached inventory items that have been downloaded for a user's save slot
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot to find items for
|
||||
* @param OutInventoryItems the list of inventory items that should be filled in
|
||||
*/
|
||||
function GetInventoryItems(string McpId, string SaveSlotId, out array<McpInventoryItem> OutInventoryItems);
|
||||
|
||||
/**
|
||||
* Access the currently cached inventory item that have been downloaded for a user's save slot by global id
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot to find items for
|
||||
* @param InstanceItemId id for the item that is to be found
|
||||
* @param OutInventoryItem the item that should be filled in
|
||||
*
|
||||
* @return true if found
|
||||
*/
|
||||
function bool GetInventoryItem(string McpId, string SaveSlotId, string InstanceItemId, out McpInventoryItem OutInventoryItem);
|
||||
|
||||
/**
|
||||
* Purchase item and add it to the user's inventory
|
||||
* Server determines the transaction is valid
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param GlobalItemId id for the item that is being purchased
|
||||
* @param PurchaseItemIds list of instance item ids to be used when making the purchase
|
||||
* @param StoreVersion current known version # of backend store by client
|
||||
* @param Scalar to associate with the item and pass back to client
|
||||
*/
|
||||
function PurchaseItem(string McpId, string SaveSlotId, string GlobalItemId, array<string> PurchaseItemIds, int Quantity, int StoreVersion, float Scalar);
|
||||
|
||||
/**
|
||||
* Called once item purchase completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param GlobalItemId id for the item that is being purchased
|
||||
* @param UpdatedItemIds list of global item ids for inventory items that were updated
|
||||
* @param bWasSuccessful whether the item purchase succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnPurchaseItemComplete(string McpId, string SaveSlotId, string GlobalItemId, array<string> UpdatedItemIds, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Sell item and remove it from the user's inventory
|
||||
* Server determines the transaction is valid
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being sold
|
||||
* @param StoreVersion current known version # of backend store by client
|
||||
* @param ExpectedResultItems optional list of items/quantities that the client expects to receive from the sale
|
||||
*/
|
||||
function SellItem(string McpId, string SaveSlotId, string InstanceItemId, int Quantity, int StoreVersion, optional const out array<McpInventoryItemContainer> ExpectedResultItems);
|
||||
|
||||
/**
|
||||
* Called once item sell completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being sold
|
||||
* @param UpdatedItemIds list of global item ids for inventory items that were updated
|
||||
* @param bWasSuccessful whether the item sell succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnSellItemComplete(string McpId, string SaveSlotId, string InstanceItemId, array<string> UpdatedItemIds, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Earn item and add it to the user's inventory
|
||||
* Server determines the transaction is valid
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param GlobalItemId id for the item that is being earned
|
||||
* @param Quantity number of the items that were earned
|
||||
* @param StoreVersion current known version # of backend store by client
|
||||
*/
|
||||
function EarnItem(string McpId, string SaveSlotId, string GlobalItemId, int Quantity, int StoreVersion);
|
||||
|
||||
/**
|
||||
* Called once item earn completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param GlobalItemId id for the item that is being earned
|
||||
* @param UpdatedItemIds list of global item ids for inventory items that were updated
|
||||
* @param bWasSuccessful whether the item earn succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnEarnItemComplete(string McpId, string SaveSlotId, string GlobalItemId, array<string> UpdatedItemIds, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Consume item and remove it's consumed quantity from the user's inventory
|
||||
* Server determines the transaction is valid
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being consumed
|
||||
* @param Quantity number of the items that were consumed
|
||||
* @param StoreVersion current known version # of backend store by client
|
||||
*/
|
||||
function ConsumeItem(string McpId, string SaveSlotId, string InstanceItemId, int Quantity, int StoreVersion);
|
||||
|
||||
/**
|
||||
* Called once item consume completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being consumed
|
||||
* @param UpdatedItemIds list of global item ids for inventory items that were updated
|
||||
* @param bWasSuccessful whether the item consume succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnConsumeItemComplete(string McpId, string SaveSlotId, string InstanceItemId, array<string> UpdatedItemIds, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Delete item and remove it from the user's inventory
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being deleted
|
||||
* @param StoreVersion current known version # of backend store by client
|
||||
*/
|
||||
function DeleteItem(string McpId, string SaveSlotId, string InstanceItemId, int StoreVersion);
|
||||
|
||||
/**
|
||||
* Called once item delete completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item is in
|
||||
* @param InstanceItemId id for the item that is being deleted
|
||||
* @param bWasSuccessful whether the item delete succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteItemComplete(string McpId, string SaveSlotId, string InstanceItemId, bool bWasSuccessful, string Error);
|
||||
|
||||
/**
|
||||
* Record an IAP (In App Purchase) by sending receipt to server for validation
|
||||
* Results in list of items being added to inventory if successful
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item(s) will be placed in once validated
|
||||
* @param Receipt IAP receipt to validate the purchase on the server
|
||||
*/
|
||||
function RecordIap(string McpId, string SaveSlotId, string Receipt);
|
||||
|
||||
/**
|
||||
* Called once record IAP operation completes
|
||||
*
|
||||
* @param McpId the id of the user that made the request
|
||||
* @param SaveSlotId the save slot that the item(s) will be placed in once validated
|
||||
* @param UpdatedItemIds list of item ids for inventory items that were updated
|
||||
* @param bWasSuccessful whether the IAP succeeded and was validated or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnRecordIapComplete(string McpId, string SaveSlotId, array<string> UpdatedItemIds, bool bWasSuccessful, string Error);
|
1523
IpDrv/Classes/McpUserInventoryManager.uc
Normal file
1523
IpDrv/Classes/McpUserInventoryManager.uc
Normal file
File diff suppressed because it is too large
Load Diff
692
IpDrv/Classes/McpUserManager.uc
Normal file
692
IpDrv/Classes/McpUserManager.uc
Normal file
@ -0,0 +1,692 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Concrete implementation for mapping McpIds to external account ids
|
||||
*/
|
||||
class McpUserManager extends McpUserManagerBase;
|
||||
|
||||
/**
|
||||
* Holds the set of user statuses that were downloaded
|
||||
*/
|
||||
var array<McpUserStatus> UserStatuses;
|
||||
|
||||
/** The URL to use when making user registration requests to generate an id */
|
||||
var config String RegisterUserMcpUrl;
|
||||
|
||||
/** The URL to use when making user registration requests via Facebook id/token */
|
||||
var config String RegisterUserFacebookUrl;
|
||||
|
||||
/** The URL to use when making user query requests */
|
||||
var config String QueryUserUrl;
|
||||
|
||||
/** The URL to use when making querying for multiple user statuses */
|
||||
var config String QueryUsersUrl;
|
||||
|
||||
/** The URL to use when making user deletion requests */
|
||||
var config String DeleteUserUrl;
|
||||
|
||||
/** The URL to use when making user Facebook authentication requests */
|
||||
var config String FacebookAuthUrl;
|
||||
|
||||
/** The URL to use when making user authentication requests */
|
||||
var config String McpAuthUrl;
|
||||
|
||||
/** Holds the state information for an outstanding user request */
|
||||
struct UserRequest
|
||||
{
|
||||
/** The MCP id that was returned by the backend */
|
||||
var string McpId;
|
||||
/** The request object for this request */
|
||||
var HttpRequestInterface Request;
|
||||
};
|
||||
|
||||
/** The set of add mapping requests that are pending */
|
||||
var array<HttpRequestInterface> RegisterUserRequests;
|
||||
|
||||
/** The set of query requests that are pending */
|
||||
var array<HttpRequestInterface> QueryUsersRequests;
|
||||
|
||||
/** The set of delete requests that are pending */
|
||||
var array<UserRequest> DeleteUserRequests;
|
||||
|
||||
/** The set of delete requests that are pending */
|
||||
var array<HttpRequestInterface> AuthUserRequests;
|
||||
|
||||
/**
|
||||
* Creates a new user mapped to the UDID that is specified
|
||||
*/
|
||||
function RegisterUserGenerated()
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ RegisterUserMcpUrl $ GetAppAccessURL();
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnRegisterUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = RegisterUserRequests.Length;
|
||||
RegisterUserRequests.Length = AddAt + 1;
|
||||
RegisterUserRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start RegisterUser web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a newly generated or existing Mcp id to the Facebook id/token requested.
|
||||
* Note: Facebook id authenticity is verified via the token
|
||||
*
|
||||
* @param FacebookId user's FB id to generate Mcp id for
|
||||
* @param FacebookAuthToken FB auth token obtained by signing in to FB
|
||||
*/
|
||||
function RegisterUserFacebook(string FacebookId, string FacebookAuthToken)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ RegisterUserFacebookUrl $ GetAppAccessURL() $
|
||||
"&facebookId=" $ FacebookId $
|
||||
"&facebookToken=" $ FacebookAuthToken;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnRegisterUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = RegisterUserRequests.Length;
|
||||
RegisterUserRequests.Length = AddAt + 1;
|
||||
RegisterUserRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start RegisterUserFacebook web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the user json
|
||||
*
|
||||
* @param JsonPayload the json data to parse
|
||||
*
|
||||
* @return the index of the user that was parsed
|
||||
*/
|
||||
protected function int ParseUser(String JsonPayload)
|
||||
{
|
||||
local JsonObject ParsedJson;
|
||||
local int UserIndex;
|
||||
local string McpId;
|
||||
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonPayload);
|
||||
// If it doesn't have this field, this is bogus JSON
|
||||
if (ParsedJson.HasKey("unique_user_id"))
|
||||
{
|
||||
// Grab the McpId first, since that is our key
|
||||
McpId = ParsedJson.GetStringValue("unique_user_id");
|
||||
// See if we already have a user
|
||||
UserIndex = UserStatuses.Find('McpId', McpId);
|
||||
if (UserIndex == INDEX_NONE)
|
||||
{
|
||||
// Not stored yet, so add one
|
||||
UserIndex = UserStatuses.Length;
|
||||
UserStatuses.Length = UserIndex + 1;
|
||||
UserStatuses[UserIndex].McpId = McpId;
|
||||
}
|
||||
// These values are only there for the owner and not friends
|
||||
if (ParsedJson.HasKey("client_secret"))
|
||||
{
|
||||
UserStatuses[UserIndex].SecretKey = ParsedJson.GetStringValue("client_secret");
|
||||
}
|
||||
if (ParsedJson.HasKey("ticket"))
|
||||
{
|
||||
UserStatuses[UserIndex].Ticket = ParsedJson.GetStringValue("ticket");
|
||||
}
|
||||
if (ParsedJson.HasKey("udid"))
|
||||
{
|
||||
UserStatuses[UserIndex].UDID = ParsedJson.GetStringValue("udid");
|
||||
}
|
||||
// These are always there
|
||||
UserStatuses[UserIndex].RegisteredDate = ParsedJson.GetStringValue("registered_date");
|
||||
UserStatuses[UserIndex].LastActiveDate = ParsedJson.GetStringValue("last_active_date");
|
||||
UserStatuses[UserIndex].DaysInactive = ParsedJson.GetIntValue("days_inactive");
|
||||
UserStatuses[UserIndex].bIsBanned = ParsedJson.GetBoolValue("is_banned");
|
||||
}
|
||||
else
|
||||
{
|
||||
UserIndex = INDEX_NONE;
|
||||
}
|
||||
// Tell the caller which record was updated
|
||||
return UserIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the register user result and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnRegisterUserRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local int UserIndex;
|
||||
local string ResponseString;
|
||||
local string McpId;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = RegisterUserRequests.Find(Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// The response string is the JSON payload for the user
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Parse the JSON payload into a user
|
||||
UserIndex = ParseUser(ResponseString);
|
||||
if (UserIndex == INDEX_NONE)
|
||||
{
|
||||
bWasSuccessful = false;
|
||||
}
|
||||
}
|
||||
McpId = bWasSuccessful ? UserStatuses[UserIndex].McpId : "";
|
||||
// Notify anyone waiting on this
|
||||
OnRegisterUserComplete(McpId,
|
||||
bWasSuccessful,
|
||||
ResponseString);
|
||||
`Log("Register user McpId(" $ McpId $ ") was successful " $
|
||||
bWasSuccessful $ " with ResponseCode(" $ ResponseCode $ ")");
|
||||
RegisterUserRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates a user is who they claim themselves to be using Facebook as the authority
|
||||
*
|
||||
* @param FacebookId the Facebook user that is being authenticated
|
||||
* @param FacebookToken the secret that authenticates the user
|
||||
* @param UDID the device id the user is logging in from
|
||||
*/
|
||||
function AuthenticateUserFacebook(string FacebookId, string FacebookToken, string UDID)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ FacebookAuthUrl $ GetAppAccessURL() $
|
||||
"&facebookId=" $ FacebookId $
|
||||
"&facebookToken=" $ FacebookToken $
|
||||
"&udid=" $ UDID;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnAuthenticateUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = AuthUserRequests.Length;
|
||||
AuthUserRequests.Length = AddAt + 1;
|
||||
AuthUserRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start AuthenticateUserFacebook web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates a user is the same as the one that was registered
|
||||
*
|
||||
* @param McpId the user that is being authenticated
|
||||
* @param ClientSecret the secret that authenticates the user
|
||||
* @param UDID the device id the user is logging in from
|
||||
*/
|
||||
function AuthenticateUserMCP(string McpId, string ClientSecret, string UDID)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ McpAuthUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&clientSecret=" $ ClientSecret $
|
||||
"&udid=" $ UDID;
|
||||
|
||||
`log("Started Authenticate, url: "$Url);
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("POST");
|
||||
Request.OnProcessRequestComplete = OnAuthenticateUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = AuthUserRequests.Length;
|
||||
AuthUserRequests.Length = AddAt + 1;
|
||||
AuthUserRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start AuthenticateUserMCP web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnAuthenticateUserRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local int UserIndex;
|
||||
local string ResponseString;
|
||||
local string McpId;
|
||||
local string Ticket;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = AuthUserRequests.Find(Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// The response string is the JSON payload for the user
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Parse the JSON payload into a user
|
||||
UserIndex = ParseUser(ResponseString);
|
||||
if (UserIndex == INDEX_NONE)
|
||||
{
|
||||
bWasSuccessful = false;
|
||||
}
|
||||
}
|
||||
// If it was successful, grab the id and ticket for notifying the caller
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
McpId = UserStatuses[UserIndex].McpId;
|
||||
Ticket = UserStatuses[UserIndex].Ticket;
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnAuthenticateUserComplete(McpId, Ticket, bWasSuccessful, ResponseString);
|
||||
`Log("Authenticate user was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ") and Ticket ("$Ticket$")");
|
||||
AuthUserRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the backend for the status of a users
|
||||
*
|
||||
* @param McpId the id of the user to get the status for
|
||||
* @param bShouldUpdateLastActive if true, the act of getting the status updates the active time stamp
|
||||
*/
|
||||
function QueryUser(string McpId, optional bool bShouldUpdateLastActive)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none && McpId != "")
|
||||
{
|
||||
Url = GetBaseURL() $ QueryUserUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId $
|
||||
"&updateLastActive=" $ bShouldUpdateLastActive;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("GET");
|
||||
Request.OnProcessRequestComplete = OnQueryUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = QueryUsersRequests.Length;
|
||||
QueryUsersRequests.Length = AddAt + 1;
|
||||
QueryUsersRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start QueryUser web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnQueryUserRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local int UserIndex;
|
||||
local string ResponseString;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = QueryUsersRequests.Find(Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// The response string is the JSON payload for the user
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Parse the JSON payload into a user
|
||||
UserIndex = ParseUser(ResponseString);
|
||||
if (UserIndex == INDEX_NONE)
|
||||
{
|
||||
bWasSuccessful = false;
|
||||
}
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnQueryUsersComplete(bWasSuccessful, ResponseString);
|
||||
`Log("Query user was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
QueryUsersRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the backend for the status of a list of users
|
||||
*
|
||||
* @param McpIds the set of ids to get read the status of
|
||||
*/
|
||||
function QueryUsers(const out array<String> McpIds)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
local string JsonPayload;
|
||||
local int Index;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ QueryUsersUrl $ GetAppAccessURL();
|
||||
|
||||
// Make a json string from our list of ids
|
||||
JsonPayload = "[ ";
|
||||
for (Index = 0; Index < McpIds.Length; Index++)
|
||||
{
|
||||
JsonPayload $= "\"" $ McpIds[Index] $ "\"";
|
||||
// Only add the string if this isn't the last item
|
||||
if (Index + 1 < McpIds.Length)
|
||||
{
|
||||
JsonPayload $= ",";
|
||||
}
|
||||
}
|
||||
JsonPayload $= " ]";
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetContentAsString(JsonPayload);
|
||||
Request.SetVerb("POST");
|
||||
Request.SetHeader("Content-Type","multipart/form-data");
|
||||
Request.OnProcessRequestComplete = OnQueryUsersRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = QueryUsersRequests.Length;
|
||||
QueryUsersRequests.Length = AddAt + 1;
|
||||
QueryUsersRequests[AddAt] = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start QueryUsers web request for URL(" $ Url $ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the json which contains an array of user data
|
||||
*
|
||||
* @param JsonPayload the json data to parse
|
||||
*
|
||||
* @return the index of the user that was parsed
|
||||
*/
|
||||
protected function ParseUsers(String JsonPayload)
|
||||
{
|
||||
local JsonObject ParsedJson;
|
||||
local int JsonIndex;
|
||||
local int UserIndex;
|
||||
local string McpId;
|
||||
|
||||
ParsedJson = class'JsonObject'.static.DecodeJson(JsonPayload);
|
||||
// Parse each user, adding them if needed
|
||||
for (JsonIndex = 0; JsonIndex < ParsedJson.ObjectArray.Length; JsonIndex++)
|
||||
{
|
||||
// If it doesn't have this field, this is bogus JSON
|
||||
if (ParsedJson.HasKey("unique_user_id"))
|
||||
{
|
||||
// Grab the McpId first, since that is our key
|
||||
McpId = ParsedJson.GetStringValue("unique_user_id");
|
||||
// See if we already have a user
|
||||
UserIndex = UserStatuses.Find('McpId', McpId);
|
||||
if (UserIndex == INDEX_NONE)
|
||||
{
|
||||
// Not stored yet, so add one
|
||||
UserIndex = UserStatuses.Length;
|
||||
UserStatuses.Length = UserIndex + 1;
|
||||
UserStatuses[UserIndex].McpId = McpId;
|
||||
}
|
||||
// These values are only there for the owner and not friends
|
||||
if (ParsedJson.HasKey("client_secret"))
|
||||
{
|
||||
UserStatuses[UserIndex].SecretKey = ParsedJson.GetStringValue("client_secret");
|
||||
}
|
||||
if (ParsedJson.HasKey("ticket"))
|
||||
{
|
||||
UserStatuses[UserIndex].Ticket = ParsedJson.GetStringValue("ticket");
|
||||
}
|
||||
if (ParsedJson.HasKey("udid"))
|
||||
{
|
||||
UserStatuses[UserIndex].UDID = ParsedJson.GetStringValue("udid");
|
||||
}
|
||||
// These are always there
|
||||
UserStatuses[UserIndex].RegisteredDate = ParsedJson.GetStringValue("registered_date");
|
||||
UserStatuses[UserIndex].LastActiveDate = ParsedJson.GetStringValue("last_active_date");
|
||||
UserStatuses[UserIndex].DaysInactive = ParsedJson.GetIntValue("days_inactive");
|
||||
UserStatuses[UserIndex].bIsBanned = ParsedJson.GetBoolValue("is_banned");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the request/response has completed. Used to process the returned data and notify any
|
||||
* registered delegate
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnQueryUsersRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int ResponseCode;
|
||||
local string ResponseString;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = QueryUsersRequests.Find(Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// The response string is the JSON payload for the user
|
||||
ResponseString = Response.GetContentAsString();
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Parse the JSON payload into a user
|
||||
ParseUsers(ResponseString);
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnQueryUsersComplete(bWasSuccessful, ResponseString);
|
||||
`Log("Query users was successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
QueryUsersRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of user statuses queried so far
|
||||
*
|
||||
* @param Users the out array that gets the copied data
|
||||
*/
|
||||
function GetUsers(out array<McpUserStatus> Users)
|
||||
{
|
||||
Users = UserStatuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user status entry for a single user
|
||||
*
|
||||
* @param McpId the id of the user that we want to find
|
||||
* @param User the out result to copy user data
|
||||
*
|
||||
* @return true if user was found
|
||||
*/
|
||||
function bool GetUser(string McpId, out McpUserStatus User)
|
||||
{
|
||||
local int UserIndex;
|
||||
|
||||
UserIndex = UserStatuses.Find('McpId', McpId);
|
||||
if (UserIndex != INDEX_NONE)
|
||||
{
|
||||
User = UserStatuses[UserIndex];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all data for a user
|
||||
*
|
||||
* @param McpId the user that is being expunged from the system
|
||||
*/
|
||||
function DeleteUser(string McpId)
|
||||
{
|
||||
local String Url;
|
||||
local HttpRequestInterface Request;
|
||||
local int AddAt;
|
||||
|
||||
Request = class'HttpFactory'.static.CreateRequest();
|
||||
if (Request != none)
|
||||
{
|
||||
Url = GetBaseURL() $ DeleteUserUrl $ GetAppAccessURL() $
|
||||
"&uniqueUserId=" $ McpId;
|
||||
|
||||
// Build our web request with the above URL
|
||||
Request.SetURL(Url);
|
||||
Request.SetVerb("DELETE");
|
||||
Request.OnProcessRequestComplete = OnDeleteUserRequestComplete;
|
||||
// Store off the data for reporting later
|
||||
AddAt = DeleteUserRequests.Length;
|
||||
DeleteUserRequests.Length = AddAt + 1;
|
||||
DeleteUserRequests[AddAt].McpId = McpId;
|
||||
DeleteUserRequests[AddAt].Request = Request;
|
||||
|
||||
// Now kick off the request
|
||||
if (!Request.ProcessRequest())
|
||||
{
|
||||
`Log("Failed to start DeleteUser web request for URL(" $ Url $ ")");
|
||||
}
|
||||
`Log("URL is " $ Url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the delete request completes
|
||||
*
|
||||
* @param Request the request object that was used
|
||||
* @param Response the response object that was generated
|
||||
* @param bWasSuccessful whether or not the request completed successfully
|
||||
*/
|
||||
function OnDeleteUserRequestComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bWasSuccessful)
|
||||
{
|
||||
local int Index;
|
||||
local int UserIndex;
|
||||
local int ResponseCode;
|
||||
|
||||
// Search for the corresponding entry in the array
|
||||
Index = DeleteUserRequests.Find('Request', Request);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
ResponseCode = 500;
|
||||
if (Response != none)
|
||||
{
|
||||
ResponseCode = Response.GetResponseCode();
|
||||
}
|
||||
// Both of these need to be true for the request to be a success
|
||||
bWasSuccessful = bWasSuccessful && ResponseCode == 200;
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Delete this user from our list
|
||||
UserIndex = UserStatuses.Find('McpId', DeleteUserRequests[Index].McpId);
|
||||
if (UserIndex != INDEX_NONE)
|
||||
{
|
||||
UserStatuses.Remove(UserIndex, 1);
|
||||
}
|
||||
}
|
||||
// Notify anyone waiting on this
|
||||
OnDeleteUserComplete(bWasSuccessful,
|
||||
Response.GetContentAsString());
|
||||
`Log("Delete user for URL(" $ Request.GetURL() $ ") successful " $ bWasSuccessful $
|
||||
" with ResponseCode(" $ ResponseCode $ ")");
|
||||
DeleteUserRequests.Remove(Index,1);
|
||||
}
|
||||
}
|
160
IpDrv/Classes/McpUserManagerBase.uc
Normal file
160
IpDrv/Classes/McpUserManagerBase.uc
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Provides the interface for registering and querying users
|
||||
*/
|
||||
class McpUserManagerBase extends McpServiceBase
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/** The class name to use in the factory method to create our instance */
|
||||
var config String McpUserManagerClassName;
|
||||
|
||||
/**
|
||||
* Holds the status information for a MCP user
|
||||
*/
|
||||
struct McpUserStatus
|
||||
{
|
||||
/** The McpId of the user */
|
||||
var String McpId;
|
||||
/** The secret key (private field, owner only) */
|
||||
var String SecretKey;
|
||||
/** The auth ticket for this user (private field, owner only) */
|
||||
var String Ticket;
|
||||
/** The device id this user was registered on (private field, owner only) */
|
||||
var string UDID;
|
||||
/** The date the user was registered on */
|
||||
var string RegisteredDate;
|
||||
/** The last activity date for this user */
|
||||
var string LastActiveDate;
|
||||
/** The number of days inactive */
|
||||
var int DaysInactive;
|
||||
/** Whether this user has been banned from playing this game */
|
||||
var bool bIsBanned;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the object that implements this interface or none if missing or failed to create/load
|
||||
*/
|
||||
final static function McpUserManagerBase CreateInstance()
|
||||
{
|
||||
local class<McpUserManagerBase> McpUserManagerBaseClass;
|
||||
local McpUserManagerBase NewInstance;
|
||||
|
||||
McpUserManagerBaseClass = class<McpUserManagerBase>(DynamicLoadObject(default.McpUserManagerClassName,class'Class'));
|
||||
// If the class was loaded successfully, create a new instance of it
|
||||
if (McpUserManagerBaseClass != None)
|
||||
{
|
||||
NewInstance = new McpUserManagerBaseClass;
|
||||
NewInstance.Init();
|
||||
}
|
||||
|
||||
return NewInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user
|
||||
*/
|
||||
function RegisterUserGenerated();
|
||||
|
||||
/**
|
||||
* Maps a newly generated or existing Mcp id to the Facebook id/token requested.
|
||||
* Note: Facebook id authenticity is verified via the token
|
||||
*
|
||||
* @param FacebookId user's FB id to generate Mcp id for
|
||||
* @param UDID the UDID for the device
|
||||
* @param FacebookAuthToken FB auth token obtained by signing in to FB
|
||||
*/
|
||||
function RegisterUserFacebook(string FacebookId, string FacebookAuthToken);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that was just created
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnRegisterUserComplete(string McpId, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Authenticates a user is who they claim themselves to be using Facebook as the authority
|
||||
*
|
||||
* @param FacebookId the Facebook user that is being authenticated
|
||||
* @param FacebookToken the secret that authenticates the user
|
||||
* @param UDID the device id the user is logging in from
|
||||
*/
|
||||
function AuthenticateUserFacebook(string FacebookId, string FacebookToken, string UDID);
|
||||
|
||||
/**
|
||||
* Authenticates a user is the same as the one that was registered
|
||||
*
|
||||
* @param McpId the user that is being authenticated
|
||||
* @param ClientSecret the secret that authenticates the user
|
||||
* @param UDID the device id the user is logging in from
|
||||
*/
|
||||
function AuthenticateUserMcp(string McpId, string ClientSecret, string UDID);
|
||||
|
||||
/**
|
||||
* Called once the results come back from the server to indicate success/failure of the operation
|
||||
*
|
||||
* @param McpId the id of the user that was just authenticated
|
||||
* @param Token the security token to use for subsequent user based calls
|
||||
* @param bWasSuccessful whether the mapping succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnAuthenticateUserComplete(string McpId, string Token, bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Queries the backend for the status of a users
|
||||
*
|
||||
* @param McpId the id of the user to get the status for
|
||||
* @param bShouldUpdateLastActive if true, the act of getting the status updates the active time stamp
|
||||
*/
|
||||
function QueryUser(string McpId, optional bool bShouldUpdateLastActive);
|
||||
|
||||
/**
|
||||
* Queries the backend for the status of a list of users
|
||||
*
|
||||
* @param McpIds the set of ids to get read the status of
|
||||
*/
|
||||
function QueryUsers(const out array<String> McpIds);
|
||||
|
||||
/**
|
||||
* Called once the query results come back from the server to indicate success/failure of the request
|
||||
*
|
||||
* @param bWasSuccessful whether the query succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnQueryUsersComplete(bool bWasSuccessful, String Error);
|
||||
|
||||
/**
|
||||
* Returns the set of user statuses queried so far
|
||||
*
|
||||
* @param Users the out array that gets the copied data
|
||||
*/
|
||||
function GetUsers(out array<McpUserStatus> Users);
|
||||
|
||||
/**
|
||||
* Gets the user status entry for a single user
|
||||
*
|
||||
* @param McpId the id of the user that we want to find
|
||||
* @param User the out result to copy user data
|
||||
*
|
||||
* @return true if user was found
|
||||
*/
|
||||
function bool GetUser(string McpId, out McpUserStatus User);
|
||||
|
||||
/**
|
||||
* Deletes all data for a user
|
||||
*
|
||||
* @param McpId the user that is being expunged from the system
|
||||
*/
|
||||
function DeleteUser(string McpId);
|
||||
|
||||
/**
|
||||
* Called once the delete request completes
|
||||
*
|
||||
* @param bWasSuccessful whether the request succeeded or not
|
||||
* @param Error string information about the error (if an error)
|
||||
*/
|
||||
delegate OnDeleteUserComplete(bool bWasSuccessful, String Error);
|
258
IpDrv/Classes/MeshBeacon.uc
Normal file
258
IpDrv/Classes/MeshBeacon.uc
Normal file
@ -0,0 +1,258 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is the base class for the client/host mesh beacon classes.
|
||||
*/
|
||||
class MeshBeacon extends Object
|
||||
native
|
||||
inherits(FTickableObject)
|
||||
config(Engine);
|
||||
|
||||
/** Packet ids used to communicate between host & client mesh beacons */
|
||||
enum EMeshBeaconPacketType
|
||||
{
|
||||
// 0 packet type treated as undefined
|
||||
MB_Packet_UnknownType,
|
||||
// Sent by a client when a new connection is established and to send history from past bandwidth tests
|
||||
MB_Packet_ClientNewConnectionRequest,
|
||||
// Sent by a client when starting a bandwidth test to the host. Immediately followed by dummy buffer based on test size
|
||||
MB_Packet_ClientBeginBandwidthTest,
|
||||
// Sent by a client in response to a host request to create a new session
|
||||
MB_Packet_ClientCreateNewSessionResponse,
|
||||
// Sent by the host to acknowledge it received the connection request with bandwidth history from a client
|
||||
MB_Packet_HostNewConnectionResponse,
|
||||
// Sent by the host to a client to request a new bandwidth test
|
||||
MB_Packet_HostBandwidthTestRequest,
|
||||
// Sent by the host to a client that had initiated the bandwidth test, followed by the results of the test
|
||||
MB_Packet_HostCompletedBandwidthTest,
|
||||
// Sent by the host to all clients to initiate travel to a new session
|
||||
MB_Packet_HostTravelRequest,
|
||||
// Sent by the host to a client to request a new session to be created on the client. A list of players is sent to register with the new session.
|
||||
MB_Packet_HostCreateNewSessionRequest,
|
||||
// Used to flag dummy buffers sent for bandwidth testing
|
||||
MB_Packet_DummyData,
|
||||
// Sent periodically to tell the host/client that the other end is there
|
||||
MB_Packet_Heartbeat
|
||||
};
|
||||
|
||||
/** Result of a new client connection request */
|
||||
enum EMeshBeaconConnectionResult
|
||||
{
|
||||
// Client was able to connect successfully
|
||||
MB_ConnectionResult_Succeeded,
|
||||
// Client already has a connection, duplicate
|
||||
MB_ConnectionResult_Duplicate,
|
||||
// Client connection request failed due to timeout
|
||||
MB_ConnectionResult_Timeout,
|
||||
// Client connection request failed due to socket error
|
||||
MB_ConnectionResult_Error
|
||||
};
|
||||
|
||||
/** State of current bandwidth testing on a client connection */
|
||||
enum EMeshBeaconBandwidthTestState
|
||||
{
|
||||
// Bandwidth test for a connection has not been started/completed yet
|
||||
MB_BandwidthTestState_NotStarted,
|
||||
// Bandwidth test has been requested for the connection but the start request hasn't been to the client yet
|
||||
MB_BandwidthTestState_RequestPending,
|
||||
// Start request has been sent to client from host but test hasn't been started by client yet
|
||||
MB_BandwidthTestState_StartPending,
|
||||
// Test has been started for client and is currently in progress
|
||||
MB_BandwidthTestState_InProgress,
|
||||
// Test has completed for the client successfully
|
||||
MB_BandwidthTestState_Completed,
|
||||
// Test never completely finished, but didn't error either. Bandwidth results based on incomplete data
|
||||
MB_BandwidthTestState_Incomplete,
|
||||
// Test never finished due to timeout waiting for client.
|
||||
MB_BandwidthTestState_Timeout,
|
||||
// Test was started but never completed due to error
|
||||
MB_BandwidthTestState_Error
|
||||
};
|
||||
|
||||
/** Result of a bandwidth test between host/clietn connection */
|
||||
enum EMeshBeaconBandwidthTestResult
|
||||
{
|
||||
// Test has completed for the client successfully
|
||||
MB_BandwidthTestResult_Succeeded,
|
||||
// Test never finished due to timeout waiting for client.
|
||||
MB_BandwidthTestResult_Timeout,
|
||||
// Test was started but never completed due to error
|
||||
MB_BandwidthTestResult_Error
|
||||
};
|
||||
|
||||
/** Bandwidth tests that are supported */
|
||||
enum EMeshBeaconBandwidthTestType
|
||||
{
|
||||
// Test for rate at which data can be uploaded
|
||||
MB_BandwidthTestType_Upstream,
|
||||
// Test for rate at which data can be downloaded
|
||||
MB_BandwidthTestType_Downstream,
|
||||
// Test for time it takes to send/receive a packet
|
||||
MB_BandwidthTestType_RoundtripLatency,
|
||||
};
|
||||
|
||||
/** Bandwidth data for a connection */
|
||||
struct native ConnectionBandwidthStats
|
||||
{
|
||||
/** Upstream rate in bytes per second */
|
||||
var int UpstreamRate;
|
||||
/** Downstream rate in bytes per second */
|
||||
var int DownstreamRate;
|
||||
/** Roundtrip latency in milliseconds */
|
||||
var int RoundtripLatency;
|
||||
|
||||
structcpptext
|
||||
{
|
||||
|
||||
/** Constructors */
|
||||
FConnectionBandwidthStats() {}
|
||||
FConnectionBandwidthStats(EEventParm)
|
||||
{
|
||||
appMemzero(this, sizeof(FConnectionBandwidthStats));
|
||||
}
|
||||
/**
|
||||
* Serialize from NBO buffer to FConnectionBandwidthStats
|
||||
*/
|
||||
friend FNboSerializeFromBuffer& operator>>(FNboSerializeFromBuffer& Ar,FConnectionBandwidthStats& BandwidthStats);
|
||||
/**
|
||||
* Serialize from FConnectionBandwidthStats to NBO buffer
|
||||
*/
|
||||
friend FNboSerializeToBuffer& operator<<(FNboSerializeToBuffer& Ar,const FConnectionBandwidthStats& BandwidthStats);
|
||||
}
|
||||
};
|
||||
|
||||
/** Player that is to be a member of a new session */
|
||||
struct native PlayerMember
|
||||
{
|
||||
/** The team the player is on */
|
||||
var int TeamNum;
|
||||
/** The skill rating of the player */
|
||||
var int Skill;
|
||||
/** The unique net id for the player */
|
||||
var UniqueNetId NetId;
|
||||
|
||||
structcpptext
|
||||
{
|
||||
/** Constructors */
|
||||
FPlayerMember() {}
|
||||
FPlayerMember(EEventParm)
|
||||
{
|
||||
appMemzero(this, sizeof(FPlayerMember));
|
||||
}
|
||||
/**
|
||||
* Serialize from NBO buffer to FPlayerMember
|
||||
*/
|
||||
friend FNboSerializeFromBuffer& operator>>(FNboSerializeFromBuffer& Ar,FPlayerMember& PlayerEntry);
|
||||
/**
|
||||
* Serialize from FPlayerMember to NBO buffer
|
||||
*/
|
||||
friend FNboSerializeToBuffer& operator<<(FNboSerializeToBuffer& Ar,const FPlayerMember& PlayerEntry);
|
||||
}
|
||||
};
|
||||
|
||||
/** The port that the mesh beacon will listen on */
|
||||
var config int MeshBeaconPort;
|
||||
|
||||
/** The object that is used to send/receive data with the remote host/client */
|
||||
var native transient pointer Socket{FSocket};
|
||||
|
||||
/** Used to determine whether to use deferred destruction or not */
|
||||
var transient bool bIsInTick;
|
||||
|
||||
/** The maximum amount of time to pass between heartbeat packets being sent */
|
||||
var config float HeartbeatTimeout;
|
||||
|
||||
/** The elapsed time that has passed since the last heartbeat */
|
||||
var float ElapsedHeartbeatTime;
|
||||
|
||||
/** True if the beacon should be destroyed at the end of the tick */
|
||||
var transient bool bWantsDeferredDestroy;
|
||||
|
||||
/** Whether to the socket(s) or not (not during travel) */
|
||||
var bool bShouldTick;
|
||||
|
||||
/** The name to use when logging (helps debugging) */
|
||||
var name BeaconName;
|
||||
|
||||
/** Size of socket send buffer. Once this is filled then socket blocks on the next send. */
|
||||
var config int SocketSendBufferSize;
|
||||
|
||||
/** Size of socket recv buffer. Once this is filled then socket blocks on the next recv. */
|
||||
var config int SocketReceiveBufferSize;
|
||||
|
||||
/** Maximum size of data that is allowed to be sent for bandwidth testing */
|
||||
var config int MaxBandwidthTestBufferSize;
|
||||
|
||||
/** Minimum size of data that is required to be sent for acurate bandwidth testing */
|
||||
var config int MinBandwidthTestBufferSize;
|
||||
|
||||
/** Maximum time allowed to send the buffer for bandwidth testing */
|
||||
var config float MaxBandwidthTestSendTime;
|
||||
|
||||
/** Maximum time allowed to receive the buffer for bandwidth testing */
|
||||
var config float MaxBandwidthTestReceiveTime;
|
||||
|
||||
/** Maximum number of entries allowed for the bandwidth history of a client connection */
|
||||
var config int MaxBandwidthHistoryEntries;
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
|
||||
/**
|
||||
* Returns whether it is okay to tick this object. E.g. objects being loaded in the background shouldn't be ticked
|
||||
* till they are finalized and unreachable objects cannot be ticked either.
|
||||
*
|
||||
* @return TRUE if tickable, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL IsTickable() const
|
||||
{
|
||||
// We cannot tick objects that are unreachable or are in the process of being loaded in the background.
|
||||
return !HasAnyFlags( RF_Unreachable | RF_AsyncLoading );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if an object should be ticked when the game is paused.
|
||||
*
|
||||
* @return always TRUE as networking needs to be ticked even when paused
|
||||
*/
|
||||
virtual UBOOL IsTickableWhenPaused() const
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Sends a heartbeat packet to the specified socket
|
||||
*
|
||||
* @param Socket the socket to send the data on
|
||||
*
|
||||
* @return TRUE if it sent ok, FALSE if there was an error
|
||||
*/
|
||||
UBOOL SendHeartbeat(FSocket* Socket);
|
||||
|
||||
/**
|
||||
* Handles dummy packets that are received by reading from the buffer until there is no more data or a non-dummy packet is seen.
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
*/
|
||||
void ProcessDummyPackets(FNboSerializeFromBuffer& FromBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops listening for requests/responses and releases any allocated memory
|
||||
*/
|
||||
native event DestroyBeacon();
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bShouldTick=true
|
||||
}
|
373
IpDrv/Classes/MeshBeaconClient.uc
Normal file
373
IpDrv/Classes/MeshBeaconClient.uc
Normal file
@ -0,0 +1,373 @@
|
||||
/**
|
||||
* 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<ConnectionBandwidthStats> 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<ClientBeaconAddressResolver> 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<OnlineGameSearch> 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<OnlineGameSearch> SearchClass,const out array<PlayerMember> 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<OnlineGameSearch> 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
|
461
IpDrv/Classes/MeshBeaconHost.uc
Normal file
461
IpDrv/Classes/MeshBeaconHost.uc
Normal file
@ -0,0 +1,461 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is used to handle connections from client mesh beacons in order to
|
||||
* establish a mesh network.
|
||||
*/
|
||||
class MeshBeaconHost extends MeshBeacon
|
||||
native;
|
||||
|
||||
/** Stats stored for the current bandwidth test on a client connection */
|
||||
struct native ClientConnectionBandwidthTestData
|
||||
{
|
||||
/** Current progress of bandwidth test. Only one client should be MB_BandwidthTestState_InProgress at a time. */
|
||||
var EMeshBeaconBandwidthTestState CurrentState;
|
||||
/** Type of bandwidth test currently running */
|
||||
var EMeshBeaconBandwidthTestType TestType;
|
||||
/** Total bytes needed to complete the test */
|
||||
var int BytesTotalNeeded;
|
||||
/** Total bytes received by the client so far */
|
||||
var int BytesReceived;
|
||||
/** Time when request was first sent to client to start the test*/
|
||||
var double RequestTestStartTime;
|
||||
/** Time when first response was received from client to being the test */
|
||||
var double TestStartTime;
|
||||
/** Resulting stats from the bandwidth test */
|
||||
var ConnectionBandwidthStats BandwidthStats;
|
||||
};
|
||||
|
||||
/** Holds the information for a client and whether they've timed out */
|
||||
struct native ClientMeshBeaconConnection
|
||||
{
|
||||
/** The unique id of the player for this connection */
|
||||
var UniqueNetId PlayerNetId;
|
||||
/** How long it's been since the last heartbeat */
|
||||
var float ElapsedHeartbeatTime;
|
||||
/** The socket this client is communicating on */
|
||||
var native transient pointer Socket{FSocket};
|
||||
/** True if the client connection has already been accepted for this player */
|
||||
var bool bConnectionAccepted;
|
||||
/** Bandwidth test being run for the client */
|
||||
var ClientConnectionBandwidthTestData BandwidthTest;
|
||||
/** The NAT of the client as reported by the 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;
|
||||
/**
|
||||
* Previous bandwidth history reported by the client ordered from newest to oldest.
|
||||
* New bandwidth tests that occur on this host also get added to this history.
|
||||
*/
|
||||
var array<ConnectionBandwidthStats> BandwidthHistory;
|
||||
/** Elapsed time in minutes since the last bandwidth test */
|
||||
var int MinutesSinceLastTest;
|
||||
};
|
||||
|
||||
/** The object that is used to send/receive data with the remote host/client */
|
||||
var const array<ClientMeshBeaconConnection> ClientConnections;
|
||||
|
||||
/** List of players this beacon is waiting to establish connections to. */
|
||||
var private array<UniqueNetId> PendingPlayerConnections;
|
||||
|
||||
/** Net Id of player that is hosting this beacon */
|
||||
var const UniqueNetId OwningPlayerId;
|
||||
|
||||
/** TRUE if new bandwidth test requests should be handled. Set to false to ignore any pending and new requests. */
|
||||
var private bool bAllowBandwidthTesting;
|
||||
|
||||
/** The number of connections to allow before refusing them */
|
||||
var config int ConnectionBacklog;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Accepts any pending connections and adds them to our queue
|
||||
*/
|
||||
void AcceptConnections(void);
|
||||
|
||||
/**
|
||||
* Reads the socket and processes any data from it
|
||||
*
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
* @return TRUE if the socket is ok, FALSE if it is in error
|
||||
*/
|
||||
UBOOL ReadClientData(FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Processes a packet that was received from a client
|
||||
*
|
||||
* @param Packet the packet that the client sent
|
||||
* @param PacketSize the size of the packet to process
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessClientPacket(BYTE* Packet,INT PacketSize,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Routes the packet received from a client to the correct handler based on its type.
|
||||
* Overridden by base implementations to handle custom data packet types
|
||||
*
|
||||
* @param ClientPacketType packet ID from EMeshBeaconPacketType (or derived version) that represents a client request
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
* @return TRUE if the requested packet type was processed
|
||||
*/
|
||||
UBOOL HandleClientPacketByType(BYTE ClientPacketType,FNboSerializeFromBuffer& FromBuffer,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Read the client data for a new connection request. Includes player ID, NAT type, bandwidth history.
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessClientConnectionRequest(FNboSerializeFromBuffer& FromBuffer,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Sends the results of a connection request by the client.
|
||||
*
|
||||
* @param ConnectionResult result of the connection request
|
||||
* @param ClientConn the client connection with socket to send the response on
|
||||
*/
|
||||
void SendClientConnectionResponse(EMeshBeaconConnectionResult ConnectionResult,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* The client has started sending data for a new bandwidth test.
|
||||
* Begin measurements for test and process data that is received.
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessClientBeginBandwidthTest(FNboSerializeFromBuffer& FromBuffer,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* The client currently has a bandwidth test that has been started and is now in progress.
|
||||
* Process data that is received and handle timeout and finishing the test.
|
||||
* Only packets of type MB_Packet_DummyData are expected from the client once the test has started.
|
||||
*
|
||||
* @param PacketType type of packet read from the buffer
|
||||
* @param AvailableToRead data still available to read from the buffer
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessClientInProgressBandwidthTest(BYTE PacketType,INT AvailableToRead,FNboSerializeFromBuffer& FromBuffer,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Begin processing for a new upstream bandwidth test on a client. All packets
|
||||
* from the client are expected to be dummy packets from this point until NumBytesBeingSent is
|
||||
* reached or we hit timeout receiving the data (MaxBandwidthTestReceiveTime).
|
||||
*
|
||||
* @param ClientConn the client connection that is sending packets for the test
|
||||
* @param NumBytesBeingSent expected size of test data being sent in bytes for the bandwidth test to complete
|
||||
*/
|
||||
void BeginUpstreamTest(FClientMeshBeaconConnection& ClientConn, INT NumBytesBeingSent);
|
||||
|
||||
/**
|
||||
* Finish process for an in-progress upstream bandwidth test on a client. The test
|
||||
* is marked as completed successfully if all the expected data for the test was received
|
||||
* or if the test ended prematurely but there was still enough data (MinBandwidthTestBufferSize)
|
||||
* to calculate results.
|
||||
*
|
||||
* @param ClientConn the client connection that is sending packets for the test
|
||||
*/
|
||||
void FinishUpstreamTest(FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Sends a request to client to start a new bandwidth test.
|
||||
*
|
||||
* @param TestType EMeshBeaconBandwidthTestType type of bandwidth test to request
|
||||
* @param TestBufferSize size of buffer to use for the test
|
||||
* @param ClientConn the client connection with socket to send the response on
|
||||
*/
|
||||
void SendBandwidthTestStartRequest(BYTE TestType,INT TestBufferSize,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Sends the results of a completed bandwidth test to the client.
|
||||
*
|
||||
* @param TestResult result of the bandwidth test
|
||||
* @param ClientConn the client connection with socket to send the response on
|
||||
*/
|
||||
void SendBandwidthTestCompletedResponse(EMeshBeaconBandwidthTestResult TestResult,FClientMeshBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* The client has create a new game session and has sent the session results back.
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessClientCreateNewSessionResponse(FNboSerializeFromBuffer& FromBuffer,FClientMeshBeaconConnection& ClientConn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a listening host mesh beacon to accept new client connections.
|
||||
*
|
||||
* @param InOwningPlayerId Net Id of player that is hosting this beacon
|
||||
* @return true if the beacon was created successfully, false otherwise
|
||||
*/
|
||||
native function bool InitHostBeacon(UniqueNetId InOwningPlayerId);
|
||||
|
||||
/**
|
||||
* Stops listening for clients and releases any allocated memory
|
||||
*/
|
||||
native event DestroyBeacon();
|
||||
|
||||
/**
|
||||
* Send a request to a client connection to initiate a new bandwidth test.
|
||||
*
|
||||
* @param PlayerNetId player with an active connection to receive test request
|
||||
* @param TestType EMeshBeaconBandwidthTestType type of bandwidth test to request
|
||||
* @param TestBufferSize size of buffer in bytes to use for running the test
|
||||
* @return TRUE if the request was successfully sent to the client
|
||||
*/
|
||||
native function bool RequestClientBandwidthTest(UniqueNetId PlayerNetId,EMeshBeaconBandwidthTestType TestType,int TestBufferSize);
|
||||
|
||||
/**
|
||||
* Determine if a client is currently running a bandwidth test.
|
||||
*
|
||||
* @return TRUE if a client connection is currently running a bandwidth test
|
||||
*/
|
||||
native function bool HasInProgressBandwidthTest();
|
||||
|
||||
/**
|
||||
* Cancel any bandwidth tests that are already in progress.
|
||||
*/
|
||||
native function CancelInProgressBandwidthTests();
|
||||
|
||||
/**
|
||||
* Determine if a client is currently waiting/pending for a bandwidth test.
|
||||
*
|
||||
* @return TRUE if a client connection is currently pending a bandwidth test
|
||||
*/
|
||||
native function bool HasPendingBandwidthTest();
|
||||
|
||||
/**
|
||||
* Cancel any bandwidth tests that are pending.
|
||||
*/
|
||||
native function CancelPendingBandwidthTests();
|
||||
|
||||
/**
|
||||
* Enable/disable future bandwidth test requests and current pending tests.
|
||||
*
|
||||
* @param bEnabled true to allow bandwidth testing to be processed by the beacon
|
||||
*/
|
||||
function AllowBandwidthTesting(bool bEnabled)
|
||||
{
|
||||
bAllowBandwidthTesting = bEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate called by the host mesh beacon after establishing a new client socket and
|
||||
* receiving the data for a new connection request.
|
||||
*
|
||||
* @param NewClientConnection client that sent the request for a new connection
|
||||
*/
|
||||
delegate OnReceivedClientConnectionRequest(const out ClientMeshBeaconConnection NewClientConnection);
|
||||
|
||||
/**
|
||||
* Delegate called by the host mesh beacon when bandwidth testing has started for a client connection.
|
||||
* This occurs only when the client sends the start packet to initiate the test.
|
||||
*
|
||||
* @param PlayerNetId net id for player of client connection that started the test
|
||||
* @param TestType test to run based on enum of EMeshBeaconBandwidthTestType supported bandwidth test types
|
||||
*/
|
||||
delegate OnStartedBandwidthTest(UniqueNetId PlayerNetId,EMeshBeaconBandwidthTestType TestType);
|
||||
|
||||
/**
|
||||
* Delegate called by the host mesh beacon when bandwidth testing has completed for a client connection.
|
||||
* This occurs when the test completes successfully or due to error/timeout.
|
||||
*
|
||||
* @param PlayerNetId net id for player of client connection that finished the test
|
||||
* @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 OnFinishedBandwidthTest(
|
||||
UniqueNetId PlayerNetId,
|
||||
EMeshBeaconBandwidthTestType TestType,
|
||||
EMeshBeaconBandwidthTestResult TestResult,
|
||||
const out ConnectionBandwidthStats BandwidthStats);
|
||||
|
||||
/**
|
||||
* Set list of pending player ids we are waiting to connect with.
|
||||
* Once all connections are established then the OnAllPendingPlayersConnected delegate is called.
|
||||
*
|
||||
* @param Players list of player ids we are waiting to connect
|
||||
*/
|
||||
function SetPendingPlayerConnections(const out array<UniqueNetId> Players)
|
||||
{
|
||||
PendingPlayerConnections = Players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given player has an active connection on this host beacon.
|
||||
*
|
||||
* @param PlayerNetId player we are searching for
|
||||
* @return index within ClientConnections for the player's connection, -1 if not found
|
||||
*/
|
||||
native function int GetConnectionIndexForPlayer(UniqueNetId PlayerNetId);
|
||||
|
||||
/**
|
||||
* Determine if the players all have connections on this host beacon
|
||||
*
|
||||
* @param Players list of player ids we are searching for
|
||||
* @return TRUE if all players had connections
|
||||
*/
|
||||
native function bool AllPlayersConnected(const out array<UniqueNetId> Players);
|
||||
|
||||
/**
|
||||
* Delegate called by the host mesh beacon when all players in the PendingPlayerConnections list get connections.
|
||||
*/
|
||||
delegate OnAllPendingPlayersConnected();
|
||||
|
||||
/**
|
||||
* Tells all of the clients to go to a specific session (contained in platform
|
||||
* specific info). Used to route all clients to one destination.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
native function TellClientsToTravel(name SessionName,class<OnlineGameSearch> SearchClass,const out byte PlatformSpecificInfo[80]);
|
||||
|
||||
/**
|
||||
* Sends a request to a specified client to create a new game session.
|
||||
*
|
||||
* @param PlayerNetId net id of player for client connection to send request to
|
||||
* @param SessionName the name of the session to create
|
||||
* @param SearchClass the search that should be with corresponding game settings when creating the session
|
||||
* @param Players list of players to register on the newly created session
|
||||
*/
|
||||
native function bool RequestClientCreateNewSession(UniqueNetId PlayerNetId,name SessionName,class<OnlineGameSearch> SearchClass,const out array<PlayerMember> Players);
|
||||
|
||||
/**
|
||||
* Delegate called by the host mesh beacon when it gets the results of a new game session creation on a client.
|
||||
*
|
||||
* @param bSucceeded TRUE if the the new session was created on the client
|
||||
* @param SessionName the name of the session to create
|
||||
* @param SearchClass the search that should be with corresponding game settings when creating the session
|
||||
* @param PlatformSpecificInfo the platform specific binary data of the new session
|
||||
*/
|
||||
delegate OnReceivedClientCreateNewSessionResult(bool bSucceeded,name SessionName,class<OnlineGameSearch> SearchClass,const out byte PlatformSpecificInfo[80]);
|
||||
|
||||
`if(`notdefined(FINAL_RELEASE))
|
||||
/**
|
||||
* Logs the all the connected clients of this this beacon
|
||||
*/
|
||||
function DumpConnections()
|
||||
{
|
||||
local int ClientIdx, HistoryIdx;
|
||||
local UniqueNetId NetId;
|
||||
|
||||
`Log("Debug info for Beacon: "$BeaconName,,'DevBeacon');
|
||||
for (ClientIdx=0; ClientIdx < ClientConnections.Length; ClientIdx++)
|
||||
{
|
||||
NetId = ClientConnections[ClientIdx].PlayerNetId;
|
||||
`Log("",,'DevBeacon');
|
||||
`Log("Client connection entry: "$ClientIdx,,'DevBeacon');
|
||||
`Log(" PlayerNetId: "$class'OnlineSubsystem'.static.UniqueNetIdToString(NetId),,'DevBeacon');
|
||||
`Log(" NatType: "$ClientConnections[ClientIdx].NatType,,'DevBeacon');
|
||||
`Log(" GoodHostRatio: "$ClientConnections[ClientIdx].GoodHostRatio,,'DevBeacon');
|
||||
`Log(" bCanHostVs: "$ClientConnections[ClientIdx].bCanHostVs,,'DevBeacon');
|
||||
`Log(" MinutesSinceLastTest: "$ClientConnections[ClientIdx].MinutesSinceLastTest,,'DevBeacon');
|
||||
`Log(" BandwidthTest.CurrentState: "$ClientConnections[ClientIdx].BandwidthTest.CurrentState,,'DevBeacon');
|
||||
`Log(" BandwidthTest.TestType: "$ClientConnections[ClientIdx].BandwidthTest.TestType,,'DevBeacon');
|
||||
`Log(" Bandwidth History: "$ClientConnections[ClientIdx].BandwidthHistory.Length,,'DevBeacon');
|
||||
for (HistoryIdx=0; HistoryIdx < ClientConnections[ClientIdx].BandwidthHistory.Length; HistoryIdx++)
|
||||
{
|
||||
`Log(" "
|
||||
$" Upstream bytes/sec: "$ClientConnections[ClientIdx].BandwidthHistory[HistoryIdx].UpstreamRate
|
||||
$" Downstream bytes/sec: "$ClientConnections[ClientIdx].BandwidthHistory[HistoryIdx].DownstreamRate
|
||||
$" Roundrtrip msec: "$ClientConnections[ClientIdx].BandwidthHistory[HistoryIdx].RoundtripLatency,,'DevBeacon');
|
||||
}
|
||||
}
|
||||
`Log("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Render debug info about the client mesh beacon
|
||||
*
|
||||
* @param Canvas canvas object to use for rendering debug info
|
||||
* @param CurOptimalHostId net id of player that should be highlighted as the current optimal host
|
||||
*/
|
||||
function DebugRender(Canvas Canvas, UniqueNetId CurOptimalHostId)
|
||||
{
|
||||
local int ClientIdx,HistoryIdx;
|
||||
local UniqueNetId NetId;
|
||||
local float XL,YL;
|
||||
local float Offset;
|
||||
|
||||
Offset = 50;
|
||||
|
||||
Canvas.Font = class'Engine'.Static.GetTinyFont();
|
||||
Canvas.StrLen("============================================================",XL,YL);
|
||||
YL = Canvas.SizeY-(Offset*2);
|
||||
|
||||
Canvas.SetPos(Offset,Offset);
|
||||
Canvas.SetDrawColor(0,0,255,64);
|
||||
Canvas.DrawTile(Canvas.DefaultTexture,XL,YL,0,0,1,1);
|
||||
|
||||
Canvas.SetPos(Offset,Offset);
|
||||
Canvas.SetDrawColor(255,255,255);
|
||||
Canvas.DrawText("Debug info for Beacon:"$BeaconName);
|
||||
|
||||
if (CurOptimalHostId == OwningPlayerId)
|
||||
{
|
||||
Canvas.SetDrawColor(255,255,0);
|
||||
}
|
||||
Canvas.DrawText("Owning Host: "$class'OnlineSubsystem'.static.UniqueNetIdToString(OwningPlayerId));
|
||||
|
||||
for (ClientIdx=0; ClientIdx < ClientConnections.Length; ClientIdx++)
|
||||
{
|
||||
Canvas.SetDrawColor(255,255,255);
|
||||
if (Canvas.CurY >= YL)
|
||||
{
|
||||
Canvas.SetPos(Canvas.CurX+XL,Offset);
|
||||
}
|
||||
NetId = ClientConnections[ClientIdx].PlayerNetId;
|
||||
Canvas.DrawText("============================================================");
|
||||
Canvas.DrawText("Client connection entry: "$ClientIdx);
|
||||
Canvas.SetPos(Canvas.CurX+10,Canvas.CurY);
|
||||
if (CurOptimalHostId == NetId)
|
||||
{
|
||||
Canvas.SetDrawColor(255,255,0);
|
||||
}
|
||||
Canvas.DrawText("PlayerNetId: "$class'OnlineSubsystem'.static.UniqueNetIdToString(NetId));
|
||||
Canvas.SetDrawColor(255,255,255);
|
||||
Canvas.DrawText("NatType: "$ClientConnections[ClientIdx].NatType);
|
||||
Canvas.DrawText("GoodHostRatio: "$ClientConnections[ClientIdx].GoodHostRatio);
|
||||
Canvas.DrawText("bCanHostVs: "$ClientConnections[ClientIdx].bCanHostVs);
|
||||
Canvas.DrawText("MinutesSinceLastTest: "$ClientConnections[ClientIdx].MinutesSinceLastTest);
|
||||
Canvas.DrawText("Current BandwidthTest: ");
|
||||
Canvas.SetPos(Canvas.CurX+10,Canvas.CurY);
|
||||
Canvas.DrawText("CurrentState: "$ClientConnections[ClientIdx].BandwidthTest.CurrentState);
|
||||
Canvas.DrawText("TestType: "$ClientConnections[ClientIdx].BandwidthTest.TestType);
|
||||
Canvas.DrawText("BytesTotalNeeded: "$ClientConnections[ClientIdx].BandwidthTest.BytesTotalNeeded);
|
||||
Canvas.DrawText("BytesReceived: "$ClientConnections[ClientIdx].BandwidthTest.BytesReceived);
|
||||
Canvas.DrawText("UpstreamRate bytes/sec: "$ClientConnections[ClientIdx].BandwidthTest.BandwidthStats.UpstreamRate);
|
||||
Canvas.SetPos(Canvas.CurX-10,Canvas.CurY);
|
||||
Canvas.DrawText("Bandwidth History: "$ClientConnections[ClientIdx].BandwidthHistory.Length);
|
||||
Canvas.SetPos(Canvas.CurX+10,Canvas.CurY);
|
||||
for (HistoryIdx=0; HistoryIdx < ClientConnections[ClientIdx].BandwidthHistory.Length; HistoryIdx++)
|
||||
{
|
||||
Canvas.DrawText("Upstream bytes/sec: "$ClientConnections[ClientIdx].BandwidthHistory[HistoryIdx].UpstreamRate);
|
||||
}
|
||||
Canvas.SetPos(Canvas.CurX-20,Canvas.CurY);
|
||||
}
|
||||
}
|
||||
|
||||
`endif
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bAllowBandwidthTesting=true
|
||||
}
|
798
IpDrv/Classes/OnlineAuthInterfaceImpl.uc
Normal file
798
IpDrv/Classes/OnlineAuthInterfaceImpl.uc
Normal file
@ -0,0 +1,798 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class that implements a base cross-platform version of the auth interface
|
||||
*/
|
||||
Class OnlineAuthInterfaceImpl extends Object within OnlineSubsystemCommonImpl
|
||||
implements(OnlineAuthInterface)
|
||||
native;
|
||||
|
||||
|
||||
/** The owning subsystem that this object is providing an implementation for */
|
||||
var OnlineSubsystemCommonImpl OwningSubsystem;
|
||||
|
||||
/** Whether or not the auth interface is ready to perform authentication */
|
||||
var const bool bAuthReady;
|
||||
|
||||
|
||||
/** Auth session arrays; these use TSparseArray, which allow safe removal of array elements during iteration */
|
||||
|
||||
/** If we are a server, contains auth sessions for clients connected to the server */
|
||||
var native const SparseArray_Mirror ClientAuthSessions{TSparseArray<FAuthSession>};
|
||||
|
||||
/** If we are a client, contains auth sessions for servers we are connected to */
|
||||
var native const SparseArray_Mirror ServerAuthSessions{TSparseArray<FAuthSession>};
|
||||
|
||||
/** If we are a client, contains auth sessions for other clients we are playing with */
|
||||
var native const SparseArray_Mirror PeerAuthSessions{TSparseArray<FAuthSession>};
|
||||
|
||||
|
||||
/** If we are a client, contains auth sessions we created for a server */
|
||||
var native const SparseArray_Mirror LocalClientAuthSessions{TSparseArray<FLocalAuthSession>};
|
||||
|
||||
/** If we are a server, contains auth sessions we created for clients */
|
||||
var native const SparseArray_Mirror LocalServerAuthSessions{TSparseArray<FLocalAuthSession>};
|
||||
|
||||
/** If we are a client, contains auth sessions we created for other clients */
|
||||
var native const SparseArray_Mirror LocalPeerAuthSessions{TSparseArray<FLocalAuthSession>};
|
||||
|
||||
|
||||
/** Delegate/callback tracking arrays */
|
||||
|
||||
/** The list of 'OnAuthReady' delegates fired when the auth interface is ready to perform authentication */
|
||||
var array<delegate<OnAuthReady> > AuthReadyDelegates;
|
||||
|
||||
/** The list of 'OnClientAuthRequest' delegates fired when the client receives an auth request from the server */
|
||||
var array<delegate<OnClientAuthRequest> > ClientAuthRequestDelegates;
|
||||
|
||||
/** The list of 'OnServerAuthRequest' delegates fired when the server receives an auth request from a client */
|
||||
var array<delegate<OnServerAuthRequest> > ServerAuthRequestDelegates;
|
||||
|
||||
/** The list of 'OnClientAuthResponse' delegates fired when the server receives auth data from a client */
|
||||
var array<delegate<OnClientAuthResponse> > ClientAuthResponseDelegates;
|
||||
|
||||
/** The list of 'OnServerAuthResponse' delegates fired when the client receives auth data from the server */
|
||||
var array<delegate<OnServerAuthResponse> > ServerAuthResponseDelegates;
|
||||
|
||||
/** The list of 'OnClientAuthComplete' delegates fired when the server receives the authentication result for a client */
|
||||
var array<delegate<OnClientAuthComplete> > ClientAuthCompleteDelegates;
|
||||
|
||||
/** The list of 'OnServerAuthComplete' delegates fired when the client receives the authentication result for the server */
|
||||
var array<delegate<OnServerAuthComplete> > ServerAuthCompleteDelegates;
|
||||
|
||||
/** The list of 'OnClientAuthEndSessionRequest' delegates fired when the client receives a request from the server, to end an active auth session */
|
||||
var array<delegate<OnClientAuthEndSessionRequest> > ClientAuthEndSessionRequestDelegates;
|
||||
|
||||
/** The list of 'OnServerAuthRetryRequest' delegates fired when the server receives a request from the client, to retry server auth */
|
||||
var array<delegate<OnServerAuthRetryRequest> > ServerAuthRetryRequestDelegates;
|
||||
|
||||
/** The list of 'OnClientConnectionClose' delegates fired when a client connection is closing on the server */
|
||||
var array<delegate<OnClientConnectionClose> > ClientConnectionCloseDelegates;
|
||||
|
||||
/** The list of 'OnServerConnectionClose' delegates fired when a server connection is closing on the client */
|
||||
var array<delegate<OnServerConnectionClose> > ServerConnectionCloseDelegates;
|
||||
|
||||
|
||||
/**
|
||||
* Used to check if the auth interface is ready to perform authentication
|
||||
*
|
||||
* @return whether or not the auth interface is ready
|
||||
*/
|
||||
function bool IsReady()
|
||||
{
|
||||
return bAuthReady;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the auth interface is ready to perform authentication
|
||||
*/
|
||||
delegate OnAuthReady();
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the auth interface is ready to perform authentication
|
||||
*
|
||||
* @param AuthReadyDelegate The delegate to use for notification
|
||||
*/
|
||||
function AddAuthReadyDelegate(delegate<OnAuthReady> AuthReadyDelegate)
|
||||
{
|
||||
if (AuthReadyDelegates.Find(AuthReadyDelegate) == INDEX_None)
|
||||
{
|
||||
AuthReadyDelegates[AuthReadyDelegates.Length] = AuthReadyDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param AuthReadyDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearAuthReadyDelegate(delegate<OnAuthReady> AuthReadyDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = AuthReadyDelegates.Find(AuthReadyDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
AuthReadyDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
delegate OnClientAuthRequest(UniqueNetId ServerUID, IpAddr ServerIP, int ServerPort, bool bSecure);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the client receives a message from the server, requesting a client auth session
|
||||
*
|
||||
* @param ClientAuthRequestDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddClientAuthRequestDelegate(delegate<OnClientAuthRequest> ClientAuthRequestDelegate)
|
||||
{
|
||||
if (ClientAuthRequestDelegates.Find(ClientAuthRequestDelegate) == INDEX_None)
|
||||
{
|
||||
ClientAuthRequestDelegates[ClientAuthRequestDelegates.Length] = ClientAuthRequestDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ClientAuthRequestDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearClientAuthRequestDelegate(delegate<OnClientAuthRequest> ClientAuthRequestDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ClientAuthRequestDelegates.Find(ClientAuthRequestDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ClientAuthRequestDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server receives a message from a client, requesting a server auth session
|
||||
*
|
||||
* @param ClientConnection The NetConnection of the client the request came from
|
||||
* @param ClientUID The UID of the client making the request
|
||||
* @param ClientIP The IP of the client making the request
|
||||
* @param ClientPort The port the client is on
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
delegate OnServerAuthRequest(Player ClientConnection, UniqueNetId ClientUID, IpAddr ClientIP, int ClientPort);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the server receives a message from a client, requesting a server auth session
|
||||
*
|
||||
* @param ServerAuthRequestDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddServerAuthRequestDelegate(delegate<OnServerAuthRequest> ServerAuthRequestDelegate)
|
||||
{
|
||||
if (ServerAuthRequestDelegates.Find(ServerAuthRequestDelegate) == INDEX_None)
|
||||
{
|
||||
ServerAuthRequestDelegates[ServerAuthRequestDelegates.Length] = ServerAuthRequestDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ServerAuthRequestDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearServerAuthRequestDelegate(delegate<OnServerAuthRequest> ServerAuthRequestDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ServerAuthRequestDelegates.Find(ServerAuthRequestDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ServerAuthRequestDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server receives auth data from a client, needed for authentication
|
||||
*
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientIP The IP of the client
|
||||
* @param AuthTicketUID The UID used to reference the auth data
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
delegate OnClientAuthResponse(UniqueNetId ClientUID, IpAddr ClientIP, int AuthTicketUID);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the server receives a auth data from a client
|
||||
*
|
||||
* @param ClientAuthResponseDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddClientAuthResponseDelegate(delegate<OnClientAuthResponse> ClientAuthResponseDelegate)
|
||||
{
|
||||
if (ClientAuthResponseDelegates.Find(ClientAuthResponseDelegate) == INDEX_None)
|
||||
{
|
||||
ClientAuthResponseDelegates[ClientAuthResponseDelegates.Length] = ClientAuthResponseDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ClientAuthResponseDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearClientAuthResponseDelegate(delegate<OnClientAuthResponse> ClientAuthResponseDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ClientAuthResponseDelegates.Find(ClientAuthResponseDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ClientAuthResponseDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
delegate OnServerAuthResponse(UniqueNetId ServerUID, IpAddr ServerIP, int AuthTicketUID);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the client receives a auth data from the server
|
||||
*
|
||||
* @param ServerAuthResponseDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddServerAuthResponseDelegate(delegate<OnServerAuthResponse> ServerAuthResponseDelegate)
|
||||
{
|
||||
if (ServerAuthResponseDelegates.Find(ServerAuthResponseDelegate) == INDEX_None)
|
||||
{
|
||||
ServerAuthResponseDelegates[ServerAuthResponseDelegates.Length] = ServerAuthResponseDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ServerAuthResponseDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearServerAuthResponseDelegate(delegate<OnServerAuthResponse> ServerAuthResponseDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ServerAuthResponseDelegates.Find(ServerAuthResponseDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ServerAuthResponseDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the server, when the authentication result for a client auth session has returned
|
||||
* NOTE: This is the first place, where a clients UID is verified as valid
|
||||
*
|
||||
* @param bSuccess whether or not authentication was successful
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientConnection The connection associated with the client (for retrieving auth session data)
|
||||
* @param ExtraInfo Extra information about authentication, e.g. failure reasons
|
||||
*/
|
||||
delegate OnClientAuthComplete(bool bSuccess, UniqueNetId ClientUID, Player ClientConnection, string ExtraInfo);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the server receives the authentication result for a client
|
||||
*
|
||||
* @param ClientAuthCompleteDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddClientAuthCompleteDelegate(delegate<OnClientAuthComplete> ClientAuthCompleteDelegate)
|
||||
{
|
||||
if (ClientAuthCompleteDelegates.Find(ClientAuthCompleteDelegate) == INDEX_None)
|
||||
{
|
||||
ClientAuthCompleteDelegates[ClientAuthCompleteDelegates.Length] = ClientAuthCompleteDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ClientAuthCompleteDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearClientAuthCompleteDelegate(delegate<OnClientAuthComplete> ClientAuthCompleteDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ClientAuthCompleteDelegates.Find(ClientAuthCompleteDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ClientAuthCompleteDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
delegate OnServerAuthComplete(bool bSuccess, UniqueNetId ServerUID, Player ServerConnection, string ExtraInfo);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the client receives the authentication result for the server
|
||||
*
|
||||
* @param ServerAuthCompleteDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddServerAuthCompleteDelegate(delegate<OnServerAuthComplete> ServerAuthCompleteDelegate)
|
||||
{
|
||||
if (ServerAuthCompleteDelegates.Find(ServerAuthCompleteDelegate) == INDEX_None)
|
||||
{
|
||||
ServerAuthCompleteDelegates[ServerAuthCompleteDelegates.Length] = ServerAuthCompleteDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ServerAuthCompleteDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearServerAuthCompleteDelegate(delegate<OnServerAuthComplete> ServerAuthCompleteDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ServerAuthCompleteDelegates.Find(ServerAuthCompleteDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ServerAuthCompleteDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the client receives a request from the server, to end an active auth session
|
||||
*
|
||||
* @param ServerConnection The server NetConnection
|
||||
*/
|
||||
delegate OnClientAuthEndSessionRequest(Player ServerConnection);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the client receives a request from the server, to end an active auth session
|
||||
*
|
||||
* @param ClientAuthEndSessionRequestDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddClientAuthEndSessionRequestDelegate(delegate<OnClientAuthEndSessionRequest> ClientAuthEndSessionRequestDelegate)
|
||||
{
|
||||
if (ClientAuthEndSessionRequestDelegates.Find(ClientAuthEndSessionRequestDelegate) == INDEX_None)
|
||||
{
|
||||
ClientAuthEndSessionRequestDelegates[ClientAuthEndSessionRequestDelegates.Length] = ClientAuthEndSessionRequestDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ClientAuthEndSessionRequestDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearClientAuthEndSessionRequestDelegate(delegate<OnClientAuthEndSessionRequest> ClientAuthEndSessionRequestDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ClientAuthEndSessionRequestDelegates.Find(ClientAuthEndSessionRequestDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ClientAuthEndSessionRequestDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server receives a server auth retry request from a client
|
||||
*
|
||||
* @param ClientConnection The client NetConnection
|
||||
*/
|
||||
delegate OnServerAuthRetryRequest(Player ClientConnection);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the server receives a request from the client, to retry server auth
|
||||
*
|
||||
* @param ServerAuthRetryRequestDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddServerAuthRetryRequestDelegate(delegate<OnServerAuthRetryRequest> ServerAuthRetryRequestDelegate)
|
||||
{
|
||||
if (ServerAuthRetryRequestDelegates.Find(ServerAuthRetryRequestDelegate) == INDEX_None)
|
||||
{
|
||||
ServerAuthRetryRequestDelegates[ServerAuthRetryRequestDelegates.Length] = ServerAuthRetryRequestDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ServerAuthRetryRequestDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearServerAuthRetryRequestDelegate(delegate<OnServerAuthRetryRequest> ServerAuthRetryRequestDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ServerAuthRetryRequestDelegates.Find(ServerAuthRetryRequestDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ServerAuthRetryRequestDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the server when a clients net connection is closing (so auth sessions can be ended)
|
||||
*
|
||||
* @param ClientConnection The client NetConnection that is closing
|
||||
*/
|
||||
delegate OnClientConnectionClose(Player ClientConnection);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the a client net connection is closing
|
||||
*
|
||||
* @param ClientConnectionCloseDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddClientConnectionCloseDelegate(delegate<OnClientConnectionClose> ClientConnectionCloseDelegate)
|
||||
{
|
||||
if (ClientConnectionCloseDelegates.Find(ClientConnectionCloseDelegate) == INDEX_None)
|
||||
{
|
||||
ClientConnectionCloseDelegates[ClientConnectionCloseDelegates.Length] = ClientConnectionCloseDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ClientConnectionCloseDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearClientConnectionCloseDelegate(delegate<OnClientConnectionClose> ClientConnectionCloseDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ClientConnectionCloseDelegates.Find(ClientConnectionCloseDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ClientConnectionCloseDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the client when a server net connection is closing (so auth sessions can be ended)
|
||||
*
|
||||
* @param ServerConnection The server NetConnection that is closing
|
||||
*/
|
||||
delegate OnServerConnectionClose(Player ServerConnection);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify when the a server net connection is closing
|
||||
*
|
||||
* @param ServerConnectionCloseDelegate The delegate to use for notifications
|
||||
*/
|
||||
function AddServerConnectionCloseDelegate(delegate<OnServerConnectionClose> ServerConnectionCloseDelegate)
|
||||
{
|
||||
if (ServerConnectionCloseDelegates.Find(ServerConnectionCloseDelegate) == INDEX_None)
|
||||
{
|
||||
ServerConnectionCloseDelegates[ServerConnectionCloseDelegates.Length] = ServerConnectionCloseDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ServerConnectionCloseDelegate The delegate to remove from the list
|
||||
*/
|
||||
function ClearServerConnectionCloseDelegate(delegate<OnServerConnectionClose> ServerConnectionCloseDelegate)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = ServerConnectionCloseDelegates.Find(ServerConnectionCloseDelegate);
|
||||
|
||||
if (i != INDEX_None)
|
||||
{
|
||||
ServerConnectionCloseDelegates.Remove(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a client auth request to the specified client
|
||||
* NOTE: It is important to specify the ClientUID from PreLogin
|
||||
*
|
||||
* @param ClientConnection The NetConnection of the client to send the request to
|
||||
* @param ClientUID The UID of the client (as taken from PreLogin)
|
||||
* @return whether or not the request kicked off successfully
|
||||
*/
|
||||
function bool SendClientAuthRequest(Player ClientConnection, UniqueNetId ClientUID);
|
||||
|
||||
/**
|
||||
* Sends a server auth request to the server
|
||||
*
|
||||
* @param ServerUID The UID of the server
|
||||
* @return whether or not the request kicked off successfully
|
||||
*/
|
||||
function bool SendServerAuthRequest(UniqueNetId ServerUID);
|
||||
|
||||
/**
|
||||
* Sends the specified auth ticket from the client to the server
|
||||
*
|
||||
* @param AuthTicketUID The UID of the auth ticket, as retrieved by CreateClientAuthSession
|
||||
* @return whether or not the auth ticket was sent successfully
|
||||
*/
|
||||
native function bool SendClientAuthResponse(int AuthTicketUID);
|
||||
|
||||
/**
|
||||
* Sends the specified auth ticket from the server to the client
|
||||
*
|
||||
* @param ClientConnection The NetConnection of the client to send the auth ticket to
|
||||
* @param AuthTicketUID The UID of the auth ticket, as retrieved by CreateServerAuthSession
|
||||
* @return whether or not the auth ticket was sent successfully
|
||||
*/
|
||||
native function bool SendServerAuthResponse(Player ClientConnection, int AuthTicketUID);
|
||||
|
||||
/**
|
||||
* Sends an auth kill request to the specified client
|
||||
*
|
||||
* @param ClientConnection The NetConnection of the client to send the request to
|
||||
* @return whether or not the request was sent successfully
|
||||
*/
|
||||
native function bool SendClientAuthEndSessionRequest(Player ClientConnection);
|
||||
|
||||
/**
|
||||
* Sends a server auth retry request to the server
|
||||
*
|
||||
* @return whether or not the request was sent successfully
|
||||
*/
|
||||
native function bool SendServerAuthRetryRequest();
|
||||
|
||||
|
||||
/**
|
||||
* Client auth functions, for authenticating clients with a game server
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a client auth session with the server; the session doesn't start until the auth ticket is verified by the server
|
||||
* NOTE: This must be called clientside
|
||||
*
|
||||
* @param ServerUID The UID of the server
|
||||
* @param ServerIP The external/public IP address of the server
|
||||
* @param ServerPort The port of the server
|
||||
* @param bSecure whether or not the server has cheat protection enabled
|
||||
* @param OutAuthTicketUID Outputs the UID of the auth data, which is used to verify the auth session on the server
|
||||
* @return whether or not the local half of the auth session was kicked off successfully
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
function bool CreateClientAuthSession(UniqueNetId ServerUID, IpAddr ServerIP, int ServerPort, bool bSecure, out int OutAuthTicketUID);
|
||||
|
||||
/**
|
||||
* Kicks off asynchronous verification and setup of a client auth session, on the server;
|
||||
* auth success/failure is returned through OnClientAuthComplete
|
||||
*
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientIP The IP address of the client
|
||||
* @param ClientPort The port the client is on
|
||||
* @param AuthTicketUID The UID for the auth data sent by the client (as obtained through OnClientAuthResponse)
|
||||
* @return whether or not asynchronous verification was kicked off successfully
|
||||
*/
|
||||
function bool VerifyClientAuthSession(UniqueNetId ClientUID, IpAddr ClientIP, int ClientPort, int AuthTicketUID);
|
||||
|
||||
/**
|
||||
* Ends the clientside half of a client auth session
|
||||
* NOTE: This call must be matched on the server, with EndRemoteClientAuthSession
|
||||
*
|
||||
* @param ServerUID The UID of the server
|
||||
* @param ServerIP The external (public) IP address of the server
|
||||
* @param ServerPort The port of the server
|
||||
*/
|
||||
native final function EndLocalClientAuthSession(UniqueNetId ServerUID, IpAddr ServerIP, int ServerPort);
|
||||
|
||||
/**
|
||||
* Ends the serverside half of a client auth session
|
||||
* NOTE: This call must be matched on the client, with EndLocalClientAuthSession
|
||||
*
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientIP The IP address of the client
|
||||
*/
|
||||
native final function EndRemoteClientAuthSession(UniqueNetId ClientUID, IpAddr ClientIP);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
|
||||
/**
|
||||
* Ends the clientside halves of all client auth sessions
|
||||
* NOTE: This is the same as iterating AllLocalClientAuthSessions and ending each session with EndLocalClientAuthSession
|
||||
*/
|
||||
native function EndAllLocalClientAuthSessions();
|
||||
|
||||
/**
|
||||
* Ends the serverside halves of all client auth sessions
|
||||
* NOTE: This is the same as iterating AllClientAuthSessions and ending each session with EndRemoteClientAuthSession
|
||||
*/
|
||||
native function EndAllRemoteClientAuthSessions();
|
||||
|
||||
|
||||
/**
|
||||
* Server auth functions, for authenticating the server with clients
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a server auth session with a client; the session doesn't start until the auth ticket is verified by the client
|
||||
* NOTE: This must be called serverside; if using server auth, the server should create a server auth session for every client
|
||||
*
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientIP The IP address of the client
|
||||
* @param ClientPort The port of the client
|
||||
* @param OutAuthTicketUID Outputs the UID of the auth data, which is used to verify the auth session on the client
|
||||
* @return whether or not the local half of the auth session was kicked off successfully
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
function bool CreateServerAuthSession(UniqueNetId ClientUID, IpAddr ClientIP, int ClientPort, out int OutAuthTicketUID);
|
||||
|
||||
/**
|
||||
* Kicks off asynchronous verification and setup of a server auth session, on the client;
|
||||
* auth success/failure is returned through OnServerAuthComplete
|
||||
*
|
||||
* @param ServerUID The UID of the server
|
||||
* @param ServerIP The external/public IP address of the server
|
||||
* @param AuthTicketUID The UID of the auth data sent by the server (as obtained through OnServerAuthResponse)
|
||||
* @return whether or not asynchronous verification was kicked off successfully
|
||||
*/
|
||||
function bool VerifyServerAuthSession(UniqueNetId ServerUID, IpAddr ServerIP, int AuthTicketUID);
|
||||
|
||||
/**
|
||||
* Ends the serverside half of a server auth session
|
||||
* NOTE: This call must be matched on the other end, with EndRemoteServerAuthSession
|
||||
*
|
||||
* @param ClientUID The UID of the client
|
||||
* @param ClientIP The IP address of the client
|
||||
*/
|
||||
native final function EndLocalServerAuthSession(UniqueNetId ClientUID, IpAddr ClientIP);
|
||||
|
||||
/**
|
||||
* Ends the clientside half of a server auth session
|
||||
* NOTE: This call must be matched on the other end, with EndLocalServerAuthSession
|
||||
*
|
||||
* @param ServerUID The UID of the server
|
||||
* @param ServerIP The external/public IP address of the server
|
||||
*/
|
||||
native final function EndRemoteServerAuthSession(UniqueNetId ServerUID, IpAddr ServerIP);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Ends the serverside halves of all server auth sessions
|
||||
* NOTE: This is the same as iterating AllLocalServerAuthSessions and ending each session with EndLocalServerAuthSession
|
||||
*/
|
||||
native function EndAllLocalServerAuthSessions();
|
||||
|
||||
/**
|
||||
* Ends the clientside halves of all server auth sessions
|
||||
* NOTE: This is the same as iterating AllServerAuthSessions and ending each session with EndRemoteServerAuthSession
|
||||
*/
|
||||
native function EndAllRemoteServerAuthSessions();
|
||||
|
||||
|
||||
/**
|
||||
* Auth info access functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* On a server, iterates all auth sessions for clients connected to the server
|
||||
* NOTE: This iterator is remove-safe; ending a client auth session from within this iterator will not mess up the order of iteration
|
||||
*
|
||||
* @param OutSessionInfo Outputs the currently iterated auth session
|
||||
*/
|
||||
native function iterator AllClientAuthSessions(out AuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* On a client, iterates all auth sessions we created for a server
|
||||
* NOTE: This iterator is remove-safe; ending a local client auth session from within this iterator will not mess up the order of iteration
|
||||
*
|
||||
* @param OutSessionInfo Outputs the currently iterated auth session
|
||||
*/
|
||||
native function iterator AllLocalClientAuthSessions(out LocalAuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* On a client, iterates all auth sessions for servers we are connecting/connected to
|
||||
* NOTE: This iterator is remove-safe; ending a server auth session from within this iterator will not mess up the order of iteration
|
||||
*
|
||||
* @param OutSessionInfo Outputs the currently iterated auth session
|
||||
*/
|
||||
native function iterator AllServerAuthSessions(out AuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* On a server, iterates all auth sessions we created for clients
|
||||
* NOTE: This iterator is remove-safe; ending a local server auth session from within this iterator will not mess up the order of iteration
|
||||
*
|
||||
* @param OutSessionInfo Outputs the currently iterated auth session
|
||||
*/
|
||||
native function iterator AllLocalServerAuthSessions(out LocalAuthSession OutSessionInfo);
|
||||
|
||||
|
||||
/**
|
||||
* Finds the active/pending client auth session, for the client associated with the specified NetConnection
|
||||
*
|
||||
* @param ClientConnection The NetConnection associated with the client
|
||||
* @param OutSessionInfo Outputs the auth session info for the client
|
||||
* @return Returns TRUE if a session was found for the client, FALSE otherwise
|
||||
*/
|
||||
native function bool FindClientAuthSession(Player ClientConnection, out AuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* Finds the clientside half of an active/pending client auth session
|
||||
*
|
||||
* @param ServerConnection The NetConnection associated with the server
|
||||
* @param OutSessionInfo Outputs the auth session info for the client
|
||||
* @return Returns TRUE if a session was found for the client, FALSE otherwise
|
||||
*/
|
||||
native function bool FindLocalClientAuthSession(Player ServerConnection, out LocalAuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* Finds the active/pending server auth session, for the specified server connection
|
||||
*
|
||||
* @param ServerConnection The NetConnection associated with the server
|
||||
* @param OutSessionInfo Outputs the auth session info for the server
|
||||
* @return Returns TRUE if a session was found for the server, FALSE otherwise
|
||||
*/
|
||||
native function bool FindServerAuthSession(Player ServerConnection, out AuthSession OutSessionInfo);
|
||||
|
||||
/**
|
||||
* Finds the serverside half of an active/pending server auth session
|
||||
*
|
||||
* @param ClientConnection The NetConnection associated with the client
|
||||
* @param OutSessionInfo Outputs the auth session info for the server
|
||||
* @return Returns TRUE if a session was found for the server, FALSE otherwise
|
||||
*/
|
||||
native function bool FindLocalServerAuthSession(Player ClientConnection, out LocalAuthSession OutSessionInfo);
|
||||
|
||||
|
||||
/**
|
||||
* Platform specific server information
|
||||
*/
|
||||
|
||||
/**
|
||||
* If this is a server, retrieves the platform-specific UID of the server; used for authentication (not supported on all platforms)
|
||||
* NOTE: This is primarily used serverside, for listen host authentication
|
||||
*
|
||||
* @param OutServerUID The UID of the server
|
||||
* @return whether or not the server UID was retrieved
|
||||
*/
|
||||
function bool GetServerUniqueId(out UniqueNetId OutServerUID);
|
||||
|
||||
/**
|
||||
* If this is a server, retrieves the platform-specific IP and port of the server; used for authentication
|
||||
* NOTE: This is primarily used serverside, for listen host authentication
|
||||
*
|
||||
* @param OutServerIP The public IP of the server (or, for platforms which don't support it, the local IP)
|
||||
* @param OutServerPort The port of the server
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
function bool GetServerAddr(out IpAddr OutServerIP, out int OutServerPort);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
219
IpDrv/Classes/OnlineEventsInterfaceMcp.uc
Normal file
219
IpDrv/Classes/OnlineEventsInterfaceMcp.uc
Normal file
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides an in game gameplay events/stats upload mechanism via the MCP backend
|
||||
*/
|
||||
class OnlineEventsInterfaceMcp extends MCPBase
|
||||
native
|
||||
implements(OnlineEventsInterface);
|
||||
|
||||
/**
|
||||
* The types of events that are to be uploaded
|
||||
* Keep in sync with [IpDrv.OnlineEventsInterfaceMcp] entries
|
||||
*/
|
||||
enum EEventUploadType
|
||||
{
|
||||
EUT_GenericStats,
|
||||
EUT_ProfileData,
|
||||
EUT_MatchmakingData,
|
||||
EUT_PlaylistPopulation
|
||||
};
|
||||
|
||||
/** Holds the configuration and instance data for event uploading */
|
||||
struct native EventUploadConfig
|
||||
{
|
||||
/** The type of upload this config is for */
|
||||
var const EEventUploadType UploadType;
|
||||
/** The URL to send the data to */
|
||||
var const string UploadUrl;
|
||||
/** The amount of time to wait before erroring out */
|
||||
var const float TimeOut;
|
||||
/** Whether to compress the data before sending or not */
|
||||
var const bool bUseCompression;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the array of upload task configurations
|
||||
*/
|
||||
var const config array<EventUploadConfig> EventUploadConfigs;
|
||||
|
||||
/** List of HTTP and compression objects that are POSTing the data */
|
||||
var native const array<pointer> MCPEventPostObjects{struct FMCPEventPoster};
|
||||
|
||||
/** A list of upload types that are disabled (don't upload) */
|
||||
var config array<EEventUploadType> DisabledUploadTypes;
|
||||
|
||||
/** if true, the stats data will be sent as a binary blob instead of XML */
|
||||
var const config bool bBinaryStats;
|
||||
|
||||
cpptext
|
||||
{
|
||||
protected:
|
||||
// FTickableObject interface
|
||||
/**
|
||||
* Ticks any outstanding async tasks that need processing
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
|
||||
// Event upload specific methods
|
||||
/**
|
||||
* Finds the upload config for the type
|
||||
*
|
||||
* @param UploadType the type of upload that is being processed
|
||||
*
|
||||
* @return pointer to the config item or NULL if not found
|
||||
*/
|
||||
inline FEventUploadConfig* FindUploadConfig(BYTE UploadType)
|
||||
{
|
||||
// Make sure this config wasn't disabled
|
||||
INT ItemIndex = DisabledUploadTypes.FindItemIndex(UploadType);
|
||||
if (ItemIndex == INDEX_NONE)
|
||||
{
|
||||
for (INT EventIndex = 0; EventIndex < EventUploadConfigs.Num(); EventIndex++)
|
||||
{
|
||||
if (EventUploadConfigs(EventIndex).UploadType == UploadType)
|
||||
{
|
||||
return &EventUploadConfigs(EventIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method for POST-ing a payload to an URL (determined by upload type)
|
||||
*
|
||||
* @param UploadType the type of upload that is happening
|
||||
* @param Payload the data to send
|
||||
* @param NetId unique id of the player sending the data
|
||||
*
|
||||
* @return TRUE if the send started successfully, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL UploadPayload(BYTE UploadType,const FString& Payload,const FUniqueNetId NetId);
|
||||
|
||||
/**
|
||||
* Common method for POST-ing a payload to an URL (determined by upload type)
|
||||
*
|
||||
* @param UploadType the type of upload that is happening
|
||||
* @param Payload the data to send
|
||||
* @param NetId unique id of the player sending the data
|
||||
*
|
||||
* @return TRUE if the send started successfully, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL UploadBinaryPayload(BYTE UploadType,const TArray<BYTE>& Payload,const FUniqueNetId NetId);
|
||||
|
||||
/**
|
||||
* Final method for POST-ing a payload to a URL. At this point it is assumed to be binary data
|
||||
*
|
||||
* @param bWasText will be true if the original post was text data
|
||||
* @param UploadType the type of upload that is happening
|
||||
* @param Payload the data to send
|
||||
* @param NetId unique id of the player sending the data
|
||||
*
|
||||
* @return TRUE if the send started successfully, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL UploadFinalPayload(UBOOL bWasText,BYTE UploadType,const TArray<BYTE>& Payload,const FUniqueNetId NetId);
|
||||
|
||||
/**
|
||||
* Converts the net id to a string
|
||||
*
|
||||
* @param Id the net id to convert
|
||||
*
|
||||
* @return the string form of the id
|
||||
*/
|
||||
virtual FString FormatAsString(const FUniqueNetId& Id)
|
||||
{
|
||||
return FString::Printf(TEXT("%I64u"),(QWORD&)Id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out escape characters that can't be sent to MCP via XML and
|
||||
* replaces them with the XML allowed sequences
|
||||
*
|
||||
* @param Source the source string to modify
|
||||
*
|
||||
* @return a new string with the data escaped
|
||||
*/
|
||||
virtual FString EscapeString(const FString& Source);
|
||||
|
||||
/**
|
||||
* Builds the URL of additional parameters used when posting playlist population data
|
||||
*
|
||||
* @param PlaylistId the playlist id being reported
|
||||
* @param NumPlayers the number of players on the host
|
||||
*
|
||||
* @return the URL to use with all of the per platform extras
|
||||
*/
|
||||
virtual FString BuildPlaylistPopulationURLParameters(INT PlaylistId,INT NumPlayers);
|
||||
|
||||
/**
|
||||
* Builds the URL of additional parameters used when posting data
|
||||
*
|
||||
* @param NetId the unique id of the player sending their data
|
||||
*
|
||||
* @return the URL to use with all of the per platform extras
|
||||
*/
|
||||
virtual FString BuildGenericURLParameters(const FUniqueNetId NetId);
|
||||
|
||||
/**
|
||||
* Captures hardware information as a string for uploading to MCP
|
||||
*/
|
||||
virtual FString BuildHardwareXmlData(void)
|
||||
{
|
||||
return FString(TEXT("<Hardware />\r\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return platform specific XML data
|
||||
*/
|
||||
virtual FString BuildPlatformXmlData(void)
|
||||
{
|
||||
return TEXT("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the profile data to the server for statistics aggregation
|
||||
*
|
||||
* @param UniqueId the unique id for the player
|
||||
* @param PlayerNick the player's nick name
|
||||
* @param ProfileSettings the profile object that is being sent
|
||||
* @param PlayerStorage the player storage object that is being sent
|
||||
*
|
||||
* @return true if the async task was started successfully, false otherwise
|
||||
*/
|
||||
native function bool UploadPlayerData(UniqueNetId UniqueId,string PlayerNick,OnlineProfileSettings ProfileSettings,OnlinePlayerStorage PlayerStorage);
|
||||
|
||||
/**
|
||||
* Sends gameplay event data to MCP
|
||||
*
|
||||
* @param UniqueId the player that is sending the stats
|
||||
* @param Payload the stats data to upload
|
||||
*
|
||||
* @return true if the async send started ok, false otherwise
|
||||
*/
|
||||
native function bool UploadGameplayEventsData(UniqueNetId UniqueId,const out array<byte> Payload);
|
||||
|
||||
/**
|
||||
* Sends the network backend the playlist population for this host
|
||||
*
|
||||
* @param PlaylistId the playlist we are updating the population for
|
||||
* @param NumPlayers the number of players on this host in this playlist
|
||||
*
|
||||
* @return true if the async send started ok, false otherwise
|
||||
*/
|
||||
native function bool UpdatePlaylistPopulation(int PlaylistId,int NumPlayers);
|
||||
|
||||
/**
|
||||
* Sends matchmaking stats data to MCP
|
||||
*
|
||||
* @param UniqueId the unique id for the player
|
||||
* @param MMStats object that contains aggregated matchmaking stats data
|
||||
*
|
||||
* @return true if the async send started ok, false otherwise
|
||||
*/
|
||||
native function bool UploadMatchmakingStats(UniqueNetId UniqueId,OnlineMatchmakingStats MMStats);
|
1340
IpDrv/Classes/OnlineGameInterfaceImpl.uc
Normal file
1340
IpDrv/Classes/OnlineGameInterfaceImpl.uc
Normal file
File diff suppressed because it is too large
Load Diff
290
IpDrv/Classes/OnlineImageDownloaderWeb.uc
Normal file
290
IpDrv/Classes/OnlineImageDownloaderWeb.uc
Normal file
@ -0,0 +1,290 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
class OnlineImageDownloaderWeb extends Object
|
||||
config(Engine);
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci)
|
||||
|
||||
enum EOnlineImageDownloadState
|
||||
{
|
||||
/** Download has not been kicked off yet */
|
||||
PIDS_NotStarted,
|
||||
/** Currently waiting for download to finish */
|
||||
PIDS_Downloading,
|
||||
/** Downloaded successfully and Texture is ready to be used */
|
||||
PIDS_Succeeded,
|
||||
/** Download failed. Can't assume Texture contents are valid/updated */
|
||||
PIDS_Failed
|
||||
};
|
||||
|
||||
/** Entry for a cached FB profile image */
|
||||
struct OnlineImageDownload
|
||||
{
|
||||
/** URL for the image */
|
||||
var string URL;
|
||||
/** HTTP request object used to download the image */
|
||||
var HttpRequestInterface HTTPRequest;
|
||||
/** Current download state */
|
||||
var EOnlineImageDownloadState State;
|
||||
/** Marked for deletion/reuse */
|
||||
var bool bPendingRemoval;
|
||||
/** Texture that will be updated with the downloaded image */
|
||||
var Texture2DDynamic Texture;
|
||||
};
|
||||
/** Cache of textures images and the web requests used to download them */
|
||||
var array<OnlineImageDownload> DownloadImages;
|
||||
/** Maximum downloads that can be in flight at the same time */
|
||||
var config int MaxSimultaneousDownloads;
|
||||
|
||||
/**
|
||||
* Called whenever a download for an image has completed
|
||||
*
|
||||
* @param OnlineImageDownload cached entry that was downloaded
|
||||
*/
|
||||
delegate OnOnlineImageDownloaded(OnlineImageDownload CachedEntry);
|
||||
|
||||
/**
|
||||
* Retrieve the texture for a given image URL if it has been successfully downloaded and is still cached
|
||||
*
|
||||
* @param URL original url of image request
|
||||
*/
|
||||
function Texture GetOnlineImageTexture(string URL)
|
||||
{
|
||||
local int FoundIdx;
|
||||
|
||||
FoundIdx = DownloadImages.Find('URL',URL);
|
||||
if (FoundIdx != INDEX_NONE &&
|
||||
DownloadImages[FoundIdx].State == PIDS_Succeeded)
|
||||
{
|
||||
return DownloadImages[FoundIdx].Texture;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the downloading/caching of the images for the given list of URLs
|
||||
*
|
||||
* @param URLs list of addresses to download
|
||||
*/
|
||||
function RequestOnlineImages(array<string> URLs)
|
||||
{
|
||||
local string URL;
|
||||
local int FoundIdx,Idx;
|
||||
|
||||
// Start by marking any entries no longer needed for removal
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
// If existing User id entry not in new list of user ids then mark for removal
|
||||
DownloadImages[Idx].bPendingRemoval = URLs.Find(DownloadImages[Idx].URL) == INDEX_NONE;
|
||||
}
|
||||
// Update/Add new user ids that are not being processed
|
||||
foreach URLs(URL)
|
||||
{
|
||||
FoundIdx = DownloadImages.Find('URL',URL);
|
||||
// If found existing entry then just treat as if downloaded
|
||||
if (FoundIdx != INDEX_NONE)
|
||||
{
|
||||
OnOnlineImageDownloaded(DownloadImages[FoundIdx]);
|
||||
}
|
||||
// If no existing cached entry then need to update/add
|
||||
else
|
||||
{
|
||||
// Find an entry marked for removal
|
||||
FoundIdx = DownloadImages.Find('bPendingRemoval',true);
|
||||
if (FoundIdx == INDEX_NONE)
|
||||
{
|
||||
// Add a new entry since no empty spots
|
||||
FoundIdx = DownloadImages.Length;
|
||||
DownloadImages.Length = DownloadImages.Length+1;
|
||||
}
|
||||
// Setup new cached image entry for user
|
||||
DownloadImages[FoundIdx].URL = URL;
|
||||
DownloadImages[FoundIdx].HTTPRequest = None;
|
||||
DownloadImages[FoundIdx].State = PIDS_NotStarted;
|
||||
DownloadImages[FoundIdx].bPendingRemoval = false;
|
||||
if (DownloadImages[FoundIdx].Texture == None)
|
||||
{
|
||||
DownloadImages[FoundIdx].Texture = class'Texture2DDynamic'.static.Create(50,50);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove the unused entries
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
if (DownloadImages[Idx].bPendingRemoval)
|
||||
{
|
||||
DownloadImages.Remove(Idx--,1);
|
||||
}
|
||||
}
|
||||
// Try to start next download
|
||||
DownloadNextImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return total # of entries that are still being downloaded
|
||||
*/
|
||||
function int GetNumPendingDownloads()
|
||||
{
|
||||
local int Idx,Count;
|
||||
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
if (DownloadImages[Idx].State == PIDS_Downloading)
|
||||
{
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out the cached entries for the given user ids
|
||||
*
|
||||
* @param FBUserIds list of FB ids to clear
|
||||
*/
|
||||
function ClearDownloads(array<string> URLs)
|
||||
{
|
||||
local int Idx;
|
||||
|
||||
// Remove the entries matching the FB user ids
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
if (URLs.Find(DownloadImages[Idx].URL) != INDEX_NONE)
|
||||
{
|
||||
DownloadImages.Remove(Idx--,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out all of the cached entries. Even if currently in flight
|
||||
*/
|
||||
function ClearAllDownloads()
|
||||
{
|
||||
DownloadImages.Length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kick off the download for the next image. Up to MaxSimultaneousDownloads can be in flight at once
|
||||
*/
|
||||
private function DownloadNextImage()
|
||||
{
|
||||
local int Idx,PendingDownloads;
|
||||
|
||||
// Current pending downloads
|
||||
PendingDownloads = GetNumPendingDownloads();
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
// Stop if we cant download any more
|
||||
if (PendingDownloads >= MaxSimultaneousDownloads)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Find next available entry that needs to be processed
|
||||
if (DownloadImages[Idx].State == PIDS_NotStarted)
|
||||
{
|
||||
//`log("FacebookImage:DownloadNextImage:2");
|
||||
|
||||
DownloadImages[Idx].HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (DownloadImages[Idx].HTTPRequest != None)
|
||||
{
|
||||
DownloadImages[Idx].HTTPRequest.SetVerb("GET");
|
||||
DownloadImages[Idx].HTTPRequest.SetURL(DownloadImages[Idx].URL);
|
||||
DownloadImages[Idx].HTTPRequest.SetProcessRequestCompleteDelegate(OnDownloadComplete);
|
||||
if (DownloadImages[Idx].HTTPRequest.ProcessRequest())
|
||||
{
|
||||
DownloadImages[Idx].State = PIDS_Downloading;
|
||||
PendingDownloads++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the download has completed for a single image
|
||||
*
|
||||
* @param OriginalRequest HTTP request object that was used to kick off the web request
|
||||
* @param Response contains the response code, headers, and data from the GET
|
||||
* @param bDidSucceed TRUE if the request completed
|
||||
*/
|
||||
private function OnDownloadComplete(HttpRequestInterface OriginalRequest, HttpResponseInterface Response, bool bDidSucceed)
|
||||
{
|
||||
local int FoundIdx;
|
||||
local array<byte> JPEGData;
|
||||
|
||||
// Match up the request object in the list of cached entries
|
||||
FoundIdx = DownloadImages.Find('HTTPRequest',OriginalRequest);
|
||||
if (FoundIdx != INDEX_NONE)
|
||||
{
|
||||
// Check for valid/successful response, and that the contents are a JPEG file
|
||||
if (bDidSucceed &&
|
||||
Response != None &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK &&
|
||||
InStr(Response.GetHeader("Content-Type"),"jpeg",false,true) != INDEX_NONE)
|
||||
{
|
||||
// Mark successful completion
|
||||
DownloadImages[FoundIdx].State = PIDS_Succeeded;
|
||||
// Copy JPEG image data
|
||||
Response.GetContent(JPEGData);
|
||||
|
||||
// Update the texture mip with the image data
|
||||
DownloadImages[FoundIdx].Texture.UpdateMipFromJPEG(0,JPEGData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to download
|
||||
DownloadImages[FoundIdx].State = PIDS_Failed;
|
||||
}
|
||||
// Delegate called when download completed
|
||||
OnOnlineImageDownloaded(DownloadImages[FoundIdx]);
|
||||
// Done downloading so no longer need the request object
|
||||
DownloadImages[FoundIdx].HTTPRequest = None;
|
||||
}
|
||||
// Try to start next download
|
||||
DownloadNextImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug draw the images that have downloaded
|
||||
*
|
||||
* @param Canvas used to draw the profile image textures on screen
|
||||
*/
|
||||
function DebugDraw(Canvas Canvas)
|
||||
{
|
||||
local float PosX,PosY;
|
||||
local int Idx;
|
||||
|
||||
PosX=0;
|
||||
PosY=0;
|
||||
for (Idx=0; Idx < DownloadImages.Length; Idx++)
|
||||
{
|
||||
if (DownloadImages[Idx].State == PIDS_Succeeded)
|
||||
{
|
||||
Canvas.SetDrawColor(255,255,255,255);
|
||||
Canvas.SetPos(PosX,PosY);
|
||||
Canvas.DrawTexture(DownloadImages[Idx].Texture,1);
|
||||
Canvas.SetDrawColor(0,255,0,255);
|
||||
Canvas.SetPos(PosX,PosY);
|
||||
Canvas.DrawBox(DownloadImages[Idx].Texture.SizeX,DownloadImages[Idx].Texture.SizeY);
|
||||
PosY += DownloadImages[Idx].Texture.SizeY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Canvas.DrawBox(50,50);
|
||||
PosY += 50;
|
||||
}
|
||||
if (PosY > Canvas.ClipY)
|
||||
{
|
||||
PosY = 0;
|
||||
PosX += 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
143
IpDrv/Classes/OnlineNewsInterfaceMcp.uc
Normal file
143
IpDrv/Classes/OnlineNewsInterfaceMcp.uc
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides an in game news mechanism via the MCP backend
|
||||
*/
|
||||
class OnlineNewsInterfaceMcp extends MCPBase
|
||||
native
|
||||
implements(OnlineNewsInterface);
|
||||
|
||||
/** Holds the IP, hostname, URL, and news results for a particular news type */
|
||||
struct native NewsCacheEntry
|
||||
{
|
||||
/** The URL to the news page that we're reading */
|
||||
var const string NewsUrl;
|
||||
/** The current async read state for the operation */
|
||||
var EOnlineEnumerationReadState ReadState;
|
||||
/** The type of news that we are reading */
|
||||
var const EOnlineNewsType NewsType;
|
||||
/** The results of the read */
|
||||
var string NewsItem;
|
||||
/** The amount of time before giving up on the read */
|
||||
var const float TimeOut;
|
||||
/** Whether the news item is in unicode or ansi */
|
||||
var const bool bIsUnicode;
|
||||
/** Pointer to the native helper object that performs the download */
|
||||
var const native pointer HttpDownloader{class FHttpDownloadString};
|
||||
};
|
||||
|
||||
/** The list of cached news items (ips, results, etc.) */
|
||||
var config array<NewsCacheEntry> NewsItems;
|
||||
|
||||
/** The list of delegates to notify when the news read is complete */
|
||||
var array<delegate<OnReadNewsCompleted> > ReadNewsDelegates;
|
||||
|
||||
/** Whether there are outstanding requests that need ticking or not */
|
||||
var transient bool bNeedsTicking;
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
/**
|
||||
* Ticks any outstanding news read requests
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
|
||||
// News specific methods
|
||||
/**
|
||||
* Finds the news cache entry for the specified type
|
||||
*
|
||||
* @param NewsType the type of news being read
|
||||
*
|
||||
* @return pointer to the news item or NULL if not found
|
||||
*/
|
||||
inline FNewsCacheEntry* FindNewsCacheEntry(BYTE NewsType)
|
||||
{
|
||||
for (INT NewsIndex = 0; NewsIndex < NewsItems.Num(); NewsIndex++)
|
||||
{
|
||||
if (NewsItems(NewsIndex).NewsType == NewsType)
|
||||
{
|
||||
return &NewsItems(NewsIndex);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the game specific news from the online subsystem
|
||||
*
|
||||
* @param LocalUserNum the local user the news is being read for
|
||||
* @param NewsType the type of news to read
|
||||
*
|
||||
* @return true if the async task was successfully started, false otherwise
|
||||
*/
|
||||
native function bool ReadNews(byte LocalUserNum,EOnlineNewsType NewsType);
|
||||
|
||||
/**
|
||||
* Delegate used in notifying the UI/game that the news read operation completed
|
||||
*
|
||||
* @param bWasSuccessful true if the read completed ok, false otherwise
|
||||
* @param NewsType the type of news read that just completed
|
||||
*/
|
||||
delegate OnReadNewsCompleted(bool bWasSuccessful,EOnlineNewsType NewsType);
|
||||
|
||||
/**
|
||||
* Sets the delegate used to notify the gameplay code that news reading has completed
|
||||
*
|
||||
* @param ReadGameNewsDelegate the delegate to use for notifications
|
||||
*/
|
||||
function AddReadNewsCompletedDelegate(delegate<OnReadNewsCompleted> ReadNewsDelegate)
|
||||
{
|
||||
if (ReadNewsDelegates.Find(ReadNewsDelegate) == INDEX_NONE)
|
||||
{
|
||||
ReadNewsDelegates[ReadNewsDelegates.Length] = ReadNewsDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified delegate from the notification list
|
||||
*
|
||||
* @param ReadGameNewsDelegate the delegate to use for notifications
|
||||
*/
|
||||
function ClearReadNewsCompletedDelegate(delegate<OnReadNewsCompleted> ReadGameNewsDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = ReadNewsDelegates.Find(ReadGameNewsDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
ReadNewsDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the game specific news item from the cache
|
||||
*
|
||||
* @param LocalUserNum the local user the news is being read for
|
||||
* @param NewsType the type of news to read
|
||||
*
|
||||
* @return an empty string if no data was read, otherwise the contents of the news read
|
||||
*/
|
||||
function string GetNews(byte LocalUserNum,EOnlineNewsType NewsType)
|
||||
{
|
||||
local int NewsIndex;
|
||||
|
||||
// Search through the list of news items and return the one that matches
|
||||
for (NewsIndex = 0; NewsIndex < NewsItems.Length; NewsIndex++)
|
||||
{
|
||||
if (NewsItems[NewsIndex].NewsType == NewsType)
|
||||
{
|
||||
return NewsItems[NewsIndex].NewsItem;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
761
IpDrv/Classes/OnlinePlaylistManager.uc
Normal file
761
IpDrv/Classes/OnlinePlaylistManager.uc
Normal file
@ -0,0 +1,761 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class holds the set of playlists that the game exposes, handles
|
||||
* downloading updates to the playlists via MCP/TitleFiles, and creates the
|
||||
* game settings objects that make up a playlist
|
||||
*/
|
||||
class OnlinePlaylistManager extends Object
|
||||
native
|
||||
inherits(FTickableObject)
|
||||
config(Playlist);
|
||||
|
||||
/** Contains a game settings class name to load and instance using the specified URL to override defaults */
|
||||
struct native ConfiguredGameSetting
|
||||
{
|
||||
/** The unique (within the playlist) id for this game setting */
|
||||
var int GameSettingId;
|
||||
/** The name of the class to load and instance */
|
||||
var string GameSettingsClassName;
|
||||
/** The URL to use to replace settings with (see UpdateFromURL()) */
|
||||
var string Url;
|
||||
/** Holds the object that was created for this entry in a playlist */
|
||||
var transient OnlineGameSettings GameSettings;
|
||||
};
|
||||
|
||||
/** Allows for per playlist inventory swapping */
|
||||
struct native InventorySwap
|
||||
{
|
||||
/** The inventory to replace */
|
||||
var name Original;
|
||||
/** The inventory to replace it with */
|
||||
var string SwapTo;
|
||||
};
|
||||
|
||||
// Defined match types
|
||||
const PLAYER_MATCH = 0;
|
||||
const RANKED_MATCH = 1;
|
||||
const REC_MATCH = 2;
|
||||
const PRIVATE_MATCH = 3;
|
||||
|
||||
/** A playlist contains 1 or more game configurations that players can choose between */
|
||||
struct native Playlist
|
||||
{
|
||||
/** Holds the list of game configurations that are part of this playlist */
|
||||
var array<ConfiguredGameSetting> ConfiguredGames;
|
||||
/** The unique id for this playlist */
|
||||
var int PlaylistId;
|
||||
/** The load balance id for this playlist */
|
||||
var int LoadBalanceId;
|
||||
/** The string to use to lookup the display name for this playlist */
|
||||
var string LocalizationString;
|
||||
/** The set of content/maps (or DLC bundles) that must be present in order to play on this playlist */
|
||||
var array<int> ContentIds;
|
||||
/** The number of players per team if different from the defaults */
|
||||
var int TeamSize;
|
||||
/** The number of teams per match if different from the defaults */
|
||||
var int TeamCount;
|
||||
/** The max party size for this playlist if different from the team size */
|
||||
var int MaxPartySize;
|
||||
/** The string to use in the UI for this playlist */
|
||||
var string Name;
|
||||
/** URL to append to the MP options in GameInfo.InitGame() */
|
||||
var string Url;
|
||||
/** The type of match this is (ranked, player, recreational, whatever you want) */
|
||||
var int MatchType;
|
||||
/** Whether dedicated server searches are supported with this playlist */
|
||||
var bool bDisableDedicatedServerSearches;
|
||||
/** The custom map cycle for this playlist */
|
||||
var array<name> MapCycle;
|
||||
/** The list of inventory swaps for the playlist */
|
||||
var array<InventorySwap> InventorySwaps;
|
||||
};
|
||||
|
||||
/** This is the complete set of playlists available to choose from */
|
||||
var config array<Playlist> Playlists;
|
||||
|
||||
/** The file names to request when downloading a playlist from MCP/TMS/etc */
|
||||
var array<string> PlaylistFileNames;
|
||||
|
||||
/** The set of UIDataStore_GameResource objects to refresh once the download has completed */
|
||||
var config array<name> DatastoresToRefresh;
|
||||
|
||||
/** Used to know when we should finalize the objects */
|
||||
var int DownloadCount;
|
||||
|
||||
/** Incremented when successful to determine whether to update at all */
|
||||
var int SuccessfulCount;
|
||||
|
||||
/** The version number of the playlist that was downloaded */
|
||||
var config int VersionNumber;
|
||||
|
||||
/** Holds the overall and per region playlist population numbers */
|
||||
struct native PlaylistPopulation
|
||||
{
|
||||
/** The unique id for this playlist */
|
||||
var int PlaylistId;
|
||||
/** The total across all regions */
|
||||
var int WorldwideTotal;
|
||||
/** The total for the player's region */
|
||||
var int RegionTotal;
|
||||
};
|
||||
|
||||
/** The list of playlists and the number of players in them */
|
||||
var config array<PlaylistPopulation> PopulationData;
|
||||
|
||||
/** The total number of players across all playlists worldwide */
|
||||
var int WorldwideTotalPlayers;
|
||||
|
||||
/** The total number of players across all playlists in the region */
|
||||
var int RegionTotalPlayers;
|
||||
|
||||
/** Cached object ref that we use for accessing the TitleFileInterface */
|
||||
var transient OnlineTitleFileInterface TitleFileInterface;
|
||||
|
||||
/** The name of the population data file to request */
|
||||
var string PopulationFileName;
|
||||
|
||||
/** The next time the playlist population data needs to be sent */
|
||||
var transient float NextPlaylistPopulationUpdateTime;
|
||||
|
||||
/** How often (in seconds) we should update the population data */
|
||||
var config float PlaylistPopulationUpdateInterval;
|
||||
|
||||
/** The lowest number playlist id to report to the backend. Used to turn off "not mp" playlist ids */
|
||||
var config int MinPlaylistIdToReport;
|
||||
|
||||
/** The playlist id that is being played */
|
||||
var transient int CurrentPlaylistId;
|
||||
|
||||
/** The name of the interface to request as our upload object */
|
||||
var config name EventsInterfaceName;
|
||||
|
||||
/** The datacenter id to use for this machine */
|
||||
var config int DataCenterId;
|
||||
|
||||
/** The name of the datacenter file to request */
|
||||
var string DataCenterFileName;
|
||||
|
||||
/** The time of the last download */
|
||||
var transient float LastPlaylistDownloadTime;
|
||||
|
||||
/** How often to refresh the playlists */
|
||||
var config float PlaylistRefreshInterval;
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
|
||||
/**
|
||||
* Returns whether it is okay to tick this object. E.g. objects being loaded in the background shouldn't be ticked
|
||||
* till they are finalized and unreachable objects cannot be ticked either.
|
||||
*
|
||||
* @return TRUE if tickable, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL IsTickable() const
|
||||
{
|
||||
// We cannot tick objects that are unreachable or are in the process of being loaded in the background.
|
||||
return !HasAnyFlags(RF_Unreachable | RF_AsyncLoading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if an object should be ticked when the game is paused.
|
||||
*
|
||||
* @return always TRUE as networking needs to be ticked even when paused
|
||||
*/
|
||||
virtual UBOOL IsTickableWhenPaused() const
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an update of the playlist population information is needed or not
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate fired when the playlist has been downloaded and processed
|
||||
*
|
||||
* @param bWasSuccessful true if the playlist was read correctly, false if failed
|
||||
*/
|
||||
delegate OnReadPlaylistComplete(bool bWasSuccessful);
|
||||
|
||||
/**
|
||||
* Reads the playlist from either MCP or from some form of title storage
|
||||
*/
|
||||
function DownloadPlaylist()
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
local int FileIndex;
|
||||
|
||||
// Force a reset of the files if we need an update
|
||||
if (ShouldRefreshPlaylists())
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
if (SuccessfulCount == 0)
|
||||
{
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.Patcher != None)
|
||||
{
|
||||
// Request notification of file downloads
|
||||
OnlineSub.Patcher.AddReadFileDelegate(OnReadTitleFileComplete);
|
||||
// Reset our counts since we might be re-downloading
|
||||
DownloadCount = 0;
|
||||
SuccessfulCount = 0;
|
||||
// Don't rebuild the list of files when already there
|
||||
if (PlaylistFileNames.Length == 0)
|
||||
{
|
||||
DetermineFilesToDownload();
|
||||
}
|
||||
// Iterate the files that we read and request them
|
||||
for (FileIndex = 0; FileIndex < PlaylistFileNames.Length; FileIndex++)
|
||||
{
|
||||
OnlineSub.Patcher.AddFileToDownload(PlaylistFileNames[FileIndex]);
|
||||
}
|
||||
// Don't read data center or force update the playlist population interval
|
||||
// except on the first time we load the data
|
||||
if (LastPlaylistDownloadTime == 0)
|
||||
{
|
||||
// Download the datacenter file now too
|
||||
if (class'WorldInfo'.static.GetWorldInfo().NetMode != NM_DedicatedServer)
|
||||
{
|
||||
ReadDataCenterId();
|
||||
}
|
||||
// Force an update of the playlist population data
|
||||
NextPlaylistPopulationUpdateTime = PlaylistPopulationUpdateInterval;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log("No online layer present, using defaults for playlist",,'DevMCP');
|
||||
// Initialize all playlist objects with defaults
|
||||
FinalizePlaylistObjects();
|
||||
// Notify the completion
|
||||
OnReadPlaylistComplete(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Notify the completion
|
||||
OnReadPlaylistComplete(SuccessfulCount == DownloadCount);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses the current loc setting and game ini name to build the download list */
|
||||
native function DetermineFilesToDownload();
|
||||
|
||||
/** @return true if the playlists should be refreshed, false otherwise */
|
||||
native function bool ShouldRefreshPlaylists();
|
||||
|
||||
/**
|
||||
* Notifies us when the download of the playlist file is complete
|
||||
*
|
||||
* @param bWasSuccessful true if the download completed ok, false otherwise
|
||||
* @param FileName the file that was downloaded (or failed to)
|
||||
*/
|
||||
function OnReadTitleFileComplete(bool bWasSuccessful,string FileName)
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
local int FileIndex;
|
||||
|
||||
for (FileIndex = 0; FileIndex < PlaylistFileNames.Length; FileIndex++)
|
||||
{
|
||||
if (PlaylistFileNames[FileIndex] == FileName)
|
||||
{
|
||||
// Increment how many we've downloaded
|
||||
DownloadCount++;
|
||||
SuccessfulCount += int(bWasSuccessful);
|
||||
// If they have all been downloaded, rebuild the playlist
|
||||
if (DownloadCount == PlaylistFileNames.Length)
|
||||
{
|
||||
if (SuccessfulCount != DownloadCount)
|
||||
{
|
||||
`Log("PlaylistManager: not all files downloaded correctly, using defaults where applicable",,'DevMCP');
|
||||
}
|
||||
// Rebuild the playlist and update any objects/ui
|
||||
FinalizePlaylistObjects();
|
||||
// Notify our requester
|
||||
OnReadPlaylistComplete(SuccessfulCount == DownloadCount);
|
||||
|
||||
// Remove the delegates since we are done
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.Patcher != None)
|
||||
{
|
||||
OnlineSub.Patcher.ClearReadFileDelegate(OnReadTitleFileComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the configuration data to create the requested objects and then applies any
|
||||
* specific game settings changes to them
|
||||
*/
|
||||
native function FinalizePlaylistObjects();
|
||||
|
||||
/**
|
||||
* Finds the game settings object associated with this playlist and game settings id
|
||||
*
|
||||
* @param PlaylistId the playlist we are searching
|
||||
* @param GameSettingsId the game settings id being searched for
|
||||
*
|
||||
* @return the game settings specified or None if not found
|
||||
*/
|
||||
function OnlineGameSettings GetGameSettings(int PlaylistId,int GameSettingsId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
local int GameIndex;
|
||||
|
||||
// Find the matching playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
// Search through the registered games for this playlist
|
||||
for (GameIndex = 0; GameIndex < Playlists[PlaylistIndex].ConfiguredGames.Length; GameIndex++)
|
||||
{
|
||||
if (Playlists[PlaylistIndex].ConfiguredGames[GameIndex].GameSettingId == GameSettingsId)
|
||||
{
|
||||
return Playlists[PlaylistIndex].ConfiguredGames[GameIndex].GameSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any game settings exist for the given playlistid
|
||||
*
|
||||
* @param PlaylistId playlist to check for game settings
|
||||
* @return TRUE if game settings exist for the given id
|
||||
*/
|
||||
function bool HasAnyGameSettings(int PlaylistId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
local int GameIndex;
|
||||
|
||||
// Find the matching playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
// Search through the registered games for this playlist
|
||||
for (GameIndex = 0; GameIndex < Playlists[PlaylistIndex].ConfiguredGames.Length; GameIndex++)
|
||||
{
|
||||
if (Playlists[PlaylistIndex].ConfiguredGames[GameIndex].GameSettings != None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if this playlist can be found on dedicated servers
|
||||
*
|
||||
* @param PlaylistId playlist to check for dedicated server support
|
||||
*/
|
||||
function bool PlaylistSupportsDedicatedServers(int PlaylistId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
// Search through the registered games for this playlist
|
||||
return !Playlists[PlaylistIndex].bDisableDedicatedServerSearches;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the team information for the specified playlist and returns it in the out vars
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
* @param TeamSize out var getting the number of players per team
|
||||
* @param TeamCount out var getting the number of teams per match
|
||||
* @param MaxPartySize out var getting the number of players per party
|
||||
*/
|
||||
function GetTeamInfoFromPlaylist(int PlaylistId,out int TeamSize,out int TeamCount,out int MaxPartySize)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
TeamSize = 0;
|
||||
TeamCount = 0;
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
TeamSize = Playlists[PlaylistIndex].TeamSize;
|
||||
TeamCount = Playlists[PlaylistIndex].TeamCount;
|
||||
MaxPartySize = Playlists[PlaylistIndex].MaxPartySize;
|
||||
// If it wasn't set up for this playlist, use the team size
|
||||
if (MaxPartySize == 0)
|
||||
{
|
||||
MaxPartySize = TeamSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the load balance id for the specified playlist and returns it in the out vars.
|
||||
* The load balance id can be used to change the match mode during a search.
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
* @param LoadBalanceId out var getting the id for load balancing
|
||||
*/
|
||||
function GetLoadBalanceIdFromPlaylist(int PlaylistId,out int LoadBalanceId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
LoadBalanceId = 0;
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
LoadBalanceId = Playlists[PlaylistIndex].LoadBalanceId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given playlist entry is arbitrated or not
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
*
|
||||
* @return TRUE if the playlist is arbitrated
|
||||
*/
|
||||
function bool IsPlaylistArbitrated(int PlaylistId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
return Playlists[PlaylistIndex].MatchType == RANKED_MATCH;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given playlist entry is arbitrated or not
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
*
|
||||
* @return the match type for the playlist
|
||||
*/
|
||||
function int GetMatchType(int PlaylistId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
return Playlists[PlaylistIndex].MatchType;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified playlist and returns any additional URL options
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
*
|
||||
* @return the URL string associated with this playlist
|
||||
*/
|
||||
function string GetUrlFromPlaylist(int PlaylistId)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
return Playlists[PlaylistIndex].Url;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified playlist and returns the map cycle for it
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
* @param MapCycle the out var that gets the map cycle
|
||||
*/
|
||||
function GetMapCycleFromPlaylist(int PlaylistId,out array<name> MapCycle)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
// Don't overwrite if there isn't one specified
|
||||
if (Playlists[PlaylistIndex].MapCycle.Length > 0)
|
||||
{
|
||||
MapCycle = Playlists[PlaylistIndex].MapCycle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a per playlist inventory swap
|
||||
*
|
||||
* @param PlaylistId the playlist we are checking for swaps in
|
||||
* @param SourceInventory the source item we are checking for swapping
|
||||
*
|
||||
* @return the swapped item or the original if not found
|
||||
*/
|
||||
function class<Inventory> GetInventorySwapFromPlaylist(int PlaylistId,class<Inventory> SourceInventory)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
local int SwapIndex;
|
||||
|
||||
// Find the playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
SwapIndex = Playlists[PlaylistIndex].InventorySwaps.Find('Original',SourceInventory.Name);
|
||||
if (SwapIndex != INDEX_NONE)
|
||||
{
|
||||
return class<Inventory>(DynamicLoadObject(Playlists[PlaylistIndex].InventorySwaps[SwapIndex].SwapTo,class'class'));
|
||||
}
|
||||
}
|
||||
return SourceInventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified playlist and return the content ids in the out var
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
* @param ContentIds the list to set the content ids in
|
||||
*/
|
||||
function GetContentIdsFromPlaylist(int PlaylistId,out array<int> ContentIds)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
// Find the matching playlist
|
||||
PlaylistIndex = Playlists.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
ContentIds = Playlists[PlaylistIndex].ContentIds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the playlists to be re-requested from the server
|
||||
*/
|
||||
function Reset()
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
|
||||
DownloadCount = 0;
|
||||
SuccessfulCount = 0;
|
||||
|
||||
// Clear out any cached file contents if present
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.Patcher != None)
|
||||
{
|
||||
OnlineSub.Patcher.ClearCachedFiles();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the player population data for playlists by region
|
||||
*/
|
||||
function ReadPlaylistPopulation()
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
|
||||
// Get the object to download with the first time
|
||||
if (TitleFileInterface == None)
|
||||
{
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None)
|
||||
{
|
||||
// Ask for the interface by name
|
||||
TitleFileInterface = OnlineSub.TitleFileInterface;
|
||||
}
|
||||
}
|
||||
if (TitleFileInterface != None)
|
||||
{
|
||||
// Force an update of this information
|
||||
TitleFileInterface.ClearDownloadedFile(PopulationFileName);
|
||||
// Set the callback for notifications of files completing
|
||||
TitleFileInterface.AddReadTitleFileCompleteDelegate(OnReadPlaylistPopulationComplete);
|
||||
// Request the playlist population numbers
|
||||
TitleFileInterface.ReadTitleFile(PopulationFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
`warn("Cannot download playlist population due to missing TitleFileInterface object");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies us when the download of a file is complete
|
||||
*
|
||||
* @param bWasSuccessful true if the download completed ok, false otherwise
|
||||
* @param FileName the file that was downloaded (or failed to)
|
||||
*/
|
||||
function OnReadPlaylistPopulationComplete(bool bWasSuccessful,string FileName)
|
||||
{
|
||||
local array<byte> FileData;
|
||||
|
||||
if (FileName == PopulationFileName)
|
||||
{
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
// Read the contents so that they can be processed
|
||||
if (TitleFileInterface.GetTitleFileContents(FileName,FileData))
|
||||
{
|
||||
ParsePlaylistPopulationData(FileData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log("Failed to download the file ("$FileName$") from TitleFileInterface",,'DevMCP');
|
||||
}
|
||||
// Notify any listener
|
||||
OnPlaylistPopulationDataUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the listener when playlist population data was updated
|
||||
*/
|
||||
delegate OnPlaylistPopulationDataUpdated();
|
||||
|
||||
/**
|
||||
* Converts the data into the structure used by the playlist manager
|
||||
*
|
||||
* @param Data the data that was downloaded
|
||||
*/
|
||||
native function ParsePlaylistPopulationData(const out array<byte> Data);
|
||||
|
||||
/**
|
||||
* Finds the population information for the specified playlist and returns it in the out vars
|
||||
*
|
||||
* @param PlaylistId the playlist being searched for
|
||||
* @param WorldwideTotal out var getting the number of players worldwide
|
||||
* @param RegionTotal out var getting the number of players in this region
|
||||
*/
|
||||
function GetPopulationInfoFromPlaylist(int PlaylistId,out int WorldwideTotal,out int RegionTotal)
|
||||
{
|
||||
local int PlaylistIndex;
|
||||
|
||||
WorldwideTotal = 0;
|
||||
RegionTotal = 0;
|
||||
// Find the playlist
|
||||
PlaylistIndex = PopulationData.Find('PlaylistId',PlaylistId);
|
||||
if (PlaylistIndex != INDEX_NONE)
|
||||
{
|
||||
WorldwideTotal = PopulationData[PlaylistIndex].WorldwideTotal;
|
||||
RegionTotal = PopulationData[PlaylistIndex].RegionTotal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once enough time has elapsed that a playlist update is required
|
||||
*
|
||||
* @param NumPlayers the numbers of players to report (easier to get at in native)
|
||||
*/
|
||||
event SendPlaylistPopulationUpdate(int NumPlayers)
|
||||
{
|
||||
local OnlineEventsInterface EventsInterface;
|
||||
local OnlineSubsystem OnlineSub;
|
||||
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None)
|
||||
{
|
||||
EventsInterface = OnlineEventsInterface(OnlineSub.GetNamedInterface(EventsInterfaceName));
|
||||
// Always send population data if requested (dedicated might have NumPlayers=0 but we want the heartbeat)
|
||||
if (EventsInterface != None)
|
||||
{
|
||||
`Log("Updating playlist population with PlaylistId="$CurrentPlaylistId$" and NumPlayers="$NumPlayers,,'DevMCP');
|
||||
// Send this to the network backend
|
||||
EventsInterface.UpdatePlaylistPopulation(CurrentPlaylistId,NumPlayers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the network backend which datacenter this machine is to use
|
||||
*/
|
||||
function ReadDataCenterId()
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
|
||||
// Get the object to download with the first time
|
||||
if (TitleFileInterface == None)
|
||||
{
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None)
|
||||
{
|
||||
// Ask for the interface by name
|
||||
TitleFileInterface = OnlineSub.TitleFileInterface;
|
||||
}
|
||||
}
|
||||
if (TitleFileInterface != None)
|
||||
{
|
||||
// Set the callback for notifications of files completing
|
||||
TitleFileInterface.AddReadTitleFileCompleteDelegate(OnReadDataCenterIdComplete);
|
||||
// Request the datacenter id
|
||||
TitleFileInterface.ReadTitleFile(DataCenterFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
`warn("Cannot download datacenter id due to missing TitleFileInterface object");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies us when the download of a file is complete
|
||||
*
|
||||
* @param bWasSuccessful true if the download completed ok, false otherwise
|
||||
* @param FileName the file that was downloaded (or failed to)
|
||||
*/
|
||||
function OnReadDataCenterIdComplete(bool bWasSuccessful,string FileName)
|
||||
{
|
||||
local array<byte> FileData;
|
||||
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
if (FileName == DataCenterFileName)
|
||||
{
|
||||
// Read the contents so that they can be processed
|
||||
if (TitleFileInterface.GetTitleFileContents(FileName,FileData))
|
||||
{
|
||||
ParseDataCenterId(FileData);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log("Failed to download the file ("$FileName$") from TitleFileInterface",,'DevMCP');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the data into the datacenter id
|
||||
*
|
||||
* @param Data the data that was downloaded
|
||||
*/
|
||||
native function ParseDataCenterId(const out array<byte> Data);
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
PopulationFileName="PlaylistPopulationData.ini"
|
||||
DataCenterFileName="DataCenter.Id"
|
||||
}
|
21
IpDrv/Classes/OnlinePlaylistProvider.uc
Normal file
21
IpDrv/Classes/OnlinePlaylistProvider.uc
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*
|
||||
* Per object config provider that exposes dynamic playlists to the UI system
|
||||
*/
|
||||
class OnlinePlaylistProvider extends UIResourceDataProvider
|
||||
native(inherit)
|
||||
PerObjectConfig
|
||||
Config(Playlist);
|
||||
|
||||
/** Unique identifier for this Playlist */
|
||||
var config int PlaylistId;
|
||||
|
||||
/** List of the names of the OnlinePlaylistGameTypeProvider for the game modes supported by this playlist */
|
||||
var config array<Name> PlaylistGameTypeNames;
|
||||
|
||||
/** Localized display name for the playlist */
|
||||
var config localized string DisplayName;
|
||||
|
||||
/** Value to determine sorting priority (highest is first in the list) */
|
||||
var config int Priority;
|
76
IpDrv/Classes/OnlineSubsystemCommonImpl.uc
Normal file
76
IpDrv/Classes/OnlineSubsystemCommonImpl.uc
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class that implements commonly needed members/features across all platforms
|
||||
*/
|
||||
class OnlineSubsystemCommonImpl extends OnlineSubsystem
|
||||
native
|
||||
abstract
|
||||
config(Engine);
|
||||
|
||||
/**
|
||||
* Holds the pointer to the platform specific FVoiceInterface implementation
|
||||
* used for voice communication
|
||||
*/
|
||||
var const native transient pointer VoiceEngine{class FVoiceInterface};
|
||||
|
||||
/** Holds the maximum number of local talkers allowed */
|
||||
var config int MaxLocalTalkers;
|
||||
|
||||
/** Holds the maximum number of remote talkers allowed (clamped to 30 which is XHV max) */
|
||||
var config int MaxRemoteTalkers;
|
||||
|
||||
/** Whether speech recognition is enabled */
|
||||
var config bool bIsUsingSpeechRecognition;
|
||||
|
||||
/** The object that handles the game interface implementation across platforms */
|
||||
var OnlineGameInterfaceImpl GameInterfaceImpl;
|
||||
|
||||
/** The object that handles the auth interface implementation across platforms */
|
||||
var OnlineAuthInterfaceImpl AuthInterfaceImpl;
|
||||
|
||||
/**
|
||||
* Returns the name of the player for the specified index
|
||||
*
|
||||
* @param UserIndex the user to return the name of
|
||||
*
|
||||
* @return the name of the player at the specified index
|
||||
*/
|
||||
event string GetPlayerNicknameFromIndex(int UserIndex);
|
||||
|
||||
/**
|
||||
* Determine if the player is registered in the specified session
|
||||
*
|
||||
* @param PlayerId the player to check if in session or not
|
||||
* @return TRUE if the player is a registrant in the session
|
||||
*/
|
||||
native function bool IsPlayerInSession(name SessionName,UniqueNetId PlayerId);
|
||||
|
||||
/**
|
||||
* Get a list of the net ids for the players currently registered on the session
|
||||
*
|
||||
* @param SessionName name of the session to find
|
||||
* @param OutRegisteredPlayers [out] list of player net ids in the session (empty if not found)
|
||||
*/
|
||||
function GetRegisteredPlayers(name SessionName,out array<UniqueNetId> OutRegisteredPlayers)
|
||||
{
|
||||
local int Idx,PlayerIdx;
|
||||
|
||||
OutRegisteredPlayers.Length = 0;
|
||||
for (Idx=0; Idx < Sessions.Length; Idx++)
|
||||
{
|
||||
// find session by name
|
||||
if (Sessions[Idx].SessionName == SessionName)
|
||||
{
|
||||
// return list of player ids currently registered on the session
|
||||
OutRegisteredPlayers.Length = Sessions[Idx].Registrants.Length;
|
||||
for (PlayerIdx=0; PlayerIdx < Sessions[Idx].Registrants.Length; PlayerIdx++)
|
||||
{
|
||||
OutRegisteredPlayers[PlayerIdx] = Sessions[Idx].Registrants[PlayerIdx].PlayerNetId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
194
IpDrv/Classes/OnlineTitleFileDownloadBase.uc
Normal file
194
IpDrv/Classes/OnlineTitleFileDownloadBase.uc
Normal file
@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a mechanism for downloading arbitrary files from the MCP server
|
||||
*/
|
||||
class OnlineTitleFileDownloadBase extends MCPBase
|
||||
native
|
||||
implements(OnlineTitleFileInterface)
|
||||
dependson(OnlineSubsystem);
|
||||
|
||||
/** Compression types supported */
|
||||
enum EMcpFileCompressionType
|
||||
{
|
||||
MFCT_NONE,
|
||||
MFCT_ZLIB
|
||||
};
|
||||
/** Holds the data used in downloading a file along with its web request */
|
||||
struct native TitleFileWeb extends OnlineSubsystem.TitleFile
|
||||
{
|
||||
/** web response or string data if download succeeded */
|
||||
var string StringData;
|
||||
/** HTTP request that is in flight for the file request */
|
||||
var HttpRequestInterface HTTPRequest;
|
||||
/** The compression type of this File so the client knows how to de-compress the payload */
|
||||
var EMcpFileCompressionType FileCompressionType;
|
||||
};
|
||||
|
||||
/** The list of delegates to notify when a file is read */
|
||||
var private array<delegate<OnReadTitleFileComplete> > ReadTitleFileCompleteDelegates;
|
||||
|
||||
/** The list of delegates to notify when a file list read is done */
|
||||
var array<delegate<OnRequestTitleFileListComplete> > RequestTitleFileListCompleteDelegates;
|
||||
|
||||
/** The base URL to used for contacting for files, such that BaseUrl?TitleID=1234&FileName=MyFile.ini is the complete URL */
|
||||
var config string BaseUrl;
|
||||
|
||||
/** Base URL for getting list of EMS files */
|
||||
var config string RequestFileListURL;
|
||||
/** Base URL for downloading a single EMS file */
|
||||
var config string RequestFileURL;
|
||||
|
||||
/** The amount of time to allow for downloading of the file */
|
||||
var config float TimeOut;
|
||||
|
||||
/** Allows the game to route a specific file or sets of files to a specific URL. If there is no special mapping for a file, then the base URL is used */
|
||||
struct native FileNameToURLMapping
|
||||
{
|
||||
/** The name of the file to route to a specific URL */
|
||||
var name FileName;
|
||||
/** The URL to route the request to */
|
||||
var name UrlMapping;
|
||||
};
|
||||
|
||||
/** The routing table to look in when trying to find special URL handlers */
|
||||
var config array<FileNameToURLMapping> FilesToUrls;
|
||||
|
||||
/**
|
||||
* Delegate fired when a file read from the network platform's title specific storage is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file read was successful or not
|
||||
* @param FileName the name of the file this was for
|
||||
*/
|
||||
delegate OnReadTitleFileComplete(bool bWasSuccessful,string FileName);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous read of the specified file from the network platform's
|
||||
* title specific file store
|
||||
*
|
||||
* @param FileToRead the name of the file to read
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
function bool ReadTitleFile(string FileToRead, optional EOnlineFileType FileType = OFT_Binary);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been read
|
||||
*
|
||||
* @param ReadTitleFileCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddReadTitleFileCompleteDelegate(delegate<OnReadTitleFileComplete> ReadTitleFileCompleteDelegate)
|
||||
{
|
||||
if (ReadTitleFileCompleteDelegates.Find(ReadTitleFileCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
ReadTitleFileCompleteDelegates[ReadTitleFileCompleteDelegates.Length] = ReadTitleFileCompleteDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param ReadTitleFileCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearReadTitleFileCompleteDelegate(delegate<OnReadTitleFileComplete> ReadTitleFileCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = ReadTitleFileCompleteDelegates.Find(ReadTitleFileCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
ReadTitleFileCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the file data into the specified buffer for the specified file
|
||||
*
|
||||
* @param FileName the name of the file to read
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the data was copied, false otherwise
|
||||
*/
|
||||
function bool GetTitleFileContents(string FileName,out array<byte> FileContents);
|
||||
|
||||
/**
|
||||
* Determines the async state of the tile file read operation
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the async state of the file read
|
||||
*/
|
||||
function EOnlineEnumerationReadState GetTitleFileState(string FileName);
|
||||
|
||||
/**
|
||||
* Empties the set of downloaded files if possible (no async tasks outstanding)
|
||||
*
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
function bool ClearDownloadedFiles();
|
||||
|
||||
/**
|
||||
* Empties the cached data for this file if it is not being downloaded currently
|
||||
*
|
||||
* @param FileName the name of the file to remove from the cache
|
||||
*
|
||||
* @return true if it could be deleted, false if it could not
|
||||
*/
|
||||
function bool ClearDownloadedFile(string FileName);
|
||||
|
||||
/**
|
||||
* Async call to request a list of files (returned as string) from EMS
|
||||
*/
|
||||
//@HSL_BEGIN_XBOX
|
||||
function bool RequestTitleFileList();
|
||||
|
||||
/**
|
||||
* Delegate fired when the request for a list of files completes
|
||||
*
|
||||
* @param bWasSuccessful whether the request completed successfully
|
||||
* @param ResultStr contains the list of files and associated meta data
|
||||
*/
|
||||
delegate OnRequestTitleFileListComplete(bool bWasSuccessful, array<string> FilePaths);
|
||||
//@HSL_END_XBOX
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when the list of requested files has been received
|
||||
*
|
||||
* @param RequestTitleFileListDelegate the delegate to add
|
||||
*/
|
||||
function AddRequestTitleFileListCompleteDelegate(delegate<OnRequestTitleFileListComplete> RequestTitleFileListDelegate)
|
||||
{
|
||||
if (RequestTitleFileListCompleteDelegates.Find(RequestTitleFileListDelegate) == INDEX_NONE)
|
||||
{
|
||||
RequestTitleFileListCompleteDelegates.AddItem(RequestTitleFileListDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param RequestTitleFileListDelegate the delegate to remove
|
||||
*/
|
||||
function ClearRequestTitleFileListCompleteDelegate(delegate<OnRequestTitleFileListComplete> RequestTitleFileListDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = RequestTitleFileListCompleteDelegates.Find(RequestTitleFileListDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
RequestTitleFileListCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the filename to URL mapping table for the specified filename
|
||||
*
|
||||
* @param FileName the file to search the table for
|
||||
*
|
||||
* @param the URL to use to request the file or BaseURL if no special mapping is present
|
||||
*/
|
||||
native function string GetUrlForFile(string FileName);
|
134
IpDrv/Classes/OnlineTitleFileDownloadMcp.uc
Normal file
134
IpDrv/Classes/OnlineTitleFileDownloadMcp.uc
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a mechanism for downloading arbitrary files from the MCP server
|
||||
*/
|
||||
class OnlineTitleFileDownloadMcp extends OnlineTitleFileDownloadBase
|
||||
native;
|
||||
|
||||
/** Struct that matches one download object per file for parallel downloading */
|
||||
struct native TitleFileMcp extends OnlineSubsystem.TitleFile
|
||||
{
|
||||
/** The class that will communicate with backend to download the file */
|
||||
var private native const pointer HttpDownloader{class FHttpDownloadBinary};
|
||||
};
|
||||
|
||||
/** The list of title files that have been read or are being read */
|
||||
var private array<TitleFileMcp> TitleFiles;
|
||||
|
||||
/** The number of files in the array being processed */
|
||||
var transient int DownloadCount;
|
||||
|
||||
/**
|
||||
* Starts an asynchronous read of the specified file from the network platform's
|
||||
* title specific file store
|
||||
*
|
||||
* @param FileToRead the name of the file to read
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
native function bool ReadTitleFile(string FileToRead, optional EOnlineFileType FileType = OFT_Binary);
|
||||
|
||||
/**
|
||||
* Copies the file data into the specified buffer for the specified file
|
||||
*
|
||||
* @param FileName the name of the file to read
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the data was copied, false otherwise
|
||||
*/
|
||||
native function bool GetTitleFileContents(string FileName,out array<byte> FileContents);
|
||||
|
||||
/**
|
||||
* Determines the async state of the tile file read operation
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the async state of the file read
|
||||
*/
|
||||
function EOnlineEnumerationReadState GetTitleFileState(string FileName)
|
||||
{
|
||||
local int FileIndex;
|
||||
|
||||
FileIndex = TitleFiles.Find('FileName',FileName);
|
||||
if (FileIndex != INDEX_NONE)
|
||||
{
|
||||
return TitleFiles[FileIndex].AsyncState;
|
||||
}
|
||||
return OERS_Failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the set of downloaded files if possible (no async tasks outstanding)
|
||||
*
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
native function bool ClearDownloadedFiles();
|
||||
|
||||
/**
|
||||
* Empties the cached data for this file if it is not being downloaded currently
|
||||
*
|
||||
* @param FileName the name of the file to remove from the cache
|
||||
*
|
||||
* @return true if it could be deleted, false if it could not
|
||||
*/
|
||||
native function bool ClearDownloadedFile(string FileName);
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
/**
|
||||
* Ticks any outstanding async tasks that need processing
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
|
||||
// Helpers
|
||||
|
||||
/**
|
||||
* Searches the list of files for the one that matches the filename
|
||||
*
|
||||
* @param FileName the file to search for
|
||||
*
|
||||
* @return the file details
|
||||
*/
|
||||
FORCEINLINE FTitleFileMcp* GetTitleFile(const FString& FileName)
|
||||
{
|
||||
// Search for the specified file
|
||||
for (INT Index = 0; Index < TitleFiles.Num(); Index++)
|
||||
{
|
||||
FTitleFileMcp* TitleFile = &TitleFiles(Index);
|
||||
if (TitleFile &&
|
||||
TitleFile->Filename == FileName)
|
||||
{
|
||||
return TitleFile;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the delegates so the caller knows the file download is complete
|
||||
*
|
||||
* @param TitleFile the information for the file that was downloaded
|
||||
*/
|
||||
void TriggerDelegates(const FTitleFile* TitleFile);
|
||||
|
||||
/**
|
||||
* Builds the URL to use when fetching the specified file
|
||||
*
|
||||
* @param FileName the file that is being requested
|
||||
*
|
||||
* @return the URL to use with all of the per platform extras
|
||||
*/
|
||||
virtual FString BuildURLParameters(const FString& FileName)
|
||||
{
|
||||
return FString::Printf(TEXT("TitleID=%d&PlatformID=%d&Filename=%s"),
|
||||
appGetTitleId(),
|
||||
(DWORD)appGetPlatformType(),
|
||||
*FileName);
|
||||
}
|
||||
}
|
337
IpDrv/Classes/OnlineTitleFileDownloadWeb.uc
Normal file
337
IpDrv/Classes/OnlineTitleFileDownloadWeb.uc
Normal file
@ -0,0 +1,337 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title file downloading implementation via web service request
|
||||
*/
|
||||
class OnlineTitleFileDownloadWeb extends OnlineTitleFileDownloadBase
|
||||
native;
|
||||
|
||||
`include(Engine\Classes\HttpStatusCodes.uci);
|
||||
|
||||
/** The list of title files that have been read or are being read */
|
||||
var private array<TitleFileWeb> TitleFiles;
|
||||
|
||||
/**
|
||||
* Uncompress the title file
|
||||
*
|
||||
* @param FileCompressionType enum that that informs how to uncompress the file
|
||||
* @param CompressedFileContents Source data to uncompress
|
||||
* @param UncompressedFileContents the out buffer to copy the data into
|
||||
*/
|
||||
native function bool UncompressTitleFileContents(EMcpFileCompressionType FileCompressionType, const out array<byte> CompressedFileContents, out array<byte> UncompressedFileContents);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous read of the specified file from the network platform's
|
||||
* title specific file store
|
||||
*
|
||||
* @param FileToRead the name of the file to read
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
function bool ReadTitleFile(string FileToRead, optional EOnlineFileType FileType = OFT_Binary)
|
||||
{
|
||||
local int FileIndex,Idx;
|
||||
local string URL;
|
||||
|
||||
// check for a prior request
|
||||
FileIndex = INDEX_NONE;
|
||||
for (Idx=0; Idx < TitleFiles.Length; Idx++)
|
||||
{
|
||||
// case sensitive
|
||||
if (InStr(TitleFiles[Idx].Filename,FileToRead,true,false) != INDEX_NONE)
|
||||
{
|
||||
FileIndex = Idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// add new entry for this file request if not found
|
||||
if (FileIndex == INDEX_NONE)
|
||||
{
|
||||
FileIndex = TitleFiles.Length;
|
||||
TitleFiles.Length = TitleFiles.Length + 1;
|
||||
TitleFiles[FileIndex].Filename = FileToRead;
|
||||
TitleFiles[FileIndex].AsyncState = OERS_NotStarted;
|
||||
}
|
||||
// file has been downloaded before successfully so already done
|
||||
if (TitleFiles[FileIndex].AsyncState == OERS_Done)
|
||||
{
|
||||
TriggerDelegates(true,FileToRead);
|
||||
}
|
||||
// file has been downloaded before but failed
|
||||
else if (TitleFiles[FileIndex].AsyncState == OERS_Failed)
|
||||
{
|
||||
TriggerDelegates(false,FileToRead);
|
||||
return false;
|
||||
}
|
||||
// download needs to start if not already in progress
|
||||
else if (TitleFiles[FileIndex].AsyncState != OERS_InProgress)
|
||||
{
|
||||
// mark the file entry as pending download
|
||||
TitleFiles[FileIndex].AsyncState = OERS_InProgress;
|
||||
// tack on the filename to the base/overridden URL
|
||||
URL = GetUrlForFile(FileToRead) $ FileToRead;
|
||||
`Log(`location @ "starting read for title file"
|
||||
@"url="$URL);
|
||||
// send off web request and register for delegate for its completion
|
||||
TitleFiles[FileIndex].HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (TitleFiles[FileIndex].HTTPRequest != None)
|
||||
{
|
||||
TitleFiles[FileIndex].HTTPRequest.OnProcessRequestComplete = OnFileDownloadComplete;
|
||||
TitleFiles[FileIndex].HTTPRequest.SetVerb("GET");
|
||||
TitleFiles[FileIndex].HTTPRequest.SetURL(URL);
|
||||
TitleFiles[FileIndex].HTTPRequest.ProcessRequest();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate called on each completed web request
|
||||
*
|
||||
* @param OriginalRequest - The original request object that spawned the response
|
||||
* @param HttpResponse - The response object. Could be None if the request failed spectacularly. If the request failed to receive a complete
|
||||
* response for some reason, this could contain a valid Response object with as much info as could be retrieved.
|
||||
* Always use the bDidSucceed parameter to determine if the entire response was received successfully.
|
||||
* @param bSucceeded - whether the response succeeded. If it did not, you should not trust the payload or headers.
|
||||
* Basically indicates a net failure occurred while receiving the response.
|
||||
*/
|
||||
private function OnFileDownloadComplete(HttpRequestInterface Request, HttpResponseInterface Response, bool bDidSucceed)
|
||||
{
|
||||
local bool bSuccess;
|
||||
local int FileIndex,Idx;
|
||||
local string Filename;
|
||||
local array<byte> BinaryData;
|
||||
local string FileCompressionTypeString;
|
||||
|
||||
if (bDidSucceed)
|
||||
{
|
||||
FileIndex = INDEX_NONE;
|
||||
// find file entry for this request based on what was passed on the URL
|
||||
for (Idx=0; Idx < TitleFiles.Length; Idx++)
|
||||
{
|
||||
if (TitleFiles[Idx].HTTPRequest == Request)
|
||||
{
|
||||
FileIndex = Idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
`Log(`location @ ""
|
||||
@"FileIndex="$FileIndex
|
||||
@"OriginalURL="$Request.GetURL()
|
||||
@"ResponseCode="$Response.GetResponseCode()
|
||||
@"ContentLength="$Response.GetContentLength());
|
||||
|
||||
if (FileIndex != INDEX_NONE)
|
||||
{
|
||||
Filename = TitleFiles[FileIndex].Filename;
|
||||
// remove ref to request since it is now complete
|
||||
TitleFiles[FileIndex].HTTPRequest = None;
|
||||
TitleFiles[FileIndex].AsyncState = OERS_Failed;
|
||||
// only successful response code as we're not handling any redirects
|
||||
if (Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
bSuccess = true;
|
||||
|
||||
// copy the payload data from the web request
|
||||
Response.GetContent(BinaryData);
|
||||
TitleFiles[FileIndex].Data = BinaryData;
|
||||
// Get the Compression Type from the Response Header
|
||||
FileCompressionTypeString = Response.GetHeader("Mcp-Content-Encoding");
|
||||
// Convert CompressionTypeString stored in the datastore to the enum used on the client
|
||||
switch(FileCompressionTypeString)
|
||||
{
|
||||
case "MFCT_ZLIB":
|
||||
TitleFiles[FileIndex].FileCompressionType = MFCT_ZLIB;
|
||||
break;
|
||||
default:
|
||||
TitleFiles[FileIndex].FileCompressionType = MFCT_NONE;
|
||||
}
|
||||
// mark as successfully done
|
||||
TitleFiles[FileIndex].AsyncState = OERS_Done;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear failed data
|
||||
TitleFiles[FileIndex].AsyncState = OERS_Failed;
|
||||
TitleFiles[FileIndex].Data.Length = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`location @ "No entry found for"
|
||||
@"FileIndex="$FileIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`Log(`location @ "web request for file download failed");
|
||||
}
|
||||
// web request is complete and delegate is triggered for success/failure
|
||||
TriggerDelegates(bSuccess,Filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the delegates registered on this interface for each file download request
|
||||
*
|
||||
* @param bSuccess true if the request was successful
|
||||
* @param FileRead name of the file that was read
|
||||
*/
|
||||
private native function TriggerDelegates(bool bSuccess,string FileRead);
|
||||
|
||||
/**
|
||||
* Copies the file data into the specified buffer for the specified file
|
||||
*
|
||||
* @param FileName the name of the file to read
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the data was copied, false otherwise
|
||||
*/
|
||||
native function bool GetTitleFileContents(string FileName,out array<byte> FileContents);
|
||||
|
||||
/**
|
||||
* Determines the async state of the tile file read operation
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the async state of the file read
|
||||
*/
|
||||
function EOnlineEnumerationReadState GetTitleFileState(string FileName)
|
||||
{
|
||||
local int FileIndex;
|
||||
|
||||
FileIndex = TitleFiles.Find('FileName',FileName);
|
||||
if (FileIndex != INDEX_NONE)
|
||||
{
|
||||
return TitleFiles[FileIndex].AsyncState;
|
||||
}
|
||||
return OERS_Failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the set of downloaded files if possible (no async tasks outstanding)
|
||||
*
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
native function bool ClearDownloadedFiles();
|
||||
|
||||
/**
|
||||
* Empties the cached data for this file if it is not being downloaded currently
|
||||
*
|
||||
* @param FileName the name of the file to remove from the cache
|
||||
*
|
||||
* @return true if it could be deleted, false if it could not
|
||||
*/
|
||||
native function bool ClearDownloadedFile(string FileName);
|
||||
|
||||
/**
|
||||
* Async call to request a list of files (returned as string) from EMS
|
||||
*/
|
||||
function bool RequestTitleFileList()
|
||||
{
|
||||
local HttpRequestInterface HTTPRequest;
|
||||
local string URL;
|
||||
|
||||
HTTPRequest = class'HttpFactory'.static.CreateRequest();
|
||||
if (HTTPRequest != None)
|
||||
{
|
||||
URL = GetBaseURL() $ RequestFileListURL $ GetAppAccessURL();
|
||||
HTTPRequest.OnProcessRequestComplete = OnFileListReceived;
|
||||
HTTPRequest.SetVerb("GET");
|
||||
HTTPRequest.SetURL(URL);
|
||||
HTTPRequest.ProcessRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"HTTPRequest object missing");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegate for when the EMS file list is received
|
||||
*
|
||||
* @param OriginalRequest - The original request object that spawned the response
|
||||
* @param HttpResponse - The response object. Could be None if the request failed spectacularly. If the request failed to receive a complete
|
||||
* response for some reason, this could contain a valid Response object with as much info as could be retrieved.
|
||||
* Always use the bDidSucceed parameter to determine if the entire response was received successfully.
|
||||
* @param bSucceeded - whether the response succeeded. If it did not, you should not trust the payload or headers.
|
||||
* Basically indicates a net failure occurred while receiving the response.
|
||||
*/
|
||||
function OnFileListReceived(HttpRequestInterface Request, HttpResponseInterface Response, bool bDidSucceed)
|
||||
{
|
||||
local int Index;
|
||||
local delegate<OnRequestTitleFileListComplete> RequestTitleFileListDelegate;
|
||||
local array<string> ResponseStr;
|
||||
local bool bSuccess;
|
||||
|
||||
ResponseStr.length = 0;
|
||||
|
||||
if (bDidSucceed)
|
||||
{
|
||||
if (Response != None &&
|
||||
Response.GetResponseCode() == `HTTP_STATUS_OK)
|
||||
{
|
||||
ResponseStr.AddItem(Response.GetContentAsString());
|
||||
bSuccess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Download of file list failed. Bad response."
|
||||
@"ResponseCode="$Response.GetResponseCode()
|
||||
@"URL="$Request.GetURL());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log(`location@"Download of file list failed.");
|
||||
}
|
||||
|
||||
// Call the completion delegate for receiving the file list
|
||||
for (Index=0; Index < RequestTitleFileListCompleteDelegates.Length; Index++)
|
||||
{
|
||||
RequestTitleFileListDelegate = RequestTitleFileListCompleteDelegates[Index];
|
||||
if (RequestTitleFileListDelegate != None)
|
||||
{
|
||||
RequestTitleFileListDelegate(bSuccess,ResponseStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the clashmob specific Url for downloading a given file
|
||||
*
|
||||
* @param FileName the file to search the table for
|
||||
*
|
||||
* @param the URL to use to request the file or BaseURL if no special mapping is present
|
||||
*/
|
||||
function string GetUrlForFile(string FileName)
|
||||
{
|
||||
local string Url;
|
||||
|
||||
Url = GetBaseURL() $ RequestFileURL $ GetAppAccessURL() $
|
||||
"&dlName=";
|
||||
|
||||
return Url;
|
||||
}
|
||||
|
||||
cpptext
|
||||
{
|
||||
/**
|
||||
* Ticks any outstanding async tasks that need processing
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
|
||||
/**
|
||||
* Searches the list of files for the one that matches the filename
|
||||
*
|
||||
* @param FileName the file to search for
|
||||
*
|
||||
* @return the file details
|
||||
*/
|
||||
FTitleFileWeb* GetTitleFile(const FString& FileName);
|
||||
}
|
214
IpDrv/Classes/PartyBeacon.uc
Normal file
214
IpDrv/Classes/PartyBeacon.uc
Normal file
@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is the base class for the client/host beacon classes.
|
||||
*/
|
||||
class PartyBeacon extends Object
|
||||
native
|
||||
inherits(FTickableObject)
|
||||
config(Engine);
|
||||
|
||||
/** The port that the party beacon will listen on */
|
||||
var config int PartyBeaconPort;
|
||||
|
||||
/** The object that is used to send/receive data with the remote host/client */
|
||||
var native transient pointer Socket{FSocket};
|
||||
|
||||
/** The types of packets being sent */
|
||||
enum EReservationPacketType
|
||||
{
|
||||
RPT_UnknownPacketType,
|
||||
// Sent by a client that wants to reserve space
|
||||
RPT_ClientReservationRequest,
|
||||
// Sent by a client that wants to update an existing party reservation
|
||||
RPT_ClientReservationUpdateRequest,
|
||||
// Sent by a client that is backing out of its request
|
||||
RPT_ClientCancellationRequest,
|
||||
// Sent by the host telling the client whether the reservation was accepted or not
|
||||
RPT_HostReservationResponse,
|
||||
// Sent by the host telling all clients the current reservation count
|
||||
RPT_HostReservationCountUpdate,
|
||||
// Sent by the host telling all clients to travel to a specified destination
|
||||
RPT_HostTravelRequest,
|
||||
// When doing full party matching, tells the clients the host is ready
|
||||
RPT_HostIsReady,
|
||||
// Tells the clients that the host has canceled this matching session
|
||||
RPT_HostHasCancelled,
|
||||
// Sent periodically to tell the host/client that the other end is there
|
||||
RPT_Heartbeat
|
||||
};
|
||||
|
||||
/** The result code that will be returned during party reservation */
|
||||
enum EPartyReservationResult
|
||||
{
|
||||
// An unknown error happened
|
||||
PRR_GeneralError,
|
||||
// All available reservations are booked
|
||||
PRR_PartyLimitReached,
|
||||
// Wrong number of players to join the session
|
||||
PRR_IncorrectPlayerCount,
|
||||
// No response from the host
|
||||
PRR_RequestTimedOut,
|
||||
// Already have a reservation entry for the requesting party leader
|
||||
PRR_ReservationDuplicate,
|
||||
// Couldn't find the party leader specified for a reservation update request
|
||||
PRR_ReservationNotFound,
|
||||
// Space was available and it's time to join
|
||||
PRR_ReservationAccepted,
|
||||
// The beacon is paused and not accepting new connections
|
||||
PRR_ReservationDenied
|
||||
};
|
||||
|
||||
/** A player that has/is requesting a reservation */
|
||||
struct native PlayerReservation
|
||||
{
|
||||
/** The unique identifier for this player */
|
||||
var UniqueNetId NetId;
|
||||
/** The skill of the player */
|
||||
var int Skill;
|
||||
/** The player experience level */
|
||||
var int XpLevel;
|
||||
/** The raw skill value */
|
||||
var double Mu;
|
||||
/** The uncertainty of that raw skill value */
|
||||
var double Sigma;
|
||||
/** Seconds since we checked to see if the player reservation exists in the session */
|
||||
var float ElapsedSessionTime;
|
||||
|
||||
structcpptext
|
||||
{
|
||||
/** Constructors */
|
||||
FPlayerReservation() {}
|
||||
FPlayerReservation(EEventParm)
|
||||
{
|
||||
appMemzero(this, sizeof(FPlayerReservation));
|
||||
}
|
||||
/**
|
||||
* Serialize from NBO buffer to FPlayerReservation
|
||||
*/
|
||||
friend FNboSerializeFromBuffer& operator>>(FNboSerializeFromBuffer& Ar,FPlayerReservation& PlayerRes);
|
||||
/**
|
||||
* Serialize from FPlayerReservation to NBO buffer
|
||||
*/
|
||||
friend FNboSerializeToBuffer& operator<<(FNboSerializeToBuffer& Ar,const FPlayerReservation& PlayerRes);
|
||||
}
|
||||
};
|
||||
|
||||
/** Holds information about a party that has reserved space in the session */
|
||||
struct native PartyReservation
|
||||
{
|
||||
/** The team this party was assigned to */
|
||||
var int TeamNum;
|
||||
/** The party leader that has requested a reservation */
|
||||
var UniqueNetId PartyLeader;
|
||||
/** The list of members of the party (includes the party leader) */
|
||||
var array<PlayerReservation> PartyMembers;
|
||||
};
|
||||
|
||||
/** Used to determine whether to use deferred destruction or not */
|
||||
var bool bIsInTick;
|
||||
|
||||
/** True if the beacon should be destroyed at the end of the tick */
|
||||
var bool bWantsDeferredDestroy;
|
||||
|
||||
/** The maximum amount of time to pass between heartbeat packets being sent */
|
||||
var config float HeartbeatTimeout;
|
||||
|
||||
/** The elapsed time that has passed since the last heartbeat */
|
||||
var float ElapsedHeartbeatTime;
|
||||
|
||||
/** Whether to the socket(s) or not (not during travel) */
|
||||
var bool bShouldTick;
|
||||
|
||||
/** The name to use when logging (helps debugging) */
|
||||
var name BeaconName;
|
||||
|
||||
cpptext
|
||||
{
|
||||
// FTickableObject interface
|
||||
|
||||
/**
|
||||
* Returns whether it is okay to tick this object. E.g. objects being loaded in the background shouldn't be ticked
|
||||
* till they are finalized and unreachable objects cannot be ticked either.
|
||||
*
|
||||
* @return TRUE if tickable, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL IsTickable() const
|
||||
{
|
||||
// We cannot tick objects that are unreachable or are in the process of being loaded in the background.
|
||||
return !HasAnyFlags( RF_Unreachable | RF_AsyncLoading );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if an object should be ticked when the game is paused.
|
||||
*
|
||||
* @return always TRUE as networking needs to be ticked even when paused
|
||||
*/
|
||||
virtual UBOOL IsTickableWhenPaused() const
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Converts a host response code to a readable string
|
||||
*
|
||||
* @param Result the code to translate
|
||||
*
|
||||
* @return the string that maps to it
|
||||
*/
|
||||
inline const TCHAR* PartyReservationResultToString(EPartyReservationResult Result)
|
||||
{
|
||||
switch (Result)
|
||||
{
|
||||
case PRR_PartyLimitReached: return TEXT("PRR_PartyLimitReached");
|
||||
case PRR_IncorrectPlayerCount: return TEXT("PRR_IncorrectPlayerCount");
|
||||
case PRR_RequestTimedOut: return TEXT("PRR_RequestTimedOut");
|
||||
case PRR_ReservationDuplicate: return TEXT("PRR_ReservationDuplicate");
|
||||
case PRR_ReservationNotFound: return TEXT("PRR_ReservationNotFound");
|
||||
case PRR_ReservationAccepted: return TEXT("PRR_ReservationAccepted");
|
||||
case PRR_ReservationDenied: return TEXT("PRR_ReservationDenied");
|
||||
}
|
||||
return TEXT("PRR_GeneralError");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a heartbeat packet to the specified socket
|
||||
*
|
||||
* @param Socket the socket to send the data on
|
||||
*
|
||||
* @return TRUE if it sent ok, FALSE if there was an error
|
||||
*/
|
||||
UBOOL SendHeartbeat(FSocket* Socket);
|
||||
|
||||
/**
|
||||
* @return the max value for the packet types handled by this beacon
|
||||
*/
|
||||
virtual BYTE GetMaxPacketValue()
|
||||
{
|
||||
return RPT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops listening for requests/responses and releases any allocated memory
|
||||
*/
|
||||
native event DestroyBeacon();
|
||||
|
||||
/**
|
||||
* Called when the beacon has completed destroying its socket
|
||||
*/
|
||||
delegate OnDestroyComplete();
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bShouldTick=true
|
||||
}
|
247
IpDrv/Classes/PartyBeaconClient.uc
Normal file
247
IpDrv/Classes/PartyBeaconClient.uc
Normal file
@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is used to connect to a network beacon in order to request
|
||||
* a reservation for their party with that host
|
||||
*/
|
||||
class PartyBeaconClient extends PartyBeacon
|
||||
native;
|
||||
|
||||
/** Holds a reference to the data that is used to reach the potential host */
|
||||
var const OnlineGameSearchResult HostPendingRequest;
|
||||
|
||||
/** The request to send once the socket is established */
|
||||
var PartyReservation PendingRequest;
|
||||
|
||||
/** Used to drive the client state machine */
|
||||
enum EPartyBeaconClientState
|
||||
{
|
||||
// Inactive or unknown
|
||||
PBCS_None,
|
||||
// A connection request is outstanding with host
|
||||
PBCS_Connecting,
|
||||
// Connected to the host and is ready to send
|
||||
PBCS_Connected,
|
||||
// Failed to establish a connection
|
||||
PBCS_ConnectionFailed,
|
||||
// Client has sent to the host and is awaiting for replies
|
||||
PBCS_AwaitingResponse,
|
||||
// The client has closed the connection
|
||||
PBCS_Closed
|
||||
};
|
||||
|
||||
/** The state of the client beacon */
|
||||
var EPartyBeaconClientState ClientBeaconState;
|
||||
|
||||
/** Determine the pending request to be sent to the host */
|
||||
enum EPartyBeaconClientRequest
|
||||
{
|
||||
// Pending client request for creating a new reservation on the host
|
||||
PBClientRequest_NewReservation,
|
||||
// Pending client request for updating an existing reservation on the host
|
||||
PBClientRequest_UpdateReservation
|
||||
};
|
||||
|
||||
/** The pending request to be sent */
|
||||
var EPartyBeaconClientRequest ClientBeaconRequestType;
|
||||
|
||||
/** Indicates how long the client should wait for a response before timing out and trying a new server */
|
||||
var config float ReservationRequestTimeout;
|
||||
|
||||
/** Used to track how long we've been waiting for a response */
|
||||
var float ReservationRequestElapsedTime;
|
||||
|
||||
/** 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<ClientBeaconAddressResolver> ResolverClass;
|
||||
|
||||
/** Platform specific address resolver for this beacon. Instantiated using the ResolverClass type. */
|
||||
var ClientBeaconAddressResolver Resolver;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Once the socket has been established, it sends the pending request to the host
|
||||
*/
|
||||
virtual void SendReservationRequest(void);
|
||||
|
||||
/**
|
||||
* Processes a packet that was received from the host indicating success or
|
||||
* failure for our reservation
|
||||
*
|
||||
* @param Packet the packet that the host sent
|
||||
* @param PacketSize the size of the packet to process
|
||||
*/
|
||||
void ProcessHostResponse(BYTE* Packet,INT PacketSize);
|
||||
|
||||
/**
|
||||
* Routes the response packet received from a host to the correct handler based on its type.
|
||||
* Overridden by base implementations to handle custom packet types
|
||||
*
|
||||
* @param HostResponsePacketType packet ID from EReservationPacketType (or derived version) 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
|
||||
*/
|
||||
virtual UBOOL HandleHostResponsePacketType(BYTE HostResponsePacketType,FNboSerializeFromBuffer& FromBuffer);
|
||||
|
||||
/**
|
||||
* Processes a reservation response packet that was received from the host
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
*/
|
||||
virtual void ProcessReservationResponse(FNboSerializeFromBuffer& FromBuffer);
|
||||
|
||||
/**
|
||||
* Processes a reservation count update packet that was received from the host
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
*/
|
||||
virtual void ProcessReservationCountUpdate(FNboSerializeFromBuffer& FromBuffer);
|
||||
|
||||
/**
|
||||
* Processes a heartbeat update, sends a heartbeat back, and clears the timer
|
||||
*/
|
||||
void ProcessHeartbeat(void);
|
||||
|
||||
/**
|
||||
* Notifies the delegates that the host is ready to play
|
||||
*/
|
||||
void ProcessHostIsReady(void);
|
||||
|
||||
/**
|
||||
* Processes a travel request packet that was received from the host
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
*/
|
||||
void ProcessTravelRequest(FNboSerializeFromBuffer& FromBuffer);
|
||||
|
||||
/** 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);
|
||||
|
||||
/**
|
||||
* Checks the socket for a response from the and processes if present
|
||||
*/
|
||||
void ReadResponse(void);
|
||||
|
||||
/** Common routine for canceling matchmaking */
|
||||
inline void ProcessHostCancelled(void)
|
||||
{
|
||||
CleanupAddress();
|
||||
delegateOnHostHasCancelled();
|
||||
}
|
||||
|
||||
/** Common routine for notifying of a timeout trying to talk to host */
|
||||
inline void ProcessHostTimeout(void)
|
||||
{
|
||||
CleanupAddress();
|
||||
delegateOnReservationRequestComplete(PRR_RequestTimedOut);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the beacon when a reservation request has been responded to by the destination host
|
||||
*
|
||||
* @param ReservationResult whether there was space allocated for the party or not
|
||||
*/
|
||||
delegate OnReservationRequestComplete(EPartyReservationResult ReservationResult);
|
||||
|
||||
/**
|
||||
* Called by the beacon when the host sends a reservation count update packet so
|
||||
* that any UI can be updated
|
||||
*
|
||||
* @param ReservationRemaining the number of reservations that are still available
|
||||
*/
|
||||
delegate OnReservationCountUpdated(int ReservationRemaining);
|
||||
|
||||
/**
|
||||
* Called by the 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<OnlineGameSearch> SearchClass,byte PlatformSpecificInfo[80]);
|
||||
|
||||
/**
|
||||
* Called by the beacon when the host sends the "ready" packet, so the client
|
||||
* can connect to the host to start the match
|
||||
*/
|
||||
delegate OnHostIsReady();
|
||||
|
||||
/**
|
||||
* Called by the beacon when the host sends the "cancellation" packet, so the client
|
||||
* can return to finding a new host
|
||||
*/
|
||||
delegate OnHostHasCancelled();
|
||||
|
||||
/**
|
||||
* Sends a request to the remote host to allow the specified members to reserve space
|
||||
* in the host's session. 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 RequestingPartyLeader the leader of this party that will be joining
|
||||
* @param Players the list of players that want to reserve space
|
||||
*
|
||||
* @return true if the request able to be sent, false if it failed to send
|
||||
*/
|
||||
native function bool RequestReservation(const out OnlineGameSearchResult DesiredHost,UniqueNetId RequestingPartyLeader,const out array<PlayerReservation> Players);
|
||||
|
||||
/**
|
||||
* Sends a request to the remote host to update an existing reservation for the
|
||||
* specified party leader. Any new players not already in the party leader's reservation
|
||||
* will be added to that reservation on the host. Host sends a EPartyReservationResult back.
|
||||
*
|
||||
* @param DesiredHost the server that the connection will be made to
|
||||
* @param RequestingPartyLeader party leader that will be updating his existing reservation
|
||||
* @param PlayersToAdd the list of players that want to reserve space in an existing reservation
|
||||
*
|
||||
* @return true if the request able to be sent, false if it failed to send
|
||||
*/
|
||||
native function bool RequestReservationUpdate(const out OnlineGameSearchResult DesiredHost,UniqueNetId RequestingPartyLeader,const out array<PlayerReservation> PlayersToAdd);
|
||||
|
||||
/**
|
||||
* Sends a cancellation message to the remote host so that it knows there is more
|
||||
* space available
|
||||
*
|
||||
* @param CancellingPartyLeader the leader of this party that wants to cancel
|
||||
*
|
||||
* @return true if the request able to be sent, false if it failed to send
|
||||
*/
|
||||
native function bool CancelReservation(UniqueNetId CancellingPartyLeader);
|
||||
|
||||
/**
|
||||
* Stops listening for requests/responses and releases any allocated memory
|
||||
*/
|
||||
native event DestroyBeacon();
|
515
IpDrv/Classes/PartyBeaconHost.uc
Normal file
515
IpDrv/Classes/PartyBeaconHost.uc
Normal file
@ -0,0 +1,515 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is used to create a network accessible beacon for responding
|
||||
* to reservation requests for party matches. It handles all tracking of
|
||||
* who has reserved space, how much space is available, and how many parties
|
||||
* can reserve space.
|
||||
*/
|
||||
class PartyBeaconHost extends PartyBeacon
|
||||
native;
|
||||
|
||||
/** Holds the information for a client and whether they've timed out */
|
||||
struct native ClientBeaconConnection
|
||||
{
|
||||
/** The unique id of the party leader for this connection */
|
||||
var UniqueNetId PartyLeader;
|
||||
/** How long it's been since the last heartbeat */
|
||||
var float ElapsedHeartbeatTime;
|
||||
/** The socket this client is communicating on */
|
||||
var native transient pointer Socket{FSocket};
|
||||
};
|
||||
|
||||
/** The object that is used to send/receive data with the remote host/client */
|
||||
var const array<ClientBeaconConnection> Clients;
|
||||
|
||||
/** The number of teams that these players will be divided amongst */
|
||||
var const int NumTeams;
|
||||
|
||||
/** The number of players required for a full team */
|
||||
var const int NumPlayersPerTeam;
|
||||
|
||||
/** The number of players that are allowed to reserve space */
|
||||
var const int NumReservations;
|
||||
|
||||
/** The number of slots that have been consumed by parties in total (saves having to iterate and sum) */
|
||||
var const int NumConsumedReservations;
|
||||
|
||||
/** The list of accepted reservations */
|
||||
var const array<PartyReservation> Reservations;
|
||||
|
||||
/** The online session name that players will register with */
|
||||
var name OnlineSessionName;
|
||||
|
||||
/** The number of connections to allow before refusing them */
|
||||
var config int ConnectionBacklog;
|
||||
|
||||
/** Team to force all reservations to in single team situations */
|
||||
var const int ForceTeamNum;
|
||||
|
||||
/** The team the host (owner of the beacon) is assigned to when random teams are chosen */
|
||||
var const int ReservedHostTeamNum;
|
||||
|
||||
/** Force new reservations to teams with the smallest accommodating need size, otherwise team assignment is random */
|
||||
var bool bBestFitTeamAssignment;
|
||||
|
||||
enum EPartyBeaconHostState
|
||||
{
|
||||
/** New reservations are accepted if possible */
|
||||
PBHS_AllowReservations,
|
||||
/** New reservations are denied */
|
||||
PBHS_DenyReservations
|
||||
};
|
||||
/** Current state of the beacon wrt allowing reservation requests */
|
||||
var const EPartyBeaconHostState BeaconState;
|
||||
|
||||
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);
|
||||
|
||||
/** Accepts any pending connections and adds them to our queue */
|
||||
void AcceptConnections(void);
|
||||
|
||||
/**
|
||||
* Reads the socket and processes any data from it
|
||||
*
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*
|
||||
* @return TRUE if the socket is ok, FALSE if it is in error
|
||||
*/
|
||||
UBOOL ReadClientData(FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Processes a packet that was received from a client
|
||||
*
|
||||
* @param Packet the packet that the client sent
|
||||
* @param PacketSize the size of the packet to process
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void ProcessRequest(BYTE* Packet,INT PacketSize,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Routes the packet received from a client to the correct handler based on its type.
|
||||
* Overridden by base implementations to handle custom data packet types
|
||||
*
|
||||
* @param RequestPacketType packet ID from EReservationPacketType (or derived version) that represents a client request
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
* @return TRUE if the requested packet type was processed
|
||||
*/
|
||||
virtual UBOOL HandleClientRequestPacketType(BYTE RequestPacketType,FNboSerializeFromBuffer& FromBuffer,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Processes a reservation packet that was received from a client
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
virtual void ProcessReservationRequest(FNboSerializeFromBuffer& FromBuffer,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Processes a reservation update packet that was received from a client
|
||||
* The update finds an existing reservation based on the party leader and
|
||||
* then tries to add new players to it (can't remove). Then sends a response to
|
||||
* the client based on the update results (see EPartyReservationResult).
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
virtual void ProcessReservationUpdateRequest(FNboSerializeFromBuffer& FromBuffer,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Processes a cancellation packet that was received from a client
|
||||
*
|
||||
* @param FromBuffer the packet serializer to read from
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
virtual void ProcessCancellationRequest(FNboSerializeFromBuffer& FromBuffer,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Sends a client the specified response code
|
||||
*
|
||||
* @param Result the result being sent to the client
|
||||
* @param ClientSocket the client socket to send the response on
|
||||
*/
|
||||
void SendReservationResponse(EPartyReservationResult Result,FSocket* ClientSocket);
|
||||
|
||||
/**
|
||||
* Tells clients that a reservation update has occured and sends them the current
|
||||
* number of remaining reservations so they can update their UI
|
||||
*/
|
||||
void SendReservationUpdates(void);
|
||||
|
||||
/**
|
||||
* Initializes the team array so that random choices can be made from it
|
||||
* Also initializes the host's team number (random from range)
|
||||
*/
|
||||
void InitTeamArray(void);
|
||||
|
||||
/**
|
||||
* Determine if there are any teams that can fit the current party request.
|
||||
*
|
||||
* @param PartySize number of players in the party making a reservation request
|
||||
* @return TRUE if there are teams available, FALSE otherwise
|
||||
*/
|
||||
virtual UBOOL AreTeamsAvailable(INT PartySize);
|
||||
|
||||
/**
|
||||
* Determine the team number for the given party reservation request.
|
||||
* Uses the list of current reservations to determine what teams have open slots.
|
||||
*
|
||||
* @param PartyRequest the party reservation request received from the client beacon
|
||||
* @return index of the team to assign to all members of this party
|
||||
*/
|
||||
virtual INT GetTeamAssignment(const FPartyReservation& PartyRequest);
|
||||
|
||||
/**
|
||||
* Readjust existing team assignments for party reservations in order to open the max
|
||||
* available slots possible.
|
||||
*/
|
||||
void BestFitTeamAssignmentJiggle();
|
||||
|
||||
/**
|
||||
* Determine the number of players on a given team based on current reservation entries
|
||||
*
|
||||
* @param TeamIdx index of team to search for
|
||||
* @return number of players on a given team
|
||||
*/
|
||||
INT GetNumPlayersOnTeam(INT TeamIdx) const;
|
||||
|
||||
/**
|
||||
* Find an existing player entry in a party reservation
|
||||
*
|
||||
* @param ExistingReservation party reservation entry
|
||||
* @param PlayerMember member to search for in the existing party reservation
|
||||
* @return index of party member in the given reservation, -1 if not found
|
||||
*/
|
||||
INT GetReservationPlayerMember(const FPartyReservation& ExistingReservation, const FUniqueNetId& PlayerMember) const;
|
||||
|
||||
/**
|
||||
* Removes the specified party leader (and party) from the arrays and notifies
|
||||
* any connected clients of the change in status
|
||||
*
|
||||
* @param PartyLeader the leader of the party to remove
|
||||
* @param ClientConn the client connection that sent the packet
|
||||
*/
|
||||
void CancelPartyReservation(FUniqueNetId& PartyLeader,FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Determine if the reservation entry for a disconnected client should be removed.
|
||||
*
|
||||
* @param ClientConn the client connection that is disconnecting
|
||||
* @return TRUE if the reservation for the disconnected client should be removed
|
||||
*/
|
||||
virtual UBOOL ShouldCancelReservationOnDisconnect(const FClientBeaconConnection& ClientConn);
|
||||
|
||||
/**
|
||||
* Called whenever a new player is added to a reservation on this beacon
|
||||
*
|
||||
* @param PlayerRes player reservation entry that was added
|
||||
*/
|
||||
virtual void NewPlayerAdded(const FPlayerReservation& PlayerRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses new reservation request on the beacon
|
||||
*
|
||||
* @param bPause if true then new reservation requests are denied
|
||||
*/
|
||||
native function PauseReservationRequests(bool bPause);
|
||||
|
||||
/**
|
||||
* Creates a listening host beacon with the specified number of parties, players, and
|
||||
* the session name that remote parties will be registered under
|
||||
*
|
||||
* @param InNumTeams the number of teams that are expected to join
|
||||
* @param InNumPlayersPerTeam the number of players that are allowed to be on each team
|
||||
* @param InNumReservations the total number of players to allow to join (if different than team * players)
|
||||
* @param InSessionName the name of the session to add the players to when a reservation occurs
|
||||
* @param InForceTeamNum the team to force to (only single team situations)
|
||||
*
|
||||
* @return true if the beacon was created successfully, false otherwise
|
||||
*/
|
||||
native function bool InitHostBeacon(int InNumTeams,int InNumPlayersPerTeam,int InNumReservations,name InSessionName, optional int InForceTeamNum=0);
|
||||
|
||||
/**
|
||||
* Add a new party to the reservation list.
|
||||
* Avoids adding duplicate entries based on party leader.
|
||||
*
|
||||
* @param PartyLeader the party leader that is adding the reservation
|
||||
* @param PlayerMembers players (including party leader) being added to the reservation
|
||||
* @param TeamNum team assignment of the new party
|
||||
* @param bIsHost treat the party as the game host
|
||||
* @return EPartyReservationResult similar to a client update request
|
||||
*/
|
||||
native function EPartyReservationResult AddPartyReservationEntry(UniqueNetId PartyLeader, const out array<PlayerReservation> PlayerMembers, int TeamNum, bool bIsHost);
|
||||
|
||||
/**
|
||||
* Update a party with an existing reservation
|
||||
* Avoids adding duplicate entries to the player members of a party.
|
||||
*
|
||||
* @param PartyLeader the party leader for which the existing reservation entry is being updated
|
||||
* @param PlayerMembers players (not including party leader) being added to the existing reservation
|
||||
* @return EPartyReservationResult similar to a client update request
|
||||
*/
|
||||
native function EPartyReservationResult UpdatePartyReservationEntry(UniqueNetId PartyLeader, const out array<PlayerReservation> PlayerMembers);
|
||||
|
||||
/**
|
||||
* Find an existing reservation for the party leader
|
||||
*
|
||||
* @param PartyLeader the party leader to find a reservation entry for
|
||||
* @return index of party leader in list of reservations, -1 if not found
|
||||
*/
|
||||
native function int GetExistingReservation(const out UniqueNetId PartyLeader);
|
||||
|
||||
/**
|
||||
* Called when a player logs out of the current game. The player's
|
||||
* party reservation entry is freed up so that a new reservation request
|
||||
* can be accepted.
|
||||
*
|
||||
* @param PlayerId the net Id of the player that just logged out
|
||||
* @param bMaintainParty if TRUE then preserve party members of a reservation when the party leader logs out
|
||||
*/
|
||||
native function HandlePlayerLogout(UniqueNetId PlayerId, bool bMaintainParty);
|
||||
|
||||
/**
|
||||
* Called by the beacon when a reservation occurs or is cancelled so that UI can be updated, etc.
|
||||
*/
|
||||
delegate OnReservationChange();
|
||||
|
||||
/**
|
||||
* Called by the beacon when all of the available reservations have been filled
|
||||
*/
|
||||
delegate OnReservationsFull();
|
||||
|
||||
/**
|
||||
* Called by the beacon when a client cancels a reservation
|
||||
*
|
||||
* @param PartyLeader the party leader that is cancelling the reservation
|
||||
*/
|
||||
delegate OnClientCancellationReceived(UniqueNetId PartyLeader);
|
||||
|
||||
/**
|
||||
* Stops listening for clients and releases any allocated memory
|
||||
*/
|
||||
native event DestroyBeacon();
|
||||
|
||||
/**
|
||||
* Tells all of the clients to go to a specific session (contained in platform
|
||||
* specific info). Used to route clients that aren't in the same party to one destination.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
native function TellClientsToTravel(name SessionName,class<OnlineGameSearch> SearchClass,byte PlatformSpecificInfo[80]);
|
||||
|
||||
/**
|
||||
* Tells all of the clients that the host is ready for them to travel to the host connection
|
||||
*/
|
||||
native function TellClientsHostIsReady();
|
||||
|
||||
/**
|
||||
* Tells all of the clients that the host has cancelled the matchmaking beacon and that they
|
||||
* need to find a different host
|
||||
*/
|
||||
native function TellClientsHostHasCancelled();
|
||||
|
||||
/**
|
||||
* Determine if the beacon has filled all open reservation slots
|
||||
*
|
||||
* @return TRUE if all reservations have been consumed by party members
|
||||
*/
|
||||
function bool AreReservationsFull()
|
||||
{
|
||||
return NumConsumedReservations == NumReservations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all of the parties as part of the session that this beacon is associated with
|
||||
*/
|
||||
event RegisterPartyMembers()
|
||||
{
|
||||
local int Index;
|
||||
local int PartyIndex;
|
||||
local OnlineSubsystem OnlineSub;
|
||||
local OnlineRecentPlayersList PlayersList;
|
||||
local array<UniqueNetId> Members;
|
||||
local PlayerReservation PlayerRes;
|
||||
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.GameInterface != None)
|
||||
{
|
||||
// Iterate through the parties adding the players from each to the session
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
for (Index = 0; Index < Reservations[PartyIndex].PartyMembers.Length; Index++)
|
||||
{
|
||||
PlayerRes = Reservations[PartyIndex].PartyMembers[Index];
|
||||
OnlineSub.GameInterface.RegisterPlayer(OnlineSessionName,PlayerRes.NetId,false);
|
||||
Members.AddItem(PlayerRes.NetId);
|
||||
}
|
||||
// Add the remote party members to the recent players list if available
|
||||
PlayersList = OnlineRecentPlayersList(OnlineSub.GetNamedInterface('RecentPlayersList'));
|
||||
if (PlayersList != None)
|
||||
{
|
||||
PlayersList.AddPartyToRecentParties(Reservations[PartyIndex].PartyLeader,Members);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters each of the party members at the specified reservation with the session
|
||||
*/
|
||||
event UnregisterPartyMembers()
|
||||
{
|
||||
local int Index;
|
||||
local int PartyIndex;
|
||||
local OnlineSubsystem OnlineSub;
|
||||
local PlayerReservation PlayerRes;
|
||||
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.GameInterface != None)
|
||||
{
|
||||
// Iterate through the parties removing the players from each from the session
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
for (Index = 0; Index < Reservations[PartyIndex].PartyMembers.Length; Index++)
|
||||
{
|
||||
PlayerRes = Reservations[PartyIndex].PartyMembers[Index];
|
||||
OnlineSub.GameInterface.UnregisterPlayer(OnlineSessionName,PlayerRes.NetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters each of the party members that have the specified party leader
|
||||
*
|
||||
* @param PartyLeader the leader to search for in the reservation array
|
||||
*/
|
||||
event UnregisterParty(UniqueNetId PartyLeader)
|
||||
{
|
||||
local int PlayerIndex;
|
||||
local int PartyIndex;
|
||||
local OnlineSubsystem OnlineSub;
|
||||
local PlayerReservation PlayerRes;
|
||||
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.GameInterface != None)
|
||||
{
|
||||
// Iterate through the parties removing the players from each from the session
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
// If this is the reservation in question, remove the members from the session
|
||||
if (Reservations[PartyIndex].PartyLeader == PartyLeader)
|
||||
{
|
||||
for (PlayerIndex = 0; PlayerIndex < Reservations[PartyIndex].PartyMembers.Length; PlayerIndex++)
|
||||
{
|
||||
PlayerRes = Reservations[PartyIndex].PartyMembers[PlayerIndex];
|
||||
OnlineSub.GameInterface.UnregisterPlayer(OnlineSessionName,PlayerRes.NetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the skills from all reservations to the search object so that they can
|
||||
* be included in the search information
|
||||
*
|
||||
* @param Search the search object to update
|
||||
*/
|
||||
native function AppendReservationSkillsToSearch(OnlineGameSearch Search);
|
||||
|
||||
/**
|
||||
* Gathers all the unique ids for players that have reservations
|
||||
*/
|
||||
function GetPlayers(out array<UniqueNetId> Players)
|
||||
{
|
||||
local int PlayerIndex;
|
||||
local int PartyIndex;
|
||||
local PlayerReservation PlayerRes;
|
||||
|
||||
// Iterate through the parties adding the players from each to the out array
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
for (PlayerIndex = 0; PlayerIndex < Reservations[PartyIndex].PartyMembers.Length; PlayerIndex++)
|
||||
{
|
||||
PlayerRes = Reservations[PartyIndex].PartyMembers[PlayerIndex];
|
||||
Players.AddItem(PlayerRes.NetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers all the unique ids for party leaders that have reservations
|
||||
*/
|
||||
function GetPartyLeaders(out array<UniqueNetId> PartyLeaders)
|
||||
{
|
||||
local int PartyIndex;
|
||||
|
||||
// Iterate through the parties adding the players from each to the out array
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
PartyLeaders.AddItem(Reservations[PartyIndex].PartyLeader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the maximum team size that can be accommodated based
|
||||
* on the current reservation slots occupied.
|
||||
*
|
||||
* @return maximum team size that is currently available
|
||||
*/
|
||||
native function int GetMaxAvailableTeamSize();
|
||||
|
||||
`if(`notdefined(FINAL_RELEASE))
|
||||
/**
|
||||
* Logs the reservation information for this beacon
|
||||
*/
|
||||
function DumpReservations()
|
||||
{
|
||||
local int PartyIndex;
|
||||
local int MemberIndex;
|
||||
local UniqueNetId NetId;
|
||||
local PlayerReservation PlayerRes;
|
||||
|
||||
`Log("Debug info for Beacon: "$BeaconName,,'DevBeacon');
|
||||
`Log("Session that reservations are for: "$OnlineSessionName,,'DevBeacon');
|
||||
`Log("Number of teams: "$NumTeams,,'DevBeacon');
|
||||
`Log("Number players per team: "$NumPlayersPerTeam,,'DevBeacon');
|
||||
`Log("Number total reservations: "$NumReservations,,'DevBeacon');
|
||||
`Log("Number consumed reservations: "$NumConsumedReservations,,'DevBeacon');
|
||||
`Log("Number of party reservations: "$Reservations.Length,,'DevBeacon');
|
||||
`Log("Reserved host team: "$ReservedHostTeamNum,,'DevBeacon');
|
||||
// Log each party that has a reservation
|
||||
for (PartyIndex = 0; PartyIndex < Reservations.Length; PartyIndex++)
|
||||
{
|
||||
NetId = Reservations[PartyIndex].PartyLeader;
|
||||
`Log(" Party leader: "$class'OnlineSubsystem'.static.UniqueNetIdToString(NetId),,'DevBeacon');
|
||||
`Log(" Party team: "$Reservations[PartyIndex].TeamNum,,'DevBeacon');
|
||||
`Log(" Party size: "$Reservations[PartyIndex].PartyMembers.Length,,'DevBeacon');
|
||||
// Log each member of the party
|
||||
for (MemberIndex = 0; MemberIndex < Reservations[PartyIndex].PartyMembers.Length; MemberIndex++)
|
||||
{
|
||||
PlayerRes = Reservations[PartyIndex].PartyMembers[MemberIndex];
|
||||
`Log(" Party member: "$class'OnlineSubsystem'.static.UniqueNetIdToString(PlayerRes.NetId)
|
||||
$" skill: "$PlayerRes.Skill
|
||||
$" xp: "$PlayerRes.XpLevel,,'DevBeacon');
|
||||
}
|
||||
}
|
||||
`Log("",,'DevBeacon');
|
||||
}
|
||||
`endif
|
108
IpDrv/Classes/TcpLink.uc
Normal file
108
IpDrv/Classes/TcpLink.uc
Normal file
@ -0,0 +1,108 @@
|
||||
//=============================================================================
|
||||
// TcpLink: An Internet TCP/IP connection.
|
||||
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
//=============================================================================
|
||||
class TcpLink extends InternetLink
|
||||
native
|
||||
transient;
|
||||
|
||||
cpptext
|
||||
{
|
||||
ATcpLink();
|
||||
void PostScriptDestroyed();
|
||||
UBOOL Tick( FLOAT DeltaTime, enum ELevelTick TickType );
|
||||
|
||||
void CheckConnectionAttempt();
|
||||
void CheckConnectionQueue();
|
||||
void PollConnections();
|
||||
UBOOL FlushSendBuffer();
|
||||
void ShutdownConnection();
|
||||
virtual UBOOL ShouldTickInEntry() { return true; }
|
||||
virtual INT NativeReadBinary(INT Count, BYTE*& B);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Variables.
|
||||
|
||||
// LinkState is only valid for TcpLink at this time.
|
||||
var enum ELinkState
|
||||
{
|
||||
STATE_Initialized, // Sockets is initialized
|
||||
STATE_Ready, // Port bound, ready for activity
|
||||
STATE_Listening, // Listening for connections
|
||||
STATE_Connecting, // Attempting to connect
|
||||
STATE_Connected, // Open and connected
|
||||
STATE_ListenClosePending,// Socket in process of closing
|
||||
STATE_ConnectClosePending,// Socket in process of closing
|
||||
STATE_ListenClosing, // Socket in process of closing
|
||||
STATE_ConnectClosing // Socket in process of closing
|
||||
} LinkState;
|
||||
|
||||
var IpAddr RemoteAddr; // Contains address of peer connected to from a Listen()
|
||||
|
||||
// If AcceptClass is not None, an actor of class AcceptClass will be spawned when an
|
||||
// incoming connecting is accepted, leaving the listener open to accept more connections.
|
||||
// Accepted() is called only in the child class. You can use the LostChild() and GainedChild()
|
||||
// events to track your children.
|
||||
var class<TcpLink> AcceptClass;
|
||||
var const Array<byte> SendFIFO; // send fifo
|
||||
var const string RecvBuf; // Recveive buffer (only used with MODE_Line)
|
||||
//-----------------------------------------------------------------------------
|
||||
// natives.
|
||||
|
||||
// BindPort: Binds a free port or optional port specified in argument one.
|
||||
native function int BindPort( optional int PortNum, optional bool bUseNextAvailable );
|
||||
|
||||
// Listen: Listen for connections. Can handle up to 5 simultaneous connections.
|
||||
// Returns false if failed to place socket in listen mode.
|
||||
native function bool Listen();
|
||||
|
||||
// Open: Open a connection to a foreign host.
|
||||
native function bool Open( IpAddr Addr );
|
||||
|
||||
// Close: Closes the current connection.
|
||||
native function bool Close();
|
||||
|
||||
// IsConnected: Returns true if connected.
|
||||
native function bool IsConnected();
|
||||
|
||||
// SendText: Sends text string.
|
||||
// Appends a cr/lf if LinkMode=MODE_Line. Returns number of bytes sent.
|
||||
native function int SendText( coerce string Str );
|
||||
|
||||
// SendBinary: Send data as a byte array.
|
||||
native function int SendBinary( int Count, byte B[255] );
|
||||
|
||||
// ReadText: Reads text string.
|
||||
// Returns number of bytes read.
|
||||
native function int ReadText( out string Str );
|
||||
|
||||
// ReadBinary: Read data as a byte array.
|
||||
native function int ReadBinary( int Count, out byte B[255] );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Events.
|
||||
|
||||
// Accepted: Called during STATE_Listening when a new connection is accepted.
|
||||
event Accepted();
|
||||
|
||||
// Opened: Called when socket successfully connects.
|
||||
event Opened();
|
||||
|
||||
// Closed: Called when Close() completes or the connection is dropped.
|
||||
event Closed();
|
||||
|
||||
// ReceivedText: Called when data is received and connection mode is MODE_Text.
|
||||
event ReceivedText( string Text );
|
||||
|
||||
// ReceivedLine: Called when data is received and connection mode is MODE_Line.
|
||||
// \r\n is stripped from the line
|
||||
event ReceivedLine( string Line );
|
||||
|
||||
// ReceivedBinary: Called when data is received and connection mode is MODE_Binary.
|
||||
event ReceivedBinary( int Count, byte B[255] );
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bAlwaysTick=True
|
||||
}
|
237
IpDrv/Classes/TitleFileDownloadCache.uc
Normal file
237
IpDrv/Classes/TitleFileDownloadCache.uc
Normal file
@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Local caching functionality for downloaded title files
|
||||
*/
|
||||
class TitleFileDownloadCache extends MCPBase
|
||||
native
|
||||
config(Engine)
|
||||
implements(OnlineTitleFileCacheInterface)
|
||||
dependson(OnlineSubsystem);
|
||||
|
||||
/** File operations being performed */
|
||||
enum ETitleFileFileOp
|
||||
{
|
||||
TitleFile_None,
|
||||
TitleFile_Save,
|
||||
TitleFile_Load
|
||||
};
|
||||
|
||||
/** Entry for a file that has been loaded/saved or is in the process of doing so */
|
||||
struct native TitleFileCacheEntry extends OnlineSubsystem.TitleFile
|
||||
{
|
||||
/** Logical name to assign to the physical filename */
|
||||
var string LogicalName;
|
||||
/** CRC hash of the file that was read */
|
||||
var string Hash;
|
||||
/** Last file operation for this cached entry */
|
||||
var ETitleFileFileOp FileOp;
|
||||
/** Archive for loading/saving file. Only valid during async operation */
|
||||
var private native const pointer Ar{class FArchive};
|
||||
};
|
||||
/** List of files that have been processed */
|
||||
var array<TitleFileCacheEntry> TitleFiles;
|
||||
|
||||
/** The list of delegates to notify when a file is loaded */
|
||||
var private array<delegate<OnLoadTitleFileComplete> > LoadCompleteDelegates;
|
||||
/** The list of delegates to notify when a file is saved */
|
||||
var private array<delegate<OnSaveTitleFileComplete> > SaveCompleteDelegates;
|
||||
|
||||
/**
|
||||
* Starts an asynchronous read of the specified file from the local cache
|
||||
*
|
||||
* @param FileName the name of the file to read
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
native function bool LoadTitleFile(string FileName);
|
||||
|
||||
/**
|
||||
* Delegate fired when a file read from the local cache is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file read was successful or not
|
||||
* @param FileName the name of the file this was for
|
||||
*/
|
||||
delegate OnLoadTitleFileComplete(bool bWasSuccessful,string FileName);
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been read
|
||||
*
|
||||
* @param LoadCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddLoadTitleFileCompleteDelegate(delegate<OnLoadTitleFileComplete> LoadCompleteDelegate)
|
||||
{
|
||||
if (LoadCompleteDelegates.Find(LoadCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
LoadCompleteDelegates[LoadCompleteDelegates.Length] = LoadCompleteDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param LoadCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearLoadTitleFileCompleteDelegate(delegate<OnLoadTitleFileComplete> LoadCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = LoadCompleteDelegates.Find(LoadCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
LoadCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous write of the specified file to disk
|
||||
*
|
||||
* @param FileName the name of the file to save
|
||||
* @param LogicalName the name to associate with the physical filename
|
||||
* @param FileContents the buffer to write data from
|
||||
*
|
||||
* @return true if the calls starts successfully, false otherwise
|
||||
*/
|
||||
native function bool SaveTitleFile(string FileName,string LogicalName,array<byte> FileContents);
|
||||
|
||||
/**
|
||||
* Delegate fired when a file save to the local cache is complete
|
||||
*
|
||||
* @param bWasSuccessful whether the file read was successful or not
|
||||
* @param FileName the name of the file this was for
|
||||
*/
|
||||
delegate OnSaveTitleFileComplete(bool bWasSuccessful,string FileName);
|
||||
|
||||
/**
|
||||
* Adds the delegate to the list to be notified when a requested file has been saved
|
||||
*
|
||||
* @param SaveCompleteDelegate the delegate to add
|
||||
*/
|
||||
function AddSaveTitleFileCompleteDelegate(delegate<OnSaveTitleFileComplete> SaveCompleteDelegate)
|
||||
{
|
||||
if (SaveCompleteDelegates.Find(SaveCompleteDelegate) == INDEX_NONE)
|
||||
{
|
||||
SaveCompleteDelegates[SaveCompleteDelegates.Length] = SaveCompleteDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate from the notify list
|
||||
*
|
||||
* @param SaveCompleteDelegate the delegate to remove
|
||||
*/
|
||||
function ClearSaveTitleFileCompleteDelegate(delegate<OnSaveTitleFileComplete> SaveCompleteDelegate)
|
||||
{
|
||||
local int RemoveIndex;
|
||||
|
||||
RemoveIndex = SaveCompleteDelegates.Find(SaveCompleteDelegate);
|
||||
if (RemoveIndex != INDEX_NONE)
|
||||
{
|
||||
SaveCompleteDelegates.Remove(RemoveIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the file data into the specified buffer for the specified file
|
||||
*
|
||||
* @param FileName the name of the file to read
|
||||
* @param FileContents the out buffer to copy the data into
|
||||
*
|
||||
* @return true if the data was copied, false otherwise
|
||||
*/
|
||||
native function bool GetTitleFileContents(string FileName,out array<byte> FileContents);
|
||||
|
||||
/**
|
||||
* Determines the async state of the tile file read operation
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the async state of the file read
|
||||
*/
|
||||
native function EOnlineEnumerationReadState GetTitleFileState(string FileName);
|
||||
|
||||
/**
|
||||
* Determines the hash of the tile file that was read
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the hash string for the file
|
||||
*/
|
||||
native function string GetTitleFileHash(string FileName);
|
||||
|
||||
/**
|
||||
* Determines the hash of the tile file that was read
|
||||
*
|
||||
* @param FileName the name of the file to check on
|
||||
*
|
||||
* @return the logical name of the for the given physical filename
|
||||
*/
|
||||
native function string GetTitleFileLogicalName(string FileName);
|
||||
|
||||
/**
|
||||
* Empties the set of cached files if possible (no async tasks outstanding)
|
||||
*
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
native function bool ClearCachedFiles();
|
||||
|
||||
/**
|
||||
* Empties the cached data for this file if it is not being loaded/saved currently
|
||||
*
|
||||
* @param FileName the name of the file to remove from the cache
|
||||
*
|
||||
* @return true if it could be cleared, false if it could not
|
||||
*/
|
||||
native function bool ClearCachedFile(string FileName);
|
||||
|
||||
/**
|
||||
* Deletes the set of title files from disc
|
||||
*
|
||||
* @param MaxAgeSeconds if > 0 then any files older than max seconds are deleted, if == 0 then all files are deleted
|
||||
* @return true if they could be deleted, false if they could not
|
||||
*/
|
||||
native function bool DeleteTitleFiles(float MaxAgeSeconds);
|
||||
|
||||
/**
|
||||
* Deletes a single file from disc
|
||||
*
|
||||
* @param FileName the name of the file to delete
|
||||
*
|
||||
* @return true if it could be deleted, false if it could not
|
||||
*/
|
||||
native function bool DeleteTitleFile(string FileName);
|
||||
|
||||
|
||||
cpptext
|
||||
{
|
||||
/**
|
||||
* Ticks any outstanding async tasks that need processing
|
||||
*
|
||||
* @param DeltaTime the amount of time that has passed since the last tick
|
||||
*/
|
||||
virtual void Tick(FLOAT DeltaTime);
|
||||
|
||||
/**
|
||||
* Fires the delegates so the caller knows the file load/save is complete
|
||||
*
|
||||
* @param TitleFile the information for the file that was loaded/saved
|
||||
* @param FileOp read/write opeartion on the file to know which delegates to call
|
||||
*/
|
||||
void TriggerDelegates(const FTitleFileCacheEntry* TitleFile,ETitleFileFileOp FileOp);
|
||||
|
||||
/**
|
||||
* Searches the list of files for the one that matches the filename
|
||||
*
|
||||
* @param FileName the file to search for
|
||||
*
|
||||
* @return the file details
|
||||
*/
|
||||
FTitleFileCacheEntry* GetTitleFile(const FString& FileName);
|
||||
|
||||
/**
|
||||
* @return base path to all cached files
|
||||
*/
|
||||
FString GetCachePath() const;
|
||||
};
|
179
IpDrv/Classes/UIDataStore_OnlinePlaylists.uc
Normal file
179
IpDrv/Classes/UIDataStore_OnlinePlaylists.uc
Normal file
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Data store provides access to available playlist resources
|
||||
* The data for each playlist is provided through a data provider and is specified in the .ini file for that
|
||||
* data provider class type using the PerObjectConfig paradigm.
|
||||
*
|
||||
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
*/
|
||||
class UIDataStore_OnlinePlaylists extends UIDataStore
|
||||
native(inherit)
|
||||
Config(Game);
|
||||
|
||||
/** Constants used for accessing the data providers */
|
||||
const RANKEDPROVIDERTAG = "PlaylistsRanked";
|
||||
const UNRANKEDPROVIDERTAG = "PlaylistsUnranked";
|
||||
const RECMODEPROVIDERTAG = "PlaylistsRecMode";
|
||||
const PRIVATEPROVIDERTAG = "PlaylistsPrivate";
|
||||
|
||||
/** Name of provider class associated with this data store (uses them and all child classes) */
|
||||
var config string ProviderClassName;
|
||||
|
||||
/** Class reference for the above provider class name */
|
||||
var transient class<UIResourceDataProvider> ProviderClass;
|
||||
|
||||
/** Cached array of perobjectconfig data providers for playlists determined to be "ranked" */
|
||||
var const array<UIResourceDataProvider> RankedDataProviders;
|
||||
|
||||
/** Cached array of perobjectconfig data providers for playlists determined to be "unranked" */
|
||||
var const array<UIResourceDataProvider> UnrankedDataProviders;
|
||||
|
||||
/** Cached array of perobjectconfig data providers for playlists determined to be "recreational mode" */
|
||||
var const array<UIResourceDataProvider> RecModeDataProviders;
|
||||
|
||||
/** Cached array of perobjectconfig data providers for playlists determined to be "private" */
|
||||
var const array<UIResourceDataProvider> PrivateDataProviders;
|
||||
|
||||
/** The playlist to query about match details */
|
||||
var OnlinePlaylistManager PlaylistMan;
|
||||
|
||||
cpptext
|
||||
{
|
||||
/* === UUIDataStore_GameResource interface === */
|
||||
/**
|
||||
* Finds or creates the UIResourceDataProvider instances used by online playlists, and stores the result by ranked or unranked provider types
|
||||
*/
|
||||
virtual void InitializeListElementProviders();
|
||||
|
||||
/* === UIDataStore interface === */
|
||||
/**
|
||||
* Calls the script event so that it can access the playlist manager
|
||||
*/
|
||||
virtual void InitializeDataStore()
|
||||
{
|
||||
Super::InitializeDataStore();
|
||||
eventInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the classes referenced by the ElementProviderTypes array.
|
||||
*/
|
||||
virtual void LoadDependentClasses();
|
||||
|
||||
/**
|
||||
* Called when this data store is added to the data store manager's list of active data stores.
|
||||
*
|
||||
* @param PlayerOwner the player that will be associated with this DataStore. Only relevant if this data store is
|
||||
* associated with a particular player; NULL if this is a global data store.
|
||||
*/
|
||||
virtual void OnRegister( ULocalPlayer* PlayerOwner );
|
||||
|
||||
/* === UObject interface === */
|
||||
/** Required since maps are not yet supported by script serialization */
|
||||
virtual void AddReferencedObjects( TArray<UObject*>& ObjectArray );
|
||||
virtual void Serialize( FArchive& Ar );
|
||||
|
||||
/**
|
||||
* Called from ReloadConfig after the object has reloaded its configuration data. Reinitializes the collection of list element providers.
|
||||
*/
|
||||
virtual void PostReloadConfig( UProperty* PropertyThatWasLoaded );
|
||||
|
||||
/**
|
||||
* Callback for retrieving a textual representation of natively serialized properties. Child classes should implement this method if they wish
|
||||
* to have natively serialized property values included in things like diffcommandlet output.
|
||||
*
|
||||
* @param out_PropertyValues receives the property names and values which should be reported for this object. The map's key should be the name of
|
||||
* the property and the map's value should be the textual representation of the property's value. The property value should
|
||||
* be formatted the same way that UProperty::ExportText formats property values (i.e. for arrays, wrap in quotes and use a comma
|
||||
* as the delimiter between elements, etc.)
|
||||
* @param ExportFlags bitmask of EPropertyPortFlags used for modifying the format of the property values
|
||||
*
|
||||
* @return return TRUE if property values were added to the map.
|
||||
*/
|
||||
virtual UBOOL GetNativePropertyValues( TMap<FString,FString>& out_PropertyValues, DWORD ExportFlags=0 ) const;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs the playlist manager
|
||||
*/
|
||||
event Init()
|
||||
{
|
||||
local OnlineSubsystem OnlineSub;
|
||||
|
||||
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
|
||||
|
||||
// Only download if we have a logged in player
|
||||
if (OnlineSub != None &&
|
||||
OnlineSub.Patcher != None)
|
||||
{
|
||||
PlaylistMan = OnlinePlaylistManager(OnlineSub.GetNamedInterface('PlaylistManager'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UIResourceDataProvider instances associated with the tag.
|
||||
*
|
||||
* @param ProviderTag the tag to find instances for; should match the ProviderTag value of an element in the ElementProviderTypes array.
|
||||
* @param out_Providers receives the list of provider instances. this array is always emptied first.
|
||||
*
|
||||
* @return the list of UIResourceDataProvider instances registered for ProviderTag.
|
||||
*/
|
||||
native final function bool GetResourceProviders( name ProviderTag, out array<UIResourceDataProvider> out_Providers ) const;
|
||||
|
||||
/**
|
||||
* Searches for resource provider instance given its associated provider tag and an index within that subset
|
||||
*
|
||||
* @param ProviderTag the name of the provider type; should match the ranked or unranked provider tag
|
||||
* @param SearchField the index of the provider within the provider subset specified by the ProviderTag
|
||||
* @param out_Provider the resource provider instance found
|
||||
*
|
||||
* @return true if the out_Provider has been found, false otherwise
|
||||
*/
|
||||
native final function bool GetPlaylistProvider( name ProviderTag, int ProviderIndex, out UIResourceDataProvider out_Provider);
|
||||
|
||||
|
||||
/** Returns the OnlinePlaylistProvider with the corresponding PlaylistId */
|
||||
static function OnlinePlaylistProvider GetOnlinePlaylistProvider( name ProviderTag, int PlaylistId, optional out int ProviderIndex )
|
||||
{
|
||||
local UIDataStore_OnlinePlaylists PlaylistDS;
|
||||
local array<UIResourceDataProvider> Providers;
|
||||
local OnlinePlaylistProvider OPP;
|
||||
|
||||
ProviderIndex = INDEX_NONE;
|
||||
PlaylistDS = UIDataStore_OnlinePlaylists(class'UIRoot'.static.StaticResolveDataStore(class'UIDataStore_OnlinePlaylists'.default.Tag));
|
||||
if ( PlaylistDS != None )
|
||||
{
|
||||
PlaylistDS.GetResourceProviders(ProviderTag, Providers);
|
||||
|
||||
for (ProviderIndex = 0; ProviderIndex < Providers.length; ProviderIndex++)
|
||||
{
|
||||
OPP = OnlinePlaylistProvider(Providers[ProviderIndex]);
|
||||
if (OPP.PlaylistId == PlaylistId)
|
||||
{
|
||||
return OPP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the match type for the specified playlist
|
||||
*
|
||||
* @param PlaylistId the playlist we are searching for
|
||||
*
|
||||
* @return the match type for the playlist
|
||||
*/
|
||||
event int GetMatchTypeForPlaylistId(int PlaylistId)
|
||||
{
|
||||
if (PlaylistMan != None)
|
||||
{
|
||||
return PlaylistMan.GetMatchType(PlaylistId);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
DefaultProperties
|
||||
{
|
||||
Tag=OnlinePlaylists
|
||||
}
|
30
IpDrv/Classes/WebApplication.uc
Normal file
30
IpDrv/Classes/WebApplication.uc
Normal file
@ -0,0 +1,30 @@
|
||||
/*=============================================================================
|
||||
// WebApplication: Parent class for Web Server applications
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
class WebApplication extends Object;
|
||||
|
||||
// Set by the webserver
|
||||
var WorldInfo WorldInfo;
|
||||
var WebServer WebServer;
|
||||
var string Path;
|
||||
|
||||
function Init();
|
||||
|
||||
// This is a dummy function which should never be called
|
||||
// Here for backwards compatibility
|
||||
final function Cleanup();
|
||||
|
||||
function CleanupApp()
|
||||
{
|
||||
if (WorldInfo != None)
|
||||
WorldInfo = None;
|
||||
|
||||
if (WebServer != None)
|
||||
WebServer = None;
|
||||
}
|
||||
|
||||
function bool PreQuery(WebRequest Request, WebResponse Response) { return true; }
|
||||
function Query(WebRequest Request, WebResponse Response);
|
||||
function PostQuery(WebRequest Request, WebResponse Response);
|
||||
|
297
IpDrv/Classes/WebConnection.uc
Normal file
297
IpDrv/Classes/WebConnection.uc
Normal file
@ -0,0 +1,297 @@
|
||||
/*=============================================================================
|
||||
WebConnection is the bridge that will handle all communication between
|
||||
the web server and the client's browser.
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
|
||||
class WebConnection extends TcpLink config(Web);
|
||||
|
||||
var WebServer WebServer;
|
||||
var string ReceivedData;
|
||||
|
||||
var WebRequest Request;
|
||||
var WebResponse Response;
|
||||
var WebApplication Application;
|
||||
|
||||
var bool bDelayCleanup;
|
||||
|
||||
var int RawBytesExpecting;
|
||||
|
||||
var config int MaxValueLength;
|
||||
var config int MaxLineLength;
|
||||
|
||||
// MC: Debug
|
||||
var int ConnID;
|
||||
|
||||
event Accepted()
|
||||
{
|
||||
WebServer = WebServer(Owner);
|
||||
SetTimer(30, False);
|
||||
ConnID = WebServer.ConnID++;
|
||||
`Logd("Connection"@ConnID@"Accepted");
|
||||
}
|
||||
|
||||
event Closed()
|
||||
{
|
||||
`Logd("Connection"@ConnID@"Closed");
|
||||
Destroy();
|
||||
}
|
||||
|
||||
event Timer()
|
||||
{
|
||||
`logd("Timer Up");
|
||||
bDelayCleanup = False;
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
event ReceivedText( string Text )
|
||||
{
|
||||
local int i;
|
||||
local string S;
|
||||
|
||||
`logd("ReceivedText"@Text);
|
||||
ReceivedData $= Text;
|
||||
if(RawBytesExpecting > 0)
|
||||
{
|
||||
RawBytesExpecting -= Len(Text);
|
||||
CheckRawBytes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// remove a LF which arrived in a new packet
|
||||
// and thus didn't get cleaned up by the code below
|
||||
if(Left(ReceivedData, 1) == Chr(10))
|
||||
ReceivedData = Mid(ReceivedData, 1);
|
||||
i = InStr(ReceivedData, Chr(13));
|
||||
while(i != -1)
|
||||
{
|
||||
S = Left(ReceivedData, i);
|
||||
i++;
|
||||
// check for any LF following the CR.
|
||||
if(Mid(ReceivedData, i, 1) == Chr(10))
|
||||
i++;
|
||||
|
||||
ReceivedData = Mid(ReceivedData, i);
|
||||
|
||||
ReceivedLine(S);
|
||||
|
||||
if(LinkState != STATE_Connected)
|
||||
return;
|
||||
if(RawBytesExpecting > 0)
|
||||
{
|
||||
CheckRawBytes();
|
||||
return;
|
||||
}
|
||||
|
||||
i = InStr(ReceivedData, Chr(13));
|
||||
}
|
||||
}
|
||||
|
||||
function ReceivedLine(string S)
|
||||
{
|
||||
if (S == "")
|
||||
{
|
||||
EndOfHeaders();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Left(S, 4) ~= "GET ")
|
||||
{
|
||||
ProcessGet(S);
|
||||
}
|
||||
else if(Left(S, 5) ~= "POST ")
|
||||
{
|
||||
ProcessPost(S);
|
||||
}
|
||||
else if(Left(S, 5) ~= "HEAD ")
|
||||
{
|
||||
ProcessHead(S);
|
||||
}
|
||||
else if(Request != None)
|
||||
{
|
||||
Request.ProcessHeaderString(S);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ProcessHead(string S)
|
||||
{
|
||||
`Logd("Received HEAD:"@S);
|
||||
}
|
||||
|
||||
function ProcessGet(string S)
|
||||
{
|
||||
local int i;
|
||||
|
||||
`logd("Received GET:"@S);
|
||||
if(Request == None)
|
||||
CreateResponseObject();
|
||||
|
||||
Request.RequestType = Request_GET;
|
||||
S = Mid(S, 4);
|
||||
while(Left(S, 1) == " ")
|
||||
S = Mid(S, 1);
|
||||
|
||||
i = InStr(S, " ");
|
||||
|
||||
if(i != -1)
|
||||
S = Left(S, i);
|
||||
|
||||
i = InStr(S, "?");
|
||||
if(i != -1)
|
||||
{
|
||||
Request.DecodeFormData(Mid(S, i+1));
|
||||
S = Left(S, i);
|
||||
}
|
||||
|
||||
Application = WebServer.GetApplication(S, Request.URI);
|
||||
if(Application != None && Request.URI == "")
|
||||
{
|
||||
Response.Redirect(S$"/");
|
||||
Cleanup();
|
||||
}
|
||||
else if(Application == None && Webserver.DefaultApplication != -1)
|
||||
{
|
||||
Response.Redirect(Webserver.ApplicationPaths[Webserver.DefaultApplication]$"/");
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function ProcessPost(string S)
|
||||
{
|
||||
local int i;
|
||||
|
||||
`logd("Received POST:"@S);
|
||||
if(Request == None)
|
||||
CreateResponseObject();
|
||||
|
||||
Request.RequestType = Request_POST;
|
||||
|
||||
S = Mid(S, 5);
|
||||
while(Left(S, 1) == " ")
|
||||
S = Mid(S, 1);
|
||||
|
||||
i = InStr(S, " ");
|
||||
|
||||
if(i != -1)
|
||||
S = Left(S, i);
|
||||
|
||||
i = InStr(S, "?");
|
||||
if(i != -1)
|
||||
{
|
||||
Request.DecodeFormData(Mid(S, i+1));
|
||||
S = Left(S, i);
|
||||
}
|
||||
|
||||
Application = WebServer.GetApplication(S, Request.URI);
|
||||
if(Application != None && Request.URI == "")
|
||||
{
|
||||
// Response.Redirect(WebServer.ServerURL$S$"/");
|
||||
Response.Redirect(S$"/");
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function CreateResponseObject()
|
||||
{
|
||||
local int i;
|
||||
Request = new(None) class'WebRequest';
|
||||
Request.RemoteAddr = IpAddrToString(RemoteAddr);
|
||||
i = InStr(Request.RemoteAddr, ":");
|
||||
if (i > -1)
|
||||
{
|
||||
Request.RemoteAddr = Left(Request.RemoteAddr, i);
|
||||
}
|
||||
|
||||
Response = new(None) class'WebResponse';
|
||||
Response.Connection = Self;
|
||||
}
|
||||
|
||||
function EndOfHeaders()
|
||||
{
|
||||
if(Response == None)
|
||||
{
|
||||
CreateResponseObject();
|
||||
Response.HTTPError(400); // Bad Request
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Application == None)
|
||||
{
|
||||
Response.HTTPError(404); // FNF
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Request.ContentLength != 0 && Request.RequestType == Request_POST)
|
||||
{
|
||||
RawBytesExpecting = Request.ContentLength;
|
||||
RawBytesExpecting -= Len(ReceivedData);
|
||||
CheckRawBytes();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Application.PreQuery(Request, Response))
|
||||
{
|
||||
Application.Query(Request, Response);
|
||||
Application.PostQuery(Request, Response);
|
||||
}
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function CheckRawBytes()
|
||||
{
|
||||
if(RawBytesExpecting <= 0)
|
||||
{
|
||||
if(InStr(Locs(Request.ContentType), "application/x-www-form-urlencoded") != 0)
|
||||
{
|
||||
`log("WebConnection: Unknown form data content-type: "$Request.ContentType);
|
||||
Response.HTTPError(400); // Can't deal with this type of form data
|
||||
}
|
||||
else
|
||||
{
|
||||
Request.DecodeFormData(ReceivedData);
|
||||
if (Application.PreQuery(Request, Response))
|
||||
{
|
||||
Application.Query(Request, Response);
|
||||
Application.PostQuery(Request, Response);
|
||||
}
|
||||
ReceivedData = "";
|
||||
}
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function Cleanup()
|
||||
{
|
||||
if (bDelayCleanup)
|
||||
return;
|
||||
|
||||
if(Request != None)
|
||||
Request = None;
|
||||
|
||||
if(Response != None)
|
||||
{
|
||||
Response.Connection = None;
|
||||
Response = None;
|
||||
}
|
||||
|
||||
if (Application != None)
|
||||
Application = None;
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
final function bool IsHanging()
|
||||
{
|
||||
return bDelayCleanup;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
//MaxValueLength=512 // Maximum size of a variable value
|
||||
//MaxLineLength=4096
|
||||
}
|
170
IpDrv/Classes/WebRequest.uc
Normal file
170
IpDrv/Classes/WebRequest.uc
Normal file
@ -0,0 +1,170 @@
|
||||
/*=============================================================================
|
||||
// WebRequest: Parent class for handling/decoding web server requests
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
class WebRequest extends Object
|
||||
native;
|
||||
|
||||
enum ERequestType
|
||||
{
|
||||
Request_GET,
|
||||
Request_POST
|
||||
};
|
||||
|
||||
var string RemoteAddr;
|
||||
var string URI;
|
||||
var string Username;
|
||||
var string Password;
|
||||
var int ContentLength;
|
||||
var string ContentType;
|
||||
var ERequestType RequestType;
|
||||
|
||||
var private native const Map_Mirror HeaderMap{TMultiMap<FString, FString>}; // C++ placeholder.
|
||||
var private native const Map_Mirror VariableMap{TMultiMap<FString, FString>}; // C++ placeholder.
|
||||
|
||||
native final function string DecodeBase64(string Encoded);
|
||||
native final function string EncodeBase64(string Decoded);
|
||||
|
||||
native final function AddHeader(string HeaderName, coerce string Value);
|
||||
native final function string GetHeader(string HeaderName, optional string DefaultValue);
|
||||
native final function GetHeaders(out array<string> headers);
|
||||
|
||||
native final function AddVariable(string VariableName, coerce string Value);
|
||||
native final function string GetVariable(string VariableName, optional string DefaultValue);
|
||||
native final function int GetVariableCount(string VariableName);
|
||||
native final function string GetVariableNumber(string VariableName, int Number, optional string DefaultValue);
|
||||
native final function GetVariables(out array<string> varNames);
|
||||
|
||||
native final function Dump(); // only works in dev mode
|
||||
|
||||
function ProcessHeaderString(string S)
|
||||
{
|
||||
local int i;
|
||||
|
||||
//`log("ProcessHeaderString"@S);
|
||||
if(Left(S, 21) ~= "Authorization: Basic ")
|
||||
{
|
||||
S = DecodeBase64(Mid(S, 21));
|
||||
i = InStr(S, ":");
|
||||
if(i != -1)
|
||||
{
|
||||
Username = Left(S, i);
|
||||
Password = Mid(S, i+1);
|
||||
//`log("User:"@Username@"Password:"@Password);
|
||||
}
|
||||
}
|
||||
else if(Left(S, 16) ~= "Content-Length: ")
|
||||
{
|
||||
ContentLength = Int(Mid(S, 16, 64));
|
||||
}
|
||||
else if(Left(S, 14) ~= "Content-Type: ")
|
||||
{
|
||||
ContentType = Mid(S, 14);
|
||||
}
|
||||
|
||||
i = InStr(S, ":");
|
||||
if (i > -1)
|
||||
{
|
||||
AddHeader(Left(S, i), Mid(S, i+2)); // 2 = ": "
|
||||
}
|
||||
}
|
||||
|
||||
function DecodeFormData(string Data)
|
||||
{
|
||||
local string Token[2], ch;
|
||||
local int i, H1, H2, limit;
|
||||
local int t;
|
||||
|
||||
//`log("DecodeFormData"@Data);
|
||||
t = 0;
|
||||
for( i = 0; i < Len(Data); i++ )
|
||||
{
|
||||
if ( limit > class'WebConnection'.default.MaxValueLength || i > class'WebConnection'.default.MaxLineLength )
|
||||
break;
|
||||
|
||||
ch = mid(Data, i, 1);
|
||||
switch(ch)
|
||||
{
|
||||
case "+":
|
||||
Token[t] $= " ";
|
||||
limit++;
|
||||
break;
|
||||
|
||||
case "&":
|
||||
case "?":
|
||||
if(Token[0] != "")
|
||||
AddVariable(Token[0], Token[1]);
|
||||
|
||||
Token[0] = "";
|
||||
Token[1] = "";
|
||||
t = 0;
|
||||
|
||||
limit=0;
|
||||
break;
|
||||
|
||||
case "=":
|
||||
if(t == 0)
|
||||
{
|
||||
limit = 0;
|
||||
t = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Token[1] $= "=";
|
||||
limit++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "%":
|
||||
H1 = GetHexDigit(Mid(Data, ++i, 1));
|
||||
if ( H1 != -1 )
|
||||
{
|
||||
limit++;
|
||||
H1 *= 16;
|
||||
H2 = GetHexDigit(Mid(Data,++i,1));
|
||||
if ( H2 != -1 )
|
||||
Token[t] $= Chr(H1 + H2);
|
||||
}
|
||||
|
||||
limit++;
|
||||
break;
|
||||
|
||||
default:
|
||||
Token[t] $= ch;
|
||||
limit++;
|
||||
}
|
||||
}
|
||||
|
||||
if(Token[0] != "")
|
||||
AddVariable(Token[0], Token[1]);
|
||||
}
|
||||
|
||||
function int GetHexDigit(string D)
|
||||
{
|
||||
switch(caps(D))
|
||||
{
|
||||
case "0": return 0;
|
||||
case "1": return 1;
|
||||
case "2": return 2;
|
||||
case "3": return 3;
|
||||
case "4": return 4;
|
||||
case "5": return 5;
|
||||
case "6": return 6;
|
||||
case "7": return 7;
|
||||
case "8": return 8;
|
||||
case "9": return 9;
|
||||
case "A": return 10;
|
||||
case "B": return 11;
|
||||
case "C": return 12;
|
||||
case "D": return 13;
|
||||
case "E": return 14;
|
||||
case "F": return 15;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
255
IpDrv/Classes/WebResponse.uc
Normal file
255
IpDrv/Classes/WebResponse.uc
Normal file
@ -0,0 +1,255 @@
|
||||
/*=============================================================================
|
||||
WebResponse is used by WebApplication to handle most aspects of sending
|
||||
http information to the client. It serves as a bridge between WebApplication
|
||||
and WebConnection.
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
|
||||
class WebResponse extends Object
|
||||
native
|
||||
config(Web);
|
||||
|
||||
/*
|
||||
The correct order of sending a response is:
|
||||
|
||||
1. define content:
|
||||
AddHeader(...), Subst(...), ClearSubst(...), LoadParsedUHTM(...), headers, CharSet,
|
||||
2. HTTPResponse(...)
|
||||
(optional, implies a 200 return when not explicitly send)
|
||||
3. SendStandardHeaders(...)
|
||||
(optional, implied by SendText(...))
|
||||
4. send content:
|
||||
IncludeUHTM(...), SendText(...)
|
||||
*/
|
||||
|
||||
var array<string> headers; // headers send before the content
|
||||
var private native const Map_Mirror ReplacementMap{TMultiMap<FString, FString>}; // C++ placeholder.
|
||||
var const config string IncludePath;
|
||||
var localized string CharSet;
|
||||
var WebConnection Connection;
|
||||
var protected bool bSentText; // used to warn headers already sent
|
||||
var protected bool bSentResponse;
|
||||
|
||||
cpptext
|
||||
{
|
||||
private:
|
||||
void SendInParts(const FString &S);
|
||||
bool IncludeTextFile(const FString &Root, const FString &Filename, bool bCache=false, FString *Result = NULL);
|
||||
bool ValidWebFile(const FString &Filename);
|
||||
FString GetIncludePath();
|
||||
|
||||
//Hack to workaround script compiler code generation (mirrors the old code with fix)
|
||||
void SendBinary(INT Count,BYTE* B)
|
||||
{
|
||||
WebResponse_eventSendBinary_Parms Parms(EC_EventParm);
|
||||
Parms.Count=Count;
|
||||
appMemcpy(&Parms.B,B,sizeof(Parms.B));
|
||||
ProcessEvent(FindFunctionChecked(IPDRV_SendBinary),&Parms);
|
||||
}
|
||||
};
|
||||
|
||||
native final function bool FileExists(string Filename);
|
||||
native final function Subst(string Variable, coerce string Value, optional bool bClear);
|
||||
native final function ClearSubst();
|
||||
native final function bool IncludeUHTM(string Filename);
|
||||
native final function bool IncludeBinaryFile(string Filename);
|
||||
native final function string LoadParsedUHTM(string Filename); // For templated web items, uses Subst too
|
||||
native final function string GetHTTPExpiration(optional int OffsetSeconds);
|
||||
|
||||
native final function Dump(); // only works in dev mode
|
||||
|
||||
event SendText(string Text, optional bool bNoCRLF)
|
||||
{
|
||||
if(!bSentText)
|
||||
{
|
||||
SendStandardHeaders();
|
||||
bSentText = True;
|
||||
}
|
||||
|
||||
if(bNoCRLF)
|
||||
{
|
||||
Connection.SendText(Text);
|
||||
}
|
||||
else {
|
||||
Connection.SendText(Text$Chr(13)$Chr(10));
|
||||
}
|
||||
}
|
||||
|
||||
event SendBinary(int Count, byte B[255])
|
||||
{
|
||||
Connection.SendBinary(Count, B);
|
||||
}
|
||||
|
||||
function bool SendCachedFile(string Filename, optional string ContentType)
|
||||
{
|
||||
if(!bSentText)
|
||||
{
|
||||
SendStandardHeaders(ContentType, true);
|
||||
bSentText = True;
|
||||
}
|
||||
return IncludeUHTM(Filename);
|
||||
}
|
||||
|
||||
function FailAuthentication(string Realm)
|
||||
{
|
||||
HTTPError(401, Realm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the HTTP response code.
|
||||
*/
|
||||
function HTTPResponse(string Header)
|
||||
{
|
||||
bSentResponse = True;
|
||||
HTTPHeader(Header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will actually send a header. You should not call this method, queue the headers
|
||||
* through the AddHeader() method.
|
||||
*/
|
||||
function HTTPHeader(string Header)
|
||||
{
|
||||
if(bSentText)
|
||||
{
|
||||
`Log("Can't send headers - already called SendText()");
|
||||
}
|
||||
else {
|
||||
if (!bSentResponse)
|
||||
{
|
||||
HTTPResponse("HTTP/1.1 200 Ok");
|
||||
}
|
||||
if (Len(header) == 0)
|
||||
{
|
||||
bSentText = true;
|
||||
}
|
||||
Connection.SendText(Header$Chr(13)$Chr(10));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/update a header to the headers list. It will be send at the first possible occasion.
|
||||
* To completely remove a given header simply give it an empty value, "X-Header:"
|
||||
* To add multiple headers with the same name (need for Set-Cookie) you'll need
|
||||
* to edit the headers array directly.
|
||||
*/
|
||||
function AddHeader(string header, optional bool bReplace=true)
|
||||
{
|
||||
local int i, idx;
|
||||
local string part, entry;
|
||||
i = InStr(header, ":");
|
||||
if (i > -1)
|
||||
{
|
||||
part = Caps(Left(header, i+1)); // include the :
|
||||
}
|
||||
else {
|
||||
return; // not a valid header
|
||||
}
|
||||
foreach headers(entry, idx)
|
||||
{
|
||||
if (InStr(Caps(entry), part) > -1)
|
||||
{
|
||||
if (bReplace)
|
||||
{
|
||||
if (i+2 >= len(header))
|
||||
{
|
||||
headers.remove(idx, 1);
|
||||
}
|
||||
else {
|
||||
headers[idx] = header;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (len(header) > i+2)
|
||||
{
|
||||
// only add when it contains a value
|
||||
headers.AddItem(Header);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the stored headers.
|
||||
*/
|
||||
function SendHeaders()
|
||||
{
|
||||
local string hdr;
|
||||
foreach headers(hdr)
|
||||
{
|
||||
HTTPHeader(hdr);
|
||||
}
|
||||
}
|
||||
|
||||
function HTTPError(int ErrorNum, optional string Data)
|
||||
{
|
||||
switch(ErrorNum)
|
||||
{
|
||||
case 400:
|
||||
HTTPResponse("HTTP/1.1 400 Bad Request");
|
||||
SendText("<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD><BODY><H1>400 Bad Request</H1>If you got this error from a standard web browser, please mail epicgames.com and submit a bug report.</BODY></HTML>");
|
||||
break;
|
||||
case 401:
|
||||
HTTPResponse("HTTP/1.1 401 Unauthorized");
|
||||
AddHeader("WWW-authenticate: basic realm=\""$Data$"\"");
|
||||
SendText("<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD><BODY><H1>401 Unauthorized</H1></BODY></HTML>");
|
||||
break;
|
||||
case 404:
|
||||
HTTPResponse("HTTP/1.1 404 Not Found");
|
||||
SendText("<HTML><HEAD><TITLE>404 File Not Found</TITLE></HEAD><BODY><H1>404 File Not Found</H1>The URL you requested was not found.</BODY></HTML>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the standard response headers.
|
||||
*/
|
||||
function SendStandardHeaders( optional string ContentType, optional bool bCache )
|
||||
{
|
||||
if(ContentType == "")
|
||||
{
|
||||
ContentType = "text/html";
|
||||
}
|
||||
if(!bSentResponse)
|
||||
{
|
||||
HTTPResponse("HTTP/1.1 200 OK");
|
||||
}
|
||||
AddHeader("Server: UnrealEngine IpDrv Web Server Build "$Connection.WorldInfo.EngineVersion, false);
|
||||
AddHeader("Content-Type: "$ContentType, false);
|
||||
if (bCache)
|
||||
{
|
||||
AddHeader("Cache-Control: max-age="$Connection.WebServer.ExpirationSeconds, false);
|
||||
// Need to compute an Expires: tag .... arrgggghhh
|
||||
AddHeader("Expires: "$GetHTTPExpiration(Connection.WebServer.ExpirationSeconds), false);
|
||||
}
|
||||
AddHeader("Connection: Close"); // always close
|
||||
SendHeaders();
|
||||
HTTPHeader("");
|
||||
}
|
||||
|
||||
function Redirect(string URL)
|
||||
{
|
||||
HTTPResponse("HTTP/1.1 302 Document Moved");
|
||||
AddHeader("Location: "$URL);
|
||||
SendText("<html><head><title>Document Moved</title></head>");
|
||||
SendText("<body><h1>Object Moved</h1>This document may be found <a HREF=\""$URL$"\">here</a>.</body></html>");
|
||||
}
|
||||
|
||||
|
||||
function bool SentText()
|
||||
{
|
||||
return bSentText;
|
||||
}
|
||||
|
||||
function bool SentResponse()
|
||||
{
|
||||
return bSentResponse;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
//IncludePath="/Web"
|
||||
//CharSet="iso-8859-1"
|
||||
}
|
206
IpDrv/Classes/WebServer.uc
Normal file
206
IpDrv/Classes/WebServer.uc
Normal file
@ -0,0 +1,206 @@
|
||||
/*=============================================================================
|
||||
WebServer is responsible for listening to requests on the selected http
|
||||
port and will guide requests to the correct application.
|
||||
Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||||
=============================================================================*/
|
||||
|
||||
class WebServer extends TcpLink config(Web);
|
||||
|
||||
var config string ServerName;
|
||||
var config string Applications[10];
|
||||
var config string ApplicationPaths[10];
|
||||
var config bool bEnabled;
|
||||
var config int ListenPort;
|
||||
var config int MaxConnections;
|
||||
var config int DefaultApplication;
|
||||
var config int ExpirationSeconds; // How long images can be cached .. default is 24 hours
|
||||
|
||||
`if (`__TW_NETWORKING_)
|
||||
var transient int CurrentListenPort;
|
||||
`endif
|
||||
|
||||
var string ServerURL;
|
||||
var WebApplication ApplicationObjects[10];
|
||||
|
||||
var int ConnectionCount;
|
||||
// MC: Debug
|
||||
var int ConnID;
|
||||
|
||||
function PostBeginPlay()
|
||||
{
|
||||
local int i;
|
||||
local class<WebApplication> ApplicationClass;
|
||||
local IpAddr l;
|
||||
local string s;
|
||||
|
||||
// Destroy if not a server
|
||||
if (WorldInfo.NetMode == NM_StandAlone || WorldInfo.NetMode == NM_Client)
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!bEnabled)
|
||||
{
|
||||
`Log("Webserver is not enabled. Set bEnabled to True in Advanced Options.");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
Super.PostBeginPlay();
|
||||
|
||||
if(ServerName == "")
|
||||
{
|
||||
GetLocalIP(l);
|
||||
s = IpAddrToString(l);
|
||||
i = InStr(s, ":");
|
||||
if(i != -1)
|
||||
s = Left(s, i);
|
||||
ServerURL = "http://"$s;
|
||||
}
|
||||
else
|
||||
ServerURL = "http://"$ServerName;
|
||||
|
||||
`if (`__TW_NETWORKING_)
|
||||
CurrentListenPort = class'GameEngine'.static.GetWebAdminPort();
|
||||
if (CurrentListenPort == 0)
|
||||
{
|
||||
CurrentListenPort = ListenPort;
|
||||
}
|
||||
`endif
|
||||
|
||||
`if (`__TW_NETWORKING_)
|
||||
if(CurrentListenPort != 80)
|
||||
ServerURL = ServerURL $ ":"$string(CurrentListenPort);
|
||||
`else
|
||||
if(ListenPort != 80)
|
||||
ServerURL = ServerURL $ ":"$string(ListenPort);
|
||||
`endif
|
||||
|
||||
`if (`__TW_NETWORKING_)
|
||||
if (BindPort( CurrentListenPort ) > 0)
|
||||
`else
|
||||
if (BindPort( ListenPort ) > 0)
|
||||
`endif
|
||||
{
|
||||
if (Listen() == true)
|
||||
{
|
||||
`if (`__TW_NETWORKING_)
|
||||
`log("Web Server Created"@ServerURL@"Port:"@CurrentListenPort@"MaxCon"@MaxConnections@"ExpirationSecs"@ExpirationSeconds@"Enabled"@bEnabled);
|
||||
`else
|
||||
`log("Web Server Created"@ServerURL@"Port:"@ListenPort@"MaxCon"@MaxConnections@"ExpirationSecs"@ExpirationSeconds@"Enabled"@bEnabled);
|
||||
`endif
|
||||
//`log("~~~~~~~~~~~~~~~~~~~Loading Server Apps~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
for(i=0;i<10;i++)
|
||||
{
|
||||
if(Applications[i] == "")
|
||||
break;
|
||||
|
||||
ApplicationClass = class<WebApplication>(DynamicLoadObject(Applications[i], class'Class'));
|
||||
if(ApplicationClass != None)
|
||||
{
|
||||
//`log("Loading application"@Applications[i]@ApplicationClass@"Path:"@ApplicationPaths[i]);
|
||||
ApplicationObjects[i] = New(None) ApplicationClass;
|
||||
ApplicationObjects[i].WorldInfo = WorldInfo;
|
||||
ApplicationObjects[i].WebServer = Self;
|
||||
ApplicationObjects[i].Path = ApplicationPaths[i];
|
||||
ApplicationObjects[i].Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
`log("Failed to load"@Applications[i]);
|
||||
}
|
||||
}
|
||||
//`log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
`log("Unable to setup server for listen");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
`log("Unable to bind webserver to a port");
|
||||
}
|
||||
|
||||
//Fall through on failure, destroy ourselves
|
||||
Destroy();
|
||||
}
|
||||
|
||||
event Destroyed()
|
||||
{
|
||||
local int i;
|
||||
|
||||
`log("Destroying WebServer");
|
||||
for(i = 0; i < ArrayCount(ApplicationObjects); i++)
|
||||
{
|
||||
if (ApplicationObjects[i] != None)
|
||||
{
|
||||
ApplicationObjects[i].CleanupApp();
|
||||
}
|
||||
}
|
||||
|
||||
Super.Destroyed();
|
||||
}
|
||||
|
||||
event GainedChild( Actor C )
|
||||
{
|
||||
Super.GainedChild(C);
|
||||
ConnectionCount++;
|
||||
|
||||
// if too many connections, close down listen.
|
||||
if(MaxConnections > 0 && ConnectionCount > MaxConnections && LinkState == STATE_Listening)
|
||||
{
|
||||
`Log("WebServer: Too many connections - closing down Listen.");
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
event LostChild( Actor C )
|
||||
{
|
||||
Super.LostChild(C);
|
||||
ConnectionCount--;
|
||||
|
||||
// if closed due to too many connections, start listening again.
|
||||
if(ConnectionCount <= MaxConnections && LinkState != STATE_Listening)
|
||||
{
|
||||
`Log("WebServer: Listening again - connections have been closed.");
|
||||
Listen();
|
||||
}
|
||||
}
|
||||
|
||||
function WebApplication GetApplication(string URI, out string SubURI)
|
||||
{
|
||||
local int i, l;
|
||||
|
||||
SubURI = "";
|
||||
for(i=0;i<10;i++)
|
||||
{
|
||||
if(ApplicationPaths[i] != "")
|
||||
{
|
||||
l = Len(ApplicationPaths[i]);
|
||||
if(Left(URI, l) ~= ApplicationPaths[i] && (Len(URI) == l || Mid(URI, l, 1) == "/"))
|
||||
{
|
||||
SubURI = Mid(URI, l);
|
||||
`logd("Application handling request"@ApplicationObjects[i]@"Path:"@ApplicationPaths[i]);
|
||||
return ApplicationObjects[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`log("No application found to handle request"@URI);
|
||||
return None;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
//Applications(0)="xWebAdmin.UTServerAdmin"
|
||||
//Applications(1)="xWebAdmin.UTImageServer"
|
||||
//ApplicationPaths(0)="/ServerAdmin"
|
||||
//ApplicationPaths(1)="/images"
|
||||
//ListenPort=80
|
||||
//MaxConnections=30
|
||||
//ExpirationSeconds=86400
|
||||
AcceptClass=Class'IpDrv.WebConnection'
|
||||
}
|
Reference in New Issue
Block a user