SDK для Unity (C#)
Единый файл-синглтон с асинхронными методами (async/Task) и обработкой ошибок. Авторизация происходит автоматически по сессии BlueStudz Launcher — игроку не нужно логиниться повторно внутри игры.
Установка
- Скопируйте скрипт ниже в
Assets/Scripts/BlueStudz/BlueStudzSDK.cs. - Создайте пустой GameObject на стартовой сцене и добавьте компонент
BlueStudzSDK, заполните полеSecret Tokenиз кабинета разработчика. - Вызовите
await BlueStudzSDK.Instance.Init()при старте игры.
Справочник методов C# API
Исходный код интеграции
BlueStudzSDK.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
/// <summary>
/// BlueStudz SDK for Unity - drop this single file into Assets/Scripts/BlueStudz/.
/// Reads BLUESTUDZ_API / BLUESTUDZ_GAME_ID / BLUESTUDZ_LAUNCH_TOKEN from the
/// environment (set automatically by the BlueStudz Launcher when it starts
/// your game) so players never have to log in a second time inside your game.
/// </summary>
public class BlueStudzSDK : MonoBehaviour
{
public static BlueStudzSDK Instance { get; private set; }
[Tooltip("Secret token from your BlueStudz developer dashboard (SDK tab). Never ship this in a public repo.")]
public string SecretToken;
private string apiBase;
private string gameId;
private string userToken;
private string userId;
public bool IsAuthenticated { get; private set; }
private void Awake()
{
if (Instance != null) { Destroy(gameObject); return; }
Instance = this;
DontDestroyOnLoad(gameObject);
}
/// <summary>
/// Call once at startup. appId/secretToken override the environment
/// variables the Launcher sets - pass null to use the Launcher's values.
/// </summary>
public async Task<bool> Init(string appId = null, string secretToken = null)
{
apiBase = Environment.GetEnvironmentVariable("BLUESTUDZ_API") ?? "https://api.bluestudz.ru";
gameId = appId ?? Environment.GetEnvironmentVariable("BLUESTUDZ_GAME_ID");
SecretToken = secretToken ?? SecretToken;
var launchToken = Environment.GetEnvironmentVariable("BLUESTUDZ_LAUNCH_TOKEN");
if (string.IsNullOrEmpty(gameId) || string.IsNullOrEmpty(launchToken))
{
Debug.LogWarning("[BlueStudzSDK] Not launched via BlueStudz Launcher - running unauthenticated.");
return false;
}
try
{
var verifyResult = await PostJson<GameVerifyResponse>(
"/auth/game/verify",
"{\"launchToken\":\"" + launchToken + "\"}",
withAuth: false);
userToken = verifyResult.gameToken;
userId = verifyResult.user.id;
var authBody = $"{{\"gameId\":\"{gameId}\",\"secretToken\":\"{SecretToken}\",\"userId\":\"{userId}\",\"userToken\":\"{userToken}\"}}";
var authResult = await PostJson<SuccessResponse>("/sdk/auth/verify", authBody, withAuth: false);
IsAuthenticated = authResult.success;
return IsAuthenticated;
}
catch (Exception e)
{
Debug.LogError($"[BlueStudzSDK] Init failed: {e.Message}");
return false;
}
}
public static class Achievements
{
public static async Task<bool> Unlock(string achievementId)
{
var body = "{\"achievementId\":\"" + achievementId + "\"}";
var result = await Instance.PostJson<SuccessResponse>("/sdk/achievements/unlock", body, withAuth: true, withSecret: true);
return result.success;
}
}
public static class CloudSaves
{
/// <summary>
/// sha256/sizeBytes/storageKey come from your own upload of the save
/// archive to the presigned URL from POST /storage/presign; this call
/// only registers the resulting file with BlueStudz's conflict detection.
/// </summary>
public static async Task<CloudSaveResult> Sync(string sha256, long sizeBytes, string storageKey, int? baseVersion)
{
var baseVersionJson = baseVersion.HasValue ? baseVersion.Value.ToString() : "null";
var body = $"{{\"sha256\":\"{sha256}\",\"sizeBytes\":{sizeBytes},\"storageKey\":\"{storageKey}\",\"baseVersion\":{baseVersionJson}}}";
return await Instance.PostJson<CloudSaveResult>("/sdk/cloudsaves/sync", body, withAuth: true);
}
}
public static class Presence
{
/// <summary>Marks the player as active in this game (or clears it).</summary>
public static async Task<bool> SetStatus(bool active)
{
var body = "{\"active\":" + (active ? "true" : "false") + "}";
var result = await Instance.PostJson<SuccessResponse>("/sdk/presence/status", body, withAuth: true);
return result.success;
}
}
private async Task<T> PostJson<T>(string path, string jsonBody, bool withAuth, bool withSecret = false)
{
using var req = new UnityWebRequest($"{apiBase}{path}", "POST");
var bytes = Encoding.UTF8.GetBytes(jsonBody);
req.uploadHandler = new UploadHandlerRaw(bytes);
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
if (withAuth) req.SetRequestHeader("Authorization", $"Bearer {userToken}");
if (withAuth) req.SetRequestHeader("x-bs-gameid", gameId);
if (withSecret) req.SetRequestHeader("x-bs-secrettoken", SecretToken);
var op = req.SendWebRequest();
while (!op.isDone) await Task.Yield();
if (req.result != UnityWebRequest.Result.Success)
throw new Exception($"{path} failed: {req.error} ({req.downloadHandler.text})");
return JsonUtility.FromJson<T>(req.downloadHandler.text);
}
[Serializable] private class SuccessResponse { public bool success; }
[Serializable] private class GameVerifyUser { public string id; }
[Serializable] private class GameVerifyResponse { public string gameToken; public GameVerifyUser user; }
[Serializable] public class CloudSaveResult { public bool success; public bool conflict; public string status; }
}
Пример использования
csharp
await BlueStudzSDK.Instance.Init();
if (BlueStudzSDK.Instance.IsAuthenticated)
{
await BlueStudzSDK.Achievements.Unlock("first_blood");
await BlueStudzSDK.Presence.SetStatus(true);
}