question

kevin-4 avatar image
kevin-4 asked

PlayFab Login calls break Unity application lifecycle

Take the code below for instance:

string customID = SystemInfo.deviceUniqueIdentifier;
string deviceModel = SystemInfo.deviceModel;
string operatingSystem = SystemInfo.operatingSystem;


LoginWithCustomIDRequest request = new LoginWithCustomIDRequest() {
			TitleId = PlayFabSettings.TitleId,
			CreateAccount = true,
			CustomId = customID
};


PlayFabClientAPI.LoginWithCustomID(request, (result) => {
			Debug.Log("Success!");
},
(error) => {
			Debug.Log("Error");
});

The call to PlayFabClientAPI.LoginWithCustomID breaks calls to onApplicationPause(), apart of the Unity application lifecycle. This issue persists across all scenes in the app after any PlayFab login call was made. This is a serious issue as we currently do all local data saving in onApplicationPause (as it's the only one guaranteed to be called on Android when an app is exiting). In the code snippet above, if you take out the call to PlayFabClientAPI.LoginWithCustomID, onApplicationPause() will still be called normally.

This was testing using the latest PlayFab SDK and Editor Extension version 2.39.180316. This was also testing in a brand new project with nothing included except PlayFab. This bug is scene in the Unity Editor, will testing to see if it occurs in standalone PC builds and Android builds next.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

kevin-4 avatar image kevin-4 commented ·

The bug is present on standalone PC builds and Android builds as well.

0 Likes 0 ·

1 Answer

·
1807605288 avatar image
1807605288 answered

If I understand your question, I think you're saying that the asynchronous nature of PlayFab API calls is conflicting with the Unity trigger: onApplicationPause().

I want to clarify some things:
In the code example you show, are you saying that the Debug.Log() never shows up?
What is the expected result vs actual result for your situation?

I can certainly anticipate some unfavorable behavior between an async PlayFab call and the onApplicationPause() event, but I can't quite determine what you are trying to accomplish.

I also assume you're not actually calling LoginWithCustomID from onApplicationPause(), but rather something like UpdateUserData, and calling it directly from within onApplicationPause().

If I've made all the correct assumptions, then what you're dealing with is this:

PlayFab API calls are asynchronous, and happen in the background for 100-1000 ms after they are called. If we blocked the main thread to execute calls, your game would lock up every time you made an API call, which would be very bad.

Unfortunately, onApplicationPause() is the very last thing executed by Unity before the app puts itself in hibernation. So your PlayFab API call won't complete.

This will be universal with all methods. LoginWithCustomId is probably most noticeable, because login takes much longer than most other calls.

It's not reliable to trigger a data save to PlayFab from onApplicationPause(). By the time you get that event, it's too late, you don't have 100ms left to make the attempt. This is a feature of Unity, and really can't be called a defect in either Unity nor PlayFab.

Your only real recourse is to save data at times that are meaningful to your game. When a player gains a level, or moves to a new room, or etc. It is true that there could be data-loss if the player hard-stops a game without logging out properly, so you have to design around it.

5 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

kevin-4 avatar image kevin-4 commented ·
public class TestFile : MonoBehaviour {
		void Start() {
			string customID = SystemInfo.deviceUniqueIdentifier;
			string deviceModel = SystemInfo.deviceModel;
			string operatingSystem = SystemInfo.operatingSystem;


			LoginWithCustomIDRequest request = new LoginWithCustomIDRequest() {
				TitleId = PlayFabSettings.TitleId,
				CreateAccount = true,
				CustomId = customID
			};


			PlayFabClientAPI.LoginWithCustomID(request, (result) => {
				Debug.Log("Success!");
			}, (error) => {
				Debug.Log("Error");
			});
		}


		void OnApplicationPause(bool paused) {
			Debug.LogError("Paused: " + paused);
			
		}


		void OnApplicationQuit() {
			Debug.LogError("Quit!");
		}
	}
0 Likes 0 ·
kevin-4 avatar image kevin-4 commented ·

Yes you misunderstood me - but the fault is mine, I should have placed a full class snippet to explain my point better. I just added one to the comment above for you to test on your end. I am not calling PlayFab calls from onApplicationPause. The issue I am having is that after I make *any* PlayFab login call, onApplicationPause no longer gets called. And not just in the current scene, but in any scene. For the life of the app. If you run the code attached but delete the async LoginWithCustomID code block and its callbacks, onApplicationPause is called. If you leave that code block in, onApplicationPause is no longer called. onApplicationQuit does not seem to be affected.

I *do* already only make calls to PlayFab after meaningful game events. I use onApplicationPause and onApplicationQuit in order to save encrypted data locally to the user's device/computer (for offline mode use). The issue is after authenticating the user, onApplicationPause is never called again. The scripts that define on pause and on quit live on a gameobject that never gets destroyed. And after removing the PlayFab code the methods get called normally.

0 Likes 0 ·
kevin-4 avatar image kevin-4 kevin-4 commented ·

Also, forgot to include: I'm using Unity 2017.3.1 f1 on Windows 10.

0 Likes 0 ·
1807605288 avatar image 1807605288 ♦ commented ·

OIC, got it.

Yes, this has already been reported. We plan to add an optional toggle for this, but haven't done it yet.

Specifically, our HTTP service does this:
https://github.com/PlayFab/UnitySDK/blob/66d236a16a0a09f17b5e36574159cfd2edf6cd04/Source/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabHTTP.cs#L80

Application.runInBackground = true;

Which means you no longer get OnApplicationPause() events, because the app stays running.

For now, after your login call, you can toggle this back off. this would be a second line beside Debug.Log("Success!"); in your example.

We will add a toggle so this can be defined properly in PlayFabSettings in the next build.

0 Likes 0 ·
kevin-4 avatar image kevin-4 1807605288 ♦ commented ·

So in both call backs (success and error) for all PlayFab login calls, just add Application.runInBackground = false? Got it, will test in a bit.

Is there any downside to doing this? Does the PlayFab SDK expect to/need to run in the background? Will this be toggled back to true during later calls to the PlayFab API like when I update a user's data?

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.