// SAQA Games SDK — Unity үшін C# оралымы // // Орналасуы: Assets/Saqa/SaqaSDK.cs // Қосымша қажет: Assets/Plugins/WebGL/SaqaSDK.jslib // // Қолданылуы: // SaqaSDK.Init(ready => { // Debug.Log(SaqaSDK.IsAuthorized ? SaqaSDK.PlayerName : "қонақ"); // SaqaSDK.GameReady(); // }); // // Редакторда да, WebGL-ден тыс платформаларда да қателік бермейді — // SDK қолжетімсіз болса, шақырулар бірден сәтсіз жауап қайтарады. using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; namespace Saqa { /// Кез келген SDK шақыруының нәтижесі. public class SaqaResult { /// Сәтті аяқталды ма. public bool Ok; /// Жауаптың JSON көрінісі (қате болса — null). public string Json; /// Қате мәтіні. public string Error; /// Қате коды: AUTH_REQUIRED, REQUEST_FAILED, SDK_UNAVAILABLE… public string Code; /// Аккаунтқа кіру қажет пе. public bool NeedsAuth => Code == "AUTH_REQUIRED"; /// JSON-ды өз класыңызға айналдырады. public T As() => string.IsNullOrEmpty(Json) ? default : JsonUtility.FromJson(Json); } [Serializable] public class SaqaPlayerEntry { public string id; public string name; public string avatarUrl; } [Serializable] public class SaqaLeaderboardEntry { public int rank; public int score; public string extraData; public SaqaPlayerEntry player; } [Serializable] public class SaqaLeaderboard { public SaqaLeaderboardEntry[] entries; public SaqaLeaderboardEntry userEntry; } [Serializable] public class SaqaAdResult { public bool wasShown; public bool rewarded; } public class SaqaSDK : MonoBehaviour { // ── Күй ────────────────────────────────────────────────────────────── public static bool IsReady { get; private set; } public static bool IsAvailable { get; private set; } public static bool IsAuthorized { get; private set; } public static string PlayerId { get; private set; } = ""; public static string PlayerName { get; private set; } = ""; public static string AvatarUrl { get; private set; } = ""; public static string Language { get; private set; } = "kk"; public static string DeviceType { get; private set; } = "desktop"; private const string GameObjectName = "SaqaSDK"; private static SaqaSDK _instance; private static Action _onReady; private static int _nextId = 1; private static readonly Dictionary> Pending = new Dictionary>(); #if UNITY_WEBGL && !UNITY_EDITOR [DllImport("__Internal")] private static extern void SaqaJsInit(string goName); [DllImport("__Internal")] private static extern void SaqaJsCall(int id, string method, string argsJson); #else private static void SaqaJsInit(string goName) { } private static void SaqaJsCall(int id, string method, string argsJson) { } #endif /// /// SDK-ні іске қосады. Ойын басталғанда бір рет шақырыңыз. /// Callback SDK дайын болғанда (немесе қолжетімсіз екені анықталғанда) шақырылады. /// public static void Init(Action onReady = null) { if (_instance != null) { onReady?.Invoke(IsAvailable); return; } _onReady = onReady; var go = new GameObject(GameObjectName); _instance = go.AddComponent(); DontDestroyOnLoad(go); #if UNITY_WEBGL && !UNITY_EDITOR SaqaJsInit(GameObjectName); #else // Редакторда SDK жоқ — ойын қонақ режимінде жұмыс істейді IsReady = true; IsAvailable = false; onReady?.Invoke(false); #endif } // ── Ойыншы деректері ───────────────────────────────────────────────── /// Сақталған деректерді оқиды. Нәтиже — JSON нысаны. public static void GetData(Action callback, params string[] keys) => Call("getData", "{\"keys\":" + JsonArray(keys) + "}", callback); /// /// Деректерді сақтайды. — JSON нысаны, /// мысалы: {"level":7,"coins":250} /// public static void SetData(string json, Action callback = null, bool merge = true) => Call("setData", "{\"data\":" + Obj(json) + ",\"merge\":" + Bool(merge) + "}", callback); public static void GetStats(Action callback, params string[] keys) => Call("getStats", "{\"keys\":" + JsonArray(keys) + "}", callback); public static void SetStats(string json, Action callback = null) => Call("setStats", "{\"stats\":" + Obj(json) + "}", callback); /// Көрсеткіштерді арттырады, мысалы: {"wins":1,"deaths":2} public static void IncrementStats(string json, Action callback = null) => Call("incrementStats", "{\"increments\":" + Obj(json) + "}", callback); // ── Лидербордтар ───────────────────────────────────────────────────── /// Нәтижені лидербордқа жібереді. Сервер тек жақсарған нәтижені жазады. public static void SetScore(string leaderboard, long score, string extraData = null, Action callback = null) { var extra = string.IsNullOrEmpty(extraData) ? "null" : Str(extraData); Call("setScore", "{\"name\":" + Str(leaderboard) + ",\"score\":" + score + ",\"extraData\":" + extra + "}", callback); } /// Лидербордтың үздіктерін алады. Нәтижені SaqaLeaderboard-қа айналдырыңыз. public static void GetEntries(string leaderboard, Action callback, int quantityTop = 20, bool includeUser = true) => Call("getEntries", "{\"name\":" + Str(leaderboard) + ",\"quantityTop\":" + quantityTop + ",\"includeUser\":" + Bool(includeUser) + "}", callback); // ── Жарнама ────────────────────────────────────────────────────────── /// Толықэкранды жарнама. Ойынды алдын ала тоқтатыңыз. public static void ShowFullscreenAd(Action callback = null) => Call("fullscreenAd", "{}", callback); /// Сыйақылы жарнама. rewarded=true болса ғана сыйлық беріңіз. public static void ShowRewardedAd(Action callback = null) => Call("rewardedAd", "{}", callback); // ── Авторизация және экран ─────────────────────────────────────────── public static void OpenAuthDialog(Action callback = null) => Call("openAuthDialog", "{}", callback); public static void RequestFullscreen(Action callback = null) => Call("requestFullscreen", "{}", callback); public static void ExitFullscreen(Action callback = null) => Call("exitFullscreen", "{}", callback); // ── Өмірлік цикл ───────────────────────────────────────────────────── /// Ойын жүктеліп, ойнауға дайын болғанда шақырыңыз. public static void GameReady() => Call("gameReady", "{}", null); /// Нақты ойын процесі басталғанда. public static void GameplayStart() => Call("gameplayStart", "{}", null); /// Ойын процесі тоқтағанда (пауза, мәзір, ойын соңы). public static void GameplayStop() => Call("gameplayStop", "{}", null); // ── Ішкі механика ──────────────────────────────────────────────────── private static void Call(string method, string argsJson, Action callback) { if (_instance == null) { callback?.Invoke(new SaqaResult { Ok = false, Error = "SaqaSDK.Init() шақырылмаған", Code = "NOT_INITIALIZED" }); return; } if (!IsAvailable) { callback?.Invoke(new SaqaResult { Ok = false, Error = "SDK қолжетімсіз", Code = "SDK_UNAVAILABLE" }); return; } var id = _nextId++; if (callback != null) Pending[id] = callback; SaqaJsCall(id, method, argsJson); } [Serializable] private class ReadyMessage { public bool available; public bool authorized; public string playerId; public string playerName; public string avatarUrl; public string lang; public string device; } [Serializable] private class ResponseMessage { public int id; public bool ok; public string payload; public string error; public string code; } // JavaScript жағынан SendMessage арқылы шақырылады private void HandleReady(string json) { var msg = JsonUtility.FromJson(json); IsReady = true; IsAvailable = msg.available; IsAuthorized = msg.authorized; PlayerId = msg.playerId ?? ""; PlayerName = msg.playerName ?? ""; AvatarUrl = msg.avatarUrl ?? ""; if (!string.IsNullOrEmpty(msg.lang)) Language = msg.lang; if (!string.IsNullOrEmpty(msg.device)) DeviceType = msg.device; var cb = _onReady; _onReady = null; cb?.Invoke(IsAvailable); } private void HandleResponse(string json) { var msg = JsonUtility.FromJson(json); if (!Pending.TryGetValue(msg.id, out var callback)) return; Pending.Remove(msg.id); callback?.Invoke(new SaqaResult { Ok = msg.ok, Json = msg.payload, Error = msg.error, Code = msg.code }); } // ── Кішкентай JSON көмекшілері ─────────────────────────────────────── // Unity-дің JsonUtility нысан құрастыруға ыңғайсыз, сондықтан // қарапайым жолмен жинаймыз. private static string Bool(bool v) => v ? "true" : "false"; private static string Obj(string json) => string.IsNullOrWhiteSpace(json) ? "{}" : json; private static string Str(string value) { if (value == null) return "null"; var sb = new System.Text.StringBuilder("\""); foreach (var c in value) { switch (c) { case '"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; default: if (c < 32) sb.Append("\\u").Append(((int)c).ToString("x4")); else sb.Append(c); break; } } return sb.Append('"').ToString(); } private static string JsonArray(string[] items) { if (items == null || items.Length == 0) return "[]"; var parts = new string[items.Length]; for (var i = 0; i < items.Length; i++) parts[i] = Str(items[i]); return "[" + string.Join(",", parts) + "]"; } } }