commit 47f9a8bafa004da6125e5cf5981de8137612bce2 Author: CORE-FOLDCC\Core <1813547935@qq.com> Date: Fri Apr 10 22:04:51 2026 +0800 Initial Brisk Unity SDK project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a0d140 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +[Bb]uild/ +[Bb]uilds/ +[Ll]ogs/ +[Uu]serSettings/ + +MemoryCaptures/ +Recordings/ + +.vs/ +.vscode/ +.idea/ + +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +Assets/AssetStoreTools* + +sysinfo.txt +.DS_Store +Thumbs.db diff --git a/Assets/BriskSdk.meta b/Assets/BriskSdk.meta new file mode 100644 index 0000000..344d557 --- /dev/null +++ b/Assets/BriskSdk.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e20cdfd5d9d1a44fb063fb9c39fd214 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime.meta b/Assets/BriskSdk/Runtime.meta new file mode 100644 index 0000000..cced990 --- /dev/null +++ b/Assets/BriskSdk/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d0a61600db090304ba41001e462674ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Announcement.meta b/Assets/BriskSdk/Runtime/Announcement.meta new file mode 100644 index 0000000..c0ba39a --- /dev/null +++ b/Assets/BriskSdk/Runtime/Announcement.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e05fdcaece3a1344ca3e98889cba8fc7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Announcement/BriskAnnouncementsModule.cs b/Assets/BriskSdk/Runtime/Announcement/BriskAnnouncementsModule.cs new file mode 100644 index 0000000..ea46be0 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Announcement/BriskAnnouncementsModule.cs.meta b/Assets/BriskSdk/Runtime/Announcement/BriskAnnouncementsModule.cs.meta new file mode 100644 index 0000000..6379256 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Archive.meta b/Assets/BriskSdk/Runtime/Archive.meta new file mode 100644 index 0000000..698c246 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Archive.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d88c956c19b23c8499e142fff707b76c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Archive/BriskArchiveModule.cs b/Assets/BriskSdk/Runtime/Archive/BriskArchiveModule.cs new file mode 100644 index 0000000..3e6d0cd --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Archive/BriskArchiveModule.cs.meta b/Assets/BriskSdk/Runtime/Archive/BriskArchiveModule.cs.meta new file mode 100644 index 0000000..2c0f242 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Auth.meta b/Assets/BriskSdk/Runtime/Auth.meta new file mode 100644 index 0000000..a76872a --- /dev/null +++ b/Assets/BriskSdk/Runtime/Auth.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a26d0e58a94c264198010eae586c7b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Auth/BriskAuthModule.cs b/Assets/BriskSdk/Runtime/Auth/BriskAuthModule.cs new file mode 100644 index 0000000..275d4ba --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Auth/BriskAuthModule.cs.meta b/Assets/BriskSdk/Runtime/Auth/BriskAuthModule.cs.meta new file mode 100644 index 0000000..6a0b708 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/BriskSdk.Runtime.asmdef b/Assets/BriskSdk/Runtime/BriskSdk.Runtime.asmdef new file mode 100644 index 0000000..c316b49 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/BriskSdk.Runtime.asmdef.meta b/Assets/BriskSdk/Runtime/BriskSdk.Runtime.asmdef.meta new file mode 100644 index 0000000..a4a53b3 --- /dev/null +++ b/Assets/BriskSdk/Runtime/BriskSdk.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f8ab9462792dad445acc3b318dfa2372 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Config.meta b/Assets/BriskSdk/Runtime/Config.meta new file mode 100644 index 0000000..55a4901 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8b88bc9f438e4d64089acbb0ac667c03 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Config/BriskConfigModule.cs b/Assets/BriskSdk/Runtime/Config/BriskConfigModule.cs new file mode 100644 index 0000000..75e71a8 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Config/BriskConfigModule.cs.meta b/Assets/BriskSdk/Runtime/Config/BriskConfigModule.cs.meta new file mode 100644 index 0000000..ee7955d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core.meta b/Assets/BriskSdk/Runtime/Core.meta new file mode 100644 index 0000000..651f750 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a5b8196730f24c0438900010f497476f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Core/Brisk.cs b/Assets/BriskSdk/Runtime/Core/Brisk.cs new file mode 100644 index 0000000..4dd02f5 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/Brisk.cs.meta b/Assets/BriskSdk/Runtime/Core/Brisk.cs.meta new file mode 100644 index 0000000..5eeef6a --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskBinaryResponse.cs b/Assets/BriskSdk/Runtime/Core/BriskBinaryResponse.cs new file mode 100644 index 0000000..febf41b --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskBinaryResponse.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskBinaryResponse.cs.meta new file mode 100644 index 0000000..cfe0010 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskContext.cs b/Assets/BriskSdk/Runtime/Core/BriskContext.cs new file mode 100644 index 0000000..d9e8914 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskContext.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskContext.cs.meta new file mode 100644 index 0000000..9badfee --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs new file mode 100644 index 0000000..cad93d4 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs.meta new file mode 100644 index 0000000..400a845 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs new file mode 100644 index 0000000..c5781a8 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs.meta new file mode 100644 index 0000000..686c101 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskErrorClassifier.cs b/Assets/BriskSdk/Runtime/Core/BriskErrorClassifier.cs new file mode 100644 index 0000000..6f91d73 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskErrorClassifier.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskErrorClassifier.cs.meta new file mode 100644 index 0000000..92eb4e4 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskHttpClient.cs b/Assets/BriskSdk/Runtime/Core/BriskHttpClient.cs new file mode 100644 index 0000000..8c87207 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskHttpClient.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskHttpClient.cs.meta new file mode 100644 index 0000000..a6b13f4 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskJson.cs b/Assets/BriskSdk/Runtime/Core/BriskJson.cs new file mode 100644 index 0000000..3aa4c91 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskJson.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskJson.cs.meta new file mode 100644 index 0000000..5f2af0b --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModelMapper.cs b/Assets/BriskSdk/Runtime/Core/BriskModelMapper.cs new file mode 100644 index 0000000..6fd9cd3 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModelMapper.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskModelMapper.cs.meta new file mode 100644 index 0000000..041403f --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModuleBase.cs b/Assets/BriskSdk/Runtime/Core/BriskModuleBase.cs new file mode 100644 index 0000000..8e707e7 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModuleBase.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskModuleBase.cs.meta new file mode 100644 index 0000000..bca65f0 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModuleExecutor.cs b/Assets/BriskSdk/Runtime/Core/BriskModuleExecutor.cs new file mode 100644 index 0000000..3651dc0 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskModuleExecutor.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskModuleExecutor.cs.meta new file mode 100644 index 0000000..2ef94b9 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskOptions.cs b/Assets/BriskSdk/Runtime/Core/BriskOptions.cs new file mode 100644 index 0000000..a54deae --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskOptions.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskOptions.cs.meta new file mode 100644 index 0000000..9a9f896 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskSession.cs b/Assets/BriskSdk/Runtime/Core/BriskSession.cs new file mode 100644 index 0000000..eedd2fc --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskSession.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskSession.cs.meta new file mode 100644 index 0000000..e15aec4 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskValueReader.cs b/Assets/BriskSdk/Runtime/Core/BriskValueReader.cs new file mode 100644 index 0000000..2723fd7 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskValueReader.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskValueReader.cs.meta new file mode 100644 index 0000000..4e4ad76 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskVersionComparer.cs b/Assets/BriskSdk/Runtime/Core/BriskVersionComparer.cs new file mode 100644 index 0000000..0e06ff7 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/BriskVersionComparer.cs.meta b/Assets/BriskSdk/Runtime/Core/BriskVersionComparer.cs.meta new file mode 100644 index 0000000..a2d690d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/IBriskErrorPresenter.cs b/Assets/BriskSdk/Runtime/Core/IBriskErrorPresenter.cs new file mode 100644 index 0000000..8dddfe3 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Core/IBriskErrorPresenter.cs @@ -0,0 +1,6 @@ +public interface IBriskErrorPresenter +{ + void ShowBlockingError(BriskBlockingException exception); + + void ShowAuthExpired(BriskAuthExpiredException exception); +} diff --git a/Assets/BriskSdk/Runtime/Core/IBriskErrorPresenter.cs.meta b/Assets/BriskSdk/Runtime/Core/IBriskErrorPresenter.cs.meta new file mode 100644 index 0000000..45dd2af --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/IBriskTokenStore.cs b/Assets/BriskSdk/Runtime/Core/IBriskTokenStore.cs new file mode 100644 index 0000000..9b452ff --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Core/IBriskTokenStore.cs.meta b/Assets/BriskSdk/Runtime/Core/IBriskTokenStore.cs.meta new file mode 100644 index 0000000..5dbc371 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions.meta b/Assets/BriskSdk/Runtime/Exceptions.meta new file mode 100644 index 0000000..6ca7c45 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Exceptions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49fea6e9e08845941b4ed3bb8e2beb68 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Exceptions/BriskAccountBannedException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskAccountBannedException.cs new file mode 100644 index 0000000..1dec9f9 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskAccountBannedException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskAccountBannedException.cs.meta new file mode 100644 index 0000000..5633a30 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskApiException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskApiException.cs new file mode 100644 index 0000000..b474b7a --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskApiException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskApiException.cs.meta new file mode 100644 index 0000000..e1dff94 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskAuthExpiredException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskAuthExpiredException.cs new file mode 100644 index 0000000..35218ab --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskAuthExpiredException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskAuthExpiredException.cs.meta new file mode 100644 index 0000000..ec908dc --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskBlockingException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskBlockingException.cs new file mode 100644 index 0000000..6f94c5e --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskBlockingException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskBlockingException.cs.meta new file mode 100644 index 0000000..d655606 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskClientUpdateRequiredException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskClientUpdateRequiredException.cs new file mode 100644 index 0000000..d4a996d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskClientUpdateRequiredException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskClientUpdateRequiredException.cs.meta new file mode 100644 index 0000000..8648fe3 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskException.cs new file mode 100644 index 0000000..9552d0f --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskException.cs.meta new file mode 100644 index 0000000..5330e4f --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskHttpException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskHttpException.cs new file mode 100644 index 0000000..996d437 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskHttpException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskHttpException.cs.meta new file mode 100644 index 0000000..344c5f7 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskMaintenanceException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskMaintenanceException.cs new file mode 100644 index 0000000..c4b2835 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskMaintenanceException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskMaintenanceException.cs.meta new file mode 100644 index 0000000..ff7181f --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskNetworkException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskNetworkException.cs new file mode 100644 index 0000000..bbdfbf3 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskNetworkException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskNetworkException.cs.meta new file mode 100644 index 0000000..b52fa6d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskNotInitializedException.cs b/Assets/BriskSdk/Runtime/Exceptions/BriskNotInitializedException.cs new file mode 100644 index 0000000..a76d948 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Exceptions/BriskNotInitializedException.cs.meta b/Assets/BriskSdk/Runtime/Exceptions/BriskNotInitializedException.cs.meta new file mode 100644 index 0000000..808dd92 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Leaderboard.meta b/Assets/BriskSdk/Runtime/Leaderboard.meta new file mode 100644 index 0000000..d43e65b --- /dev/null +++ b/Assets/BriskSdk/Runtime/Leaderboard.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d16fffbab52af64eb1906f343d5382d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Leaderboard/BriskLeaderboardModule.cs b/Assets/BriskSdk/Runtime/Leaderboard/BriskLeaderboardModule.cs new file mode 100644 index 0000000..6f8d171 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Leaderboard/BriskLeaderboardModule.cs.meta b/Assets/BriskSdk/Runtime/Leaderboard/BriskLeaderboardModule.cs.meta new file mode 100644 index 0000000..83dc89f --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models.meta b/Assets/BriskSdk/Runtime/Models.meta new file mode 100644 index 0000000..f6da7e0 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2dec6cd98bed43a4b9c2bbddd56d9db8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Models/BriskAnnouncementItem.cs b/Assets/BriskSdk/Runtime/Models/BriskAnnouncementItem.cs new file mode 100644 index 0000000..d53c10e --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskAnnouncementItem.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskAnnouncementItem.cs.meta new file mode 100644 index 0000000..e17287d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveDownloadResult.cs b/Assets/BriskSdk/Runtime/Models/BriskArchiveDownloadResult.cs new file mode 100644 index 0000000..080a979 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Models/BriskArchiveDownloadResult.cs @@ -0,0 +1,6 @@ +public sealed class BriskArchiveDownloadResult +{ + public byte[] Bytes; + public int Version; + public string Checksum; +} diff --git a/Assets/BriskSdk/Runtime/Models/BriskArchiveDownloadResult.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskArchiveDownloadResult.cs.meta new file mode 100644 index 0000000..cf9d781 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveMeta.cs b/Assets/BriskSdk/Runtime/Models/BriskArchiveMeta.cs new file mode 100644 index 0000000..808ff7b --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveMeta.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskArchiveMeta.cs.meta new file mode 100644 index 0000000..6a64a23 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveSlot.cs b/Assets/BriskSdk/Runtime/Models/BriskArchiveSlot.cs new file mode 100644 index 0000000..75113aa --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveSlot.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskArchiveSlot.cs.meta new file mode 100644 index 0000000..b8af155 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveUploadResult.cs b/Assets/BriskSdk/Runtime/Models/BriskArchiveUploadResult.cs new file mode 100644 index 0000000..d5b7c64 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskArchiveUploadResult.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskArchiveUploadResult.cs.meta new file mode 100644 index 0000000..08caa76 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskBootstrapResult.cs b/Assets/BriskSdk/Runtime/Models/BriskBootstrapResult.cs new file mode 100644 index 0000000..8558121 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskBootstrapResult.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskBootstrapResult.cs.meta new file mode 100644 index 0000000..1733c93 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskConfigCurrent.cs b/Assets/BriskSdk/Runtime/Models/BriskConfigCurrent.cs new file mode 100644 index 0000000..9f5e264 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskConfigCurrent.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskConfigCurrent.cs.meta new file mode 100644 index 0000000..90c85cb --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskIdentity.cs b/Assets/BriskSdk/Runtime/Models/BriskIdentity.cs new file mode 100644 index 0000000..fc2525a --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskIdentity.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskIdentity.cs.meta new file mode 100644 index 0000000..54f63d5 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLeaderboardEntry.cs b/Assets/BriskSdk/Runtime/Models/BriskLeaderboardEntry.cs new file mode 100644 index 0000000..3856b2a --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLeaderboardEntry.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskLeaderboardEntry.cs.meta new file mode 100644 index 0000000..92e3f84 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLeaderboardPlayerRank.cs b/Assets/BriskSdk/Runtime/Models/BriskLeaderboardPlayerRank.cs new file mode 100644 index 0000000..7840b18 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLeaderboardPlayerRank.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskLeaderboardPlayerRank.cs.meta new file mode 100644 index 0000000..6a86024 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLoginResult.cs b/Assets/BriskSdk/Runtime/Models/BriskLoginResult.cs new file mode 100644 index 0000000..536112c --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskLoginResult.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskLoginResult.cs.meta new file mode 100644 index 0000000..2f8767b --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskPlayerMe.cs b/Assets/BriskSdk/Runtime/Models/BriskPlayerMe.cs new file mode 100644 index 0000000..5c929c2 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskPlayerMe.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskPlayerMe.cs.meta new file mode 100644 index 0000000..ccbd487 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskProfile.cs b/Assets/BriskSdk/Runtime/Models/BriskProfile.cs new file mode 100644 index 0000000..7acce66 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Models/BriskProfile.cs @@ -0,0 +1,6 @@ +public sealed class BriskProfile +{ + public string Nickname; + public string AvatarUrl; + public object ProfileJson; +} diff --git a/Assets/BriskSdk/Runtime/Models/BriskProfile.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskProfile.cs.meta new file mode 100644 index 0000000..9bc1717 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskRankSeasonInfo.cs b/Assets/BriskSdk/Runtime/Models/BriskRankSeasonInfo.cs new file mode 100644 index 0000000..2aef9dc --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskRankSeasonInfo.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskRankSeasonInfo.cs.meta new file mode 100644 index 0000000..be1c4a8 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskSpaceStats.cs b/Assets/BriskSdk/Runtime/Models/BriskSpaceStats.cs new file mode 100644 index 0000000..d59e7e1 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Models/BriskSpaceStats.cs @@ -0,0 +1,5 @@ +public sealed class BriskSpaceStats +{ + public int LikeCount; + public int VisitCount; +} diff --git a/Assets/BriskSdk/Runtime/Models/BriskSpaceStats.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskSpaceStats.cs.meta new file mode 100644 index 0000000..b687bf3 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskSpaceView.cs b/Assets/BriskSdk/Runtime/Models/BriskSpaceView.cs new file mode 100644 index 0000000..9118e09 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskSpaceView.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskSpaceView.cs.meta new file mode 100644 index 0000000..e1d7882 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskSpaceVisit.cs b/Assets/BriskSdk/Runtime/Models/BriskSpaceVisit.cs new file mode 100644 index 0000000..4a8c219 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskSpaceVisit.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskSpaceVisit.cs.meta new file mode 100644 index 0000000..4c50059 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskStoredSession.cs b/Assets/BriskSdk/Runtime/Models/BriskStoredSession.cs new file mode 100644 index 0000000..7bc978c --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Models/BriskStoredSession.cs.meta b/Assets/BriskSdk/Runtime/Models/BriskStoredSession.cs.meta new file mode 100644 index 0000000..989166d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Player.meta b/Assets/BriskSdk/Runtime/Player.meta new file mode 100644 index 0000000..0611b7e --- /dev/null +++ b/Assets/BriskSdk/Runtime/Player.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f43770549c50484a86f41d3b665031f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Player/BriskPlayerModule.cs b/Assets/BriskSdk/Runtime/Player/BriskPlayerModule.cs new file mode 100644 index 0000000..6fdde7a --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Player/BriskPlayerModule.cs.meta b/Assets/BriskSdk/Runtime/Player/BriskPlayerModule.cs.meta new file mode 100644 index 0000000..c04d937 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Space.meta b/Assets/BriskSdk/Runtime/Space.meta new file mode 100644 index 0000000..fa37585 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Space.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa5f23f84a311c14c8f7122d88f877b6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Space/BriskSpaceModule.cs b/Assets/BriskSdk/Runtime/Space/BriskSpaceModule.cs new file mode 100644 index 0000000..18f4266 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Space/BriskSpaceModule.cs.meta b/Assets/BriskSdk/Runtime/Space/BriskSpaceModule.cs.meta new file mode 100644 index 0000000..d83cb99 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Storage.meta b/Assets/BriskSdk/Runtime/Storage.meta new file mode 100644 index 0000000..0b14562 --- /dev/null +++ b/Assets/BriskSdk/Runtime/Storage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fbb6f72c75fb7b04c8c61dde77ad54db +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Runtime/Storage/BriskPlayerPrefsTokenStore.cs b/Assets/BriskSdk/Runtime/Storage/BriskPlayerPrefsTokenStore.cs new file mode 100644 index 0000000..6e30c85 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Runtime/Storage/BriskPlayerPrefsTokenStore.cs.meta b/Assets/BriskSdk/Runtime/Storage/BriskPlayerPrefsTokenStore.cs.meta new file mode 100644 index 0000000..cbe8595 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Samples.meta b/Assets/BriskSdk/Samples.meta new file mode 100644 index 0000000..42b5af0 --- /dev/null +++ b/Assets/BriskSdk/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 64068fc4b75ca2c438106c63e4f8ae95 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Samples/QuickStart.meta b/Assets/BriskSdk/Samples/QuickStart.meta new file mode 100644 index 0000000..57ba976 --- /dev/null +++ b/Assets/BriskSdk/Samples/QuickStart.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1ae9c726dbd5eeb4eb29785618aeda61 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs b/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs new file mode 100644 index 0000000..76f731d --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs.meta b/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs.meta new file mode 100644 index 0000000..dba2122 --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Samples/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef b/Assets/BriskSdk/Samples/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef new file mode 100644 index 0000000..1dccfde --- /dev/null +++ b/Assets/BriskSdk/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/Assets/BriskSdk/Samples/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta b/Assets/BriskSdk/Samples/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta new file mode 100644 index 0000000..670721f --- /dev/null +++ b/Assets/BriskSdk/Samples/QuickStart/FoldCC.CCFramework.BriskGameServer.Samples.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb967fe96da344fe857f47cc0b935c7e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta new file mode 100644 index 0000000..7fe8e10 --- /dev/null +++ b/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 131a6b21c8605f84396be9f6751fb6e3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/BriskQuickStartScene.unity b/Assets/Scenes/BriskQuickStartScene.unity new file mode 100644 index 0000000..93e35f1 --- /dev/null +++ b/Assets/Scenes/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/Assets/Scenes/BriskQuickStartScene.unity.meta b/Assets/Scenes/BriskQuickStartScene.unity.meta new file mode 100644 index 0000000..d5a1ed3 --- /dev/null +++ b/Assets/Scenes/BriskQuickStartScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b46771630e3d468cb63c9440b79c3d15 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity new file mode 100644 index 0000000..9421266 --- /dev/null +++ b/Assets/Scenes/SampleScene.unity @@ -0,0 +1,208 @@ +%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} diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/SampleScene.unity.meta new file mode 100644 index 0000000..c1e3c88 --- /dev/null +++ b/Assets/Scenes/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2cda990e2423bbf4892e6590ba056729 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Brisk Unity SDK 实施计划.md b/Brisk Unity SDK 实施计划.md new file mode 100644 index 0000000..9561b32 --- /dev/null +++ b/Brisk Unity SDK 实施计划.md @@ -0,0 +1,202 @@ +# Brisk Unity SDK 实施计划 + +本文档用于记录 Brisk Unity SDK 的分阶段实施步骤,作为当前项目的实现路线图。 + +## 阶段 1:骨架与约束 + +目标: + +- 固定 SDK 的公开外形 +- 固定目录结构与命名 +- 建立可编译的基础代码骨架 + +范围: + +- 创建并完善 `Assets/BriskSdk/Runtime` 开发结构 +- 创建顶层入口 `Brisk` +- 创建基础对象: + - `BriskOptions` + - `BriskContext` + - `BriskSession` + - `BriskIdentity` +- 创建模块入口占位: + - `Brisk.Auth` + - `Brisk.Player` + - `Brisk.Config` + - `Brisk.Announcements` + - `Brisk.Leaderboard` + - `Brisk.Archive` + - `Brisk.Space` +- 创建基础异常与核心接口占位 +- 固定首版方法签名 +- 对齐新版后端身份模型: + - 初始化不再要求 `channel / platform` + - 登录改为 `login_provider / login_user_id / code` + +阶段完成标准: + +- Unity 工程内存在完整 SDK 运行时骨架 +- 代码可编译 +- 对外 API 入口已固定为 `Brisk.xxx` + +## 阶段 2:初始化与会话基础 + +目标: + +- 打通 SDK 生命周期入口 +- 打通 bootstrap 与本地会话恢复 + +范围: + +- 实现 `Brisk.InitializeAsync` +- 规范化 `BaseUrl` +- 调用 `GET /client/bootstrap` +- 缓存 bootstrap 结果 +- 接入本地 token 存储 +- 初始化时恢复本地会话 +- 可选调用 `player/me` 校验旧会话有效性 + +阶段完成标准: + +- SDK 能完成初始化 +- 能缓存 bootstrap 信息 +- 能恢复本地登录态 + +## 阶段 3:网络底层与严重错误处理 + +目标: + +- 打稳底层请求能力 +- 只统一接管严重错误 + +范围: + +- 实现 `BriskHttpClient` +- 实现统一 JSON 解包 +- 实现异常体系 +- 实现严重错误识别: + - 维护中 + - 封号 + - 登录态失效 +- 接入默认错误展示器 +- 支持项目方自定义严重错误展示 UI + +阶段完成标准: + +- 请求层可用 +- 严重错误可由 SDK 默认 UI 处理 +- 普通错误继续交由调用方自行处理 + +## 阶段 4:登录与玩家基础模块 + +目标: + +- 打通玩家态核心链路 + +范围: + +- 实现 `Brisk.Auth.LoginAsync` +- 或更明确地实现: + - `Brisk.Auth.LoginWithUserIdAsync` + - `Brisk.Auth.LoginWithCodeAsync` +- 实现 `Brisk.Auth.LogoutAsync` +- 实现 `Brisk.Player.GetMeAsync` +- 自动附加 Bearer Token +- 管理登录态与相关事件 + +阶段完成标准: + +- 初始化后可完成登录 +- 可获取当前玩家信息 + +## 阶段 5:首批高频业务模块 + +目标: + +- 覆盖首版最高频使用场景 + +范围: + +- `Brisk.Config` +- `Brisk.Announcements` +- `Brisk.Leaderboard` + +排行榜首版接口: + +- `GetTopAsync` +- `GetMeAsync` +- `GetAroundMeAsync` +- `SubmitScoreAsync` +- `GetCurrentSeasonAsync` +- `GetSeasonHistoryAsync` +- `GetSeasonHistoryDetailAsync` + +阶段完成标准: + +- SDK 已具备主要交付价值 +- 排行榜体验可用于首轮接入 + +## 阶段 6:差异化模块 + +目标: + +- 补齐 Brisk 的差异化能力 + +范围: + +- `Brisk.Archive` + - 槽位列表 + - 元信息 + - 上传 + - 下载 + - 版本冲突处理 + - 校验值处理 +- `Brisk.Space` + - 按 `player_id` 查询 + - 按 `login_provider + login_user_id` 查询 + - 点赞与取消点赞 + - 更新自己的空间 + - 我的访客 + +阶段完成标准: + +- 云存档和空间模块可实际使用 + +## 阶段 7:收尾与交付增强 + +目标: + +- 让 SDK 达到可交付、可接入状态 + +范围: + +- 默认本地存储实现 +- 默认错误展示 UI 完善 +- 日志开关与调试信息 +- 示例代码 +- Sample Scene +- 文档补充 +- 自测清单 + +补充约束: + +- 当前仓库作为 Unity 原始开发工程,活代码放在 `Assets/BriskSdk` +- 正式对外发包时,再从发布分支整理出 `PackageSource/com.foldcc.cc-framework.BriskGameServer` 子目录作为 UPM 包 + +阶段完成标准: + +- SDK 可以给外部开发者试接入 + +## 暂缓项 + +以下内容不阻塞首版主线: + +- 弱网模式与标准模式双策略 +- 更复杂的自动重试策略 +- 多套默认 UI 风格 +- 管理后台 SDK + +建议: + +- 首版先完成标准模式 +- 主线可用后再评估弱网模式 diff --git a/Brisk Unity SDK 快速开始.md b/Brisk Unity SDK 快速开始.md new file mode 100644 index 0000000..4ac7889 --- /dev/null +++ b/Brisk Unity SDK 快速开始.md @@ -0,0 +1,159 @@ +# Brisk Unity SDK 快速开始 + +本文档用于说明当前仓库内这套 Brisk Unity SDK 的最小接入方式。 + +## 1. 当前能力 + +当前 SDK 已经打通以下主链路: + +- 初始化 `Brisk.InitializeAsync` +- 本地会话恢复 +- 登录与登出 +- 玩家信息 +- 动态配置 +- 公告 +- 排行榜 +- 云存档 +- 玩家空间 + +当前主要源码位于: + +- `Assets/BriskSdk/Runtime` + +最小示例脚本位于: + +- `Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs` + +## 2. 最小初始化 + +```csharp +await Brisk.InitializeAsync(new BriskOptions +{ + BaseUrl = "https://brisk.lightyears.ltd", + GameKey = "demo-game", + ClientVersion = Application.version, + DeviceId = SystemInfo.deviceUniqueIdentifier +}); +``` + +说明: + +- `BaseUrl` 必填 +- `GameKey` 必填 +- `ClientVersion` 可选但强烈建议传 +- `DeviceId` 可选但建议传 + +## 3. 登录示例 + +按稳定用户 ID 登录: + +```csharp +await Brisk.Auth.LoginWithUserIdAsync("tap", "tap_user_10001", new BriskProfile +{ + Nickname = "Player One" +}); +``` + +按 code 登录: + +```csharp +await Brisk.Auth.LoginWithCodeAsync("tap", "third-party-code"); +``` + +## 4. 常用调用示例 + +读取当前玩家: + +```csharp +var me = await Brisk.Player.GetMeAsync(); +``` + +读取动态配置: + +```csharp +var config = await Brisk.Config.GetCurrentAsync(); +``` + +读取排行榜: + +```csharp +var top = await Brisk.Leaderboard.GetTopAsync("season-score", 20); +var meRank = await Brisk.Leaderboard.GetMeAsync("season-score"); +await Brisk.Leaderboard.SubmitScoreAsync("season-score", 128); +``` + +上传云存档: + +```csharp +var bytes = System.Text.Encoding.UTF8.GetBytes("{\"save\":1}"); +await Brisk.Archive.UploadAsync(1, bytes); +``` + +读取玩家空间: + +```csharp +var space = await Brisk.Space.GetByLoginIdentityAsync("tap", "tap_user_10001"); +``` + +## 5. 默认错误 UI + +当前 SDK 已内置默认错误展示器: + +- 维护中 +- 封号 +- 登录态失效 + +如果未自定义 `ErrorPresenter`,SDK 会自动使用默认 IMGUI 弹窗进行展示。 + +如需替换: + +```csharp +Brisk.SetErrorPresenter(myPresenter); +``` + +接口为: + +```csharp +public interface IBriskErrorPresenter +{ + void ShowBlockingError(BriskBlockingException exception); + void ShowAuthExpired(BriskAuthExpiredException exception); +} +``` + +如需在阻断错误确认后执行你自己的退出逻辑: + +```csharp +await Brisk.InitializeAsync(new BriskOptions +{ + BaseUrl = "...", + GameKey = "...", + ExitHandler = () => Application.Quit() +}); +``` + +## 6. 示例脚本使用方式 + +当前项目已提供: + +- `Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs` +- `Assets/Scenes/BriskQuickStartScene.unity` + +使用方式: + +1. 当前仓库是原始 Unity 开发工程,可直接打开 `Assets/Scenes/BriskQuickStartScene.unity` +2. 运行后会看到一个 IMGUI 调试面板,可直接测试初始化、登录、玩家、配置、公告、排行榜、云存档、空间等完整流程 +3. 勾选 `AutoRunOnStart` +4. 如需更换环境参数,可在 Inspector 里修改 `BaseUrl`、`GameKey`、`LoginProvider`、`LoginUserId` 等字段后再运行 + +也可以在组件右键菜单里执行: + +- `Run Brisk Sample` + +## 7. 当前实现状态 + +当前 SDK 已实现主流程,但还建议后续继续补: + +- 更细粒度的业务异常类型 +- 弱网策略 +- 更完整的接入文档与注释 diff --git a/Brisk Unity SDK 设计草案.md b/Brisk Unity SDK 设计草案.md new file mode 100644 index 0000000..0eeb0ce --- /dev/null +++ b/Brisk Unity SDK 设计草案.md @@ -0,0 +1,600 @@ +# Brisk Unity SDK 设计草案 + +本文档用于沉淀当前 Brisk Unity SDK 的首版设计方向,目标是: + +- 对开发者极简易用 +- 初始化一次后,各模块可直接调用 +- 保持内部结构清晰,便于后续扩展 +- 优先围绕当前服务端已实现接口设计,不预埋过多未来能力 + +参考资料: + +- `服务参考_临时/Brisk Unity SDK接入文档.md` +- `服务参考_临时/Brisk API接口与示例文档.md` +- `服务参考_临时/Brisk 错误码文档(内部实施版).md` +- `服务参考_临时/openapi.yaml` +- TapTap 官方 Unity 集成与排行榜文档 + +## 1. 设计目标 + +首版 SDK 对外遵循以下原则: + +- 对外统一使用 `Brisk.xxx` +- 模块能力统一使用 `Brisk.模块.xxx` +- 不暴露底层 HTTP、Envelope、Token 注入等细节 +- 初始化参数只保留项目级与设备级信息 +- 登录模型统一围绕 `login_provider / login_user_id / code` +- 初始化完成后,模块可直接调用 +- 错误码统一在 SDK 内部识别和分发 + +示例目标用法: + +```csharp +await Brisk.InitializeAsync(new BriskOptions +{ + BaseUrl = "https://brisk.lightyears.ltd", + GameKey = "demo-game", + ClientVersion = Application.version, + DeviceId = SystemInfo.deviceUniqueIdentifier +}); + +await Brisk.Auth.LoginWithUserIdAsync("tap", "tap_user_10001"); + +var me = await Brisk.Player.GetMeAsync(); +var top = await Brisk.Leaderboard.GetTopAsync("season-score", 20); +await Brisk.Leaderboard.SubmitScoreAsync("season-score", 128); +``` + +## 2. 对外 API 结构 + +首版建议公开如下 API 树: + +```csharp +Brisk.InitializeAsync(BriskOptions options) +Brisk.Shutdown() + +Brisk.IsInitialized +Brisk.IsLoggedIn +Brisk.AccessToken +Brisk.PlayerId +Brisk.Identity +Brisk.Options +Brisk.Bootstrap + +Brisk.Auth.LoginWithUserIdAsync(string loginProvider, string loginUserId, BriskProfile profile = null) +Brisk.Auth.LoginWithCodeAsync(string loginProvider, string code, BriskProfile profile = null) +Brisk.Auth.LogoutAsync() + +Brisk.Player.GetMeAsync() + +Brisk.Config.GetCurrentAsync() +Brisk.Config.RefreshAsync() + +Brisk.Announcements.GetListAsync() +Brisk.Announcements.MarkReadAsync(long id) + +Brisk.Leaderboard.GetTopAsync(string rankKey, int limit = 20) +Brisk.Leaderboard.GetMeAsync(string rankKey) +Brisk.Leaderboard.GetAroundMeAsync(string rankKey, int range = 10) +Brisk.Leaderboard.SubmitScoreAsync(string rankKey, long score) +Brisk.Leaderboard.GetCurrentSeasonAsync(string rankKey) +Brisk.Leaderboard.GetSeasonHistoryAsync(string rankKey, int limit = 20) +Brisk.Leaderboard.GetSeasonHistoryDetailAsync(string rankKey, string seasonId, int limit = 20) + +Brisk.Archive.GetSlotsAsync() +Brisk.Archive.GetMetaAsync(int slotNo) +Brisk.Archive.UploadAsync(int slotNo, byte[] bytes, int? baseVersion = null, string checksum = null) +Brisk.Archive.DownloadAsync(int slotNo) + +Brisk.Space.GetByPlayerIdAsync(string playerId) +Brisk.Space.GetByLoginIdentityAsync(string loginProvider, string loginUserId) +Brisk.Space.GetStatsByPlayerIdAsync(string playerId) +Brisk.Space.GetStatsByLoginIdentityAsync(string loginProvider, string loginUserId) +Brisk.Space.LikeByPlayerIdAsync(string playerId) +Brisk.Space.UnlikeByPlayerIdAsync(string playerId) +Brisk.Space.LikeByLoginIdentityAsync(string loginProvider, string loginUserId) +Brisk.Space.UnlikeByLoginIdentityAsync(string loginProvider, string loginUserId) +Brisk.Space.UpdateMyAsync(object payload) +Brisk.Space.GetMyVisitsAsync() +``` + +说明: + +- `bootstrap` 不单独暴露成模块入口,直接并入 `Brisk.InitializeAsync` +- `Brisk` 根节点只负责全局状态、生命周期、事件和模块入口 +- 具体业务能力放在 `Brisk.Auth`、`Brisk.Leaderboard` 等模块下 + +## 3. BriskOptions 设计 + +`BriskOptions` 只保留当前服务端确实需要的参数,并区分必填与推荐字段。 + +### 3.1 必填字段 + +根据当前服务端文档,以下字段应为初始化必填: + +```csharp +public sealed class BriskOptions +{ + public string BaseUrl; + public string GameKey; + public string ClientVersion; +} +``` + +原因: + +- `BaseUrl` + - SDK 所有接口的基础地址 + - 推荐直接传 API Base,例如 `https://host/api` +- `GameKey` + - `bootstrap`、`config/current`、`auth/login/exchange` 都需要 +- `ClientVersion` + - 当前服务端接口中为可选,但强烈建议传入 + - 会影响最低版本校验与动态配置命中 + +### 3.2 推荐字段 + +这些字段不是所有场景必填,但推荐在初始化时一次传入,后续由 SDK 自动复用: + +```csharp +public sealed class BriskOptions +{ + public string BaseUrl; + public string GameKey; + public string ClientVersion; + public string DeviceId; + public bool EnableLog = false; + public bool ValidateSessionOnInitialize = true; + public IBriskTokenStore TokenStore; + public IBriskErrorPresenter ErrorPresenter; + public Action ExitHandler; +} +``` + +说明: + +- `ClientVersion` + - `bootstrap`、`config/current`、`login` 推荐传入 + - 后续可用于最低版本校验 +- `DeviceId` + - `bootstrap`、`config/current`、`login` 推荐传入 + - 便于服务端做环境识别与问题排查 +- `EnableLog` + - 是否输出 SDK 调试日志 +- `ValidateSessionOnInitialize` + - 初始化时如果本地有 token,是否自动调用 `player/me` 校验有效性 +- `TokenStore` + - 登录态持久化存储接口 +- `ErrorPresenter` + - 默认错误弹窗展示接口 +- `ExitHandler` + - 遇到需要默认退出的阻断错误时,由宿主游戏接管实际退出逻辑 + +### 3.3 首版初始化建议 + +为了保持易用,首版建议如下: + +- 调用方最少只需要传 `BaseUrl`、`GameKey` +- `ClientVersion` 强烈建议传 +- `DeviceId` 建议有就传 +- 如果不传 `TokenStore`,SDK 使用默认本地实现 +- 如果不传 `ErrorPresenter`,SDK 使用默认实现或仅触发事件 +- 如果不传 `ExitHandler`,SDK 不直接强制退出,而是交给项目层自行处理 + +## 4. 初始化生命周期 + +`Brisk.InitializeAsync` 的建议流程如下: + +### 4.1 标准流程 + +```txt +1. 校验 BriskOptions +2. 规范化 BaseUrl +3. 初始化 Core 运行时对象 +4. 调用 GET /client/bootstrap +5. 缓存 bootstrap 结果 +6. 根据 bootstrap 检查维护状态与最低版本 +7. 初始化各模块入口 +8. 从 TokenStore 恢复本地 token +9. 如果配置了 ValidateSessionOnInitialize 且存在 token,则调用 GET /player/me 验证会话 +10. 进入可用状态 +``` + +### 4.2 具体建议 + +- `BaseUrl` 允许调用方传: + - `https://host/api` + - 或 `https://host` +- SDK 内部统一规范化为最终 API Base +- 初始化阶段不依赖 `login_provider / login_user_id` +- `bootstrap` 结果建议缓存到: + - `Brisk.Bootstrap` +- 如果初始化阶段检测到以下情况,直接触发全局阻断错误: + - 项目维护中 + - 最低版本不满足 + - 本地恢复到的账号已失效或已封禁 + +### 4.3 不建议的行为 + +首版不建议做以下事情: + +- 不自动静默重新登录 + - 当前服务端没有 refresh token 机制 +- 不在初始化中自动执行第三方平台登录 + - 第三方登录由游戏侧完成,Brisk 只负责兑换 Brisk 会话 +- 不把所有模块的预加载都塞进初始化 + - 公告、排行榜、空间等业务数据由调用方按需拉取 + +## 5. 登录态设计 + +首版登录核心模型: + +```txt +login_provider + login_user_id -> POST /auth/login/exchange -> Brisk access_token +或 +login_provider + code -> POST /auth/login/exchange -> Brisk access_token +``` + +身份建议分为两层: + +- 外部身份 + - `login_provider` + - `login_user_id` +- 内部身份 + - `player_id` + - `project_account_id` + +推荐登录行为: + +```txt +1. 游戏先完成第三方平台登录 +2. 游戏拿到 `login_provider` +3. 再拿到 `login_user_id` 或 `code` +4. 调用 `Brisk.Auth.LoginWithUserIdAsync(...)` 或 `Brisk.Auth.LoginWithCodeAsync(...)` +5. SDK 保存 token、过期时间、player_id、project_account_id +6. 后续玩家态接口自动附带 Bearer Token +``` + +## 6. 错误处理总设计 + +首版错误处理原则改为: + +- SDK 只统一接管严重阻断错误 +- 普通接口错误不走全局监听 +- 普通接口错误由调用方在调用处自行处理 +- SDK 默认提供一套阻断错误弹窗 UI +- 开发者可以替换默认 UI 或自行接管严重错误 + +内部处理链路建议如下: + +```txt +BriskHttpClient +-> BriskResponseParser +-> BriskErrorClassifier +-> 严重错误进入 SDK 全局处理 +-> 普通错误直接返回给当前调用方 +``` + +职责划分: + +- `BriskHttpClient` + - 发请求、收响应、拿到 HTTP 状态码 +- `BriskResponseParser` + - 解析 `code/message/data` +- `BriskErrorClassifier` + - 把错误码映射成 SDK 语义错误 +- 严重错误全局处理 + - 只处理维护、封号、登录态失效等阻断错误 +- 普通错误调用点处理 + - 由调用方 `try/catch` + - 或由调用方传入当前接口自己的回调处理 + +## 7. 错误分级 + +首版建议将错误分为以下几类: + +```csharp +public enum BriskErrorKind +{ + Retryable, + Business, + AuthExpired, + GlobalBlocking, + Fatal +} +``` + +说明: + +- `Retryable` + - 超时、网络抖动、429、临时服务不可用 +- `Business` + - 普通业务失败,例如存档冲突、参数不合法 + - 不进入 SDK 全局错误处理 +- `AuthExpired` + - 登录态失效,需要清理会话 + - 属于 SDK 全局接管范围 +- `GlobalBlocking` + - 封号、维护中、强制更新等,需要阻断当前玩家继续操作 + - 属于 SDK 全局接管范围 +- `Fatal` + - SDK 未初始化、关键配置错误、内部不可恢复异常 + - 只用于真正不可恢复的框架级错误 + +## 8. 错误码映射建议 + +根据当前错误码文档,首版建议如下处理: + +### 8.1 AuthExpired + +- `10001` 缺少 token +- `10002` token 无效 +- `10023` 缺少会话 + +默认行为: + +- 清空本地 token +- 更新 `Brisk.IsLoggedIn = false` +- 触发 `Brisk.OnAuthExpired` +- 默认弹出登录态失效提示 UI +- 抛出 `BriskAuthExpiredException` + +### 8.2 GlobalBlocking + +- `10003` 项目维护中 +- `10004` 玩家已封禁 +- `10005` 访问校验失败 +- `10025` 项目维护中 +- `10026` 玩家已封禁 + +默认行为: + +- 触发 `Brisk.OnBlockingError` +- 使用 SDK 默认 UI 弹出阻断提示 +- 如果开发者自定义了 `ErrorPresenter`,则由开发者 UI 接管展示 +- 是否退出程序由 `ExitHandler` 决定,不在网络底层写死 + +进一步建议: + +- `10003`、`10025` -> `Maintenance` +- `10004`、`10026` -> `AccountBanned` +- `10005` -> `AccessDenied` + +### 8.3 Retryable + +- `80001` 请求过于频繁 +- `80002` 限流服务不可用 + +默认行为: + +- 短退避重试 +- 超过最大次数后抛出异常 +- 不触发全局 UI + +### 8.4 Business + +- `60016` 存档版本冲突 +- `60017` 校验值不匹配 +- `60018` 存档超出大小限制 +- `60021` 存档不存在 +- `70011` 玩家不存在 +- `70017` 玩家不存在 +- `70020` 玩家不存在 + +默认行为: + +- 不做系统级强拦截 +- 不走全局错误监听 +- 抛出对应业务异常,由调用方决定如何提示 +- 如果未来个别接口希望提供便利用法,可在该接口额外支持传入当前调用专属回调 + +## 9. 异常类命名建议 + +首版建议使用以下异常层次: + +```csharp +BriskException +BriskNetworkException +BriskHttpException +BriskApiException +BriskBusinessException +BriskAuthExpiredException +BriskBlockingException +BriskMaintenanceException +BriskAccountBannedException +BriskRateLimitException +BriskArchiveConflictException +BriskArchiveChecksumException +BriskArchiveTooLargeException +BriskNotInitializedException +``` + +说明: + +- `BriskException` + - SDK 所有异常的统一基类 +- `BriskApiException` + - 已成功收到接口响应,但 `code != 0` +- `BriskBusinessException` + - 普通业务错误 +- `BriskAuthExpiredException` + - 登录态失效 +- `BriskBlockingException` + - 全局阻断错误的公共基类 +- `BriskMaintenanceException` + - 维护中 +- `BriskAccountBannedException` + - 账号封禁 +- `BriskNotInitializedException` + - 未初始化即调用模块接口 + +## 10. 全局事件命名建议 + +首版建议 `Brisk` 根节点提供以下全局事件: + +```csharp +Brisk.OnInitialized +Brisk.OnLoggedIn +Brisk.OnLoggedOut +Brisk.OnAuthExpired +Brisk.OnBlockingError +``` + +建议语义: + +- `OnInitialized` + - SDK 初始化完成 +- `OnLoggedIn` + - 登录成功 +- `OnLoggedOut` + - 主动登出完成 +- `OnAuthExpired` + - 登录态失效 +- `OnBlockingError` + - 维护、封号、访问阻断等全局错误 + +可选接管接口: + +```csharp +Brisk.SetErrorPresenter(IBriskErrorPresenter presenter) +Brisk.SetExitHandler(Action exitHandler) +``` + +## 11. 关于“底层直接弹窗并退出程序”的建议 + +这个方向本身是合理的,但不建议把“弹窗”和“退出程序”都硬编码在网络底层。 + +更推荐的做法是: + +```txt +网络底层识别错误 +-> 严重错误交给 SDK 核心处理 +-> 默认错误展示器弹窗 +-> 是否退出由宿主游戏或 ExitHandler 决定 +``` + +这样做的好处: + +- 设计更常规 +- 便于项目方接管 UI 风格 +- 便于区分“该回登录页”还是“该退出程序” +- 避免网络层和游戏表现层耦合过深 +- 避免普通接口错误被 SDK 过度吞掉或误处理 + +具体建议: + +- `登录态失效` + - 默认不退出程序 + - 默认弹窗提示后清会话并回登录流程 +- `账号封禁` + - 默认弹窗提示后由项目方决定回登录页或退出 +- `维护中` + - 默认阻断流程并提示 +- `Fatal` + - 可以作为最接近“提示后退出”的类型 + +## 12. 可选扩展:标准模式与弱网模式 + +这项需求合理,但优先级建议放在首版正常逻辑之后。 + +后续可以考虑增加: + +```csharp +public enum BriskNetworkMode +{ + Standard, + WeakNetwork +} +``` + +两者差异可以主要体现在: + +- 重试次数 +- 超时时间 +- 429 退避策略 +- 是否对部分只读接口自动重试 + +建议首版先只做: + +- 一个默认标准策略 +- 策略入口预留在 `BriskOptions` + +例如: + +```csharp +public sealed class BriskOptions +{ + public BriskNetworkMode NetworkMode = BriskNetworkMode.Standard; +} +``` + +但首版实现只落 `Standard` 即可。 + +## 13. 首版内部结构建议 + +```txt +Assets/ + BriskSdk/ + Runtime/ + Core/ + Brisk.cs + BriskOptions.cs + BriskContext.cs + BriskSession.cs + BriskHttpClient.cs + BriskModuleExecutor.cs + BriskErrorClassifier.cs + Auth/ + Player/ + Config/ + Announcement/ + Leaderboard/ + Archive/ + Space/ + Models/ + Storage/ + Exceptions/ + Samples/ + QuickStart/ + Scenes/ + BriskQuickStartScene.unity + +PackageSource/ + com.foldcc.cc-framework.BriskGameServer/ + package.json + README.md + CHANGELOG.md + Documentation~/ +``` + +对外尽量只暴露: + +- `Brisk` +- `BriskOptions` +- 各模块入口类 +- 结果数据结构 +- 必要异常类 + +## 14. 当前阶段结论 + +当前已明确的设计结论: + +- 对外统一使用 `Brisk.xxx` +- 初始化完成后各模块直接可用 +- 初始化参数不再包含 `channel / platform` +- 登录模型统一为 `login_provider / login_user_id / code` +- `BriskOptions` 最少只要求 `BaseUrl`、`GameKey` +- `ClientVersion` 为可选但强烈建议传入 +- 网络底层负责识别错误,不直接写死 UI 和退出行为 +- SDK 核心负责全局错误策略、全局事件和默认表现 +- “弱网模式”作为后续扩展项预留,不阻塞首版实现 + +## 15. 下一步建议 + +建议按以下顺序继续推进: + +1. 先搭 SDK 骨架与目录结构 +2. 先实现 `Brisk.InitializeAsync` +3. 先打通 `Auth`、`Player`、`Leaderboard` +4. 再接入 `Archive` 的二进制上传下载 +5. 最后补充全局错误展示器与默认本地存储实现 diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/CHANGELOG.md b/PackageSource/com.foldcc.cc-framework.BriskGameServer/CHANGELOG.md new file mode 100644 index 0000000..101f4c2 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## 0.1.0 + +- Initial embedded package structure +- Added Brisk runtime facade and service modules +- Added default error presenter +- Added quick start sample script and sample scene diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~.meta new file mode 100644 index 0000000..9af4425 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cc4dc90e0c0542adbd88e58e9daf4336 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md new file mode 100644 index 0000000..2ce083d --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md @@ -0,0 +1,43 @@ +# Quick Start + +## Initialize + +```csharp +await Brisk.InitializeAsync(new BriskOptions +{ + BaseUrl = "https://brisk.lightyears.ltd", + GameKey = "demo-game", + ClientVersion = Application.version, + DeviceId = SystemInfo.deviceUniqueIdentifier +}); +``` + +## Login + +```csharp +await Brisk.Auth.LoginWithUserIdAsync("tap", "tap_user_10001"); +``` + +## Common calls + +```csharp +var me = await Brisk.Player.GetMeAsync(); +var config = await Brisk.Config.GetCurrentAsync(); +var top = await Brisk.Leaderboard.GetTopAsync("season-score", 20); +await Brisk.Leaderboard.SubmitScoreAsync("season-score", 128); +``` + +## Sample + +For the current source project, open directly: + +- `Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs` +- `Assets/Scenes/BriskQuickStartScene.unity` + +The sample scene uses an IMGUI debug panel and can directly test: + +- initialize and restore +- login by `login_user_id` +- login by `code` +- player, config, announcement, leaderboard, archive, and space flows +- global event logs and request results diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md.meta b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md.meta new file mode 100644 index 0000000..87fd9dc --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/Documentation~/QuickStart.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9972933694db41dfb51eaf3332ef8374 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md b/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md new file mode 100644 index 0000000..87893e9 --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/README.md @@ -0,0 +1,38 @@ +# FoldCC Brisk Game Server SDK + +This directory is the package publishing skeleton for Brisk. + +The active Unity project source currently lives under: + +- `Assets/BriskSdk/Runtime` +- `Assets/BriskSdk/Samples/QuickStart` +- `Assets/Scenes/BriskQuickStartScene.unity` + +## Included runtime modules + +- Bootstrap and initialization +- Auth and session restore +- Player profile +- Dynamic config +- Announcements +- Leaderboard +- Archive upload and download +- Player space +- Default blocking error UI + +## Package layout + +- `Runtime` +- `Samples~` +- `Documentation~` + +## Quick start + +See: + +- `Documentation~/QuickStart.md` +- `Samples~/QuickStart` + +This sample uses an IMGUI test panel for end-to-end SDK flow verification. + +When preparing a publish branch, place the package-ready runtime, samples, and docs into this package directory and tag that branch for external consumption. diff --git a/PackageSource/com.foldcc.cc-framework.BriskGameServer/package.json b/PackageSource/com.foldcc.cc-framework.BriskGameServer/package.json new file mode 100644 index 0000000..0b27bff --- /dev/null +++ b/PackageSource/com.foldcc.cc-framework.BriskGameServer/package.json @@ -0,0 +1,17 @@ +{ + "name": "com.foldcc.cc-framework.BriskGameServer", + "displayName": "FoldCC Brisk Game Server SDK", + "version": "0.1.0", + "unity": "2021.3", + "description": "A lightweight Unity SDK for Brisk game services, including bootstrap, auth, announcements, leaderboard, archive, and player space modules.", + "keywords": [ + "brisk", + "sdk", + "leaderboard", + "archive", + "unity" + ], + "author": { + "name": "FoldCC" + } +} diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..6d8d222 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,45 @@ +{ + "dependencies": { + "com.coplaydev.unity-mcp": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main", + "com.unity.collab-proxy": "2.12.4", + "com.unity.feature.2d": "2.0.1", + "com.unity.ide.rider": "3.0.36", + "com.unity.ide.visualstudio": "2.0.22", + "com.unity.test-framework": "1.1.33", + "com.unity.textmeshpro": "3.0.7", + "com.unity.timeline": "1.7.7", + "com.unity.ugui": "1.0.0", + "com.unity.visualscripting": "1.9.4", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..628a2c1 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,485 @@ +{ + "dependencies": { + "com.coplaydev.unity-mcp": { + "version": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main", + "depth": 0, + "source": "git", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.0.2", + "com.unity.test-framework": "1.1.31" + }, + "hash": "73eb27aeccfa8e0676eaf3304136e9b85953d913" + }, + "com.unity.2d.animation": { + "version": "9.2.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "8.1.0", + "com.unity.2d.sprite": "1.0.0", + "com.unity.collections": "1.1.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.aseprite": { + "version": "1.1.9", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "6.0.6", + "com.unity.2d.sprite": "1.0.0", + "com.unity.mathematics": "1.2.6", + "com.unity.modules.animation": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.common": { + "version": "8.1.0", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.7.3", + "com.unity.2d.sprite": "1.0.0", + "com.unity.mathematics": "1.1.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.pixel-perfect": { + "version": "5.1.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.imgui": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.psdimporter": { + "version": "8.1.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "8.1.0", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.animation": "9.2.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.spriteshape": { + "version": "9.1.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "8.1.0", + "com.unity.mathematics": "1.1.0", + "com.unity.modules.physics2d": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + } + }, + "com.unity.2d.tilemap.extras": { + "version": "3.1.3", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.burst": { + "version": "1.8.21", + "depth": 3, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.collab-proxy": { + "version": "2.12.4", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.collections": { + "version": "1.2.4", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.6.6", + "com.unity.test-framework": "1.1.31" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.feature.2d": { + "version": "2.0.1", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.2d.animation": "9.2.0", + "com.unity.2d.pixel-perfect": "5.1.0", + "com.unity.2d.psdimporter": "8.1.0", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.spriteshape": "9.1.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.2d.tilemap.extras": "3.1.3", + "com.unity.2d.aseprite": "1.1.9" + } + }, + "com.unity.ide.rider": { + "version": "3.0.36", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.22", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.mathematics": { + "version": "1.2.6", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.cn" + }, + "com.unity.test-framework": { + "version": "1.1.33", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.textmeshpro": { + "version": "3.0.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.timeline": { + "version": "1.7.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.visualscripting": { + "version": "1.9.4", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.cn" + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..27287fe --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..72d1430 --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_DefaultMaxDepenetrationVelocity: 10 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..82ab0f5 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 2cda990e2423bbf4892e6590ba056729 + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..e6f57fd --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,40 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 1 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 5 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_CachingShaderPreprocessor: 1 + m_PrefabModeAllowAutoSave: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_GameObjectNamingDigits: 1 + m_GameObjectNamingScheme: 0 + m_AssetNamingUsesSpace: 1 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_DisableCookiesInLightmapper: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerEnableAuth: 0 + m_CacheServerEnableTls: 0 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..c165afb --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,64 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_DefaultRenderingLayerMask: 1 + m_LogWhenShaderIsCompiled: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..b16147e --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/ProjectSettings/MemorySettings.asset b/ProjectSettings/MemorySettings.asset new file mode 100644 index 0000000..5b5face --- /dev/null +++ b/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..ad2654e --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,93 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + 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_SettingNames: + - Humanoid diff --git a/ProjectSettings/NetworkManager.asset b/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..5f48647 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreReleasePackages: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.cn + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_ConfigSource: 0 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_Modified: 0 + m_ErrorMessage: + m_UserModificationsInstanceId: -836 + m_OriginalInstanceId: -838 + m_LoadAssets: 0 diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..6cfcdda --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..54c5118 --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,913 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 26 + productGUID: d73d15f2ba29d924ebc96bc5feb80899 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: BriskGameServer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 1 + unsupportedMSAAFallback: 0 + m_SpriteBatchVertexThreshold: 300 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 + androidPredictiveBackSupport: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + dedicatedServerOptimizations: 0 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + switchNVNGraphicsFirmwareMemory: 32 + switchMaxWorkerMultiple: 8 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + visionOSBundleVersion: 1.0 + tvOSBundleVersion: 1.0 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 + useHDRDisplay: 0 + hdrBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: + Standalone: com.DefaultCompany.2DProject + buildNumber: + Standalone: 0 + VisionOS: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 1 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 22 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + strictShaderVariantMatching: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSSimulatorArchitecture: 0 + iOSTargetOSVersionString: 12.0 + tvOSSdkVersion: 0 + tvOSSimulatorArchitecture: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 12.0 + VisionOSSdkVersion: 0 + VisionOSTargetOSVersionString: 1.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + macOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + metalCompileShaderBinary: 0 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + VisionOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + VisionOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 10ad67313f4034357812315f3c407484 + templatePackageId: com.unity.template.2d@7.0.4 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidEnableArmv9SecurityFeatures: 0 + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: + - m_BuildTarget: iPhone + m_Icons: + - m_Textures: [] + m_Width: 180 + m_Height: 180 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 87 + m_Height: 87 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 60 + m_Height: 60 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 20 + m_Height: 20 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 1024 + m_Height: 1024 + m_Kind: 4 + m_SubKind: App Store + - m_BuildTarget: Android + m_Icons: + - m_Textures: [] + m_Width: 432 + m_Height: 432 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 324 + m_Height: 324 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 216 + m_Height: 216 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 162 + m_Height: 162 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 108 + m_Height: 108 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 81 + m_Height: 81 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 0 + m_SubKind: + m_BuildTargetBatching: [] + m_BuildTargetShaderSettings: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 1 + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetGroupLoadStoreDebugModeSettings: [] + m_BuildTargetNormalMapEncoding: [] + m_BuildTargetDefaultTextureCompressionFormat: + - m_BuildTarget: Android + m_Format: 3 + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + macOSTargetOSVersion: 10.13.0 + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchEnableFileSystemTrace: 0 + switchLTOSetting: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchCompilerFlags: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchDisableHTCSPlayerConnection: 0 + switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 0 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLShowDiagnostics: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + webGLInitialMemorySize: 32 + webGLMaximumMemorySize: 2048 + webGLMemoryGrowthMode: 2 + webGLMemoryLinearGrowthStep: 16 + webGLMemoryGeometricGrowthStep: 0.2 + webGLMemoryGeometricGrowthCap: 96 + webGLPowerPreference: 2 + scriptingDefineSymbols: {} + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + il2cppCodeGeneration: {} + managedStrippingLevel: + EmbeddedLinux: 1 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Nintendo Switch: 1 + PS4: 1 + PS5: 1 + QNX: 1 + Stadia: 1 + VisionOS: 1 + WebGL: 1 + Windows Store Apps: 1 + XboxOne: 1 + iPhone: 1 + tvOS: 1 + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: BriskGameServer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: BriskGameServer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + vcxProjDefaultLanguage: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 1 + embeddedLinuxEnableGamepadInput: 1 + hmiLogStartupTiming: 0 + hmiCpuConfiguration: + apiCompatibilityLevel: 6 + activeInputHandler: 0 + windowsGamepadBackendHint: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 + virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..11e271c --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2022.3.62f3c1 +m_EditorVersionWithRevision: 2022.3.62f3c1 (1623fc0bbb97) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..bcd6706 --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,239 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Lumin: 5 + GameCoreScarlett: 5 + GameCoreXboxOne: 5 + Nintendo Switch: 5 + PS4: 5 + PS5: 5 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..a27ab5f --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,38 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_CNEventUrl: https://cdp.cloud.unity.cn/v1/events + m_CNConfigUrl: https://cdp.cloud.unity.cn/config + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity.cn + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + m_PackageRequiringCoreStatsPresent: 0 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..46f38e1 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 + m_CompiledVersion: 0 + m_RuntimeVersion: 0 diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/服务参考_临时/Brisk API接口与示例文档.md b/服务参考_临时/Brisk API接口与示例文档.md new file mode 100644 index 0000000..f3b96b5 --- /dev/null +++ b/服务参考_临时/Brisk API接口与示例文档.md @@ -0,0 +1,319 @@ +# Brisk API接口与示例文档 + +## 1. 核心约定 + +- 项目唯一标识:`game_key` +- 项目级 `channel / platform` 已移除 +- 初始化与动态配置: + - 必填 `game_key` + - 可选 `client_version` +- 登录身份: + - `login_provider` + - `login_user_id` +- 登录成功后服务端返回内部身份: + - `player_id` + - `project_account_id` + - `access_token` +- 需要跨系统跳转玩家时,空间模块与后台排行榜查询支持直接传: + - `login_provider + login_user_id` + +## 2. 客户端主流程 + +1. `GET /api/client/bootstrap` +2. `POST /api/auth/login/exchange` +3. 带 `Bearer access_token` 调用: + - `GET /api/player/me` + - `GET /api/announcements` + - 排行榜、存档、空间等业务接口 +4. `GET /api/config/current` 为公开接口,登录前后都可调用 + +## 3. 初始化 + +### 3.1 获取初始化信息 + +`GET /api/client/bootstrap` + +查询参数: + +- `game_key`:必填 +- `client_version`:可选,建议传入;不传时只会命中不设版本范围的配置 +- `device_id`:可选 + +示例: + +```bash +curl "https://brisk.lightyears.ltd/api/client/bootstrap?game_key=demo-game&client_version=1.0.0&device_id=device-001" +``` + +返回重点: + +- `project_name` +- `server_time` +- `maintenance_mode` +- `maintenance_message` +- `feature_flags` +- `dynamic_config` +- `min_client_version` + +## 4. 登录 + +### 4.1 登录换票 + +`POST /api/auth/login/exchange` + +请求体: + +- `game_key`:必填 +- `login_provider`:必填,例如 `tap` +- `login_user_id`:与 `code` 二选一 +- `code`:与 `login_user_id` 二选一 +- `device_id`:可选 +- `client_version`:可选 +- `nickname`:可选 +- `avatar_url`:可选 +- `profile_json`:可选对象 + +示例: + +```bash +curl -X POST "https://brisk.lightyears.ltd/api/auth/login/exchange" \ + -H "Content-Type: application/json" \ + -d '{ + "game_key": "demo-game", + "login_provider": "tap", + "login_user_id": "tap_user_10001", + "device_id": "android-001", + "client_version": "1.0.0", + "nickname": "Tap Player", + "avatar_url": "https://example.com/avatar.png", + "profile_json": { + "region": "cn", + "source": "tap" + } + }' +``` + +返回重点: + +- `access_token` +- `expires_in` +- `player_id` +- `project_account_id` +- `is_new_player` +- `profile.login_provider` +- `profile.login_user_id` + +## 5. 玩家与动态配置 + +### 5.1 获取当前玩家 + +`GET /api/player/me` + +返回重点: + +- `player_id` +- `project_account_id` +- `login_provider` +- `login_user_id` +- `nickname` +- `avatar_url` +- `profile_json` + +### 5.2 获取动态配置 + +`GET /api/config/current` + +查询参数: + +- `game_key`:必填 +- `client_version`:可选,建议传入;不传时只会命中不设版本范围的配置 + +说明: + +- `feature_flags` 现在是标准 key-value 对象 +- `dynamic_config` 保持为自定义 JSON 对象 +- 命中规则只看 `client_version` +- 该接口当前无需 `Bearer access_token` + +示例: + +```bash +curl "https://brisk.lightyears.ltd/api/config/current?game_key=demo-game&client_version=1.0.0" +``` + +## 6. 公告 + +### 6.1 获取公告列表 + +`GET /api/announcements` + +### 6.2 标记已读 + +`POST /api/announcements/{id}/read` + +## 7. 排行榜 + +### 7.1 客户端接口 + +- `GET /api/ranks/{rank_key}/top` +- `GET /api/ranks/{rank_key}/me` +- `GET /api/ranks/{rank_key}/around-me` +- `POST /api/ranks/{rank_key}/score` +- `GET /api/ranks/{rank_key}/season/current` +- `GET /api/ranks/{rank_key}/history` +- `GET /api/ranks/{rank_key}/history/{season_id}` + +提交分数示例: + +```bash +curl -X POST "https://brisk.lightyears.ltd/api/ranks/season-score/score" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"score": 128}' +``` + +排行榜返回项重点: + +- `rank` +- `player_id` +- `project_account_id` +- `login_provider` +- `login_user_id` +- `score` +- `nickname` +- `avatar_url` + +### 7.2 后台排行榜接口 + +- `GET /api/admin/ranks` +- `POST /api/admin/ranks` +- `PUT /api/admin/ranks/{rank_key}` +- `DELETE /api/admin/ranks/{rank_key}` +- `DELETE /api/admin/ranks/{rank_key}/players/{player_id}` +- `GET /api/admin/ranks/{rank_key}/leaderboard` +- `POST /api/admin/ranks/{rank_key}/reset` +- `GET /api/admin/ranks/{rank_key}/seasons` +- `GET /api/admin/ranks/{rank_key}/seasons/{season_id}/logs` + +后台当前榜单查询支持参数: + +- `project_id` +- `page` +- `page_size` +- `player_id` +- `project_account_id` +- `login_provider` +- `login_user_id` + +## 8. 云存档 + +说明: + +- 云存档是二进制上传/下载接口 +- 适合直接存 Unity 存档 bytes、protobuf、msgpack、压缩包等 +- 不限制为 JSON + +接口: + +- `GET /api/archives/slots` +- `GET /api/archives/slot/{slot_no}/meta` +- `POST /api/archives/slot/{slot_no}/upload` +- `GET /api/archives/slot/{slot_no}/download` + +上传示例: + +```bash +curl -X POST "https://brisk.lightyears.ltd/api/archives/slot/1/upload" \ + -H "Authorization: Bearer " \ + -F "base_version=0" \ + -F "checksum=" \ + -F "file=@save.dat" +``` + +## 9. 玩家空间 + +说明: + +- 空间数据当前为 `payload_json` +- 适合公开资料、展示文案、轻量个性化内容 +- 如果是游戏主存档,请优先走云存档模块 + +接口: + +- `PUT /api/spaces/me` +- `GET /api/spaces/{player_id}` +- `GET /api/spaces/{player_id}/stats` +- `GET /api/spaces/{player_id}/likes` +- `POST /api/spaces/{player_id}/like` +- `DELETE /api/spaces/{player_id}/like` +- `GET /api/spaces/me/visits` +- `GET /api/spaces/by-login` +- `GET /api/spaces/by-login/stats` +- `GET /api/spaces/by-login/likes` +- `POST /api/spaces/by-login/like` +- `DELETE /api/spaces/by-login/like` + +按第三方身份查看空间示例: + +```bash +curl "https://brisk.lightyears.ltd/api/spaces/by-login?login_provider=tap&login_user_id=tap_user_10001" \ + -H "Authorization: Bearer " +``` + +按第三方身份点赞示例: + +```bash +curl -X POST "https://brisk.lightyears.ltd/api/spaces/by-login/like?login_provider=tap&login_user_id=tap_user_10001" \ + -H "Authorization: Bearer " +``` + +## 10. 后台项目与配置 + +### 10.1 项目 + +- `GET /api/admin/projects` +- `GET /api/admin/projects/{id}` +- `POST /api/admin/projects` +- `PUT /api/admin/projects/{id}` +- `DELETE /api/admin/projects/{id}` +- `POST /api/admin/projects/{id}/icon` +- `GET /api/admin/projects/{id}/icon` + +说明: + +- 创建项目时只传项目信息,不再传 `channel / platform` +- `game_key` 由服务端自动生成 + +### 10.2 当前动态配置 + +- `GET /api/admin/configs/current?project_id=...` +- `PUT /api/admin/configs/current` + +### 10.3 公告 + +- `GET /api/admin/announcements` +- `GET /api/admin/announcements/{id}` +- `POST /api/admin/announcements` +- `PUT /api/admin/announcements/{id}` +- `DELETE /api/admin/announcements/{id}` + +### 10.4 玩家 + +- `GET /api/admin/players` +- `GET /api/admin/players/{player_id}` +- `POST /api/admin/players/{player_id}/ban` +- `POST /api/admin/players/{player_id}/unban` + +玩家列表支持筛选: + +- `project_id` +- `player_id` +- `project_account_id` +- `login_provider` +- `login_user_id` + +## 11. OpenAPI + +完整 OpenAPI 导出: + +- [openapi.yaml](/F:/OtherWork/BriskGameSerivce/openapi.yaml) diff --git a/服务参考_临时/Brisk Unity SDK接入文档.md b/服务参考_临时/Brisk Unity SDK接入文档.md new file mode 100644 index 0000000..f75cc81 --- /dev/null +++ b/服务参考_临时/Brisk Unity SDK接入文档.md @@ -0,0 +1,226 @@ +# Brisk Unity SDK接入文档 + +## 1. 这份文档适合做什么 + +这份文档用于在 Unity 侧封装 Brisk SDK。 + +建议把 SDK 拆成以下层次: + +- `BriskClient` +- `BriskAuth` +- `BriskConfig` +- `BriskAnnouncements` +- `BriskRanks` +- `BriskArchives` +- `BriskSpaces` + +## 2. 初始化模型 + +Unity 客户端初始化建议持有: + +- `baseUrl` +- `gameKey`(必填) +- `clientVersion`(推荐传入;服务端接口当前可选,但会影响版本命中) +- `deviceId`(推荐传入;`bootstrap` 接口当前可选) + +不再需要项目级 `channel / platform`。 + +示例: + +```csharp +var options = new BriskOptions { + BaseUrl = "https://brisk.lightyears.ltd", + GameKey = "demo-game", + ClientVersion = Application.version, + DeviceId = SystemInfo.deviceUniqueIdentifier +}; +``` + +## 3. 登录模型 + +登录属于独立模块,不属于初始化参数。 + +Unity 登录接口建议统一成: + +```csharp +Task LoginAsync( + string loginProvider, + string loginUserId = null, + string code = null, + string nickname = null, + string avatarUrl = null, + Dictionary profileJson = null +) +``` + +说明: + +- `loginProvider`:如 `tap` +- `loginUserId`:平台返回的稳定唯一用户 ID +- `code`:如果平台走 code 兑换模式则传这个 +- `loginUserId` 和 `code` 二选一 +- `nickname / avatarUrl / profileJson` 为可选同步资料 + +## 4. 推荐接入顺序 + +1. `BootstrapAsync()` +2. 从第三方 SDK 获取: + - `loginProvider` + - `loginUserId` 或 `code` + - 可选昵称、头像、扩展资料 +3. `LoginAsync(...)` +4. 保存 `accessToken` +5. 拉取: + - `GetMeAsync()` + - `GetCurrentConfigAsync()` + - `ListAnnouncementsAsync()` +6. 进入业务模块 + +## 5. 身份模型建议 + +Unity SDK 内建议同时保存两套身份: + +- 外部身份: + - `LoginProvider` + - `LoginUserId` +- 内部身份: + - `PlayerId` + - `ProjectAccountId` + +原因: + +- 跨系统跳转、和第三方 SDK 共存时,外部身份更方便 +- 游戏内高频接口仍以内部 `player_id` 为主更自然 + +## 6. 空间跳转建议 + +如果排行榜头像点击来自第三方平台,推荐直接支持: + +```csharp +Task GetSpaceByLoginIdentityAsync(string loginProvider, string loginUserId) +Task GetSpaceStatsByLoginIdentityAsync(string loginProvider, string loginUserId) +Task LikeSpaceByLoginIdentityAsync(string loginProvider, string loginUserId) +``` + +这样可以避免 Unity 侧先做一次平台 ID 到 Brisk `player_id` 的映射查询。 + +## 7. 排行榜模块建议 + +客户端侧建议封装: + +- `GetRankTopAsync(rankKey, limit)` +- `GetRankMeAsync(rankKey)` +- `GetRankAroundMeAsync(rankKey, range)` +- `SubmitScoreAsync(rankKey, score)` +- `GetCurrentSeasonAsync(rankKey)` +- `GetSeasonHistoryAsync(rankKey, limit)` +- `GetSeasonHistoryDetailAsync(rankKey, seasonId, limit)` + +榜单项需要暴露: + +- `Rank` +- `PlayerId` +- `ProjectAccountId` +- `LoginProvider` +- `LoginUserId` +- `Score` +- `Nickname` +- `AvatarUrl` + +## 8. 云存档模块建议 + +云存档适合承载真实游戏数据。 + +原因: + +- 上传下载是二进制流 +- 天然适配 bytes +- 可直接存: + - JSON bytes + - protobuf bytes + - msgpack bytes + - 压缩后的存档二进制 + +Unity SDK 建议提供: + +- `GetArchiveSlotsAsync()` +- `GetArchiveMetaAsync(slotNo)` +- `UploadArchiveAsync(slotNo, baseVersion, checksum, byte[])` +- `DownloadArchiveAsync(slotNo)` + +## 9. 玩家空间模块建议 + +玩家空间适合承载公开展示数据,不建议放主存档。 + +推荐放: + +- 简介文本 +- 头像展示字段 +- 成就展示摘要 +- 公开资料卡 + +当前接口核心是: + +- `UpdateMySpaceAsync(payloadJson)` +- `GetSpaceByPlayerIdAsync(playerId)` +- `GetSpaceByLoginIdentityAsync(loginProvider, loginUserId)` +- `LikeSpaceAsync(playerId)` +- `LikeSpaceByLoginIdentityAsync(loginProvider, loginUserId)` +- `GetMyVisitsAsync(limit)` + +## 10. 动态配置建议 + +动态配置拆为两层: + +- `feature_flags` + - 适合直接做开关或 key-value 判断 +- `dynamic_config` + - 适合结构化配置对象 + +Unity SDK 可以考虑: + +- 把 `feature_flags` 映射成 `Dictionary` +- 把 `dynamic_config` 保留原始 JSON,并允许上层自行反序列化 + +## 11. 错误与登录态处理 + +SDK 需要统一处理: + +- HTTP 非 2xx +- 业务码非 0 +- 401 / 登录态失效 +- 维护模式 +- 玩家被封禁 +- 存档版本冲突 + +建议统一抛出: + +```csharp +public sealed class BriskApiException : Exception { + public int HttpStatus { get; } + public int Code { get; } + public string ServerMessage { get; } +} +``` + +## 12. Unity 侧最小落地清单 + +第一版 SDK 至少要覆盖: + +1. 初始化 +2. 登录换票 +3. 登录态保存 +4. 玩家信息读取 +5. 动态配置读取 +6. 公告读取与已读 +7. 排行榜提交与读取 +8. 云存档上传下载 +9. 玩家空间读取、写入、点赞 +10. 按 `login_provider + login_user_id` 访问空间 + +## 13. 配套文件 + +- API 总文档: + - [Brisk API接口与示例文档.md](/F:/OtherWork/BriskGameSerivce/Brisk%20API%E6%8E%A5%E5%8F%A3%E4%B8%8E%E7%A4%BA%E4%BE%8B%E6%96%87%E6%A1%A3.md) +- OpenAPI: + - [openapi.yaml](/F:/OtherWork/BriskGameSerivce/openapi.yaml) diff --git a/服务参考_临时/Brisk 错误码文档(内部实施版).md b/服务参考_临时/Brisk 错误码文档(内部实施版).md new file mode 100644 index 0000000..1c850f4 --- /dev/null +++ b/服务参考_临时/Brisk 错误码文档(内部实施版).md @@ -0,0 +1,194 @@ +# Brisk 错误码文档(内部实施版) + +**版本**:v0.1 +**日期**:2026-04-08 +**说明**:当前工程内已使用的错误码分组与含义。 + +--- + +## 1. 返回结构 + +```json +{ + "code": 0, + "message": "ok", + "data": {} +} +``` + +- `code = 0` 表示成功 +- 非 `0` 表示失败 + +--- + +## 2. 错误码分组 + +| 范围 | 模块 | +|---|---| +| `10001 - 10099` | 客户端 token / bootstrap / auth | +| `20001 - 20099` | 后台鉴权 | +| `21000 - 21999` | 后台项目 | +| `22000 - 22999` | 后台动态配置 | +| `23000 - 23999` | 后台玩家 | +| `24000 - 24999` | 后台审计日志 | +| `30000 - 30999` | 动态配置读取 | +| `40000 - 40999` | 公告 | +| `41000 - 41999` | 后台公告 | +| `50000 - 50999` | 排行榜 | +| `51000 - 51999` | 后台榜单 | +| `60000 - 60999` | 存档 | +| `70000 - 70999` | 玩家空间 | +| `80000 - 80999` | 限流 | + +--- + +## 3. 当前已使用错误码 + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `10001` | 客户端鉴权 | 缺少 token | +| `10002` | 客户端鉴权 | token 无效 | +| `10010` | `bootstrap` | 缺少必填查询参数 | +| `10011` | `bootstrap` | 项目不存在 | +| `10020` | `auth/login/exchange` | 请求体格式错误 | +| `10021` | `auth/login/exchange` | 缺少必填字段 | +| `10022` | `auth/login/exchange` | 登录交换失败 | +| `10023` | `auth/logout` | 缺少会话 | +| `10024` | `auth/logout` | 退出登录失败 | +| `10025` | `auth/login/exchange` | 项目维护中 | +| `10026` | `auth/login/exchange` | 玩家已封禁 | +| `10003` | 客户端访问控制 | 项目维护中 | +| `10004` | 客户端访问控制 | 玩家已封禁 | +| `10005` | 客户端访问控制 | 访问校验失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `20001` | 后台鉴权 | 缺少后台 token | +| `20002` | 后台鉴权 | 后台 token 无效 | +| `20010` | `admin/auth/login` | 请求体格式错误 | +| `20011` | `admin/auth/login` | 缺少用户名或密码 | +| `20012` | `admin/auth/login` | 后台登录失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `21010` | `admin/projects` | 项目列表查询失败 | +| `21011` | `admin/projects` | 创建项目请求体错误 | +| `21012` | `admin/projects` | 缺少项目名称或 game_key | +| `21013` | `admin/projects` | 创建项目失败 | +| `21014` | `admin/projects/{id}` | 项目 ID 非法 | +| `21015` | `admin/projects/{id}` | 更新请求体错误 | +| `21016` | `admin/projects/{id}` | 项目不存在 | +| `21017` | `admin/projects/{id}` | 更新项目失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `22010` | `admin/configs` | project_id 非法 | +| `22011` | `admin/configs` | 配置列表查询失败 | +| `22012` | `admin/configs` | 创建配置请求体错误 | +| `22013` | `admin/configs` | project_id 非法 | +| `22014` | `admin/configs` | 创建配置失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `23010` | `admin/players` | project_id 非法 | +| `23011` | `admin/players` | 玩家列表查询失败 | +| `23012` | `admin/players/{player_id}/ban` | player_id 非法 | +| `23013` | `admin/players/{player_id}/ban` | 玩家不存在 | +| `23014` | `admin/players/{player_id}/ban` | 封禁失败 | +| `23015` | `admin/players/{player_id}/unban` | player_id 非法 | +| `23016` | `admin/players/{player_id}/unban` | 玩家不存在 | +| `23017` | `admin/players/{player_id}/unban` | 解封失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `24010` | `admin/audit-logs` | 审计日志查询失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `30010` | `config/current` | 缺少必填查询参数 | +| `30011` | `config/current` | 项目不存在 | +| `30012` | `config/current` | 配置读取失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `40010` | `announcements` | 缺少会话 | +| `40011` | `announcements` | 公告查询失败 | +| `40012` | `announcements/{id}/read` | 缺少会话 | +| `40013` | `announcements/{id}/read` | 公告 ID 非法 | +| `40014` | `announcements/{id}/read` | 公告不存在 | +| `40015` | `announcements/{id}/read` | 标记已读失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `41010` | `admin/announcements` | project_id 非法 | +| `41011` | `admin/announcements` | 公告列表查询失败 | +| `41012` | `admin/announcements` | 创建公告请求体错误 | +| `41013` | `admin/announcements` | 缺少公告必填字段 | +| `41014` | `admin/announcements` | 创建公告失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `50010` | `ranks/top` | 缺少会话 | +| `50011` | `ranks/top` | 榜单查询失败 | +| `50012` | `ranks/me` | 缺少会话 | +| `50013` | `ranks/me` | 玩家排名查询失败 | +| `50014` | `ranks/around-me` | 缺少会话 | +| `50015` | `ranks/around-me` | 附近排名查询失败 | +| `50016` | `ranks/score` | 缺少会话 | +| `50017` | `ranks/score` | 分数请求体错误 | +| `50018` | `ranks/score` | 分数提交失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `51010` | `admin/ranks` | project_id 非法 | +| `51011` | `admin/ranks` | 榜单列表查询失败 | +| `51012` | `admin/ranks` | 创建榜单请求体错误 | +| `51013` | `admin/ranks` | 缺少榜单必填字段 | +| `51014` | `admin/ranks` | 创建榜单失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `60010` | `archives/meta` | 缺少会话 | +| `60011` | `archives/meta` | 存档元数据查询失败 | +| `60012` | `archives/upload` | 缺少会话 | +| `60013` | `archives/upload` | 上传表单错误 | +| `60014` | `archives/upload` | 缺少文件 | +| `60015` | `archives/upload` | 文件读取失败 | +| `60016` | `archives/upload` | 版本冲突 | +| `60017` | `archives/upload` | 校验值不匹配 | +| `60018` | `archives/upload` | 存档超出大小限制 | +| `60019` | `archives/upload` | 上传失败 | +| `60020` | `archives/download` | 缺少会话 | +| `60021` | `archives/download` | 存档不存在 | +| `60022` | `archives/download` | 下载失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `70010` | `spaces/{player_id}` | 缺少会话 | +| `70011` | `spaces/{player_id}` | 玩家不存在 | +| `70012` | `spaces/{player_id}` | 空间读取失败 | +| `70013` | `spaces/me` | 缺少会话 | +| `70014` | `spaces/me` | 请求体错误 | +| `70015` | `spaces/me` | 空间更新失败 | +| `70016` | `spaces/{player_id}/like` | 缺少会话 | +| `70017` | `spaces/{player_id}/like` | 玩家不存在 | +| `70018` | `spaces/{player_id}/like` | 点赞失败 | +| `70019` | `spaces/{player_id}/like` | 缺少会话 | +| `70020` | `spaces/{player_id}/like` | 玩家不存在 | +| `70021` | `spaces/{player_id}/like` | 取消点赞失败 | +| `70022` | `spaces/me/visits` | 缺少会话 | +| `70023` | `spaces/me/visits` | 访客列表查询失败 | + +| 错误码 | 接口/模块 | 含义 | +|---|---|---| +| `80001` | 限流中间件 | 请求过于频繁 | +| `80002` | 限流中间件 | 限流服务不可用 | + +--- + +## 4. 使用规则 + +- 新接口优先复用所属模块的错误码段 +- 成功统一返回 `code = 0` +- 同一类错误尽量复用已有错误码 +- 新增错误码时同步更新本文件 diff --git a/服务参考_临时/README.md b/服务参考_临时/README.md new file mode 100644 index 0000000..5604711 --- /dev/null +++ b/服务参考_临时/README.md @@ -0,0 +1,101 @@ +# Brisk Game Service + +Brisk 游戏服务端工程。 + +当前已实现模块: + +- 客户端初始化 `bootstrap` +- 登录换票 `auth` +- 玩家信息 `player` +- 动态配置 `config` +- 公告 `announcements` +- 排行榜 `ranks` +- 云存档 `archives` +- 玩家空间 `spaces` +- 管理后台认证、项目、配置、公告、排行榜、存档、玩家、空间、审计 +- Worker 清理任务与基础限流 + +## 当前核心模型 + +- 项目唯一标识只有 `game_key` +- 项目级 `channel / platform` 已移除 +- 初始化和动态配置只依赖 `game_key`,配置筛选只看 `client_version` +- 登录模块单独处理第三方身份: + - `login_provider`:如 `tap` + - `login_user_id`:该登录平台返回的稳定唯一用户 ID +- 服务端登录成功后返回内部: + - `player_id` + - `project_account_id` + - `access_token` +- 空间、排行榜后台筛选等模块支持直接使用 `login_provider + login_user_id` +- 内部仍优先解析到 `player_id` 再执行业务查询,便于性能和统一索引 + +## 本地运行 + +推荐使用 Docker Compose: + +```bash +copy .env.example .env +docker compose up --build +``` + +默认接口: + +- `GET /health` +- `GET /api/ping` +- `GET /openapi.yaml` + +开发环境默认账号: + +- 后台用户名:`admin` +- 后台密码:`admin123456` +- 后台操作员:`operator` +- 操作员密码:`operator123456` + +## 接入说明 + +- 客户端初始化: + - 必填 `game_key` + - 可选 `client_version` + - `device_id` 仅作透传与诊断辅助 +- 客户端登录: + - 必填 `game_key` + - 必填 `login_provider` + - `login_user_id` 与 `code` 二选一 + - 可选透传 `nickname`、`avatar_url`、`profile_json` +- 云存档: + - 支持二进制文件上传/下载,适合直接存游戏存档 bytes +- 玩家空间: + - 当前为 `payload_json` 结构,适合资料卡、展示文本等轻量公开数据 + +## 测试脚本 + +Windows PowerShell: + +```powershell +.\scripts\smoke-test.ps1 +.\scripts\acceptance.ps1 -Tasks all -ManageStack +``` + +## 迁移 + +数据库迁移目录: + +- [0001_init.sql](/F:/OtherWork/BriskGameSerivce/internal/platform/db/migrations/0001_init.sql) +- [0008_identity_and_config_simplification.sql](/F:/OtherWork/BriskGameSerivce/internal/platform/db/migrations/0008_identity_and_config_simplification.sql) + +服务启动时会自动执行未应用迁移。 + +## 根目录文档 + +- [Brisk API接口与示例文档.md](/F:/OtherWork/BriskGameSerivce/Brisk%20API%E6%8E%A5%E5%8F%A3%E4%B8%8E%E7%A4%BA%E4%BE%8B%E6%96%87%E6%A1%A3.md) +- [Brisk Unity SDK接入文档.md](/F:/OtherWork/BriskGameSerivce/Brisk%20Unity%20SDK%E6%8E%A5%E5%85%A5%E6%96%87%E6%A1%A3.md) +- [openapi.yaml](/F:/OtherWork/BriskGameSerivce/openapi.yaml) + +## 目录说明 + +- `cmd/`:启动入口 +- `internal/`:内部实现 +- `deployments/`:部署配置 +- `docs/`:历史设计文档与补充资料 +- `scripts/`:脚本 diff --git a/服务参考_临时/openapi.yaml b/服务参考_临时/openapi.yaml new file mode 100644 index 0000000..91423a1 --- /dev/null +++ b/服务参考_临时/openapi.yaml @@ -0,0 +1,1551 @@ +openapi: 3.0.3 +info: + title: Brisk Game Service API + version: 0.1.0 + description: Current implemented Brisk server API for internal integration. +servers: + - url: /api +tags: + - name: bootstrap + - name: auth + - name: player + - name: config + - name: announcements + - name: ranks + - name: archives + - name: spaces + - name: admin-auth + - name: admin-audit + - name: admin-projects + - name: admin-configs + - name: admin-announcements + - name: admin-rank-groups + - name: admin-ranks + - name: admin-archives + - name: admin-players + - name: admin-spaces +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: token + schemas: + SuccessEnvelope: + type: object + properties: + code: + type: integer + example: 0 + message: + type: string + example: ok + data: + type: object + ErrorEnvelope: + type: object + properties: + code: + type: integer + message: + type: string + data: + nullable: true + ExchangeRequest: + type: object + required: [game_key, login_provider] + properties: + game_key: + type: string + login_provider: + type: string + example: tap + code: + type: string + description: Optional for code-based login. + login_user_id: + type: string + description: Stable user identifier from client-verified provider login. + device_id: + type: string + client_version: + type: string + nickname: + type: string + description: Optional profile nickname to sync into project account. + avatar_url: + type: string + description: Optional profile avatar URL to sync into project account. + profile_json: + type: object + additionalProperties: true + description: Optional extra profile payload to sync into project account. + AdminLoginRequest: + type: object + required: [username, password] + properties: + username: + type: string + password: + type: string + CreateProjectRequest: + type: object + required: [name] + properties: + name: + type: string + current_version: + type: string + status: + type: string + maintenance_mode: + type: boolean + maintenance_message: + type: string + min_client_version: + type: string + max_archive_size_bytes: + type: integer + format: int64 + max_archive_slots: + type: integer + UpdateProjectRequest: + type: object + properties: + name: + type: string + current_version: + type: string + status: + type: string + maintenance_mode: + type: boolean + maintenance_message: + type: string + min_client_version: + type: string + max_archive_size_bytes: + type: integer + format: int64 + max_archive_slots: + type: integer + UpsertCurrentConfigRequest: + type: object + required: [project_id] + properties: + project_id: + type: integer + format: int64 + feature_flags: + type: object + additionalProperties: true + dynamic_config: + type: object + additionalProperties: true + status: + type: string + CreateAnnouncementRequest: + type: object + required: [project_id, title, content, start_at, end_at] + properties: + project_id: + type: integer + format: int64 + title: + type: string + content: + type: string + content_type: + type: string + start_at: + type: string + format: date-time + end_at: + type: string + format: date-time + status: + type: string + UpdateAnnouncementRequest: + type: object + properties: + title: + type: string + content: + type: string + content_type: + type: string + start_at: + type: string + format: date-time + end_at: + type: string + format: date-time + status: + type: string + BanPlayerRequest: + type: object + properties: + reason: + type: string + ban_until: + type: string + format: date-time + permanent: + type: boolean + CreateRankRequest: + type: object + required: [project_id, name] + properties: + project_id: + type: integer + format: int64 + name: + type: string + status: + type: string + group_id: + type: integer + format: int64 + rank_mode: + type: string + timezone: + type: string + snapshot_enabled: + type: boolean + update_strategy: + type: string + sort_order: + type: string + reset_hour: + type: integer + reset_minute: + type: integer + weekly_reset_day: + type: integer + monthly_reset_day: + type: integer + blacklist_player_ids: + type: array + items: + type: string + UpdateRankRequest: + type: object + properties: + name: + type: string + status: + type: string + group_id: + type: integer + format: int64 + clear_group: + type: boolean + rank_mode: + type: string + timezone: + type: string + snapshot_enabled: + type: boolean + update_strategy: + type: string + sort_order: + type: string + reset_hour: + type: integer + reset_minute: + type: integer + weekly_reset_day: + type: integer + monthly_reset_day: + type: integer + blacklist_player_ids: + type: array + items: + type: string + AdminRankLeaderboardItem: + type: object + properties: + rank: + type: integer + format: int64 + player_id: + type: string + project_account_id: + type: string + login_provider: + type: string + login_user_id: + type: string + score: + type: integer + format: int64 + nickname: + type: string + avatar_url: + type: string + AdminRankLeaderboardResult: + type: object + properties: + rank_key: + type: string + leaderboard_key: + type: string + total: + type: integer + format: int64 + page: + type: integer + page_size: + type: integer + filter_player_id: + type: string + items: + type: array + items: + $ref: '#/components/schemas/AdminRankLeaderboardItem' + AdminRankDeletePlayerRecordResult: + type: object + properties: + rank_key: + type: string + player_id: + type: string + leaderboard_key: + type: string + removed: + type: integer + format: int64 + AdminRankResetLeaderboardResult: + type: object + properties: + rank_key: + type: string + leaderboard_key: + type: string + removed: + type: integer + format: int64 + CreateRankGroupRequest: + type: object + required: [project_id, name] + properties: + project_id: + type: integer + format: int64 + name: + type: string + sort_order: + type: integer + UpdateRankGroupRequest: + type: object + properties: + name: + type: string + sort_order: + type: integer + ScoreRequest: + type: object + required: [score] + properties: + score: + type: integer + format: int64 + SpaceUpdateRequest: + type: object + required: [payload_json] + properties: + payload_json: + type: object + additionalProperties: true +paths: + /client/bootstrap: + get: + tags: [bootstrap] + summary: Get startup config + parameters: + - in: query + name: game_key + required: true + schema: { type: string } + - in: query + name: client_version + required: false + schema: { type: string } + - in: query + name: device_id + schema: { type: string } + responses: + '200': { description: Bootstrap payload } + /auth/login/exchange: + post: + tags: [auth] + summary: Exchange login provider identity for Brisk token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ExchangeRequest' + responses: + '200': { description: Login success } + '403': { description: Player banned } + '429': { description: Rate limited } + /auth/logout: + post: + tags: [auth] + security: [{ bearerAuth: [] }] + summary: Logout current session + responses: + '200': { description: Logout success } + /player/me: + get: + tags: [player] + security: [{ bearerAuth: [] }] + summary: Get current player + responses: + '200': { description: Player info } + /config/current: + get: + tags: [config] + summary: Get current dynamic config + parameters: + - in: query + name: game_key + required: true + schema: { type: string } + - in: query + name: client_version + required: false + schema: { type: string } + responses: + '200': { description: Config payload } + /announcements: + get: + tags: [announcements] + security: [{ bearerAuth: [] }] + summary: List active announcements + responses: + '200': { description: Announcement list } + /announcements/{id}/read: + post: + tags: [announcements] + security: [{ bearerAuth: [] }] + summary: Mark announcement read + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Read recorded } + /ranks/{rank_key}/top: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get top ranking list + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Top ranking list } + /ranks/{rank_key}/me: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get current player rank + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + responses: + '200': { description: Player rank } + /ranks/{rank_key}/around-me: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get nearby ranking items + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: range + schema: { type: integer } + responses: + '200': { description: Nearby ranks } + /ranks/{rank_key}/score: + post: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Submit score, keep only best + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ScoreRequest' + responses: + '200': { description: Score result } + '429': { description: Rate limited } + /ranks/{rank_key}/season/current: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get current season info for rank + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + responses: + '200': { description: Current season info } + /ranks/{rank_key}/history: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get rank season history + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Rank season history } + /ranks/{rank_key}/history/{season_id}: + get: + tags: [ranks] + security: [{ bearerAuth: [] }] + summary: Get rank history detail by season + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: path + name: season_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank season detail } + /archives/slot/1/meta: + get: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Get single-slot archive metadata + responses: + '200': { description: Archive metadata } + /archives/slots: + get: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: List archive slots + responses: + '200': { description: Archive slot list } + /archives/slot/{slot_no}/meta: + get: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Get archive metadata by slot + parameters: + - in: path + name: slot_no + required: true + schema: { type: integer } + responses: + '200': { description: Archive metadata } + /archives/slot/1/upload: + post: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Upload single-slot archive + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [base_version, checksum, file] + properties: + base_version: + type: integer + checksum: + type: string + file: + type: string + format: binary + responses: + '200': { description: Upload success } + '409': { description: Version conflict } + '429': { description: Rate limited } + /archives/slot/{slot_no}/upload: + post: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Upload archive by slot + parameters: + - in: path + name: slot_no + required: true + schema: { type: integer } + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [base_version, checksum, file] + properties: + base_version: + type: integer + checksum: + type: string + file: + type: string + format: binary + responses: + '200': { description: Upload success } + '409': { description: Version conflict } + '429': { description: Rate limited } + /archives/slot/1/download: + get: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Download single-slot archive + responses: + '200': { description: Binary archive stream } + /archives/slot/{slot_no}/download: + get: + tags: [archives] + security: [{ bearerAuth: [] }] + summary: Download archive by slot + parameters: + - in: path + name: slot_no + required: true + schema: { type: integer } + responses: + '200': { description: Binary archive stream } + /spaces/{player_id}: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + description: Optional target login provider. When present, login_user_id is also required and overrides player_id lookup. + - in: query + name: login_user_id + schema: { type: string } + description: Optional target login user id used with login provider lookup. + responses: + '200': { description: Space payload } + /spaces/{player_id}/stats: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space stats + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + responses: + '200': { description: Space stats } + /spaces/{player_id}/likes: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space likes + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Space likes } + /spaces/me: + put: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Update my space + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SpaceUpdateRequest' + responses: + '200': { description: Space updated } + /spaces/{player_id}/like: + post: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Like player space + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + responses: + '200': { description: Liked } + '429': { description: Rate limited } + delete: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Unlike player space + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + responses: + '200': { description: Unliked } + '429': { description: Rate limited } + /spaces/by-login: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space by login identity + parameters: + - in: query + name: login_provider + required: true + schema: { type: string } + - in: query + name: login_user_id + required: true + schema: { type: string } + responses: + '200': { description: Space payload } + /spaces/by-login/stats: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space stats by login identity + parameters: + - in: query + name: login_provider + required: true + schema: { type: string } + - in: query + name: login_user_id + required: true + schema: { type: string } + responses: + '200': { description: Space stats } + /spaces/by-login/likes: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get player space likes by login identity + parameters: + - in: query + name: login_provider + required: true + schema: { type: string } + - in: query + name: login_user_id + required: true + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Space likes } + /spaces/by-login/like: + post: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Like player space by login identity + parameters: + - in: query + name: login_provider + required: true + schema: { type: string } + - in: query + name: login_user_id + required: true + schema: { type: string } + responses: + '200': { description: Liked } + '429': { description: Rate limited } + delete: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Unlike player space by login identity + parameters: + - in: query + name: login_provider + required: true + schema: { type: string } + - in: query + name: login_user_id + required: true + schema: { type: string } + responses: + '200': { description: Unliked } + '429': { description: Rate limited } + /spaces/me/visits: + get: + tags: [spaces] + security: [{ bearerAuth: [] }] + summary: Get recent visitors + parameters: + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Visitor list } + /admin/auth/login: + post: + tags: [admin-auth] + summary: Admin login + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AdminLoginRequest' + responses: + '200': { description: Admin login success } + '429': { description: Rate limited } + /admin/audit-logs: + get: + tags: [admin-audit] + security: [{ bearerAuth: [] }] + summary: List audit logs + parameters: + - in: query + name: action + schema: { type: string } + - in: query + name: resource_type + schema: { type: string } + - in: query + name: username + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Audit log list } + /admin/projects: + get: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: List projects + responses: + '200': { description: Project list } + post: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: Create project + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateProjectRequest' + responses: + '200': { description: Project created } + /admin/projects/{id}: + get: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: Get project detail + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Project detail } + put: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: Update project + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProjectRequest' + responses: + '200': { description: Project updated } + /admin/projects/{id}/icon: + get: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: Get project icon + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Project icon stream } + post: + tags: [admin-projects] + security: [{ bearerAuth: [] }] + summary: Upload project icon + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + responses: + '200': { description: Project icon uploaded } + /admin/configs/current: + get: + tags: [admin-configs] + security: [{ bearerAuth: [] }] + summary: Get current dynamic config for project + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Current config } + put: + tags: [admin-configs] + security: [{ bearerAuth: [] }] + summary: Save current dynamic config for project + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpsertCurrentConfigRequest' + responses: + '200': { description: Config saved } + /admin/announcements: + get: + tags: [admin-announcements] + security: [{ bearerAuth: [] }] + summary: List announcements + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: status + schema: { type: string } + responses: + '200': { description: Announcement list } + post: + tags: [admin-announcements] + security: [{ bearerAuth: [] }] + summary: Create announcement + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateAnnouncementRequest' + responses: + '200': { description: Announcement created } + /admin/announcements/{id}: + get: + tags: [admin-announcements] + security: [{ bearerAuth: [] }] + summary: Get announcement detail + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Announcement detail } + put: + tags: [admin-announcements] + security: [{ bearerAuth: [] }] + summary: Update announcement + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAnnouncementRequest' + responses: + '200': { description: Announcement updated } + delete: + tags: [admin-announcements] + security: [{ bearerAuth: [] }] + summary: Delete announcement + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Announcement deleted } + /admin/rank-groups: + get: + tags: [admin-rank-groups] + security: [{ bearerAuth: [] }] + summary: List rank groups + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank groups } + post: + tags: [admin-rank-groups] + security: [{ bearerAuth: [] }] + summary: Create rank group + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateRankGroupRequest' + responses: + '200': { description: Rank group created } + /admin/rank-groups/{id}: + put: + tags: [admin-rank-groups] + security: [{ bearerAuth: [] }] + summary: Update rank group + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateRankGroupRequest' + responses: + '200': { description: Rank group updated } + delete: + tags: [admin-rank-groups] + security: [{ bearerAuth: [] }] + summary: Delete rank group + parameters: + - in: path + name: id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank group deleted } + /admin/ranks: + get: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: List rank definitions + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank definitions } + post: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Create rank definition + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateRankRequest' + responses: + '200': { description: Rank created } + /admin/ranks/{rank_key}: + get: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Get rank definition detail + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank detail } + put: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Update rank definition + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateRankRequest' + responses: + '200': { description: Rank updated } + delete: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Delete rank definition + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Rank deleted } + /admin/ranks/{rank_key}/players/{player_id}: + delete: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Delete current leaderboard record by player id + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': + description: Rank player record removed + content: + application/json: + schema: + $ref: '#/components/schemas/AdminRankDeletePlayerRecordResult' + /admin/ranks/{rank_key}/leaderboard: + get: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Get current live leaderboard with pagination and filters + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: page + schema: { type: integer, default: 1 } + - in: query + name: page_size + schema: { type: integer, default: 20 } + - in: query + name: player_id + schema: { type: string } + - in: query + name: project_account_id + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_provider + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + responses: + '200': + description: Current leaderboard snapshot + content: + application/json: + schema: + $ref: '#/components/schemas/AdminRankLeaderboardResult' + /admin/ranks/{rank_key}/reset: + post: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: Reset current live leaderboard data for rank + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': + description: Current leaderboard reset result + content: + application/json: + schema: + $ref: '#/components/schemas/AdminRankResetLeaderboardResult' + /admin/ranks/{rank_key}/seasons: + get: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: List rank seasons + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Rank seasons } + /admin/ranks/{rank_key}/seasons/{season_id}/logs: + get: + tags: [admin-ranks] + security: [{ bearerAuth: [] }] + summary: List rank settlement logs + parameters: + - in: path + name: rank_key + required: true + schema: { type: string } + - in: path + name: season_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Settlement logs } + /admin/archives/overview: + get: + tags: [admin-archives] + security: [{ bearerAuth: [] }] + summary: Get archive overview + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Archive overview } + /admin/archives: + get: + tags: [admin-archives] + security: [{ bearerAuth: [] }] + summary: List archive players + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: player_id + schema: { type: string } + - in: query + name: project_account_id + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Archive player list } + /admin/archives/{player_id}: + get: + tags: [admin-archives] + security: [{ bearerAuth: [] }] + summary: Get archive slots by player + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Archive detail } + /admin/archives/{player_id}/slots/{slot_no}/download: + get: + tags: [admin-archives] + security: [{ bearerAuth: [] }] + summary: Download archive slot + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: path + name: slot_no + required: true + schema: { type: integer } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Archive file stream } + /admin/archives/{player_id}/slots/{slot_no}: + delete: + tags: [admin-archives] + security: [{ bearerAuth: [] }] + summary: Delete archive slot + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: path + name: slot_no + required: true + schema: { type: integer } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Archive slot deleted } + /admin/players: + get: + tags: [admin-players] + security: [{ bearerAuth: [] }] + summary: List players + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: player_id + schema: { type: string } + - in: query + name: project_account_id + schema: { type: string } + - in: query + name: login_user_id + schema: { type: string } + responses: + '200': { description: Player list } + /admin/players/{player_id}: + get: + tags: [admin-players] + security: [{ bearerAuth: [] }] + summary: Get player detail + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Player detail } + /admin/players/{player_id}/ban: + post: + tags: [admin-players] + security: [{ bearerAuth: [] }] + summary: Ban player + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/BanPlayerRequest' + responses: + '200': { description: Player banned } + /admin/players/{player_id}/unban: + post: + tags: [admin-players] + security: [{ bearerAuth: [] }] + summary: Unban player + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + responses: + '200': { description: Player unbanned } + /admin/spaces/overview: + get: + tags: [admin-spaces] + security: [{ bearerAuth: [] }] + summary: Get space module overview + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Space overview } + /admin/spaces: + get: + tags: [admin-spaces] + security: [{ bearerAuth: [] }] + summary: List player spaces + parameters: + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: player_id + schema: { type: string } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Space list } + /admin/spaces/{player_id}: + get: + tags: [admin-spaces] + security: [{ bearerAuth: [] }] + summary: Get player space detail + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + responses: + '200': { description: Space detail } + /admin/spaces/{player_id}/likes: + get: + tags: [admin-spaces] + security: [{ bearerAuth: [] }] + summary: Get player space likes + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Space likes } + /admin/spaces/{player_id}/visits: + get: + tags: [admin-spaces] + security: [{ bearerAuth: [] }] + summary: Get player space visits + parameters: + - in: path + name: player_id + required: true + schema: { type: string } + - in: query + name: project_id + required: true + schema: { type: integer, format: int64 } + - in: query + name: limit + schema: { type: integer } + responses: + '200': { description: Space visits }