diff --git a/Brisk Package 发布流程.md b/Brisk Package 发布流程.md new file mode 100644 index 0000000..4d1a18f --- /dev/null +++ b/Brisk Package 发布流程.md @@ -0,0 +1,52 @@ +# Brisk Package 发布流程 + +本文档用于约定当前仓库的发布方式。 + +## 开发态 + +当前仓库是 Unity 原始开发工程: + +- SDK 活代码放在 `Assets/BriskSdk/Runtime` +- 示例脚本放在 `Assets/BriskSdk/Samples/QuickStart` +- 示例场景放在 `Assets/Scenes/BriskQuickStartScene.unity` + +## 发布态 + +对外发布 Git Package 时,不直接把整个 Unity 工程作为包发布。 + +统一从以下目录产出 package 内容: + +- `PackageSource/com.foldcc.cc-framework.BriskGameServer` + +该目录用于承载: + +- `package.json` +- `README.md` +- `CHANGELOG.md` +- `Documentation~` +- `Runtime` +- `Samples~` + +## 同步步骤 + +在准备发布分支或发布 tag 前,执行: + +```powershell +./Tools/Sync-BriskPackage.ps1 +``` + +脚本会自动: + +1. 从 `Assets/BriskSdk/Runtime` 同步到 `PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime` +2. 从 `Assets/BriskSdk/Samples/QuickStart` 同步到 `PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart` +3. 把 `Assets/Scenes/BriskQuickStartScene.unity` 一并复制到 package sample 目录 + +## 建议发布流程 + +1. 在开发分支完成功能 +2. 运行 `./Tools/Sync-BriskPackage.ps1` +3. 检查 `PackageSource/com.foldcc.cc-framework.BriskGameServer` 内容 +4. 切到对外发布分支,或基于当前提交创建发布分支 +5. 只保留 package 所需目录,或以该子目录作为 git package 子路径 +6. 更新 `package.json` 版本号与 `CHANGELOG.md` +7. 打 tag 给外部项目接入 diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md b/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md index 87893e9..2542a57 100644 --- a/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md @@ -8,6 +8,10 @@ The active Unity project source currently lives under: - `Assets/BriskSdk/Samples/QuickStart` - `Assets/Scenes/BriskQuickStartScene.unity` +Sync package content from the Unity source project with: + +- `Tools/Sync-BriskPackage.ps1` + ## Included runtime modules - Bootstrap and initialization diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement.meta new file mode 100644 index 0000000..c0ba39a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e05fdcaece3a1344ca3e98889cba8fc7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs new file mode 100644 index 0000000..ea46be0 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public sealed class BriskAnnouncementsModule + : BriskModuleBase +{ + public async Task> GetListAsync() + { + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetRawDataAsync("/announcements", null, true); + return (IReadOnlyList)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(), true); + }); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs.meta new file mode 100644 index 0000000..6379256 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Announcement/BriskAnnouncementsModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76c65634a3440dd46b2ac6d5b88dcf1d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive.meta new file mode 100644 index 0000000..698c246 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d88c956c19b23c8499e142fff707b76c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs new file mode 100644 index 0000000..3e6d0cd --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs @@ -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> GetSlotsAsync() + { + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetRawDataAsync("/archives/slots", null, true); + return (IReadOnlyList)BriskModelMapper.ToArchiveSlots(data); + }); + } + + public async Task 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 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 + { + 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 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 headers, string key) + { + var value = ReadHeader(headers, key); + return int.TryParse(value, out var result) ? result : 0; + } + + private static string ReadHeader(Dictionary 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; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs.meta new file mode 100644 index 0000000..2c0f242 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Archive/BriskArchiveModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b0136b0b2b050f4ab2043d7e6bfb4ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth.meta new file mode 100644 index 0000000..a76872a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a26d0e58a94c264198010eae586c7b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs new file mode 100644 index 0000000..275d4ba --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public sealed class BriskAuthModule + : BriskModuleBase +{ + public async Task 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 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(), 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 LoginInternalAsync(Dictionary 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 CreateLoginBody(string loginProvider, BriskProfile profile, string loginUserId, string code) + { + var context = GetContext(); + var body = new Dictionary + { + { "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 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); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs.meta new file mode 100644 index 0000000..6a0b708 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Auth/BriskAuthModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03f5ec833a8862a43bea6a0b7f7788d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef new file mode 100644 index 0000000..c316b49 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef @@ -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 +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef.meta new file mode 100644 index 0000000..a4a53b3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/BriskSdk.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f8ab9462792dad445acc3b318dfa2372 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config.meta new file mode 100644 index 0000000..55a4901 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8b88bc9f438e4d64089acbb0ac667c03 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs new file mode 100644 index 0000000..75e71a8 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public sealed class BriskConfigModule + : BriskModuleBase +{ + public async Task GetCurrentAsync() + { + return await ExecutePublicAsync(async context => + { + var data = await context.HttpClient.GetDataAsync("/config/current", CreateQuery(context), false); + return BriskModelMapper.ToConfigCurrent(data); + }); + } + + public Task RefreshAsync() + { + return GetCurrentAsync(); + } + + private static Dictionary CreateQuery(BriskContext context) + { + return new Dictionary + { + { "game_key", context.Options.GameKey }, + { "client_version", context.Options.ClientVersion } + }; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs.meta new file mode 100644 index 0000000..ee7955d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Config/BriskConfigModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f50e09c77d7e3f24a9b879d4e0f21c76 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core.meta new file mode 100644 index 0000000..651f750 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a5b8196730f24c0438900010f497476f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs new file mode 100644 index 0000000..4dd02f5 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs @@ -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 OnBlockingError; + public static event Action 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 + { + { "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; + } + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs.meta new file mode 100644 index 0000000..5eeef6a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/Brisk.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f175f0540648ffe43a061deb0d75159d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs new file mode 100644 index 0000000..febf41b --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +public sealed class BriskBinaryResponse +{ + public byte[] Bytes; + public Dictionary Headers; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs.meta new file mode 100644 index 0000000..cfe0010 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskBinaryResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 952c9679a69c1a740a55492f39acd66e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs new file mode 100644 index 0000000..d9e8914 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs @@ -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; } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs.meta new file mode 100644 index 0000000..9badfee --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ca7516fa2069a024fa392f1c143a5a3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs new file mode 100644 index 0000000..cad93d4 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs @@ -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(); + if (s_instance != null) + { + return s_instance; + } + } + + var gameObject = new GameObject(HostName); + DontDestroyOnLoad(gameObject); + s_instance = gameObject.AddComponent(); + 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(); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs.meta new file mode 100644 index 0000000..400a845 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorDialog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95a9fe204ba119d4b8d7a296a7bd429b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs new file mode 100644 index 0000000..c5781a8 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs @@ -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"; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs.meta new file mode 100644 index 0000000..686c101 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskDefaultErrorPresenter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 681035ac4d9bc8147ad8ae630b131185 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs new file mode 100644 index 0000000..6f91d73 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs @@ -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); + } + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs.meta new file mode 100644 index 0000000..92eb4e4 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskErrorClassifier.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41bc55282a88d6d4aaee810c79f56415 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs new file mode 100644 index 0000000..8c87207 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs @@ -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> GetDataAsync(string path, Dictionary query = null, bool auth = false) + { + return SendForDictionaryAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth); + } + + public Task GetRawDataAsync(string path, Dictionary query = null, bool auth = false) + { + return SendRawJsonAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth); + } + + public Task> PostJsonAsync(string path, object body, bool auth = false) + { + return SendForDictionaryAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth); + } + + public Task PostJsonRawAsync(string path, object body, bool auth = false) + { + return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth); + } + + public Task PostJsonRawAsync(string path, object body, bool auth, Dictionary query) + { + return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, query, body, auth); + } + + public Task SendPutJsonRawAsync(string path, object body, bool auth = false) + { + return SendRawJsonAsync(UnityWebRequest.kHttpVerbPUT, path, null, body, auth); + } + + public Task SendDeleteJsonRawAsync(string path, Dictionary query = null, bool auth = false) + { + return SendRawJsonAsync(UnityWebRequest.kHttpVerbDELETE, path, query, null, auth); + } + + public async Task> PostMultipartAsync(string path, List 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 ?? new Dictionary(); + } + } + + public async Task GetBytesAsync(string path, Dictionary 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(), + Headers = request.GetResponseHeaders() ?? new Dictionary() + }; + } + } + + private async Task> SendForDictionaryAsync(string method, string path, Dictionary query, object body, bool auth) + { + var data = await SendRawJsonAsync(method, path, query, body, auth); + return data as Dictionary ?? new Dictionary(); + } + + private async Task SendRawJsonAsync(string method, string path, Dictionary 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 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(); + 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(); + } + + throw new BriskHttpException(httpStatus, request.error ?? "HTTP error."); + } + + var payload = BriskJson.Deserialize(responseText) as Dictionary; + 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(); + } + + 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; + 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 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(); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs.meta new file mode 100644 index 0000000..a6b13f4 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskHttpClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5a825891fadac404b97c755e6d60a3f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs new file mode 100644 index 0000000..3aa4c91 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs @@ -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 ParseObject() + { + var table = new Dictionary(); + 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 ParseArray() + { + var array = new List(); + 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(); + 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; + } + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs.meta new file mode 100644 index 0000000..5f2af0b --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskJson.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c594424856e2e74abbce01c4ad4532d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs new file mode 100644 index 0000000..6fd9cd3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs @@ -0,0 +1,309 @@ +using System.Collections.Generic; +using System.Linq; + +internal static class BriskModelMapper +{ + public static BriskLoginResult ToLoginResult(Dictionary 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 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(), + DynamicConfig = BriskValueReader.GetDictionary(data, "dynamic_config") ?? new Dictionary() + }; + } + + public static BriskConfigCurrent ToConfigCurrent(Dictionary data) + { + return new BriskConfigCurrent + { + FeatureFlags = BriskValueReader.GetDictionary(data, "feature_flags") ?? new Dictionary(), + DynamicConfig = BriskValueReader.GetDictionary(data, "dynamic_config") ?? new Dictionary() + }; + } + + public static BriskPlayerMe ToPlayerMe(Dictionary 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 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 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 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 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 ToLeaderboardEntries(object data) + { + return ExtractList(data) + .Select(item => ToLeaderboardEntry(item as Dictionary)) + .Where(item => item != null) + .ToList(); + } + + public static List ToRankSeasonInfos(object data) + { + return ExtractList(data) + .Select(item => ToRankSeasonInfo(item as Dictionary)) + .Where(item => item != null) + .ToList(); + } + + public static BriskAnnouncementItem ToAnnouncementItem(Dictionary 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 ToAnnouncementItems(object data) + { + return ExtractList(data) + .Select(item => ToAnnouncementItem(item as Dictionary)) + .Where(item => item != null) + .ToList(); + } + + public static BriskArchiveSlot ToArchiveSlot(Dictionary 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 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 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 ToArchiveSlots(object data) + { + return ExtractList(data) + .Select(item => ToArchiveSlot(item as Dictionary)) + .Where(item => item != null) + .ToList(); + } + + public static BriskSpaceView ToSpaceView(Dictionary 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 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 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 ToSpaceVisits(object data) + { + return ExtractList(data) + .Select(item => ToSpaceVisit(item as Dictionary)) + .Where(item => item != null) + .ToList(); + } + + public static Dictionary ExtractObject(object data) + { + return data as Dictionary; + } + + private static List ExtractList(object data) + { + if (data is List list) + { + return list; + } + + if (data is Dictionary dictionary) + { + return BriskValueReader.GetList(dictionary, "items") + ?? BriskValueReader.GetList(dictionary, "list") + ?? BriskValueReader.GetList(dictionary, "records") + ?? new List(); + } + + return new List(); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs.meta new file mode 100644 index 0000000..041403f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModelMapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22a86853f3fd851428010367935b7275 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs new file mode 100644 index 0000000..8e707e7 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading.Tasks; + +public abstract class BriskModuleBase +{ + protected static BriskContext GetContext() + { + return Brisk.GetRequiredContext(); + } + + protected static Task ExecuteAsync(Func> action) + { + return BriskModuleExecutor.ExecuteAsync(action); + } + + protected static Task ExecuteAsync(Func action) + { + return BriskModuleExecutor.ExecuteAsync(action); + } + + protected static Task ExecutePublicAsync(Func> action) + { + return BriskModuleExecutor.ExecutePublicAsync(action); + } + + protected static Task ExecutePublicAsync(Func 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 value, string paramName) where T : class + { + GetContext(); + if (value == null) + { + throw new ArgumentNullException(paramName); + } + + return value; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs.meta new file mode 100644 index 0000000..bca65f0 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b622a31e3d5440a0b1d442ca5ce85b02 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs new file mode 100644 index 0000000..3651dc0 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; + +internal static class BriskModuleExecutor +{ + public static Task ExecuteAsync(Func> action) + { + return ExecuteAsync(action, clearSessionOnAuthExpired: true); + } + + public static Task ExecutePublicAsync(Func> action) + { + return ExecuteAsync(action, clearSessionOnAuthExpired: false); + } + + public static async Task ExecuteAsync(Func action) + { + await ExecuteAsync(async context => + { + await action(context); + return true; + }, clearSessionOnAuthExpired: true); + } + + public static async Task ExecutePublicAsync(Func action) + { + await ExecuteAsync(async context => + { + await action(context); + return true; + }, clearSessionOnAuthExpired: false); + } + + private static async Task ExecuteAsync(Func> 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; + } + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs.meta new file mode 100644 index 0000000..2ef94b9 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskModuleExecutor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28dcdf91da7cd78469e32dfb10a0c909 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs new file mode 100644 index 0000000..a54deae --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs @@ -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"; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs.meta new file mode 100644 index 0000000..9a9f896 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26bc6b9d30c442e44bffdc02dde79df8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs new file mode 100644 index 0000000..eedd2fc --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs @@ -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(); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs.meta new file mode 100644 index 0000000..e15aec4 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskSession.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0066dd2f84510c74b90f5886bdafd97f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs new file mode 100644 index 0000000..2723fd7 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; + +internal static class BriskValueReader +{ + public static string GetString(Dictionary data, string key) + { + if (!TryGetValue(data, key, out var value) || value == null) + { + return null; + } + + return Convert.ToString(value); + } + + public static bool GetBool(Dictionary 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 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 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 GetDictionary(Dictionary data, string key) + { + if (!TryGetValue(data, key, out var value) || value == null) + { + return null; + } + + return value as Dictionary; + } + + public static List GetList(Dictionary data, string key) + { + if (!TryGetValue(data, key, out var value) || value == null) + { + return null; + } + + return value as List; + } + + public static bool TryGetValue(Dictionary data, string key, out object value) + { + value = null; + return data != null && !string.IsNullOrWhiteSpace(key) && data.TryGetValue(key, out value); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs.meta new file mode 100644 index 0000000..4e4ad76 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskValueReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21fc7c890cc76b441a502dc8b2f4d232 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs new file mode 100644 index 0000000..0e06ff7 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs @@ -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; + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs.meta new file mode 100644 index 0000000..a2d690d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/BriskVersionComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b6a0a279968c9d4b8cd139adfc5cfc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs new file mode 100644 index 0000000..8dddfe3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs @@ -0,0 +1,6 @@ +public interface IBriskErrorPresenter +{ + void ShowBlockingError(BriskBlockingException exception); + + void ShowAuthExpired(BriskAuthExpiredException exception); +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs.meta new file mode 100644 index 0000000..45dd2af --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskErrorPresenter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f86045e1bf5812748b28e3009dea58b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs new file mode 100644 index 0000000..9b452ff --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +public interface IBriskTokenStore +{ + Task SaveAsync(BriskStoredSession session); + + Task LoadAsync(); + + Task ClearAsync(); +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs.meta new file mode 100644 index 0000000..5dbc371 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Core/IBriskTokenStore.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c47e47902831504b9a76794f94bc9a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions.meta new file mode 100644 index 0000000..6ca7c45 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49fea6e9e08845941b4ed3bb8e2beb68 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs new file mode 100644 index 0000000..1dec9f9 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs @@ -0,0 +1,6 @@ +public sealed class BriskAccountBannedException : BriskBlockingException +{ + public BriskAccountBannedException(string message, int code = 0) : base(message, code) + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs.meta new file mode 100644 index 0000000..5633a30 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAccountBannedException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ed578795abc2cb4dac4996d0931c46f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs new file mode 100644 index 0000000..b474b7a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs @@ -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; } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs.meta new file mode 100644 index 0000000..e1dff94 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskApiException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6d29b76166b9b445b22fee43354d218 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs new file mode 100644 index 0000000..35218ab --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs @@ -0,0 +1,9 @@ +public sealed class BriskAuthExpiredException : BriskException +{ + public BriskAuthExpiredException(string message, int code = 0) : base(message) + { + Code = code; + } + + public int Code { get; } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs.meta new file mode 100644 index 0000000..ec908dc --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskAuthExpiredException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ca19ab0d42c3844d97cab1cbace1967 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs new file mode 100644 index 0000000..6f94c5e --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs @@ -0,0 +1,9 @@ +public class BriskBlockingException : BriskException +{ + public BriskBlockingException(string message, int code = 0) : base(message) + { + Code = code; + } + + public int Code { get; } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs.meta new file mode 100644 index 0000000..d655606 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskBlockingException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 093989dd2702e5245ab44cb552f6bf90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs new file mode 100644 index 0000000..d4a996d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs @@ -0,0 +1,6 @@ +public sealed class BriskClientUpdateRequiredException : BriskBlockingException +{ + public BriskClientUpdateRequiredException(string message, int code = 0) : base(message, code) + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs.meta new file mode 100644 index 0000000..8648fe3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskClientUpdateRequiredException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c128ebe39b7b434aa8a0e492cb33b4a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs new file mode 100644 index 0000000..9552d0f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs @@ -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) + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs.meta new file mode 100644 index 0000000..5330e4f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65c4c8a851e4a9a4b90263e7f85f6766 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs new file mode 100644 index 0000000..996d437 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs @@ -0,0 +1,9 @@ +public sealed class BriskHttpException : BriskException +{ + public BriskHttpException(int httpStatus, string message) : base(message) + { + HttpStatus = httpStatus; + } + + public int HttpStatus { get; } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs.meta new file mode 100644 index 0000000..344c5f7 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskHttpException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 361ab9eadbcad1543b3e57787cf35150 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs new file mode 100644 index 0000000..c4b2835 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs @@ -0,0 +1,6 @@ +public sealed class BriskMaintenanceException : BriskBlockingException +{ + public BriskMaintenanceException(string message, int code = 0) : base(message, code) + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs.meta new file mode 100644 index 0000000..ff7181f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskMaintenanceException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ee31a25f9bc0aa4c8d8d654e1d762e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs new file mode 100644 index 0000000..bbdfbf3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs @@ -0,0 +1,8 @@ +using System; + +public sealed class BriskNetworkException : BriskException +{ + public BriskNetworkException(string message, Exception innerException = null) : base(message, innerException) + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs.meta new file mode 100644 index 0000000..b52fa6d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNetworkException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a68082e9ccba1be429989dd434acb915 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs new file mode 100644 index 0000000..a76d948 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs @@ -0,0 +1,6 @@ +public sealed class BriskNotInitializedException : BriskException +{ + public BriskNotInitializedException() : base("Brisk has not been initialized. Call Brisk.InitializeAsync first.") + { + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs.meta new file mode 100644 index 0000000..808dd92 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Exceptions/BriskNotInitializedException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87a9ea9027d20d445bf41372cdb05015 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard.meta new file mode 100644 index 0000000..d43e65b --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d16fffbab52af64eb1906f343d5382d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs new file mode 100644 index 0000000..6f8d171 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public sealed class BriskLeaderboardModule + : BriskModuleBase +{ + public async Task> 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)BriskModelMapper.ToLeaderboardEntries(data); + }); + } + + public async Task 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> 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)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 { { "score", score } }, + true); + }); + } + + public async Task 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> 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)BriskModelMapper.ToRankSeasonInfos(data); + }); + } + + public async Task> 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)BriskModelMapper.ToLeaderboardEntries(data); + }); + } + + private static Dictionary CreateLimitQuery(int limit) + { + return limit > 0 + ? new Dictionary { { "limit", limit.ToString() } } + : null; + } + + private static Dictionary CreateRangeQuery(int range) + { + return range > 0 + ? new Dictionary { { "range", range.ToString() } } + : null; + } + + private static void ValidateRankKey(string rankKey) + { + RequireNotEmpty(rankKey, nameof(rankKey)); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs.meta new file mode 100644 index 0000000..83dc89f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Leaderboard/BriskLeaderboardModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed572788835aecd4a85114e8c5bc119b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models.meta new file mode 100644 index 0000000..f6da7e0 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2dec6cd98bed43a4b9c2bbddd56d9db8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs new file mode 100644 index 0000000..d53c10e --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs @@ -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; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs.meta new file mode 100644 index 0000000..e17287d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskAnnouncementItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d178d522cdcd164099d681f5df26bdd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs new file mode 100644 index 0000000..080a979 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs @@ -0,0 +1,6 @@ +public sealed class BriskArchiveDownloadResult +{ + public byte[] Bytes; + public int Version; + public string Checksum; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs.meta new file mode 100644 index 0000000..cf9d781 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveDownloadResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83b2a119b0f60a741b3dacdba4c3d5e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs new file mode 100644 index 0000000..808ff7b --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs @@ -0,0 +1,8 @@ +public sealed class BriskArchiveMeta +{ + public int SlotNo; + public int Version; + public string Checksum; + public long SizeBytes; + public string UpdatedAt; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs.meta new file mode 100644 index 0000000..6a64a23 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveMeta.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e98cc610ef25cd94bb51ef75800db439 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs new file mode 100644 index 0000000..75113aa --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs @@ -0,0 +1,7 @@ +public sealed class BriskArchiveSlot +{ + public int SlotNo; + public int Version; + public long SizeBytes; + public string UpdatedAt; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs.meta new file mode 100644 index 0000000..b8af155 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveSlot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55226c6a65eaeb240a924014078fabeb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs new file mode 100644 index 0000000..d5b7c64 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs @@ -0,0 +1,7 @@ +public sealed class BriskArchiveUploadResult +{ + public int SlotNo; + public int Version; + public long SizeBytes; + public string UpdatedAt; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs.meta new file mode 100644 index 0000000..08caa76 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskArchiveUploadResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06285ed27d520fc4da5771fd8726facb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs new file mode 100644 index 0000000..8558121 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs @@ -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 FeatureFlags; + public Dictionary DynamicConfig; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs.meta new file mode 100644 index 0000000..1733c93 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskBootstrapResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c63832ad6cc12d49be734f2ba69cd73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs new file mode 100644 index 0000000..9f5e264 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +public sealed class BriskConfigCurrent +{ + public Dictionary FeatureFlags; + public Dictionary DynamicConfig; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs.meta new file mode 100644 index 0000000..90c85cb --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskConfigCurrent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34d41980dbc94274b9752ce8d627df3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs new file mode 100644 index 0000000..fc2525a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs @@ -0,0 +1,7 @@ +public sealed class BriskIdentity +{ + public string LoginProvider; + public string LoginUserId; + public string PlayerId; + public string ProjectAccountId; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs.meta new file mode 100644 index 0000000..54f63d5 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskIdentity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 678340171fa294b47b160a8d483be360 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs new file mode 100644 index 0000000..3856b2a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs @@ -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; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs.meta new file mode 100644 index 0000000..92e3f84 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardEntry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b5ab9b2349cb844199249755f04e846 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs new file mode 100644 index 0000000..7840b18 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs @@ -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; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs.meta new file mode 100644 index 0000000..6a86024 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLeaderboardPlayerRank.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18677b1b0feaf0a418eb673fa7291080 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs new file mode 100644 index 0000000..536112c --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs @@ -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; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs.meta new file mode 100644 index 0000000..2f8767b --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskLoginResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c3e4e6ec1907c24e8a7e807f339683d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs new file mode 100644 index 0000000..5c929c2 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs @@ -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; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs.meta new file mode 100644 index 0000000..ccbd487 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskPlayerMe.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b21e0718e9efe54380d90b731b9a5d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs new file mode 100644 index 0000000..7acce66 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs @@ -0,0 +1,6 @@ +public sealed class BriskProfile +{ + public string Nickname; + public string AvatarUrl; + public object ProfileJson; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs.meta new file mode 100644 index 0000000..9bc1717 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskProfile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61415cc2946cbfe4daf5c5f222c7cab3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs new file mode 100644 index 0000000..2aef9dc --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs @@ -0,0 +1,7 @@ +public sealed class BriskRankSeasonInfo +{ + public string SeasonId; + public string Name; + public string StartAt; + public string EndAt; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs.meta new file mode 100644 index 0000000..be1c4a8 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskRankSeasonInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea2edbfe54d7ab949bf9cfb4ab1f2264 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs new file mode 100644 index 0000000..d59e7e1 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs @@ -0,0 +1,5 @@ +public sealed class BriskSpaceStats +{ + public int LikeCount; + public int VisitCount; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs.meta new file mode 100644 index 0000000..b687bf3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceStats.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 905e43ffd5fb1e346966747660028eb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs new file mode 100644 index 0000000..9118e09 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs @@ -0,0 +1,7 @@ +public sealed class BriskSpaceView +{ + public string PlayerId; + public string LoginProvider; + public string LoginUserId; + public object Payload; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs.meta new file mode 100644 index 0000000..e1d7882 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dbf431c9bebb5334280f1f0c0497cef0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs new file mode 100644 index 0000000..4a8c219 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs @@ -0,0 +1,9 @@ +public sealed class BriskSpaceVisit +{ + public string VisitorPlayerId; + public string VisitorLoginProvider; + public string VisitorLoginUserId; + public string VisitorNickname; + public string VisitorAvatarUrl; + public string VisitedAt; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs.meta new file mode 100644 index 0000000..4c50059 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskSpaceVisit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7911963058895c24288b8ca8c2717c68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs new file mode 100644 index 0000000..7bc978c --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs @@ -0,0 +1,11 @@ +using System; + +public sealed class BriskStoredSession +{ + public string AccessToken; + public DateTimeOffset? ExpiresAt; + public string PlayerId; + public string ProjectAccountId; + public string LoginProvider; + public string LoginUserId; +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs.meta new file mode 100644 index 0000000..989166d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Models/BriskStoredSession.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b46eac401068ce8429c68363a4a4d98e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player.meta new file mode 100644 index 0000000..0611b7e --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f43770549c50484a86f41d3b665031f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs new file mode 100644 index 0000000..6fdde7a --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; + +public sealed class BriskPlayerModule + : BriskModuleBase +{ + public async Task GetMeAsync() + { + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetDataAsync("/player/me", null, true); + var result = BriskModelMapper.ToPlayerMe(data); + context.Session.Update( + context.Session.AccessToken, + context.Session.ExpiresAt, + result.PlayerId, + result.ProjectAccountId, + result.LoginProvider, + result.LoginUserId); + + await context.TokenStore.SaveAsync(new BriskStoredSession + { + AccessToken = context.Session.AccessToken, + ExpiresAt = context.Session.ExpiresAt, + PlayerId = result.PlayerId, + ProjectAccountId = result.ProjectAccountId, + LoginProvider = result.LoginProvider, + LoginUserId = result.LoginUserId + }); + + return result; + }); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs.meta new file mode 100644 index 0000000..c04d937 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Player/BriskPlayerModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9805431056c4ad146b61871d0f3ce9ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space.meta new file mode 100644 index 0000000..fa37585 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa5f23f84a311c14c8f7122d88f877b6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs new file mode 100644 index 0000000..18f4266 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public sealed class BriskSpaceModule + : BriskModuleBase +{ + public async Task GetByPlayerIdAsync(string playerId) + { + ValidatePlayerId(playerId); + + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetDataAsync($"/spaces/{playerId}", null, true); + return BriskModelMapper.ToSpaceView(data); + }); + } + + public async Task GetByLoginIdentityAsync(string loginProvider, string loginUserId) + { + ValidateLoginIdentity(loginProvider, loginUserId); + + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetDataAsync("/spaces/by-login", CreateLoginIdentityQuery(loginProvider, loginUserId), true); + return BriskModelMapper.ToSpaceView(data); + }); + } + + public async Task GetStatsByPlayerIdAsync(string playerId) + { + ValidatePlayerId(playerId); + + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetDataAsync($"/spaces/{playerId}/stats", null, true); + return BriskModelMapper.ToSpaceStats(data); + }); + } + + public async Task GetStatsByLoginIdentityAsync(string loginProvider, string loginUserId) + { + ValidateLoginIdentity(loginProvider, loginUserId); + + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetDataAsync("/spaces/by-login/stats", CreateLoginIdentityQuery(loginProvider, loginUserId), true); + return BriskModelMapper.ToSpaceStats(data); + }); + } + + public async Task LikeByPlayerIdAsync(string playerId) + { + ValidatePlayerId(playerId); + await ExecuteAsync(async context => + { + await context.HttpClient.PostJsonRawAsync($"/spaces/{playerId}/like", new Dictionary(), true); + }); + } + + public async Task UnlikeByPlayerIdAsync(string playerId) + { + ValidatePlayerId(playerId); + await ExecuteAsync(async context => + { + await context.HttpClient.SendDeleteJsonRawAsync($"/spaces/{playerId}/like", null, true); + }); + } + + public async Task LikeByLoginIdentityAsync(string loginProvider, string loginUserId) + { + ValidateLoginIdentity(loginProvider, loginUserId); + await ExecuteAsync(async context => + { + await context.HttpClient.PostJsonRawAsync("/spaces/by-login/like", new Dictionary(), true, CreateLoginIdentityQuery(loginProvider, loginUserId)); + }); + } + + public async Task UnlikeByLoginIdentityAsync(string loginProvider, string loginUserId) + { + ValidateLoginIdentity(loginProvider, loginUserId); + await ExecuteAsync(async context => + { + await context.HttpClient.SendDeleteJsonRawAsync("/spaces/by-login/like", CreateLoginIdentityQuery(loginProvider, loginUserId), true); + }); + } + + public async Task UpdateMyAsync(object payload) + { + RequireNotNull(payload, nameof(payload)); + + await ExecuteAsync(async context => + { + await context.HttpClient.SendPutJsonRawAsync( + "/spaces/me", + new Dictionary { { "payload_json", payload } }, + true); + }); + } + + public async Task> GetMyVisitsAsync() + { + return await ExecuteAsync(async context => + { + var data = await context.HttpClient.GetRawDataAsync("/spaces/me/visits", null, true); + return (IReadOnlyList)BriskModelMapper.ToSpaceVisits(data); + }); + } + + private static Dictionary CreateLoginIdentityQuery(string loginProvider, string loginUserId) + { + return new Dictionary + { + { "login_provider", loginProvider }, + { "login_user_id", loginUserId } + }; + } + + private static void ValidatePlayerId(string playerId) + { + RequireNotEmpty(playerId, nameof(playerId)); + } + + private static void ValidateLoginIdentity(string loginProvider, string loginUserId) + { + RequireNotEmpty(loginProvider, nameof(loginProvider)); + RequireNotEmpty(loginUserId, nameof(loginUserId)); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs.meta new file mode 100644 index 0000000..d83cb99 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Space/BriskSpaceModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70f1975b4778a7a45acc5ca3664821a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage.meta new file mode 100644 index 0000000..0b14562 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fbb6f72c75fb7b04c8c61dde77ad54db +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs new file mode 100644 index 0000000..6e30c85 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs @@ -0,0 +1,88 @@ +using System; +using System.Threading.Tasks; +using UnityEngine; + +public sealed class BriskPlayerPrefsTokenStore : IBriskTokenStore +{ + private const string KeyPrefix = "BriskSdk.Session."; + + public Task SaveAsync(BriskStoredSession session) + { + if (session == null) + { + ClearStoredKeys(); + return Task.CompletedTask; + } + + SetString("access_token", session.AccessToken); + SetString("expires_at", session.ExpiresAt.HasValue ? session.ExpiresAt.Value.ToUnixTimeSeconds().ToString() : null); + SetString("player_id", session.PlayerId); + SetString("project_account_id", session.ProjectAccountId); + SetString("login_provider", session.LoginProvider); + SetString("login_user_id", session.LoginUserId); + PlayerPrefs.Save(); + return Task.CompletedTask; + } + + public Task LoadAsync() + { + var accessToken = GetString("access_token"); + if (string.IsNullOrWhiteSpace(accessToken)) + { + return Task.FromResult(null); + } + + DateTimeOffset? expiresAt = null; + var expiresAtText = GetString("expires_at"); + if (long.TryParse(expiresAtText, out var unixSeconds)) + { + expiresAt = DateTimeOffset.FromUnixTimeSeconds(unixSeconds); + } + + var session = new BriskStoredSession + { + AccessToken = accessToken, + ExpiresAt = expiresAt, + PlayerId = GetString("player_id"), + ProjectAccountId = GetString("project_account_id"), + LoginProvider = GetString("login_provider"), + LoginUserId = GetString("login_user_id") + }; + return Task.FromResult(session); + } + + public Task ClearAsync() + { + ClearStoredKeys(); + PlayerPrefs.Save(); + return Task.CompletedTask; + } + + private static void ClearStoredKeys() + { + PlayerPrefs.DeleteKey(KeyPrefix + "access_token"); + PlayerPrefs.DeleteKey(KeyPrefix + "expires_at"); + PlayerPrefs.DeleteKey(KeyPrefix + "player_id"); + PlayerPrefs.DeleteKey(KeyPrefix + "project_account_id"); + PlayerPrefs.DeleteKey(KeyPrefix + "login_provider"); + PlayerPrefs.DeleteKey(KeyPrefix + "login_user_id"); + } + + private static string GetString(string name) + { + var key = KeyPrefix + name; + return PlayerPrefs.HasKey(key) ? PlayerPrefs.GetString(key) : null; + } + + private static void SetString(string name, string value) + { + var key = KeyPrefix + name; + if (string.IsNullOrEmpty(value)) + { + PlayerPrefs.DeleteKey(key); + return; + } + + PlayerPrefs.SetString(key, value); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs.meta new file mode 100644 index 0000000..cbe8595 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Runtime/Storage/BriskPlayerPrefsTokenStore.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b47dead283188b4e9950adeb3bff79d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs new file mode 100644 index 0000000..76f731d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs @@ -0,0 +1,922 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +public sealed class BriskQuickStartSample : MonoBehaviour +{ + [Header("Init")] + public string BaseUrl = "https://brisk.lightyears.ltd"; + public string GameKey = "demo-game"; + public string ClientVersion = "1.0.0"; + public string DeviceId = "editor-device"; + public bool ValidateSessionOnInitialize = true; + + [Header("Login")] + public string LoginProvider = "tap"; + public string LoginUserId = "tap_user_10001"; + public string LoginCode = string.Empty; + public string Nickname = "Unity Sample User"; + public string AvatarUrl = string.Empty; + + [Header("Leaderboard")] + public string RankKey = "season-score"; + public string SubmitScoreValue = "128"; + public string LeaderboardLimit = "10"; + public string AroundMeRange = "5"; + public string SeasonId = string.Empty; + public string SeasonHistoryLimit = "20"; + + [Header("Archive")] + public string ArchiveSlotNo = "1"; + public string ArchiveBaseVersion = string.Empty; + [TextArea(3, 6)] + public string ArchiveContent = "{\n \"save\": 1,\n \"coins\": 128,\n \"hero\": \"mage\"\n}"; + + [Header("Announcements")] + public string AnnouncementId = string.Empty; + + [Header("Space")] + public string SpacePlayerId = string.Empty; + public string SpaceLoginProvider = "tap"; + public string SpaceLoginUserId = "tap_user_10001"; + [TextArea(3, 6)] + public string SpacePayloadText = "{\n \"mood\": \"ready\",\n \"title\": \"hello brisk\"\n}"; + + [Header("Demo")] + public bool AutoRunOnStart; + + private readonly List _eventLogs = new List(); + + private Vector2 _pageScroll; + private Vector2 _resultScroll; + private Vector2 _logScroll; + private bool _isBusy; + private string _busyAction = string.Empty; + private string _statusText = "Ready."; + private string _resultText = "No request yet."; + private string _lastErrorText = string.Empty; + private IReadOnlyList _announcementCache = Array.Empty(); + private IReadOnlyList _seasonHistoryCache = Array.Empty(); + + private void OnEnable() + { + Brisk.OnInitialized += HandleInitialized; + Brisk.OnLoggedIn += HandleLoggedIn; + Brisk.OnLoggedOut += HandleLoggedOut; + Brisk.OnAuthExpired += HandleAuthExpired; + Brisk.OnBlockingError += HandleBlockingError; + } + + private void OnDisable() + { + Brisk.OnInitialized -= HandleInitialized; + Brisk.OnLoggedIn -= HandleLoggedIn; + Brisk.OnLoggedOut -= HandleLoggedOut; + Brisk.OnAuthExpired -= HandleAuthExpired; + Brisk.OnBlockingError -= HandleBlockingError; + } + + private void Start() + { + if (AutoRunOnStart) + { + RunAction("Auto Smoke Run", RunSmokeFlowAsync); + } + } + + [ContextMenu("Run Brisk Sample")] + public void RunFromContextMenu() + { + RunAction("Context Smoke Run", RunSmokeFlowAsync); + } + + private void OnGUI() + { + var area = new Rect(12f, 12f, Screen.width - 24f, Screen.height - 24f); + GUILayout.BeginArea(area, GUI.skin.box); + _pageScroll = GUILayout.BeginScrollView(_pageScroll); + + DrawHeader(); + DrawStatusPanel(); + DrawInitSection(); + DrawLoginSection(); + DrawPlayerAndConfigSection(); + DrawAnnouncementsSection(); + DrawLeaderboardSection(); + DrawArchiveSection(); + DrawSpaceSection(); + DrawOutputSection(); + + GUILayout.EndScrollView(); + GUILayout.EndArea(); + } + + private void DrawHeader() + { + GUILayout.Label("Brisk IMGUI Sample", GUI.skin.box); + GUILayout.Label("This sample is designed for full SDK flow testing inside a single scene.", GUI.skin.label); + } + + private void DrawStatusPanel() + { + BeginSection("Runtime Status"); + DrawReadOnlyRow("Initialized", Brisk.IsInitialized ? "Yes" : "No"); + DrawReadOnlyRow("Logged In", Brisk.IsLoggedIn ? "Yes" : "No"); + DrawReadOnlyRow("PlayerId", Brisk.PlayerId); + DrawReadOnlyRow("Identity", Brisk.Identity == null ? string.Empty : Brisk.Identity.LoginProvider + " / " + Brisk.Identity.LoginUserId); + DrawReadOnlyRow("Current Action", _isBusy ? _busyAction : "Idle"); + DrawReadOnlyRow("Status", _statusText); + + GUILayout.BeginHorizontal(); + DrawButton("Run Smoke Flow", RunSmokeFlowAsync); + DrawButton("Show Bootstrap Cache", () => + { + SetResult("Bootstrap Cache", Brisk.Bootstrap); + return Task.CompletedTask; + }, Brisk.IsInitialized); + DrawButton("Shutdown SDK", () => + { + Brisk.Shutdown(); + Log("SDK shutdown."); + _statusText = "SDK shutdown."; + return Task.CompletedTask; + }); + DrawButton("Clear Output", () => + { + _resultText = "Output cleared."; + _lastErrorText = string.Empty; + _eventLogs.Clear(); + _statusText = "Output cleared."; + return Task.CompletedTask; + }); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawInitSection() + { + BeginSection("Initialize"); + BaseUrl = DrawEditableRow("Base Url", BaseUrl); + GameKey = DrawEditableRow("Game Key", GameKey); + ClientVersion = DrawEditableRow("Client Version", ClientVersion); + DeviceId = DrawEditableRow("Device Id", DeviceId); + ValidateSessionOnInitialize = DrawToggleRow("Validate Session On Init", ValidateSessionOnInitialize); + + GUILayout.BeginHorizontal(); + DrawButton("Initialize", InitializeAsync); + DrawButton("Reinitialize", ReinitializeAsync); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawLoginSection() + { + BeginSection("Login"); + LoginProvider = DrawEditableRow("Login Provider", LoginProvider); + LoginUserId = DrawEditableRow("Login User Id", LoginUserId); + LoginCode = DrawEditableRow("Login Code", LoginCode); + Nickname = DrawEditableRow("Nickname", Nickname); + AvatarUrl = DrawEditableRow("Avatar Url", AvatarUrl); + + GUILayout.BeginHorizontal(); + DrawButton("Login By UserId", LoginWithUserIdAsync, Brisk.IsInitialized); + DrawButton("Login By Code", LoginWithCodeAsync, Brisk.IsInitialized); + DrawButton("Logout", LogoutAsync, Brisk.IsInitialized); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawPlayerAndConfigSection() + { + BeginSection("Player And Config"); + GUILayout.BeginHorizontal(); + DrawButton("Get Me", GetMeAsync, Brisk.IsLoggedIn); + DrawButton("Get Config Current", GetConfigAsync, Brisk.IsInitialized); + DrawButton("Apply Current Identity To Space", () => + { + ApplyCurrentIdentityToSpace(); + SetResult("Space Lookup Identity", new Dictionary + { + { "space_player_id", SpacePlayerId }, + { "space_login_provider", SpaceLoginProvider }, + { "space_login_user_id", SpaceLoginUserId } + }); + return Task.CompletedTask; + }, Brisk.IsInitialized); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawAnnouncementsSection() + { + BeginSection("Announcements"); + AnnouncementId = DrawEditableRow("Announcement Id", AnnouncementId); + + GUILayout.BeginHorizontal(); + DrawButton("Get Announcement List", GetAnnouncementsAsync, Brisk.IsLoggedIn); + DrawButton("Mark Read", MarkAnnouncementAsync, Brisk.IsLoggedIn); + DrawButton("Mark First Cached", MarkFirstCachedAnnouncementAsync, Brisk.IsLoggedIn && _announcementCache.Count > 0); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawLeaderboardSection() + { + BeginSection("Leaderboard"); + RankKey = DrawEditableRow("Rank Key", RankKey); + SubmitScoreValue = DrawEditableRow("Submit Score", SubmitScoreValue); + LeaderboardLimit = DrawEditableRow("Top Limit", LeaderboardLimit); + AroundMeRange = DrawEditableRow("Around Range", AroundMeRange); + SeasonId = DrawEditableRow("Season Id", SeasonId); + SeasonHistoryLimit = DrawEditableRow("History Limit", SeasonHistoryLimit); + + GUILayout.BeginHorizontal(); + DrawButton("Get Top", GetTopAsync, Brisk.IsLoggedIn); + DrawButton("Get My Rank", GetMyRankAsync, Brisk.IsLoggedIn); + DrawButton("Get Around Me", GetAroundMeAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + DrawButton("Submit Score", SubmitScoreAsync, Brisk.IsLoggedIn); + DrawButton("Get Current Season", GetCurrentSeasonAsync, Brisk.IsLoggedIn); + DrawButton("Get Season History", GetSeasonHistoryAsync, Brisk.IsLoggedIn); + DrawButton("Get Season Detail", GetSeasonDetailAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawArchiveSection() + { + BeginSection("Archive"); + ArchiveSlotNo = DrawEditableRow("Slot No", ArchiveSlotNo); + ArchiveBaseVersion = DrawEditableRow("Base Version", ArchiveBaseVersion); + ArchiveContent = DrawTextAreaRow("Archive Content", ArchiveContent, 90f); + + GUILayout.BeginHorizontal(); + DrawButton("Get Slots", GetArchiveSlotsAsync, Brisk.IsLoggedIn); + DrawButton("Get Meta", GetArchiveMetaAsync, Brisk.IsLoggedIn); + DrawButton("Upload Text", UploadArchiveAsync, Brisk.IsLoggedIn); + DrawButton("Download", DownloadArchiveAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawSpaceSection() + { + BeginSection("Space"); + SpacePlayerId = DrawEditableRow("Space Player Id", SpacePlayerId); + SpaceLoginProvider = DrawEditableRow("Space Login Provider", SpaceLoginProvider); + SpaceLoginUserId = DrawEditableRow("Space Login User Id", SpaceLoginUserId); + SpacePayloadText = DrawTextAreaRow("Space Payload Text", SpacePayloadText, 90f); + + GUILayout.BeginHorizontal(); + DrawButton("Get By PlayerId", GetSpaceByPlayerIdAsync, Brisk.IsLoggedIn); + DrawButton("Get By Login", GetSpaceByLoginAsync, Brisk.IsLoggedIn); + DrawButton("Get Stats By PlayerId", GetSpaceStatsByPlayerIdAsync, Brisk.IsLoggedIn); + DrawButton("Get Stats By Login", GetSpaceStatsByLoginAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + DrawButton("Like PlayerId", LikeByPlayerIdAsync, Brisk.IsLoggedIn); + DrawButton("Unlike PlayerId", UnlikeByPlayerIdAsync, Brisk.IsLoggedIn); + DrawButton("Like Login", LikeByLoginAsync, Brisk.IsLoggedIn); + DrawButton("Unlike Login", UnlikeByLoginAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + DrawButton("Update My Space", UpdateMySpaceAsync, Brisk.IsLoggedIn); + DrawButton("Get My Visits", GetMyVisitsAsync, Brisk.IsLoggedIn); + GUILayout.EndHorizontal(); + EndSection(); + } + + private void DrawOutputSection() + { + BeginSection("Output"); + GUILayout.Label("Latest Result", GUI.skin.label); + _resultScroll = GUILayout.BeginScrollView(_resultScroll, GUILayout.Height(240f)); + GUILayout.TextArea(_resultText, GUILayout.ExpandHeight(true)); + GUILayout.EndScrollView(); + + GUILayout.Space(8f); + GUILayout.Label("Latest Error", GUI.skin.label); + GUILayout.TextArea(string.IsNullOrWhiteSpace(_lastErrorText) ? "No error." : _lastErrorText, GUILayout.Height(90f)); + + GUILayout.Space(8f); + GUILayout.Label("Event Log", GUI.skin.label); + _logScroll = GUILayout.BeginScrollView(_logScroll, GUILayout.Height(220f)); + GUILayout.TextArea(_eventLogs.Count == 0 ? "No events yet." : string.Join("\n", _eventLogs.ToArray()), GUILayout.ExpandHeight(true)); + GUILayout.EndScrollView(); + EndSection(); + } + + private async Task InitializeAsync() + { + await Brisk.InitializeAsync(new BriskOptions + { + BaseUrl = BaseUrl, + GameKey = GameKey, + ClientVersion = ClientVersion, + DeviceId = DeviceId, + ValidateSessionOnInitialize = ValidateSessionOnInitialize, + ExitHandler = HandleExitRequested + }); + + SetResult("Initialize Result", Brisk.Bootstrap); + } + + private async Task ReinitializeAsync() + { + if (Brisk.IsInitialized) + { + Brisk.Shutdown(); + Log("SDK shutdown before reinitialize."); + } + + await InitializeAsync(); + } + + private async Task LoginWithUserIdAsync() + { + var result = await Brisk.Auth.LoginWithUserIdAsync(LoginProvider, LoginUserId, CreateProfile()); + ApplyIdentity(result.PlayerId, result.LoginProvider, result.LoginUserId); + SetResult("Login By UserId Result", result); + } + + private async Task LoginWithCodeAsync() + { + var result = await Brisk.Auth.LoginWithCodeAsync(LoginProvider, LoginCode, CreateProfile()); + ApplyIdentity(result.PlayerId, result.LoginProvider, result.LoginUserId); + SetResult("Login By Code Result", result); + } + + private async Task LogoutAsync() + { + await Brisk.Auth.LogoutAsync(); + SetResult("Logout Result", new Dictionary + { + { "logged_in", Brisk.IsLoggedIn }, + { "player_id", Brisk.PlayerId } + }); + } + + private async Task GetMeAsync() + { + var me = await Brisk.Player.GetMeAsync(); + ApplyIdentity(me.PlayerId, me.LoginProvider, me.LoginUserId); + SetResult("Player Me", me); + } + + private async Task GetConfigAsync() + { + var config = await Brisk.Config.GetCurrentAsync(); + SetResult("Config Current", config); + } + + private async Task GetAnnouncementsAsync() + { + _announcementCache = await Brisk.Announcements.GetListAsync(); + if (_announcementCache.Count > 0 && string.IsNullOrWhiteSpace(AnnouncementId)) + { + AnnouncementId = _announcementCache[0].Id.ToString(); + } + + SetResult("Announcements", _announcementCache); + } + + private async Task MarkAnnouncementAsync() + { + var id = ParseRequiredLong(AnnouncementId, nameof(AnnouncementId)); + await Brisk.Announcements.MarkReadAsync(id); + SetResult("Announcement Marked Read", new Dictionary { { "announcement_id", id } }); + } + + private async Task MarkFirstCachedAnnouncementAsync() + { + if (_announcementCache.Count == 0) + { + throw new InvalidOperationException("Announcement cache is empty. Run Get Announcement List first."); + } + + var id = _announcementCache[0].Id; + AnnouncementId = id.ToString(); + await Brisk.Announcements.MarkReadAsync(id); + SetResult("First Cached Announcement Marked Read", _announcementCache[0]); + } + + private async Task GetTopAsync() + { + var result = await Brisk.Leaderboard.GetTopAsync(RankKey, ParseOptionalInt(LeaderboardLimit, 10)); + SetResult("Leaderboard Top", result); + } + + private async Task GetMyRankAsync() + { + var result = await Brisk.Leaderboard.GetMeAsync(RankKey); + SetResult("Leaderboard My Rank", result); + } + + private async Task GetAroundMeAsync() + { + var result = await Brisk.Leaderboard.GetAroundMeAsync(RankKey, ParseOptionalInt(AroundMeRange, 5)); + SetResult("Leaderboard Around Me", result); + } + + private async Task SubmitScoreAsync() + { + var score = ParseRequiredLong(SubmitScoreValue, nameof(SubmitScoreValue)); + await Brisk.Leaderboard.SubmitScoreAsync(RankKey, score); + SetResult("Score Submitted", new Dictionary + { + { "rank_key", RankKey }, + { "score", score } + }); + } + + private async Task GetCurrentSeasonAsync() + { + var result = await Brisk.Leaderboard.GetCurrentSeasonAsync(RankKey); + SeasonId = result == null ? SeasonId : result.SeasonId; + SetResult("Current Season", result); + } + + private async Task GetSeasonHistoryAsync() + { + _seasonHistoryCache = await Brisk.Leaderboard.GetSeasonHistoryAsync(RankKey, ParseOptionalInt(SeasonHistoryLimit, 20)); + if (_seasonHistoryCache.Count > 0 && string.IsNullOrWhiteSpace(SeasonId)) + { + SeasonId = _seasonHistoryCache[0].SeasonId; + } + + SetResult("Season History", _seasonHistoryCache); + } + + private async Task GetSeasonDetailAsync() + { + var result = await Brisk.Leaderboard.GetSeasonHistoryDetailAsync(RankKey, SeasonId, ParseOptionalInt(SeasonHistoryLimit, 20)); + SetResult("Season Detail", result); + } + + private async Task GetArchiveSlotsAsync() + { + var result = await Brisk.Archive.GetSlotsAsync(); + SetResult("Archive Slots", result); + } + + private async Task GetArchiveMetaAsync() + { + var result = await Brisk.Archive.GetMetaAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo))); + ArchiveBaseVersion = result == null ? ArchiveBaseVersion : result.Version.ToString(); + SetResult("Archive Meta", result); + } + + private async Task UploadArchiveAsync() + { + var slotNo = ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo)); + var baseVersion = ParseNullableInt(ArchiveBaseVersion); + var bytes = Encoding.UTF8.GetBytes(ArchiveContent ?? string.Empty); + var result = await Brisk.Archive.UploadAsync(slotNo, bytes, baseVersion); + ArchiveBaseVersion = result == null ? ArchiveBaseVersion : result.Version.ToString(); + SetResult("Archive Upload", result); + } + + private async Task DownloadArchiveAsync() + { + var result = await Brisk.Archive.DownloadAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo))); + ArchiveBaseVersion = result == null ? ArchiveBaseVersion : result.Version.ToString(); + + var output = new Dictionary + { + { "version", result.Version }, + { "checksum", result.Checksum }, + { "byte_length", result.Bytes == null ? 0 : result.Bytes.Length }, + { "text_preview", result.Bytes == null ? string.Empty : Encoding.UTF8.GetString(result.Bytes) } + }; + + SetResult("Archive Download", output); + } + + private async Task GetSpaceByPlayerIdAsync() + { + var result = await Brisk.Space.GetByPlayerIdAsync(SpacePlayerId); + SetResult("Space By PlayerId", result); + } + + private async Task GetSpaceByLoginAsync() + { + var result = await Brisk.Space.GetByLoginIdentityAsync(SpaceLoginProvider, SpaceLoginUserId); + SetResult("Space By Login", result); + } + + private async Task GetSpaceStatsByPlayerIdAsync() + { + var result = await Brisk.Space.GetStatsByPlayerIdAsync(SpacePlayerId); + SetResult("Space Stats By PlayerId", result); + } + + private async Task GetSpaceStatsByLoginAsync() + { + var result = await Brisk.Space.GetStatsByLoginIdentityAsync(SpaceLoginProvider, SpaceLoginUserId); + SetResult("Space Stats By Login", result); + } + + private async Task LikeByPlayerIdAsync() + { + await Brisk.Space.LikeByPlayerIdAsync(SpacePlayerId); + SetResult("Like By PlayerId", new Dictionary { { "player_id", SpacePlayerId } }); + } + + private async Task UnlikeByPlayerIdAsync() + { + await Brisk.Space.UnlikeByPlayerIdAsync(SpacePlayerId); + SetResult("Unlike By PlayerId", new Dictionary { { "player_id", SpacePlayerId } }); + } + + private async Task LikeByLoginAsync() + { + await Brisk.Space.LikeByLoginIdentityAsync(SpaceLoginProvider, SpaceLoginUserId); + SetResult("Like By Login", new Dictionary + { + { "login_provider", SpaceLoginProvider }, + { "login_user_id", SpaceLoginUserId } + }); + } + + private async Task UnlikeByLoginAsync() + { + await Brisk.Space.UnlikeByLoginIdentityAsync(SpaceLoginProvider, SpaceLoginUserId); + SetResult("Unlike By Login", new Dictionary + { + { "login_provider", SpaceLoginProvider }, + { "login_user_id", SpaceLoginUserId } + }); + } + + private async Task UpdateMySpaceAsync() + { + var payload = new Dictionary + { + { "sample_text", SpacePayloadText }, + { "updated_at", DateTimeOffset.UtcNow.ToString("O") }, + { "player_id", Brisk.PlayerId }, + { "rank_key", RankKey } + }; + + await Brisk.Space.UpdateMyAsync(payload); + SetResult("Update My Space", payload); + } + + private async Task GetMyVisitsAsync() + { + var result = await Brisk.Space.GetMyVisitsAsync(); + SetResult("My Space Visits", result); + } + + private async Task RunSmokeFlowAsync() + { + await InitializeAsync(); + + if (!Brisk.IsLoggedIn) + { + await LoginWithUserIdAsync(); + } + + await GetMeAsync(); + await GetConfigAsync(); + await GetTopAsync(); + await GetAnnouncementsAsync(); + await GetArchiveSlotsAsync(); + await GetMyVisitsAsync(); + } + + private void DrawButton(string label, Func action, bool enabled = true) + { + var previous = GUI.enabled; + GUI.enabled = previous && !_isBusy && enabled; + if (GUILayout.Button(label, GUILayout.Height(30f))) + { + RunAction(label, action); + } + + GUI.enabled = previous; + } + + private void RunAction(string actionName, Func action) + { + if (_isBusy) + { + return; + } + + RunActionAsync(actionName, action); + } + + private async void RunActionAsync(string actionName, Func action) + { + _isBusy = true; + _busyAction = actionName; + _statusText = "Running: " + actionName; + _lastErrorText = string.Empty; + Log("Start: " + actionName); + + try + { + await action(); + _statusText = "Success: " + actionName; + Log("Success: " + actionName); + } + catch (Exception exception) + { + _statusText = "Failed: " + actionName; + _lastErrorText = FormatException(exception); + Log("Failed: " + actionName + " | " + exception.GetType().Name + " | " + exception.Message); + Debug.LogException(exception, this); + } + finally + { + _busyAction = string.Empty; + _isBusy = false; + } + } + + private void SetResult(string title, object value) + { + _resultText = title + "\n" + new string('=', title.Length) + "\n" + FormatValue(value); + } + + private void ApplyIdentity(string playerId, string loginProvider, string loginUserId) + { + if (!string.IsNullOrWhiteSpace(playerId)) + { + SpacePlayerId = playerId; + } + + if (!string.IsNullOrWhiteSpace(loginProvider)) + { + SpaceLoginProvider = loginProvider; + LoginProvider = loginProvider; + } + + if (!string.IsNullOrWhiteSpace(loginUserId)) + { + SpaceLoginUserId = loginUserId; + LoginUserId = loginUserId; + } + } + + private void ApplyCurrentIdentityToSpace() + { + if (Brisk.Identity != null) + { + ApplyIdentity(Brisk.Identity.PlayerId, Brisk.Identity.LoginProvider, Brisk.Identity.LoginUserId); + return; + } + + if (Brisk.IsInitialized) + { + ApplyIdentity(Brisk.PlayerId, LoginProvider, LoginUserId); + } + } + + private BriskProfile CreateProfile() + { + return new BriskProfile + { + Nickname = Nickname, + AvatarUrl = AvatarUrl + }; + } + + private void HandleInitialized() + { + Log("Event: OnInitialized"); + } + + private void HandleLoggedIn() + { + Log("Event: OnLoggedIn"); + ApplyCurrentIdentityToSpace(); + } + + private void HandleLoggedOut() + { + Log("Event: OnLoggedOut"); + } + + private void HandleAuthExpired(BriskAuthExpiredException exception) + { + Log("Event: OnAuthExpired | " + exception.Message); + } + + private void HandleBlockingError(BriskBlockingException exception) + { + Log("Event: OnBlockingError | " + exception.Message); + } + + private void HandleExitRequested() + { + Log("Exit requested by Brisk blocking flow."); + } + + private void Log(string message) + { + var line = "[" + DateTime.Now.ToString("HH:mm:ss") + "] " + message; + _eventLogs.Add(line); + if (_eventLogs.Count > 120) + { + _eventLogs.RemoveAt(0); + } + + _logScroll.y = float.MaxValue; + } + + private static int ParseRequiredInt(string value, string fieldName) + { + if (!int.TryParse(value, out var result)) + { + throw new InvalidOperationException(fieldName + " must be a valid integer."); + } + + return result; + } + + private static long ParseRequiredLong(string value, string fieldName) + { + if (!long.TryParse(value, out var result)) + { + throw new InvalidOperationException(fieldName + " must be a valid integer."); + } + + return result; + } + + private static int ParseOptionalInt(string value, int fallback) + { + return int.TryParse(value, out var result) ? result : fallback; + } + + private static int? ParseNullableInt(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + return int.TryParse(value, out var result) ? result : (int?)null; + } + + private static string FormatException(Exception exception) + { + if (exception == null) + { + return "Unknown error."; + } + + var builder = new StringBuilder(); + var current = exception; + while (current != null) + { + builder.AppendLine(current.GetType().Name + ": " + current.Message); + current = current.InnerException; + } + + return builder.ToString().TrimEnd(); + } + + private static string FormatValue(object value) + { + var builder = new StringBuilder(); + AppendValue(builder, value, 0, 0); + return builder.ToString().TrimEnd(); + } + + private static void AppendValue(StringBuilder builder, object value, int indent, int depth) + { + if (depth > 5) + { + builder.AppendLine(Indent(indent) + "..."); + return; + } + + if (value == null) + { + builder.AppendLine(Indent(indent) + "null"); + return; + } + + if (value is string stringValue) + { + builder.AppendLine(Indent(indent) + stringValue); + return; + } + + if (value is byte[] bytes) + { + builder.AppendLine(Indent(indent) + "byte[" + bytes.Length + "]"); + return; + } + + var type = value.GetType(); + if (type.IsPrimitive || value is decimal) + { + builder.AppendLine(Indent(indent) + value); + return; + } + + if (value is IDictionary dictionary) + { + foreach (DictionaryEntry entry in dictionary) + { + builder.AppendLine(Indent(indent) + entry.Key + ":"); + AppendValue(builder, entry.Value, indent + 2, depth + 1); + } + + return; + } + + if (value is IEnumerable enumerable) + { + var index = 0; + foreach (var item in enumerable) + { + builder.AppendLine(Indent(indent) + "[" + index + "]"); + AppendValue(builder, item, indent + 2, depth + 1); + index++; + } + + if (index == 0) + { + builder.AppendLine(Indent(indent) + "(empty)"); + } + + return; + } + + var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public); + if (fields.Length == 0) + { + builder.AppendLine(Indent(indent) + value); + return; + } + + foreach (var field in fields) + { + builder.AppendLine(Indent(indent) + field.Name + ":"); + AppendValue(builder, field.GetValue(value), indent + 2, depth + 1); + } + } + + private static string Indent(int indent) + { + return new string(' ', indent); + } + + private static void BeginSection(string title) + { + GUILayout.Space(10f); + GUILayout.BeginVertical(GUI.skin.box); + GUILayout.Label(title, GUI.skin.label); + GUILayout.Space(4f); + } + + private static void EndSection() + { + GUILayout.EndVertical(); + } + + private static string DrawEditableRow(string label, string value) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(label, GUILayout.Width(180f)); + var next = GUILayout.TextField(value ?? string.Empty); + GUILayout.EndHorizontal(); + return next; + } + + private static string DrawTextAreaRow(string label, string value, float height) + { + GUILayout.Label(label, GUILayout.Width(180f)); + return GUILayout.TextArea(value ?? string.Empty, GUILayout.Height(height)); + } + + private static bool DrawToggleRow(string label, bool value) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(label, GUILayout.Width(180f)); + var next = GUILayout.Toggle(value, value ? "Enabled" : "Disabled"); + GUILayout.EndHorizontal(); + return next; + } + + private static void DrawReadOnlyRow(string label, string value) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(label, GUILayout.Width(180f)); + GUILayout.Label(string.IsNullOrWhiteSpace(value) ? "-" : value, GUI.skin.box); + GUILayout.EndHorizontal(); + } +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs.meta new file mode 100644 index 0000000..dba2122 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartSample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 460c182110b2fb3419fd18a624ac8b23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity new file mode 100644 index 0000000..93e35f1 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity @@ -0,0 +1,276 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 0 + m_HDR: 1 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1600000000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1600000001} + - component: {fileID: 1600000002} + m_Layer: 0 + m_Name: Brisk Sample Runner + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1600000001 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600000000} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1600000002 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600000000} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 460c182110b2fb3419fd18a624ac8b23, type: 3} + m_Name: + m_EditorClassIdentifier: + BaseUrl: https://brisk.lightyears.ltd + GameKey: demo-game + ClientVersion: 1.0.0 + DeviceId: editor-device + ValidateSessionOnInitialize: 1 + LoginProvider: tap + LoginUserId: tap_user_10001 + LoginCode: + Nickname: Unity Sample User + AvatarUrl: + RankKey: season-score + SubmitScoreValue: 128 + LeaderboardLimit: 10 + AroundMeRange: 5 + SeasonId: + SeasonHistoryLimit: 20 + ArchiveSlotNo: 1 + ArchiveBaseVersion: + ArchiveContent: '{"save":1,"coins":128,"hero":"mage"}' + AnnouncementId: + SpacePlayerId: + SpaceLoginProvider: tap + SpaceLoginUserId: tap_user_10001 + SpacePayloadText: '{"mood":"ready","title":"hello brisk"}' + AutoRunOnStart: 0 diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity.meta new file mode 100644 index 0000000..d5a1ed3 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/BriskQuickStartScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b46771630e3d468cb63c9440b79c3d15 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef new file mode 100644 index 0000000..1dccfde --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef @@ -0,0 +1,16 @@ +{ + "name": "FoldCC.CCFramework.BriskGameServer.Samples", + "rootNamespace": "FoldCC.CCFramework.BriskGameServer.Samples", + "references": [ + "FoldCC.CCFramework.BriskGameServer.Runtime" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta new file mode 100644 index 0000000..670721f --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Samples~/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb967fe96da344fe857f47cc0b935c7e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tools/Sync-BriskPackage.ps1 b/Tools/Sync-BriskPackage.ps1 new file mode 100644 index 0000000..099de6d --- /dev/null +++ b/Tools/Sync-BriskPackage.ps1 @@ -0,0 +1,69 @@ +param( + [string]$ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +) + +$ErrorActionPreference = "Stop" + +function Assert-PathExists { + param( + [string]$Path, + [string]$Description + ) + + if (-not (Test-Path -LiteralPath $Path)) { + throw "$Description not found: $Path" + } +} + +function Reset-Directory { + param([string]$Path) + + if (Test-Path -LiteralPath $Path) { + Remove-Item -LiteralPath $Path -Recurse -Force + } + + New-Item -ItemType Directory -Path $Path | Out-Null +} + +function Copy-DirectoryContents { + param( + [string]$Source, + [string]$Destination + ) + + Assert-PathExists -Path $Source -Description "Source directory" + Reset-Directory -Path $Destination + + Get-ChildItem -LiteralPath $Source -Force | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force + } +} + +$packageRoot = Join-Path $ProjectRoot "PackageSource/com.foldcc.cc-framework.BriskGameServer" +$runtimeSource = Join-Path $ProjectRoot "Assets/BriskSdk/Runtime" +$samplesSource = Join-Path $ProjectRoot "Assets/BriskSdk/Samples/QuickStart" +$sceneSource = Join-Path $ProjectRoot "Assets/Scenes/BriskQuickStartScene.unity" +$sceneMetaSource = Join-Path $ProjectRoot "Assets/Scenes/BriskQuickStartScene.unity.meta" + +Assert-PathExists -Path $packageRoot -Description "Package root" +Assert-PathExists -Path $runtimeSource -Description "Runtime source" +Assert-PathExists -Path $samplesSource -Description "Sample source" +Assert-PathExists -Path $sceneSource -Description "Sample scene" +Assert-PathExists -Path $sceneMetaSource -Description "Sample scene meta" + +$runtimeTarget = Join-Path $packageRoot "Runtime" +$samplesTarget = Join-Path $packageRoot "Samples~/QuickStart" +$samplesRootTarget = Join-Path $packageRoot "Samples~" + +Copy-DirectoryContents -Source $runtimeSource -Destination $runtimeTarget + +if (-not (Test-Path -LiteralPath $samplesRootTarget)) { + New-Item -ItemType Directory -Path $samplesRootTarget | Out-Null +} + +Copy-DirectoryContents -Source $samplesSource -Destination $samplesTarget +Copy-Item -LiteralPath $sceneSource -Destination $samplesTarget -Force +Copy-Item -LiteralPath $sceneMetaSource -Destination $samplesTarget -Force + +Write-Host "Brisk package source synced." +Write-Host "Package root: $packageRoot"