question

Matt avatar image
Matt asked

Cloudscript call from custom game server

Hey folks,

So I'm trying to call a cloudscript function from my custom game server for both players that were matched up. The problem I'm specifically having is that my cloudscript seems to be getting the same player PlayFabId for both calls. I'm fairly sure it has to do with the way I'm using the signal and response.

This is what my server call looks like (without all of my various gameplay-related code), this is called for each player.

ExecuteCloudscriptResponse.AddOnce( ( cloudResult ) =>
{
	// log.info( currentPlayerId ) shown here has same player Id, twice
} );
ExecuteCloudscriptSignal.Dispatch( new ExecuteCloudScriptServerRequest()
{
	PlayFabId = player.PlayFabId,	// this is correct/unique in both calls
	FunctionName = "MatchComplete"
} );

Inside my cloudscript I simply have a log.info( currentPlayerId ); and I display that in the return from cloud. My cloudscript function is getting hit twice, but it seems like it's getting incorrect data? Logically the AddOnce seems like it'd be the problem seeing as how it's not dealing well with my calling this a second time, but I'm just not sure.

Thanks for the help!

-Matt

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.

Matt avatar image Matt commented ·

Now that I think about it, the cloud is probably getting both unique calls and it's just the listener that's responding to both replies with the latest AddOnce I guess. Is there normally a better process? I don't see a way for me to check the PlayFabId in the cloudResult to see which return it is.

0 Likes 0 ·
marcowilliamspf avatar image
marcowilliamspf answered

Hey Matthew,


It is a little hard to tell the context in which currentPlayerId variable you've specified is in from your example. That is not a variable in the return result.

https://api.playfab.com/documentation/server/datatype/PlayFab.server.Models/PlayFab.server.Models.ExecuteCloudScriptResult

The ideal way to do this is to return the currentPlayerId in the result returned from your cloudscript.

FunctionResult param holds the returned value from your cloudscript call. If you returned from CloudScript a JSON blob like this.

//Return from cloudscript

return {
    "PlayFabId": currentPlayerId
}

You could deserialize this as

//TODO: do proper error handling for null refs etc...
using PlayFab.Events var result = PlayFab.Json.JsonWrapper.DeserializeObject(cloudResult.FunctionResult);
var playFabId = result["PlayFabId"];

The last thing I would like to mention, which is not documented anywhere yet, is don't use PlayFab Context. I have not had a chance to remove this from the custom game server and it is going away. It is an unnecessary layer for the Custom Game Server that ads virtual no value. Instead you can call the PlayFab API's directly using PlayFab.PlayFabServerAPI.[method]

If you need events, use the events distributed with our SDK.

using  PlayFab.Events
var events = PlayFabEvents.Init();
events.OnServerExecuteCloudScriptResultEvent += (cloudResult) =>
{
	var result = PlayFab.Json.JsonWrapper.DeserializeObject<Dictionary<string, string>>(cloudResult.FunctionResult.ToString());
        var playFabId = result["PlayFabId"];
};


2 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.

Matt avatar image Matt commented ·

Thanks for the reply Marco!

So I did go the route of just returning the PlayFabId from the cloud call and adding a listener once to the response signal. There are still a couple of things I'm confused about though...

1. With the problem I encountered using AddOnce and the timing of the signals being sent out, is that something that can occur with the code derived from the UnityNetworkManagerMediator. I built my own manager, but it's built heavily from that structure.

Just to clarify, the concern is that 2 signals are sent out as closely together as possible (for instance 2 players attempt to authenticate at ~exactly the same time) but the second call on the response for AddOnce replaces/overwrites the previous, so all responses end up using the latest callback.

2. When you say not to use the PlayFab Context, do you mean not to create signals/responses using strange? Is using that event approach instead of the signals/responses better for performance? It seems like it's ultimately doing the same exact thing but in a slightly cleaner way.

Thanks for the help!

-Matt

0 Likes 0 ·
marcowilliamspf avatar image marcowilliamspf Matt commented ·

Hey Matt, I know you got this resolved already. But I wanted to add my 2 cents worth on the AddOnce issue.

Under the hood of a Signal, the dispatcher is doing the following:

public void Dispatch()
{
	Listener();
	OnceListener();
	OnceListener = delegate { };
	base.Dispatch(null);
}

As you can see, the OnceListener gets overwritten with a blank delegate after the signal is dispatched.

Therefore if you dispatch the same signal twice it wipes any other reference to it immediately.

Since Signals are injected we are actually subscribing the AddOnce to the same instance of the signal and then dispatching which means that if two events are close enough, they second call does get overwritten which is a bug (IMO).

I am in the process of refactoring the core PlayFabServer and Networking packages off of Signals & Commands and use standard event delegates. I don't have an exact ETA.
0 Likes 0 ·
Matt avatar image
Matt answered

Come to think of it, I don't actually understand how the PlayFab supplied code works and doesn't succumb to the same issue I'm having.

On GitHub, in the UnityNetworkManagerMediator, there are calls such as:

RedeemMatchmakerTicketResponseSignal.AddOnce(OnAuthUserResponse);

RedeemMatchmakerTicketSignal.Dispatch(new RedeemMatchmakerTicketRequest()

{

	Ticket = message.AuthTicket,

	LobbyId = ServerSettingsData.GameId.ToString()

});

I'm basically doing exactly the same thing with my cloud signal, but I guess because the function is called literally the same frame, the timing is reliably succeeding in messing it up. If 2 players connected at exactly the same time, wouldn't that code above encounter the same issue?

10 |1200

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

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.