Initial Brisk Unity SDK project

This commit is contained in:
2026-04-10 22:04:51 +08:00
commit 47f9a8bafa
171 changed files with 11091 additions and 0 deletions

8
Assets/BriskSdk.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1e20cdfd5d9d1a44fb063fb9c39fd214
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0a61600db090304ba41001e462674ff
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e05fdcaece3a1344ca3e98889cba8fc7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public sealed class BriskAnnouncementsModule
: BriskModuleBase
{
public async Task<IReadOnlyList<BriskAnnouncementItem>> GetListAsync()
{
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync("/announcements", null, true);
return (IReadOnlyList<BriskAnnouncementItem>)BriskModelMapper.ToAnnouncementItems(data);
});
}
public async Task MarkReadAsync(long id)
{
RequirePositive(id, nameof(id), "Announcement id must be greater than 0.");
await ExecuteAsync(async context =>
{
await context.HttpClient.PostJsonRawAsync($"/announcements/{id}/read", new Dictionary<string, object>(), true);
});
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76c65634a3440dd46b2ac6d5b88dcf1d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d88c956c19b23c8499e142fff707b76c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Networking;
public sealed class BriskArchiveModule
: BriskModuleBase
{
public async Task<IReadOnlyList<BriskArchiveSlot>> GetSlotsAsync()
{
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync("/archives/slots", null, true);
return (IReadOnlyList<BriskArchiveSlot>)BriskModelMapper.ToArchiveSlots(data);
});
}
public async Task<BriskArchiveMeta> GetMetaAsync(int slotNo)
{
ValidateSlotNo(slotNo);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetDataAsync($"/archives/slot/{slotNo}/meta", null, true);
return BriskModelMapper.ToArchiveMeta(data);
});
}
public async Task<BriskArchiveUploadResult> UploadAsync(int slotNo, byte[] bytes, int? baseVersion = null, string checksum = null)
{
ValidateSlotNo(slotNo);
if (bytes == null || bytes.Length == 0)
{
throw new ArgumentException("bytes is required.", nameof(bytes));
}
return await ExecuteAsync(async context =>
{
var finalChecksum = string.IsNullOrWhiteSpace(checksum) ? ComputeSha256(bytes) : checksum;
var sections = new List<IMultipartFormSection>
{
new MultipartFormDataSection("base_version", (baseVersion ?? 0).ToString()),
new MultipartFormDataSection("checksum", finalChecksum),
new MultipartFormFileSection("file", bytes, "archive.bin", "application/octet-stream")
};
var data = await context.HttpClient.PostMultipartAsync($"/archives/slot/{slotNo}/upload", sections, true);
return BriskModelMapper.ToArchiveUploadResult(data);
});
}
public async Task<BriskArchiveDownloadResult> DownloadAsync(int slotNo)
{
ValidateSlotNo(slotNo);
return await ExecuteAsync(async context =>
{
var response = await context.HttpClient.GetBytesAsync($"/archives/slot/{slotNo}/download", null, true);
return new BriskArchiveDownloadResult
{
Bytes = response.Bytes,
Version = ReadHeaderInt(response.Headers, "X-Archive-Version"),
Checksum = ReadHeader(response.Headers, "X-Archive-Checksum")
};
});
}
private static void ValidateSlotNo(int slotNo)
{
RequirePositive(slotNo, nameof(slotNo), "slotNo must be greater than 0.");
}
private static string ComputeSha256(byte[] bytes)
{
using (var sha = SHA256.Create())
{
var hash = sha.ComputeHash(bytes);
var builder = new StringBuilder(hash.Length * 2 + 7);
builder.Append("sha256:");
for (var i = 0; i < hash.Length; i++)
{
builder.Append(hash[i].ToString("x2"));
}
return builder.ToString();
}
}
private static int ReadHeaderInt(Dictionary<string, string> headers, string key)
{
var value = ReadHeader(headers, key);
return int.TryParse(value, out var result) ? result : 0;
}
private static string ReadHeader(Dictionary<string, string> headers, string key)
{
if (headers == null || string.IsNullOrWhiteSpace(key))
{
return null;
}
foreach (var pair in headers)
{
if (string.Equals(pair.Key, key, StringComparison.OrdinalIgnoreCase))
{
return pair.Value;
}
}
return null;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b0136b0b2b050f4ab2043d7e6bfb4ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3a26d0e58a94c264198010eae586c7b3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public sealed class BriskAuthModule
: BriskModuleBase
{
public async Task<BriskLoginResult> LoginWithUserIdAsync(string loginProvider, string loginUserId, BriskProfile profile = null)
{
RequireNotEmpty(loginProvider, nameof(loginProvider));
RequireNotEmpty(loginUserId, nameof(loginUserId));
return await LoginInternalAsync(CreateLoginBody(loginProvider, profile, loginUserId, null), loginProvider, loginUserId);
}
public async Task<BriskLoginResult> LoginWithCodeAsync(string loginProvider, string code, BriskProfile profile = null)
{
RequireNotEmpty(loginProvider, nameof(loginProvider));
RequireNotEmpty(code, nameof(code));
return await LoginInternalAsync(CreateLoginBody(loginProvider, profile, null, code), loginProvider, null);
}
public async Task LogoutAsync()
{
var context = GetContext();
try
{
if (context.Session.HasAccessToken)
{
await context.HttpClient.PostJsonAsync("/auth/logout", new Dictionary<string, object>(), true);
}
}
catch (BriskAuthExpiredException)
{
// Local logout should still succeed even if the remote token has already expired.
}
catch (BriskBlockingException exception)
{
Brisk.NotifyBlockingError(exception);
throw;
}
finally
{
context.Session.Clear();
await context.TokenStore.ClearAsync();
Brisk.NotifyLoggedOut();
}
}
private async Task<BriskLoginResult> LoginInternalAsync(Dictionary<string, object> body, string requestedLoginProvider, string requestedLoginUserId)
{
return await ExecutePublicAsync(async context =>
{
var data = await context.HttpClient.PostJsonAsync("/auth/login/exchange", body, false);
var result = BriskModelMapper.ToLoginResult(data);
if (string.IsNullOrWhiteSpace(result.LoginProvider))
{
result.LoginProvider = requestedLoginProvider;
}
if (string.IsNullOrWhiteSpace(result.LoginUserId))
{
result.LoginUserId = requestedLoginUserId;
}
await UpdateSessionAsync(context, result);
Brisk.NotifyLoggedIn();
return result;
});
}
private Dictionary<string, object> CreateLoginBody(string loginProvider, BriskProfile profile, string loginUserId, string code)
{
var context = GetContext();
var body = new Dictionary<string, object>
{
{ "game_key", context.Options.GameKey },
{ "login_provider", loginProvider }
};
AddIfNotEmpty(body, "login_user_id", loginUserId);
AddIfNotEmpty(body, "code", code);
AddIfNotEmpty(body, "device_id", context.Options.DeviceId);
AddIfNotEmpty(body, "client_version", context.Options.ClientVersion);
if (profile != null)
{
AddIfNotEmpty(body, "nickname", profile.Nickname);
AddIfNotEmpty(body, "avatar_url", profile.AvatarUrl);
if (profile.ProfileJson != null)
{
body["profile_json"] = profile.ProfileJson;
}
}
return body;
}
private static void AddIfNotEmpty(Dictionary<string, object> body, string key, string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
body[key] = value;
}
}
private async Task UpdateSessionAsync(BriskContext context, BriskLoginResult result)
{
var expiresAt = result.ExpiresIn > 0
? DateTimeOffset.UtcNow.AddSeconds(result.ExpiresIn)
: (DateTimeOffset?)null;
context.Session.Update(
result.AccessToken,
expiresAt,
result.PlayerId,
result.ProjectAccountId,
result.LoginProvider,
result.LoginUserId);
var storedSession = new BriskStoredSession
{
AccessToken = result.AccessToken,
ExpiresAt = expiresAt,
PlayerId = result.PlayerId,
ProjectAccountId = result.ProjectAccountId,
LoginProvider = result.LoginProvider,
LoginUserId = result.LoginUserId
};
await context.TokenStore.SaveAsync(storedSession);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03f5ec833a8862a43bea6a0b7f7788d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
{
"name": "FoldCC.CCFramework.BriskGameServer.Runtime",
"rootNamespace": "FoldCC.CCFramework.BriskGameServer",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f8ab9462792dad445acc3b318dfa2372
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8b88bc9f438e4d64089acbb0ac667c03
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public sealed class BriskConfigModule
: BriskModuleBase
{
public async Task<BriskConfigCurrent> GetCurrentAsync()
{
return await ExecutePublicAsync(async context =>
{
var data = await context.HttpClient.GetDataAsync("/config/current", CreateQuery(context), false);
return BriskModelMapper.ToConfigCurrent(data);
});
}
public Task<BriskConfigCurrent> RefreshAsync()
{
return GetCurrentAsync();
}
private static Dictionary<string, string> CreateQuery(BriskContext context)
{
return new Dictionary<string, string>
{
{ "game_key", context.Options.GameKey },
{ "client_version", context.Options.ClientVersion }
};
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f50e09c77d7e3f24a9b879d4e0f21c76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5b8196730f24c0438900010f497476f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public static class Brisk
{
private static BriskContext s_context;
static Brisk()
{
Auth = new BriskAuthModule();
Player = new BriskPlayerModule();
Config = new BriskConfigModule();
Announcements = new BriskAnnouncementsModule();
Leaderboard = new BriskLeaderboardModule();
Archive = new BriskArchiveModule();
Space = new BriskSpaceModule();
}
public static event Action OnInitialized;
public static event Action OnLoggedIn;
public static event Action OnLoggedOut;
public static event Action<BriskBlockingException> OnBlockingError;
public static event Action<BriskAuthExpiredException> OnAuthExpired;
public static BriskAuthModule Auth { get; }
public static BriskPlayerModule Player { get; }
public static BriskConfigModule Config { get; }
public static BriskAnnouncementsModule Announcements { get; }
public static BriskLeaderboardModule Leaderboard { get; }
public static BriskArchiveModule Archive { get; }
public static BriskSpaceModule Space { get; }
public static bool IsInitialized => s_context != null;
public static bool IsLoggedIn => s_context != null && s_context.Session.HasAccessToken;
public static string AccessToken => s_context != null ? s_context.Session.AccessToken : null;
public static string PlayerId => s_context != null ? s_context.Session.PlayerId : null;
public static BriskIdentity Identity => s_context != null ? s_context.Session.Identity : null;
public static BriskOptions Options => s_context != null ? s_context.Options : null;
public static BriskBootstrapResult Bootstrap => s_context != null ? s_context.Bootstrap : null;
public static async Task InitializeAsync(BriskOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.Validate();
var context = new BriskContext(options);
s_context = context;
try
{
await BootstrapAsync(context);
await RestoreSessionAsync(context);
OnInitialized?.Invoke();
}
catch
{
if (context.Bootstrap == null)
{
s_context = null;
}
throw;
}
}
public static void Shutdown()
{
s_context = null;
}
public static void SetErrorPresenter(IBriskErrorPresenter presenter)
{
GetRequiredContext().ErrorPresenter = presenter ?? BriskDefaultErrorPresenter.Instance;
}
public static void SetExitHandler(Action exitHandler)
{
GetRequiredContext().ExitHandler = exitHandler;
}
internal static BriskContext GetRequiredContext()
{
if (s_context == null)
{
throw new BriskNotInitializedException();
}
return s_context;
}
internal static void NotifyLoggedIn()
{
OnLoggedIn?.Invoke();
}
internal static void NotifyLoggedOut()
{
OnLoggedOut?.Invoke();
}
internal static void NotifyBlockingError(BriskBlockingException exception)
{
s_context?.ErrorPresenter?.ShowBlockingError(exception);
OnBlockingError?.Invoke(exception);
}
internal static void NotifyAuthExpired(BriskAuthExpiredException exception)
{
s_context?.ErrorPresenter?.ShowAuthExpired(exception);
OnAuthExpired?.Invoke(exception);
}
private static async Task BootstrapAsync(BriskContext context)
{
var query = new Dictionary<string, string>
{
{ "game_key", context.Options.GameKey },
{ "client_version", context.Options.ClientVersion },
{ "device_id", context.Options.DeviceId }
};
var bootstrapData = await context.HttpClient.GetDataAsync("/client/bootstrap", query, false);
var bootstrap = BriskModelMapper.ToBootstrapResult(bootstrapData);
context.Bootstrap = bootstrap;
if (bootstrap.MaintenanceMode)
{
var message = string.IsNullOrWhiteSpace(bootstrap.MaintenanceMessage)
? "Server is under maintenance."
: bootstrap.MaintenanceMessage;
var exception = new BriskMaintenanceException(message);
NotifyBlockingError(exception);
throw exception;
}
if (BriskVersionComparer.IsLessThan(context.Options.ClientVersion, bootstrap.MinClientVersion))
{
var exception = new BriskClientUpdateRequiredException("Client version is lower than the minimum supported version.");
NotifyBlockingError(exception);
throw exception;
}
}
private static async Task RestoreSessionAsync(BriskContext context)
{
var storedSession = await context.TokenStore.LoadAsync();
if (storedSession == null || string.IsNullOrWhiteSpace(storedSession.AccessToken))
{
return;
}
context.Session.Update(
storedSession.AccessToken,
storedSession.ExpiresAt,
storedSession.PlayerId,
storedSession.ProjectAccountId,
storedSession.LoginProvider,
storedSession.LoginUserId);
if (!context.Options.ValidateSessionOnInitialize)
{
return;
}
try
{
var meData = await context.HttpClient.GetDataAsync("/player/me", null, true);
var me = BriskModelMapper.ToPlayerMe(meData);
context.Session.Update(
storedSession.AccessToken,
storedSession.ExpiresAt,
me.PlayerId,
me.ProjectAccountId,
me.LoginProvider,
me.LoginUserId);
}
catch (BriskAuthExpiredException exception)
{
context.Session.Clear();
await context.TokenStore.ClearAsync();
NotifyAuthExpired(exception);
}
catch (BriskBlockingException exception)
{
NotifyBlockingError(exception);
throw;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f175f0540648ffe43a061deb0d75159d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
using System.Collections.Generic;
public sealed class BriskBinaryResponse
{
public byte[] Bytes;
public Dictionary<string, string> Headers;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 952c9679a69c1a740a55492f39acd66e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using System;
public sealed class BriskContext
{
public BriskContext(BriskOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
Session = new BriskSession();
ErrorPresenter = options.ErrorPresenter ?? BriskDefaultErrorPresenter.Instance;
ExitHandler = options.ExitHandler;
TokenStore = options.TokenStore ?? new BriskPlayerPrefsTokenStore();
HttpClient = new BriskHttpClient(this);
}
public BriskOptions Options { get; }
public BriskSession Session { get; }
public BriskBootstrapResult Bootstrap { get; set; }
public IBriskTokenStore TokenStore { get; }
public BriskHttpClient HttpClient { get; }
public IBriskErrorPresenter ErrorPresenter { get; set; }
public Action ExitHandler { get; set; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca7516fa2069a024fa392f1c143a5a3d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using System;
using UnityEngine;
internal sealed class BriskDefaultErrorDialog : MonoBehaviour
{
private const string HostName = "BriskDefaultErrorDialog";
private static BriskDefaultErrorDialog s_instance;
private string _title;
private string _message;
private string _confirmText;
private Action _onConfirm;
private bool _visible;
public static void Show(string title, string message, string confirmText, Action onConfirm = null)
{
var host = EnsureInstance();
host._title = string.IsNullOrWhiteSpace(title) ? "Brisk" : title;
host._message = string.IsNullOrWhiteSpace(message) ? "Unknown error." : message;
host._confirmText = string.IsNullOrWhiteSpace(confirmText) ? "OK" : confirmText;
host._onConfirm = onConfirm;
host._visible = true;
}
private static BriskDefaultErrorDialog EnsureInstance()
{
if (s_instance != null)
{
return s_instance;
}
var existing = GameObject.Find(HostName);
if (existing != null)
{
s_instance = existing.GetComponent<BriskDefaultErrorDialog>();
if (s_instance != null)
{
return s_instance;
}
}
var gameObject = new GameObject(HostName);
DontDestroyOnLoad(gameObject);
s_instance = gameObject.AddComponent<BriskDefaultErrorDialog>();
return s_instance;
}
private void OnGUI()
{
if (!_visible)
{
return;
}
var overlayRect = new Rect(0, 0, Screen.width, Screen.height);
var previousColor = GUI.color;
GUI.color = new Color(0f, 0f, 0f, 0.65f);
GUI.Box(overlayRect, GUIContent.none);
GUI.color = previousColor;
var width = Mathf.Min(460f, Screen.width - 40f);
var height = 220f;
var dialogRect = new Rect(
(Screen.width - width) * 0.5f,
(Screen.height - height) * 0.5f,
width,
height);
GUILayout.BeginArea(dialogRect, GUI.skin.window);
GUILayout.Space(8f);
GUILayout.Label(_title, GUI.skin.label);
GUILayout.Space(12f);
GUILayout.Label(_message, GUI.skin.label);
GUILayout.FlexibleSpace();
if (GUILayout.Button(_confirmText, GUILayout.Height(36f)))
{
var callback = _onConfirm;
_visible = false;
_onConfirm = null;
callback?.Invoke();
}
GUILayout.Space(8f);
GUILayout.EndArea();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95a9fe204ba119d4b8d7a296a7bd429b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System;
using UnityEngine;
public sealed class BriskDefaultErrorPresenter : IBriskErrorPresenter
{
private static readonly BriskDefaultErrorPresenter InstanceValue = new BriskDefaultErrorPresenter();
private BriskDefaultErrorPresenter()
{
}
public static BriskDefaultErrorPresenter Instance => InstanceValue;
public void ShowBlockingError(BriskBlockingException exception)
{
var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message)
? exception.Message
: "A blocking error occurred.";
BriskDefaultErrorDialog.Show(
title: GetBlockingTitle(exception),
message: message,
confirmText: "OK",
onConfirm: () =>
{
var exitHandler = Brisk.IsInitialized ? Brisk.Options?.ExitHandler : null;
exitHandler?.Invoke();
});
Debug.LogError("[Brisk] Blocking error: " + message);
}
public void ShowAuthExpired(BriskAuthExpiredException exception)
{
var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message)
? exception.Message
: "Login expired. Please sign in again.";
BriskDefaultErrorDialog.Show(
title: "Login Expired",
message: message,
confirmText: "OK");
Debug.LogWarning("[Brisk] Auth expired: " + message);
}
private static string GetBlockingTitle(BriskBlockingException exception)
{
if (exception is BriskMaintenanceException)
{
return "Maintenance";
}
if (exception is BriskAccountBannedException)
{
return "Account Restricted";
}
if (exception is BriskClientUpdateRequiredException)
{
return "Update Required";
}
return "Service Unavailable";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 681035ac4d9bc8147ad8ae630b131185
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
public static class BriskErrorClassifier
{
public static BriskException Classify(int httpStatus, int code, string message)
{
switch (code)
{
case 10001:
case 10002:
case 10023:
return new BriskAuthExpiredException(message, code);
case 10003:
case 10025:
return new BriskMaintenanceException(message, code);
case 10004:
case 10026:
return new BriskAccountBannedException(message, code);
default:
return new BriskApiException(httpStatus, code, message);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41bc55282a88d6d4aaee810c79f56415
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
public sealed class BriskHttpClient
{
private readonly BriskContext _context;
public BriskHttpClient(BriskContext context)
{
_context = context;
}
public Task<Dictionary<string, object>> GetDataAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendForDictionaryAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth);
}
public Task<object> GetRawDataAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth);
}
public Task<Dictionary<string, object>> PostJsonAsync(string path, object body, bool auth = false)
{
return SendForDictionaryAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth);
}
public Task<object> PostJsonRawAsync(string path, object body, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth);
}
public Task<object> PostJsonRawAsync(string path, object body, bool auth, Dictionary<string, string> query)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, query, body, auth);
}
public Task<object> SendPutJsonRawAsync(string path, object body, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPUT, path, null, body, auth);
}
public Task<object> SendDeleteJsonRawAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbDELETE, path, query, null, auth);
}
public async Task<Dictionary<string, object>> PostMultipartAsync(string path, List<IMultipartFormSection> formSections, bool auth = false)
{
using (var request = UnityWebRequest.Post(BuildUrl(path, null), formSections))
{
if (auth)
{
AddAuthorizationHeader(request);
}
request.SetRequestHeader("Accept", "application/json");
await SendRequestAsync(request);
return ParseEnvelope(request) as Dictionary<string, object> ?? new Dictionary<string, object>();
}
}
public async Task<BriskBinaryResponse> GetBytesAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
using (var request = BuildRequest(UnityWebRequest.kHttpVerbGET, path, query, null, auth))
{
await SendRequestAsync(request);
EnsureSuccessOrThrow(request);
return new BriskBinaryResponse
{
Bytes = request.downloadHandler != null ? request.downloadHandler.data : Array.Empty<byte>(),
Headers = request.GetResponseHeaders() ?? new Dictionary<string, string>()
};
}
}
private async Task<Dictionary<string, object>> SendForDictionaryAsync(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
var data = await SendRawJsonAsync(method, path, query, body, auth);
return data as Dictionary<string, object> ?? new Dictionary<string, object>();
}
private async Task<object> SendRawJsonAsync(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
using (var request = BuildRequest(method, path, query, body, auth))
{
await SendRequestAsync(request);
return ParseEnvelope(request);
}
}
private UnityWebRequest BuildRequest(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
var url = BuildUrl(path, query);
var request = new UnityWebRequest(url, method)
{
downloadHandler = new DownloadHandlerBuffer()
};
request.SetRequestHeader("Accept", "application/json");
if (body != null)
{
var json = BriskJson.Serialize(body);
var bytes = Encoding.UTF8.GetBytes(json);
request.uploadHandler = new UploadHandlerRaw(bytes);
request.SetRequestHeader("Content-Type", "application/json");
}
if (auth)
{
AddAuthorizationHeader(request);
}
return request;
}
private async Task SendRequestAsync(UnityWebRequest request)
{
try
{
var operation = request.SendWebRequest();
var taskSource = new TaskCompletionSource<bool>();
operation.completed += _ => taskSource.TrySetResult(true);
await taskSource.Task;
}
catch (Exception exception)
{
throw new BriskNetworkException("Failed to send request.", exception);
}
if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BriskNetworkException(request.error ?? "Network error.");
}
}
private object ParseEnvelope(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
var responseText = request.downloadHandler != null ? request.downloadHandler.text : null;
if (string.IsNullOrWhiteSpace(responseText))
{
if (httpStatus >= 200 && httpStatus < 300)
{
return new Dictionary<string, object>();
}
throw new BriskHttpException(httpStatus, request.error ?? "HTTP error.");
}
var payload = BriskJson.Deserialize(responseText) as Dictionary<string, object>;
if (payload == null)
{
throw new BriskHttpException(httpStatus, "Invalid JSON response.");
}
var code = BriskValueReader.GetInt(payload, "code");
var message = BriskValueReader.GetString(payload, "message") ?? request.error ?? "Request failed.";
if (code != 0 || httpStatus < 200 || httpStatus >= 300)
{
throw BriskErrorClassifier.Classify(httpStatus, code, message);
}
if (BriskValueReader.TryGetValue(payload, "data", out var dataValue))
{
return dataValue;
}
return new Dictionary<string, object>();
}
private void EnsureSuccessOrThrow(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
if (httpStatus >= 200 && httpStatus < 300)
{
return;
}
throw CreateExceptionFromResponse(request);
}
private BriskException CreateExceptionFromResponse(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
var responseText = request.downloadHandler != null ? request.downloadHandler.text : null;
if (!string.IsNullOrWhiteSpace(responseText))
{
var payload = BriskJson.Deserialize(responseText) as Dictionary<string, object>;
if (payload != null)
{
var code = BriskValueReader.GetInt(payload, "code");
var message = BriskValueReader.GetString(payload, "message") ?? request.error ?? "Request failed.";
return BriskErrorClassifier.Classify(httpStatus, code, message);
}
}
return new BriskHttpException(httpStatus, request.error ?? "HTTP error.");
}
private void AddAuthorizationHeader(UnityWebRequest request)
{
var token = _context.Session.AccessToken;
if (string.IsNullOrWhiteSpace(token))
{
throw new BriskAuthExpiredException("Missing access token.");
}
request.SetRequestHeader("Authorization", "Bearer " + token);
}
private string BuildUrl(string path, Dictionary<string, string> query)
{
var trimmedPath = path.StartsWith("/") ? path : "/" + path;
var url = _context.Options.BaseUrl + trimmedPath;
if (query == null || query.Count == 0)
{
return url;
}
var builder = new StringBuilder(url);
var first = true;
foreach (var pair in query)
{
if (string.IsNullOrWhiteSpace(pair.Value))
{
continue;
}
if (first)
{
builder.Append('?');
}
else
{
builder.Append('&');
}
builder.Append(UnityWebRequest.EscapeURL(pair.Key));
builder.Append('=');
builder.Append(UnityWebRequest.EscapeURL(pair.Value));
first = false;
}
return builder.ToString();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a825891fadac404b97c755e6d60a3f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,443 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
internal static class BriskJson
{
public static object Deserialize(string json)
{
if (string.IsNullOrWhiteSpace(json))
{
return null;
}
return Parser.Parse(json);
}
public static string Serialize(object value)
{
return Serializer.Serialize(value);
}
private sealed class Parser : IDisposable
{
private readonly StringReader _reader;
private Parser(string json)
{
_reader = new StringReader(json);
}
public static object Parse(string json)
{
using (var parser = new Parser(json))
{
return parser.ParseValue();
}
}
public void Dispose()
{
_reader.Dispose();
}
private object ParseValue()
{
EatWhitespace();
if (_reader.Peek() == -1)
{
return null;
}
switch (PeekChar)
{
case '{':
return ParseObject();
case '[':
return ParseArray();
case '"':
return ParseString();
case 't':
return ParseTrue();
case 'f':
return ParseFalse();
case 'n':
return ParseNull();
default:
return ParseNumber();
}
}
private Dictionary<string, object> ParseObject()
{
var table = new Dictionary<string, object>();
ReadChar();
while (true)
{
EatWhitespace();
if (PeekChar == '}')
{
ReadChar();
return table;
}
var key = ParseString();
EatWhitespace();
ReadChar();
var value = ParseValue();
table[key] = value;
EatWhitespace();
if (PeekChar == ',')
{
ReadChar();
continue;
}
if (PeekChar == '}')
{
ReadChar();
return table;
}
}
}
private List<object> ParseArray()
{
var array = new List<object>();
ReadChar();
while (true)
{
EatWhitespace();
if (PeekChar == ']')
{
ReadChar();
return array;
}
array.Add(ParseValue());
EatWhitespace();
if (PeekChar == ',')
{
ReadChar();
continue;
}
if (PeekChar == ']')
{
ReadChar();
return array;
}
}
}
private string ParseString()
{
var builder = new StringBuilder();
ReadChar();
while (true)
{
if (_reader.Peek() == -1)
{
break;
}
var ch = ReadChar();
if (ch == '"')
{
break;
}
if (ch == '\\')
{
if (_reader.Peek() == -1)
{
break;
}
ch = ReadChar();
switch (ch)
{
case '"':
case '\\':
case '/':
builder.Append(ch);
break;
case 'b':
builder.Append('\b');
break;
case 'f':
builder.Append('\f');
break;
case 'n':
builder.Append('\n');
break;
case 'r':
builder.Append('\r');
break;
case 't':
builder.Append('\t');
break;
case 'u':
builder.Append((char)Convert.ToInt32(ReadChars(4), 16));
break;
}
continue;
}
builder.Append(ch);
}
return builder.ToString();
}
private object ParseNumber()
{
var token = NextToken();
if (token.IndexOf('.') >= 0 || token.IndexOf('e') >= 0 || token.IndexOf('E') >= 0)
{
double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue);
return doubleValue;
}
long.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue);
return longValue;
}
private bool ParseTrue()
{
ReadChars(4);
return true;
}
private bool ParseFalse()
{
ReadChars(5);
return false;
}
private object ParseNull()
{
ReadChars(4);
return null;
}
private string NextToken()
{
var builder = new StringBuilder();
while (_reader.Peek() != -1)
{
var ch = PeekChar;
if (char.IsWhiteSpace(ch) || ch == ',' || ch == ']' || ch == '}')
{
break;
}
builder.Append(ReadChar());
}
return builder.ToString();
}
private void EatWhitespace()
{
while (_reader.Peek() != -1 && char.IsWhiteSpace(PeekChar))
{
_reader.Read();
}
}
private string ReadChars(int count)
{
var buffer = new char[count];
_reader.Read(buffer, 0, count);
return new string(buffer);
}
private char PeekChar => Convert.ToChar(_reader.Peek());
private char ReadChar() => Convert.ToChar(_reader.Read());
}
private static class Serializer
{
public static string Serialize(object value)
{
var builder = new StringBuilder();
SerializeValue(value, builder);
return builder.ToString();
}
private static void SerializeValue(object value, StringBuilder builder)
{
if (value == null)
{
builder.Append("null");
return;
}
if (value is string str)
{
SerializeString(str, builder);
return;
}
if (value is bool boolean)
{
builder.Append(boolean ? "true" : "false");
return;
}
if (value is IDictionary dictionary)
{
SerializeObject(dictionary, builder);
return;
}
if (value is IEnumerable enumerable && !(value is string))
{
SerializeArray(enumerable, builder);
return;
}
if (IsNumeric(value))
{
builder.Append(Convert.ToString(value, CultureInfo.InvariantCulture));
return;
}
SerializeObject(ToReflectionDictionary(value), builder);
}
private static void SerializeObject(IDictionary dictionary, StringBuilder builder)
{
var first = true;
builder.Append('{');
foreach (DictionaryEntry entry in dictionary)
{
if (!first)
{
builder.Append(',');
}
SerializeString(Convert.ToString(entry.Key, CultureInfo.InvariantCulture), builder);
builder.Append(':');
SerializeValue(entry.Value, builder);
first = false;
}
builder.Append('}');
}
private static void SerializeArray(IEnumerable array, StringBuilder builder)
{
var first = true;
builder.Append('[');
foreach (var item in array)
{
if (!first)
{
builder.Append(',');
}
SerializeValue(item, builder);
first = false;
}
builder.Append(']');
}
private static void SerializeString(string value, StringBuilder builder)
{
builder.Append('"');
foreach (var ch in value)
{
switch (ch)
{
case '"':
builder.Append("\\\"");
break;
case '\\':
builder.Append("\\\\");
break;
case '\b':
builder.Append("\\b");
break;
case '\f':
builder.Append("\\f");
break;
case '\n':
builder.Append("\\n");
break;
case '\r':
builder.Append("\\r");
break;
case '\t':
builder.Append("\\t");
break;
default:
if (ch < 32)
{
builder.Append("\\u");
builder.Append(((int)ch).ToString("x4"));
}
else
{
builder.Append(ch);
}
break;
}
}
builder.Append('"');
}
private static bool IsNumeric(object value)
{
switch (Type.GetTypeCode(value.GetType()))
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Single:
return true;
default:
return false;
}
}
private static IDictionary ToReflectionDictionary(object value)
{
var result = new Dictionary<string, object>();
var type = value.GetType();
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
{
result[field.Name] = field.GetValue(value);
}
foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (!property.CanRead || property.GetIndexParameters().Length > 0)
{
continue;
}
result[property.Name] = property.GetValue(value, null);
}
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c594424856e2e74abbce01c4ad4532d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,309 @@
using System.Collections.Generic;
using System.Linq;
internal static class BriskModelMapper
{
public static BriskLoginResult ToLoginResult(Dictionary<string, object> data)
{
var profileData = BriskValueReader.GetDictionary(data, "profile");
return new BriskLoginResult
{
AccessToken = BriskValueReader.GetString(data, "access_token"),
ExpiresIn = BriskValueReader.GetInt(data, "expires_in"),
PlayerId = BriskValueReader.GetString(data, "player_id"),
ProjectAccountId = BriskValueReader.GetString(data, "project_account_id"),
LoginProvider = BriskValueReader.GetString(profileData, "login_provider"),
LoginUserId = BriskValueReader.GetString(profileData, "login_user_id"),
IsNewPlayer = BriskValueReader.GetBool(data, "is_new_player"),
Profile = ToProfile(profileData)
};
}
public static BriskBootstrapResult ToBootstrapResult(Dictionary<string, object> data)
{
return new BriskBootstrapResult
{
ProjectName = BriskValueReader.GetString(data, "project_name"),
ServerTime = BriskValueReader.GetString(data, "server_time"),
MaintenanceMode = BriskValueReader.GetBool(data, "maintenance_mode"),
MaintenanceMessage = BriskValueReader.GetString(data, "maintenance_message"),
MinClientVersion = BriskValueReader.GetString(data, "min_client_version"),
FeatureFlags = BriskValueReader.GetDictionary(data, "feature_flags") ?? new Dictionary<string, object>(),
DynamicConfig = BriskValueReader.GetDictionary(data, "dynamic_config") ?? new Dictionary<string, object>()
};
}
public static BriskConfigCurrent ToConfigCurrent(Dictionary<string, object> data)
{
return new BriskConfigCurrent
{
FeatureFlags = BriskValueReader.GetDictionary(data, "feature_flags") ?? new Dictionary<string, object>(),
DynamicConfig = BriskValueReader.GetDictionary(data, "dynamic_config") ?? new Dictionary<string, object>()
};
}
public static BriskPlayerMe ToPlayerMe(Dictionary<string, object> data)
{
return new BriskPlayerMe
{
PlayerId = BriskValueReader.GetString(data, "player_id"),
ProjectAccountId = BriskValueReader.GetString(data, "project_account_id"),
LoginProvider = BriskValueReader.GetString(data, "login_provider"),
LoginUserId = BriskValueReader.GetString(data, "login_user_id"),
Nickname = BriskValueReader.GetString(data, "nickname"),
AvatarUrl = BriskValueReader.GetString(data, "avatar_url"),
ProfileJson = BriskValueReader.GetDictionary(data, "profile_json")
};
}
public static BriskProfile ToProfile(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskProfile
{
Nickname = BriskValueReader.GetString(data, "nickname"),
AvatarUrl = BriskValueReader.GetString(data, "avatar_url"),
ProfileJson = BriskValueReader.GetDictionary(data, "profile_json")
};
}
public static BriskLeaderboardEntry ToLeaderboardEntry(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskLeaderboardEntry
{
Rank = BriskValueReader.GetInt(data, "rank"),
PlayerId = BriskValueReader.GetString(data, "player_id"),
ProjectAccountId = BriskValueReader.GetString(data, "project_account_id"),
LoginProvider = BriskValueReader.GetString(data, "login_provider"),
LoginUserId = BriskValueReader.GetString(data, "login_user_id"),
Score = BriskValueReader.GetLong(data, "score"),
Nickname = BriskValueReader.GetString(data, "nickname"),
AvatarUrl = BriskValueReader.GetString(data, "avatar_url")
};
}
public static BriskLeaderboardPlayerRank ToLeaderboardPlayerRank(Dictionary<string, object> data)
{
var entry = ToLeaderboardEntry(data);
if (entry == null)
{
return null;
}
return new BriskLeaderboardPlayerRank
{
Rank = entry.Rank,
Score = entry.Score,
PlayerId = entry.PlayerId,
ProjectAccountId = entry.ProjectAccountId,
LoginProvider = entry.LoginProvider,
LoginUserId = entry.LoginUserId,
Nickname = entry.Nickname,
AvatarUrl = entry.AvatarUrl
};
}
public static BriskRankSeasonInfo ToRankSeasonInfo(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskRankSeasonInfo
{
SeasonId = BriskValueReader.GetString(data, "season_id") ?? BriskValueReader.GetString(data, "id"),
Name = BriskValueReader.GetString(data, "name"),
StartAt = BriskValueReader.GetString(data, "start_at"),
EndAt = BriskValueReader.GetString(data, "end_at")
};
}
public static List<BriskLeaderboardEntry> ToLeaderboardEntries(object data)
{
return ExtractList(data)
.Select(item => ToLeaderboardEntry(item as Dictionary<string, object>))
.Where(item => item != null)
.ToList();
}
public static List<BriskRankSeasonInfo> ToRankSeasonInfos(object data)
{
return ExtractList(data)
.Select(item => ToRankSeasonInfo(item as Dictionary<string, object>))
.Where(item => item != null)
.ToList();
}
public static BriskAnnouncementItem ToAnnouncementItem(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskAnnouncementItem
{
Id = BriskValueReader.GetLong(data, "id"),
Title = BriskValueReader.GetString(data, "title"),
Content = BriskValueReader.GetString(data, "content"),
ContentType = BriskValueReader.GetString(data, "content_type"),
StartAt = BriskValueReader.GetString(data, "start_at"),
EndAt = BriskValueReader.GetString(data, "end_at"),
IsRead = BriskValueReader.GetBool(data, "is_read")
};
}
public static List<BriskAnnouncementItem> ToAnnouncementItems(object data)
{
return ExtractList(data)
.Select(item => ToAnnouncementItem(item as Dictionary<string, object>))
.Where(item => item != null)
.ToList();
}
public static BriskArchiveSlot ToArchiveSlot(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskArchiveSlot
{
SlotNo = BriskValueReader.GetInt(data, "slot_no"),
Version = BriskValueReader.GetInt(data, "version"),
SizeBytes = BriskValueReader.GetLong(data, "size_bytes"),
UpdatedAt = BriskValueReader.GetString(data, "updated_at")
};
}
public static BriskArchiveMeta ToArchiveMeta(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskArchiveMeta
{
SlotNo = BriskValueReader.GetInt(data, "slot_no"),
Version = BriskValueReader.GetInt(data, "version"),
Checksum = BriskValueReader.GetString(data, "checksum"),
SizeBytes = BriskValueReader.GetLong(data, "size_bytes"),
UpdatedAt = BriskValueReader.GetString(data, "updated_at")
};
}
public static BriskArchiveUploadResult ToArchiveUploadResult(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskArchiveUploadResult
{
SlotNo = BriskValueReader.GetInt(data, "slot_no"),
Version = BriskValueReader.GetInt(data, "version"),
SizeBytes = BriskValueReader.GetLong(data, "size_bytes"),
UpdatedAt = BriskValueReader.GetString(data, "updated_at")
};
}
public static List<BriskArchiveSlot> ToArchiveSlots(object data)
{
return ExtractList(data)
.Select(item => ToArchiveSlot(item as Dictionary<string, object>))
.Where(item => item != null)
.ToList();
}
public static BriskSpaceView ToSpaceView(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskSpaceView
{
PlayerId = BriskValueReader.GetString(data, "player_id"),
LoginProvider = BriskValueReader.GetString(data, "login_provider"),
LoginUserId = BriskValueReader.GetString(data, "login_user_id"),
Payload = BriskValueReader.GetDictionary(data, "payload_json") ?? BriskValueReader.GetDictionary(data, "payload")
};
}
public static BriskSpaceStats ToSpaceStats(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskSpaceStats
{
LikeCount = BriskValueReader.GetInt(data, "like_count"),
VisitCount = BriskValueReader.GetInt(data, "visit_count")
};
}
public static BriskSpaceVisit ToSpaceVisit(Dictionary<string, object> data)
{
if (data == null)
{
return null;
}
return new BriskSpaceVisit
{
VisitorPlayerId = BriskValueReader.GetString(data, "visitor_player_id") ?? BriskValueReader.GetString(data, "player_id"),
VisitorLoginProvider = BriskValueReader.GetString(data, "visitor_login_provider") ?? BriskValueReader.GetString(data, "login_provider"),
VisitorLoginUserId = BriskValueReader.GetString(data, "visitor_login_user_id") ?? BriskValueReader.GetString(data, "login_user_id"),
VisitorNickname = BriskValueReader.GetString(data, "visitor_nickname") ?? BriskValueReader.GetString(data, "nickname"),
VisitorAvatarUrl = BriskValueReader.GetString(data, "visitor_avatar_url") ?? BriskValueReader.GetString(data, "avatar_url"),
VisitedAt = BriskValueReader.GetString(data, "visited_at")
};
}
public static List<BriskSpaceVisit> ToSpaceVisits(object data)
{
return ExtractList(data)
.Select(item => ToSpaceVisit(item as Dictionary<string, object>))
.Where(item => item != null)
.ToList();
}
public static Dictionary<string, object> ExtractObject(object data)
{
return data as Dictionary<string, object>;
}
private static List<object> ExtractList(object data)
{
if (data is List<object> list)
{
return list;
}
if (data is Dictionary<string, object> dictionary)
{
return BriskValueReader.GetList(dictionary, "items")
?? BriskValueReader.GetList(dictionary, "list")
?? BriskValueReader.GetList(dictionary, "records")
?? new List<object>();
}
return new List<object>();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22a86853f3fd851428010367935b7275
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
using System;
using System.Threading.Tasks;
public abstract class BriskModuleBase
{
protected static BriskContext GetContext()
{
return Brisk.GetRequiredContext();
}
protected static Task<T> ExecuteAsync<T>(Func<BriskContext, Task<T>> action)
{
return BriskModuleExecutor.ExecuteAsync(action);
}
protected static Task ExecuteAsync(Func<BriskContext, Task> action)
{
return BriskModuleExecutor.ExecuteAsync(action);
}
protected static Task<T> ExecutePublicAsync<T>(Func<BriskContext, Task<T>> action)
{
return BriskModuleExecutor.ExecutePublicAsync(action);
}
protected static Task ExecutePublicAsync(Func<BriskContext, Task> action)
{
return BriskModuleExecutor.ExecutePublicAsync(action);
}
protected static string RequireNotEmpty(string value, string paramName)
{
GetContext();
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException(paramName + " is required.", paramName);
}
return value;
}
protected static int RequirePositive(int value, string paramName, string message)
{
GetContext();
if (value <= 0)
{
throw new ArgumentOutOfRangeException(paramName, message);
}
return value;
}
protected static long RequirePositive(long value, string paramName, string message)
{
GetContext();
if (value <= 0)
{
throw new ArgumentOutOfRangeException(paramName, message);
}
return value;
}
protected static T RequireNotNull<T>(T value, string paramName) where T : class
{
GetContext();
if (value == null)
{
throw new ArgumentNullException(paramName);
}
return value;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b622a31e3d5440a0b1d442ca5ce85b02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
using System;
using System.Threading.Tasks;
internal static class BriskModuleExecutor
{
public static Task<T> ExecuteAsync<T>(Func<BriskContext, Task<T>> action)
{
return ExecuteAsync(action, clearSessionOnAuthExpired: true);
}
public static Task<T> ExecutePublicAsync<T>(Func<BriskContext, Task<T>> action)
{
return ExecuteAsync(action, clearSessionOnAuthExpired: false);
}
public static async Task ExecuteAsync(Func<BriskContext, Task> action)
{
await ExecuteAsync(async context =>
{
await action(context);
return true;
}, clearSessionOnAuthExpired: true);
}
public static async Task ExecutePublicAsync(Func<BriskContext, Task> action)
{
await ExecuteAsync(async context =>
{
await action(context);
return true;
}, clearSessionOnAuthExpired: false);
}
private static async Task<T> ExecuteAsync<T>(Func<BriskContext, Task<T>> action, bool clearSessionOnAuthExpired)
{
var context = Brisk.GetRequiredContext();
try
{
return await action(context);
}
catch (BriskAuthExpiredException exception)
{
if (clearSessionOnAuthExpired)
{
context.Session.Clear();
await context.TokenStore.ClearAsync();
Brisk.NotifyAuthExpired(exception);
}
throw;
}
catch (BriskBlockingException exception)
{
Brisk.NotifyBlockingError(exception);
throw;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28dcdf91da7cd78469e32dfb10a0c909
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System;
public sealed class BriskOptions
{
public string BaseUrl;
public string GameKey;
public string ClientVersion;
public string DeviceId;
public bool EnableLog;
public bool ValidateSessionOnInitialize = true;
public IBriskTokenStore TokenStore;
public IBriskErrorPresenter ErrorPresenter;
public Action ExitHandler;
public void Validate()
{
if (string.IsNullOrWhiteSpace(BaseUrl))
{
throw new ArgumentException("BriskOptions.BaseUrl is required.", nameof(BaseUrl));
}
if (string.IsNullOrWhiteSpace(GameKey))
{
throw new ArgumentException("BriskOptions.GameKey is required.", nameof(GameKey));
}
BaseUrl = NormalizeBaseUrl(BaseUrl);
}
private static string NormalizeBaseUrl(string baseUrl)
{
var normalized = baseUrl.Trim().TrimEnd('/');
if (normalized.EndsWith("/api", StringComparison.OrdinalIgnoreCase))
{
return normalized;
}
return normalized + "/api";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26bc6b9d30c442e44bffdc02dde79df8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System;
public sealed class BriskSession
{
public string AccessToken { get; private set; }
public DateTimeOffset? ExpiresAt { get; private set; }
public BriskIdentity Identity { get; private set; } = new BriskIdentity();
public string PlayerId => Identity.PlayerId;
public string ProjectAccountId => Identity.ProjectAccountId;
public string LoginProvider => Identity.LoginProvider;
public string LoginUserId => Identity.LoginUserId;
public bool HasAccessToken => !string.IsNullOrWhiteSpace(AccessToken);
public void Update(string accessToken, DateTimeOffset? expiresAt, string playerId, string projectAccountId, string loginProvider = null, string loginUserId = null)
{
AccessToken = accessToken;
ExpiresAt = expiresAt;
Identity = new BriskIdentity
{
PlayerId = playerId,
ProjectAccountId = projectAccountId,
LoginProvider = loginProvider,
LoginUserId = loginUserId
};
}
public void Clear()
{
AccessToken = null;
ExpiresAt = null;
Identity = new BriskIdentity();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0066dd2f84510c74b90f5886bdafd97f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
internal static class BriskValueReader
{
public static string GetString(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return null;
}
return Convert.ToString(value);
}
public static bool GetBool(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return false;
}
if (value is bool boolValue)
{
return boolValue;
}
if (bool.TryParse(Convert.ToString(value), out var parsed))
{
return parsed;
}
return false;
}
public static int GetInt(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return 0;
}
try
{
return Convert.ToInt32(value);
}
catch
{
return 0;
}
}
public static long GetLong(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return 0L;
}
try
{
return Convert.ToInt64(value);
}
catch
{
return 0L;
}
}
public static Dictionary<string, object> GetDictionary(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return null;
}
return value as Dictionary<string, object>;
}
public static List<object> GetList(Dictionary<string, object> data, string key)
{
if (!TryGetValue(data, key, out var value) || value == null)
{
return null;
}
return value as List<object>;
}
public static bool TryGetValue(Dictionary<string, object> data, string key, out object value)
{
value = null;
return data != null && !string.IsNullOrWhiteSpace(key) && data.TryGetValue(key, out value);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 21fc7c890cc76b441a502dc8b2f4d232
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using System;
internal static class BriskVersionComparer
{
public static bool IsLessThan(string currentVersion, string minVersion)
{
if (string.IsNullOrWhiteSpace(currentVersion) || string.IsNullOrWhiteSpace(minVersion))
{
return false;
}
if (!Version.TryParse(Normalize(currentVersion), out var current))
{
return false;
}
if (!Version.TryParse(Normalize(minVersion), out var minimum))
{
return false;
}
return current < minimum;
}
private static string Normalize(string version)
{
var normalized = version.Trim();
return normalized.StartsWith("v", StringComparison.OrdinalIgnoreCase)
? normalized.Substring(1)
: normalized;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b6a0a279968c9d4b8cd139adfc5cfc9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public interface IBriskErrorPresenter
{
void ShowBlockingError(BriskBlockingException exception);
void ShowAuthExpired(BriskAuthExpiredException exception);
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f86045e1bf5812748b28e3009dea58b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
public interface IBriskTokenStore
{
Task SaveAsync(BriskStoredSession session);
Task<BriskStoredSession> LoadAsync();
Task ClearAsync();
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c47e47902831504b9a76794f94bc9a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49fea6e9e08845941b4ed3bb8e2beb68
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public sealed class BriskAccountBannedException : BriskBlockingException
{
public BriskAccountBannedException(string message, int code = 0) : base(message, code)
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ed578795abc2cb4dac4996d0931c46f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
public class BriskApiException : BriskException
{
public BriskApiException(int httpStatus, int code, string serverMessage) : base(serverMessage)
{
HttpStatus = httpStatus;
Code = code;
ServerMessage = serverMessage;
}
public int HttpStatus { get; }
public int Code { get; }
public string ServerMessage { get; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6d29b76166b9b445b22fee43354d218
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
public sealed class BriskAuthExpiredException : BriskException
{
public BriskAuthExpiredException(string message, int code = 0) : base(message)
{
Code = code;
}
public int Code { get; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8ca19ab0d42c3844d97cab1cbace1967
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
public class BriskBlockingException : BriskException
{
public BriskBlockingException(string message, int code = 0) : base(message)
{
Code = code;
}
public int Code { get; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 093989dd2702e5245ab44cb552f6bf90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public sealed class BriskClientUpdateRequiredException : BriskBlockingException
{
public BriskClientUpdateRequiredException(string message, int code = 0) : base(message, code)
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c128ebe39b7b434aa8a0e492cb33b4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System;
public class BriskException : Exception
{
public BriskException()
{
}
public BriskException(string message) : base(message)
{
}
public BriskException(string message, Exception innerException) : base(message, innerException)
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65c4c8a851e4a9a4b90263e7f85f6766
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
public sealed class BriskHttpException : BriskException
{
public BriskHttpException(int httpStatus, string message) : base(message)
{
HttpStatus = httpStatus;
}
public int HttpStatus { get; }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 361ab9eadbcad1543b3e57787cf35150
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public sealed class BriskMaintenanceException : BriskBlockingException
{
public BriskMaintenanceException(string message, int code = 0) : base(message, code)
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ee31a25f9bc0aa4c8d8d654e1d762e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
using System;
public sealed class BriskNetworkException : BriskException
{
public BriskNetworkException(string message, Exception innerException = null) : base(message, innerException)
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a68082e9ccba1be429989dd434acb915
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public sealed class BriskNotInitializedException : BriskException
{
public BriskNotInitializedException() : base("Brisk has not been initialized. Call Brisk.InitializeAsync first.")
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87a9ea9027d20d445bf41372cdb05015
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4d16fffbab52af64eb1906f343d5382d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public sealed class BriskLeaderboardModule
: BriskModuleBase
{
public async Task<IReadOnlyList<BriskLeaderboardEntry>> GetTopAsync(string rankKey, int limit = 20)
{
ValidateRankKey(rankKey);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync(
$"/ranks/{rankKey}/top",
CreateLimitQuery(limit),
true);
return (IReadOnlyList<BriskLeaderboardEntry>)BriskModelMapper.ToLeaderboardEntries(data);
});
}
public async Task<BriskLeaderboardPlayerRank> GetMeAsync(string rankKey)
{
ValidateRankKey(rankKey);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync($"/ranks/{rankKey}/me", null, true);
return BriskModelMapper.ToLeaderboardPlayerRank(BriskModelMapper.ExtractObject(data));
});
}
public async Task<IReadOnlyList<BriskLeaderboardEntry>> GetAroundMeAsync(string rankKey, int range = 10)
{
ValidateRankKey(rankKey);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync(
$"/ranks/{rankKey}/around-me",
CreateRangeQuery(range),
true);
return (IReadOnlyList<BriskLeaderboardEntry>)BriskModelMapper.ToLeaderboardEntries(data);
});
}
public async Task SubmitScoreAsync(string rankKey, long score)
{
ValidateRankKey(rankKey);
await ExecuteAsync(async context =>
{
await context.HttpClient.PostJsonRawAsync(
$"/ranks/{rankKey}/score",
new Dictionary<string, object> { { "score", score } },
true);
});
}
public async Task<BriskRankSeasonInfo> GetCurrentSeasonAsync(string rankKey)
{
ValidateRankKey(rankKey);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync($"/ranks/{rankKey}/season/current", null, true);
return BriskModelMapper.ToRankSeasonInfo(BriskModelMapper.ExtractObject(data));
});
}
public async Task<IReadOnlyList<BriskRankSeasonInfo>> GetSeasonHistoryAsync(string rankKey, int limit = 20)
{
ValidateRankKey(rankKey);
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync(
$"/ranks/{rankKey}/history",
CreateLimitQuery(limit),
true);
return (IReadOnlyList<BriskRankSeasonInfo>)BriskModelMapper.ToRankSeasonInfos(data);
});
}
public async Task<IReadOnlyList<BriskLeaderboardEntry>> GetSeasonHistoryDetailAsync(string rankKey, string seasonId, int limit = 20)
{
ValidateRankKey(rankKey);
RequireNotEmpty(seasonId, nameof(seasonId));
return await ExecuteAsync(async context =>
{
var data = await context.HttpClient.GetRawDataAsync(
$"/ranks/{rankKey}/history/{seasonId}",
CreateLimitQuery(limit),
true);
return (IReadOnlyList<BriskLeaderboardEntry>)BriskModelMapper.ToLeaderboardEntries(data);
});
}
private static Dictionary<string, string> CreateLimitQuery(int limit)
{
return limit > 0
? new Dictionary<string, string> { { "limit", limit.ToString() } }
: null;
}
private static Dictionary<string, string> CreateRangeQuery(int range)
{
return range > 0
? new Dictionary<string, string> { { "range", range.ToString() } }
: null;
}
private static void ValidateRankKey(string rankKey)
{
RequireNotEmpty(rankKey, nameof(rankKey));
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed572788835aecd4a85114e8c5bc119b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2dec6cd98bed43a4b9c2bbddd56d9db8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
public sealed class BriskAnnouncementItem
{
public long Id;
public string Title;
public string Content;
public string ContentType;
public string StartAt;
public string EndAt;
public bool IsRead;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d178d522cdcd164099d681f5df26bdd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
public sealed class BriskArchiveDownloadResult
{
public byte[] Bytes;
public int Version;
public string Checksum;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83b2a119b0f60a741b3dacdba4c3d5e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
public sealed class BriskArchiveMeta
{
public int SlotNo;
public int Version;
public string Checksum;
public long SizeBytes;
public string UpdatedAt;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e98cc610ef25cd94bb51ef75800db439
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
public sealed class BriskArchiveSlot
{
public int SlotNo;
public int Version;
public long SizeBytes;
public string UpdatedAt;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55226c6a65eaeb240a924014078fabeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
public sealed class BriskArchiveUploadResult
{
public int SlotNo;
public int Version;
public long SizeBytes;
public string UpdatedAt;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06285ed27d520fc4da5771fd8726facb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
public sealed class BriskBootstrapResult
{
public string ProjectName;
public string ServerTime;
public bool MaintenanceMode;
public string MaintenanceMessage;
public string MinClientVersion;
public Dictionary<string, object> FeatureFlags;
public Dictionary<string, object> DynamicConfig;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c63832ad6cc12d49be734f2ba69cd73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
using System.Collections.Generic;
public sealed class BriskConfigCurrent
{
public Dictionary<string, object> FeatureFlags;
public Dictionary<string, object> DynamicConfig;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34d41980dbc94274b9752ce8d627df3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
public sealed class BriskIdentity
{
public string LoginProvider;
public string LoginUserId;
public string PlayerId;
public string ProjectAccountId;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 678340171fa294b47b160a8d483be360
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
public sealed class BriskLeaderboardEntry
{
public int Rank;
public string PlayerId;
public string ProjectAccountId;
public string LoginProvider;
public string LoginUserId;
public long Score;
public string Nickname;
public string AvatarUrl;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b5ab9b2349cb844199249755f04e846
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
public sealed class BriskLeaderboardPlayerRank
{
public int Rank;
public long Score;
public string PlayerId;
public string ProjectAccountId;
public string LoginProvider;
public string LoginUserId;
public string Nickname;
public string AvatarUrl;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18677b1b0feaf0a418eb673fa7291080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
public sealed class BriskLoginResult
{
public string AccessToken;
public int ExpiresIn;
public string PlayerId;
public string ProjectAccountId;
public string LoginProvider;
public string LoginUserId;
public bool IsNewPlayer;
public BriskProfile Profile;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c3e4e6ec1907c24e8a7e807f339683d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
public sealed class BriskPlayerMe
{
public string PlayerId;
public string ProjectAccountId;
public string LoginProvider;
public string LoginUserId;
public string Nickname;
public string AvatarUrl;
public object ProfileJson;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b21e0718e9efe54380d90b731b9a5d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More