Server authorative persistent data storage

If I want server authorative persistent data storage that clients cannot edit, do unity multiplayer services have any built in solution for that or do I need to use a 3rd party/roll my own?

My first thought was to fire a state change to cloud code and save the data using Cloud Saves. But I am pretty sure players can execute Cloud Code and Cloud Saves, so I would need to work out some kind of authentication system where only the server can execute that code. Not to mention Cloud Saves can be edited by players (I think) and the pay size of a cloud size is fairly low if you wanted to store a lot of data in a single document (ie: a server-only document).

Basically what I want:

Example: A player kills someone, I want to store somewhere that their kills are +1, and that they have killed playerId xyz123, so that I can use it to populate statistics on a tooltip. I want that data to be persistent forever and viewable while connected to any UGS game server. For example if the player then hovered over xyz123, they get a tooltip showing how many times they've killed that player and how many times the player has killed them.

Access Control allows policies to restrict read/write access to resources from different identities. Here is an example that shows a policy for Cloud Save where players and Cloud Code have different access levels: https://docs.unity.com/ugs-overview/en/manual/access-control#Example_policies

1 Like

Thank you, that is perfect! Follow on question from that, does the Unity Cloud Save module support the server saving to player cloud saves? How is the server supposed to write to the individual player's Cloud Save? I cannot find any documentation on Cloud Save that talks about the perspective of a non-player writing to the save or reading it.

Simply I am looking for something that would take the playerId, read the cloud save data on login, and write cloud save data as events occur (from server side).

At this point, I have managed to do the above using a service account, but I am directly interacting with APIs rather than using any of the official packages in Unity. If there is an official solution I would much prefer to use that.

It also doesn't feel right that I am injecting my service account authorization into my server using a config variable, there must be a better way?

Unity Game Servers have a built-in identity that can be used to make calls to UGS services that support it, such as Cloud Save. In practice, this means obtaining a token from the localhost agent (called the payload proxy) and using that in the Authorization: Bearer header. WIth this method, no additional secrets or credentials need to be deployed to the game server.

This is implemented in the com.unity.services.authentication package under the Unity.Services.Authentication.Server namespace.The method is called SignInFromServerAsync.

I'm not sure this is fully integrated with the Cloud Save SDK at the moment, however. I will check with the team -- hope this helps in the meantime!

Thank you, that makes it too easy! I am really looking forward to finding out if this is supported by the unity Cloud Save package, for the moment I am making Cloud Code functions to get and set the player saves by passing through the player's access token (which also requires that I request an access token for each player using the service account).

It would definitely be a lot more simple if I could just call something like CloudSave.SaveDataForPlayer(string playerId, object data);

I hate to be a pain, but I have the Unity.Services.Authentication package installed and the namespace: Unity.Services.Authentication.Server does not exist. Am I looking in the wrong place? I have 2.4.0 installed.

@jackward84 You need to go to the manifest file under Packages/ folder and update the version number to 3.2.0.

That being said, I have a similar problem with the Economy service. I can currently use a service account to call a cloud code function that can do update operations on a given player's inventory. I tested this using postman app and it works. Now I thought that I could do the following as well:

private async void Init()
{

    InitializationOptions options = new InitializationOptions();
    options.SetEnvironmentName("development");

    if (UnityServices.State == ServicesInitializationState.Uninitialized)
    {
        await UnityServices.InitializeAsync(options);
    }

    ServerAuthenticationService.Instance.Authorized += ServerAuth_Authorized;
    ServerAuthenticationService.Instance.SignInWithServiceAccountAsync(keyId, secret);
}

private async void ServerAuth_Authorized()
{
    print("Auth Successful. Making CloudCode call...");
    TestResponse response = await CloudCodeService.Instance.CallEndpointAsync<TestResponse>(
        "EconomyTesting",
        new Dictionary<string, object>()
        {
            {"playerId", "blahblah"},
            {"resourceId", "ITEM_DEMO"},
            {"instanceData", new { x = 4224, msg = "Hello Cloud Code!" }}
        }
    );
}

But when I run I get the following error from cloudcode in the console coming from the CallEndpointAsync call:
CloudCodeException: AccessTokenMissing
Access token is missing - ensure you are signed in through the Authentication SDK and try again.
Unity.Services.CloudCode.CloudCodeInternal.ValidateRequiredDependencies () (at Library/PackageCache/com.unity.services.cloudcode@2.4.0/Runtime/CloudCode.cs:201)
Unity.Services.CloudCode.CloudCodeInternal.GetRunScriptResponse (System.String function, System.Collections.Generic.Dictionary2[TKey,TValue] args) (at Library/PackageCache/com.unity.services.cloudcode@2.4.0/Runtime/CloudCode.cs:103)
Unity.Services.CloudCode.CloudCodeInternal.CallEndpointAsync[TResult] (System.String function, System.Collections.Generic.Dictionary
2[TKey,TValue] args) (at Library/PackageCache/com.unity.services.cloudcode@2.4.0/Runtime/CloudCode.cs:45)

Is there a way to sign in with a service account through the C# SDK and call cloud code for an authoritative server?

If not then what's the point of using any of the C# SDKs in a server authoritative game (most common use case!)? Do I have to write the web API calls myself and translate everything to/from JSON (This is tedious and I thought this was the purpose of Unity.Services.Authentication.Server).

Also, as a side note, you should be able to optionally pass playerId to the economy SDK functions as well. I currently have to write a duplicate of all the functions that the economy C# sdk provides but with an additional "playerId" parameter and I have to make them call cloud code as well instead which is super tedious.