One Title ID. Every feature is one function call.
Drop the playsync-unity-sdk folder into your project's Assets/ folder. No package manager needed.
// One line. That's it. PlaySyncSettings.TitleId = "YOUR_TITLE_ID";
Get your Title ID from the dashboard when you create a title.
Logs in with hardware device ID. Creates account automatically on first login.
PlaySyncAPI.Auth.LoginWithDevice( new LoginWithDeviceIdRequest { CreateAccount = true }, result => Debug.Log($"Player: {result.PlaySyncId}"), error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Auth.Register( "PlayerOne", "mypassword123", result => Debug.Log($"Registered: {result.PlaySyncId}"), error => Debug.LogError(error.ErrorMessage), email: "optional@email.com" );
PlaySyncAPI.Auth.Login( "PlayerOne", "mypassword123", result => Debug.Log("Logged in!"), error => Debug.LogError(error.ErrorMessage) );
// Check login state if (PlaySyncAPI.Auth.IsLoggedIn) Debug.Log(PlaySyncAPI.Auth.PlayerId); // Refresh expired token PlaySyncAPI.Auth.RefreshSession(ok => {}, err => {}); // Logout PlaySyncAPI.Auth.Logout();
// Single key PlaySyncAPI.Data.SetData("inventory", jsonString, result => Debug.Log($"Saved v{result.version}"), error => Debug.LogError(error.ErrorMessage) ); // Multiple keys at once PlaySyncAPI.Data.SetData( new UpdatePlayerDataRequest { Data = new Dictionary<string, string> { { "level", "5" }, { "xp", "2400" } } }, result => Debug.Log($"Saved {result.KeysUpdated} keys"), error => Debug.LogError(error.ErrorMessage) );
// Single key PlaySyncAPI.Data.GetData("inventory", result => Debug.Log(result.value), error => Debug.LogError(error.ErrorMessage) ); // Multiple keys PlaySyncAPI.Data.GetData( new GetPlayerDataRequest { Keys = new List<string> { "level", "xp" } }, result => { string level = result.Data["level"].Value; }, error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Data.DeleteData("old_key", result => Debug.Log("Deleted"), error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Data.ListKeys( result => { foreach (var key in result.keys) Debug.Log(key); }, error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Currency.GetBalance("CS", result => Debug.Log($"Coins: {result.Balance}"), error => Debug.LogError(error.ErrorMessage) );
Server validates the 24-hour timer. Players can't cheat it by changing their device clock.
PlaySyncAPI.Currency.ClaimDaily( result => { if (result.granted) Debug.Log($"Got {result.amount} coins! Balance: {result.balance}"); else Debug.Log(result.message); // "Next claim in 5h 23m" }, error => Debug.LogError(error.ErrorMessage) );
No login needed — catalog is public.
PlaySyncAPI.Inventory.GetCatalog( result => { foreach (var item in result.items) Debug.Log($"{item.display_name}: {item.price_amount} {item.price_currency}"); }, error => Debug.LogError(error.ErrorMessage) );
Server checks balance, deducts currency, and grants item — all in one call, can't be cheated.
PlaySyncAPI.Inventory.Purchase("red_hat", result => { Debug.Log($"Bought! Spent {result.currency_spent}, balance: {result.currency_balance}"); }, error => { // error.ErrorMessage = "Not enough CS. Need 500, have 200." Debug.LogError(error.ErrorMessage); } );
PlaySyncAPI.Inventory.GetMyItems( result => { foreach (var item in result.items) Debug.Log($"Own: {item.display_name} x{item.quantity}"); }, error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Inventory.HasItem("red_hat", owns => { if (owns) EquipHat(); }, error => Debug.LogError(error.ErrorMessage) );
No login needed — config is public. Set values from the dashboard.
PlaySyncAPI.Config.GetValue("ServerMessage", result => motdText.text = result.value, error => Debug.LogError(error.ErrorMessage) );
Returns a ConfigMap with typed helper methods.
PlaySyncAPI.Config.GetAll( config => { string motd = config.Get("ServerMessage"); bool maintenance = config.GetBool("MaintenanceMode"); int minVer = config.GetInt("MinVersion", 1); float discount = config.GetFloat("StoreDiscount", 0); bool exists = config.Has("SomeKey"); }, error => Debug.LogError(error.ErrorMessage) );
Fetch a script by name. Write and manage scripts in the dashboard code editor.
PlaySyncAPI.CloudScript.Execute("DailyRewards", result => { Debug.Log($"Script: {result.name} v{result.revision}"); Debug.Log(result.code); }, error => Debug.LogError(error.ErrorMessage) );
Only saves if it's the player's personal best.
PlaySyncAPI.Leaderboards.SubmitScore("high_scores", 42000f, result => { if (result.updated) Debug.Log($"New best: {result.best}"); }, error => Debug.LogError(error.ErrorMessage) );
PlaySyncAPI.Leaderboards.GetLeaderboard("high_scores", 10, result => { foreach (var e in result.Entries) Debug.Log($"#{e.Position} {e.DisplayName}: {e.StatValue}"); }, error => Debug.LogError(error.ErrorMessage) );
Every API call uses the same error callback pattern.
void OnError(PlaySyncError error) { if (error.IsBanned) ShowBanScreen(); else if (error.IsRateLimited) StartCoroutine(RetryLater()); else if (error.IsTokenExpired) PlaySyncAPI.Auth.RefreshSession(ok => {}, err => ForceLogin()); else Debug.LogError(error.ErrorMessage); }
| Property | Type | When |
|---|---|---|
IsBanned | bool | Player banned (403) |
IsRateLimited | bool | Too many requests (429) |
IsTokenExpired | bool | JWT expired (401) |
HttpCode | int | Raw status code |
ErrorMessage | string | Human-readable message |