From 88b7d9c757aa27de9f47f77c16edc7f7ce7398da Mon Sep 17 00:00:00 2001 From: "CORE-FOLDCC\\Core" <1813547935@qq.com> Date: Wed, 13 May 2026 19:05:00 +0800 Subject: [PATCH] Release Brisk SDK v0.4.1 --- Assets/BriskSdk/Runtime/Core/Brisk.cs | 56 +- .../Runtime/Core/BriskDefaultErrorDialog.cs | 700 +++++++++++++++++- .../Core/BriskDefaultErrorPresenter.cs | 64 +- Assets/BriskSdk/Runtime/Core/BriskOptions.cs | 4 + .../QuickStart/BriskQuickStartSample.cs | 99 +++ .../CHANGELOG.md | 8 + .../README.md | 6 +- .../Runtime/Core/Brisk.cs | 56 +- .../Runtime/Core/BriskDefaultErrorDialog.cs | 700 +++++++++++++++++- .../Core/BriskDefaultErrorPresenter.cs | 64 +- .../Runtime/Core/BriskOptions.cs | 4 + .../QuickStart/BriskQuickStartSample.cs | 99 +++ .../package.json | 2 +- README.md | 6 +- 14 files changed, 1755 insertions(+), 113 deletions(-) diff --git a/Assets/BriskSdk/Runtime/Core/Brisk.cs b/Assets/BriskSdk/Runtime/Core/Brisk.cs index ff77830..3e824c0 100644 --- a/Assets/BriskSdk/Runtime/Core/Brisk.cs +++ b/Assets/BriskSdk/Runtime/Core/Brisk.cs @@ -198,7 +198,16 @@ public static class Brisk internal static void NotifyAuthExpired(BriskAuthExpiredException exception) { - s_context?.ErrorPresenter?.ShowAuthExpired(exception); + NotifyAuthExpired(exception, true); + } + + internal static void NotifyAuthExpired(BriskAuthExpiredException exception, bool showDefaultPresenter) + { + if (showDefaultPresenter) + { + s_context?.ErrorPresenter?.ShowAuthExpired(exception); + } + OnAuthExpired?.Invoke(exception); } @@ -241,6 +250,15 @@ public static class Brisk return; } + if (storedSession.ExpiresAt.HasValue && storedSession.ExpiresAt.Value <= DateTimeOffset.UtcNow) + { + await HandleExpiredStartupSessionAsync( + context, + storedSession, + new BriskAuthExpiredException("Stored session expired before initialization.")); + return; + } + context.Session.Update( storedSession.AccessToken, storedSession.ExpiresAt, @@ -268,9 +286,7 @@ public static class Brisk } catch (BriskAuthExpiredException exception) { - context.Session.Clear(); - await context.TokenStore.ClearAsync(); - NotifyAuthExpired(exception); + await HandleExpiredStartupSessionAsync(context, storedSession, exception); } catch (BriskBlockingException exception) { @@ -278,4 +294,36 @@ public static class Brisk throw; } } + + private static async Task HandleExpiredStartupSessionAsync(BriskContext context, BriskStoredSession storedSession, BriskAuthExpiredException exception) + { + context.Session.Clear(); + await context.TokenStore.ClearAsync(); + NotifyAuthExpired(exception, false); + + if (!context.Options.AutoReloginOnInitialize || !HasStoredLoginIdentity(storedSession)) + { + return; + } + + try + { + await Auth.LoginWithUserIdAsync(storedSession.LoginProvider, storedSession.LoginUserId); + } + catch (BriskBlockingException) + { + throw; + } + catch + { + // 初始化期静默重登失败时,交给业务层后续第三方登录流程重新调用 Brisk.Auth。 + } + } + + private static bool HasStoredLoginIdentity(BriskStoredSession storedSession) + { + return storedSession != null + && !string.IsNullOrWhiteSpace(storedSession.LoginProvider) + && !string.IsNullOrWhiteSpace(storedSession.LoginUserId); + } } diff --git a/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs index cad93d4..1840523 100644 --- a/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs +++ b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorDialog.cs @@ -1,26 +1,74 @@ using System; +using System.Collections; using UnityEngine; +using UnityEngine.UIElements; +[DefaultExecutionOrder(10000)] internal sealed class BriskDefaultErrorDialog : MonoBehaviour { private const string HostName = "BriskDefaultErrorDialog"; + private const float OpenDuration = 0.24f; + private const float CloseDuration = 0.2f; + + private static readonly Color Ink = ColorFromHex(0x05060a); + private static readonly Color InkSoft = ColorFromHex(0x1b1d24); + private static readonly Color Muted = ColorFromHex(0x5f6572); + private static readonly Color Line = ColorFromHex(0xdde2ea); + private static readonly Color Paper = ColorFromHex(0xffffff); + private static readonly Color Soft = ColorFromHex(0xf7f8fb); + private static readonly Color Blue = ColorFromHex(0x2563ff); + private static readonly Color Teal = ColorFromHex(0x23d6c8); + private static readonly Color Violet = ColorFromHex(0x8b5cff); + private static readonly Color Dark = ColorFromHex(0x050506); private static BriskDefaultErrorDialog s_instance; - private string _title; - private string _message; - private string _confirmText; + private UIDocument _document; + private PanelSettings _panelSettings; + private VisualElement _root; + private VisualElement _stage; + private VisualElement _panel; + private Label _eyebrowLabel; + private Label _titleLabel; + private Label _messageLabel; + private VisualElement _spacer; + private VisualElement _divider; + private VisualElement _confirmButton; + private Label _confirmLabel; private Action _onConfirm; + private Coroutine _animation; private bool _visible; + private bool _closeOnBackdrop; + private bool _closeOnEscape; + private GUIStyle _legacyTitleStyle; + private GUIStyle _legacyMessageStyle; + private GUIStyle _legacyEyebrowStyle; + private GUIStyle _legacyButtonStyle; + private Texture2D _legacyButtonTexture; + private Texture2D _legacyButtonHoverTexture; - public static void Show(string title, string message, string confirmText, Action onConfirm = null) + internal static bool IsVisible => s_instance != null && s_instance._visible; + + internal static void RenderTopmostLegacyOverlay() + { + if (s_instance == null || !s_instance._visible) + { + return; + } + + s_instance.DrawLegacyOverlay(); + } + + public static void Show(string title, string message, string confirmText, Action onConfirm = null, bool closeOnBackdrop = false, bool closeOnEscape = false) { 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; + host.ShowInternal( + string.IsNullOrWhiteSpace(title) ? "Brisk" : title, + string.IsNullOrWhiteSpace(message) ? "服务暂时不可用,请稍后再试。" : message, + string.IsNullOrWhiteSpace(confirmText) ? "知道了" : confirmText, + onConfirm, + closeOnBackdrop, + closeOnEscape); } private static BriskDefaultErrorDialog EnsureInstance() @@ -46,6 +94,249 @@ internal sealed class BriskDefaultErrorDialog : MonoBehaviour return s_instance; } + private void ShowInternal(string title, string message, string confirmText, Action onConfirm, bool closeOnBackdrop, bool closeOnEscape) + { + EnsureDocument(); + ApplySafeAreaPadding(); + + _titleLabel.text = title; + _messageLabel.text = message; + _confirmLabel.text = confirmText; + _onConfirm = onConfirm; + _closeOnBackdrop = closeOnBackdrop; + _closeOnEscape = closeOnEscape; + _visible = true; + + StartDialogAnimation(PlayOpenAnimation()); + } + + private void EnsureDocument() + { + if (_document != null) + { + ApplyResponsiveLayout(); + return; + } + + _panelSettings = ScriptableObject.CreateInstance(); + _panelSettings.name = HostName + "PanelSettings"; + _panelSettings.scaleMode = PanelScaleMode.ConstantPixelSize; + _panelSettings.scale = 1f; + _panelSettings.sortingOrder = 32767f; + + _document = gameObject.AddComponent(); + _document.panelSettings = _panelSettings; + _document.sortingOrder = 32767f; + + BuildVisualTree(_document.rootVisualElement); + ApplyResponsiveLayout(); + } + + private void BuildVisualTree(VisualElement documentRoot) + { + documentRoot.Clear(); + documentRoot.pickingMode = PickingMode.Ignore; + documentRoot.style.position = Position.Absolute; + documentRoot.style.left = 0f; + documentRoot.style.top = 0f; + documentRoot.style.right = 0f; + documentRoot.style.bottom = 0f; + documentRoot.style.width = Length.Percent(100f); + documentRoot.style.height = Length.Percent(100f); + documentRoot.style.flexGrow = 1f; + + _root = new VisualElement + { + pickingMode = PickingMode.Position, + focusable = true + }; + _root.style.position = Position.Absolute; + _root.style.left = 0; + _root.style.top = 0; + _root.style.right = 0; + _root.style.bottom = 0; + _root.style.display = DisplayStyle.None; + _root.style.opacity = 0f; + _root.style.backgroundColor = new Color(0.02f, 0.02f, 0.024f, 0.58f); + _root.style.justifyContent = Justify.Center; + _root.style.alignItems = Align.Center; + _root.RegisterCallback(HandleRootKeyDown); + _root.RegisterCallback(evt => + { + if (_closeOnBackdrop && evt.target == _root) + { + BeginClose(); + } + + evt.StopPropagation(); + }); + documentRoot.Add(_root); + + _stage = new VisualElement(); + _stage.pickingMode = PickingMode.Ignore; + _stage.style.position = Position.Relative; + _stage.style.width = Length.Percent(82f); + _stage.style.maxWidth = 900f; + _stage.style.opacity = 0f; + _stage.style.marginTop = 14f; + _root.Add(_stage); + + var shadow = new VisualElement(); + shadow.pickingMode = PickingMode.Ignore; + shadow.style.position = Position.Absolute; + shadow.style.left = 0f; + shadow.style.right = 0f; + shadow.style.top = 10f; + shadow.style.bottom = -10f; + shadow.style.backgroundColor = new Color(0f, 0f, 0f, 0.09f); + shadow.style.borderTopLeftRadius = 18f; + shadow.style.borderTopRightRadius = 18f; + shadow.style.borderBottomLeftRadius = 18f; + shadow.style.borderBottomRightRadius = 18f; + _stage.Add(shadow); + + _panel = new VisualElement + { + pickingMode = PickingMode.Position + }; + _panel.style.width = Length.Percent(100f); + _panel.style.minHeight = 360f; + _panel.style.paddingTop = 36f; + _panel.style.paddingRight = 36f; + _panel.style.paddingBottom = 36f; + _panel.style.paddingLeft = 36f; + _panel.style.backgroundColor = Paper; + _panel.style.borderTopWidth = 1f; + _panel.style.borderRightWidth = 1f; + _panel.style.borderBottomWidth = 1f; + _panel.style.borderLeftWidth = 1f; + _panel.style.borderTopColor = Line; + _panel.style.borderRightColor = Line; + _panel.style.borderBottomColor = Line; + _panel.style.borderLeftColor = Line; + _panel.style.borderTopLeftRadius = 18f; + _panel.style.borderTopRightRadius = 18f; + _panel.style.borderBottomLeftRadius = 18f; + _panel.style.borderBottomRightRadius = 18f; + _panel.style.overflow = Overflow.Hidden; + _stage.Add(_panel); + + var accent = new VisualElement(); + accent.pickingMode = PickingMode.Ignore; + accent.style.position = Position.Absolute; + accent.style.top = 0f; + accent.style.left = 0f; + accent.style.right = 0f; + accent.style.height = 2f; + accent.style.flexDirection = FlexDirection.Row; + _panel.Add(accent); + AddAccentSegment(accent, Blue); + AddAccentSegment(accent, Teal); + AddAccentSegment(accent, Violet); + + _eyebrowLabel = new Label("BRISK SERVICE"); + _eyebrowLabel.style.color = Muted; + _eyebrowLabel.style.fontSize = 13f; + _eyebrowLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _eyebrowLabel.style.letterSpacing = 1.4f; + _eyebrowLabel.style.height = 18f; + _eyebrowLabel.style.flexShrink = 0f; + _eyebrowLabel.style.marginBottom = 12f; + _panel.Add(_eyebrowLabel); + + _titleLabel = new Label(); + _titleLabel.style.color = Ink; + _titleLabel.style.fontSize = 30f; + _titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _titleLabel.style.whiteSpace = WhiteSpace.Normal; + _titleLabel.style.unityTextAlign = TextAnchor.MiddleLeft; + _titleLabel.style.minHeight = 40f; + _titleLabel.style.flexShrink = 0f; + _titleLabel.style.marginBottom = 16f; + _panel.Add(_titleLabel); + + _messageLabel = new Label(); + _messageLabel.style.color = Muted; + _messageLabel.style.fontSize = 21f; + _messageLabel.style.whiteSpace = WhiteSpace.Normal; + _messageLabel.style.unityTextAlign = TextAnchor.UpperLeft; + _messageLabel.style.minHeight = 64f; + _messageLabel.style.flexShrink = 0f; + _messageLabel.style.marginBottom = 22f; + _panel.Add(_messageLabel); + + _spacer = new VisualElement(); + _spacer.pickingMode = PickingMode.Ignore; + _spacer.style.flexGrow = 1f; + _spacer.style.minHeight = 18f; + _panel.Add(_spacer); + + _divider = new VisualElement(); + _divider.style.height = 1f; + _divider.style.flexShrink = 0f; + _divider.style.backgroundColor = Line; + _divider.style.marginBottom = 22f; + _panel.Add(_divider); + + _confirmButton = new VisualElement + { + focusable = true, + pickingMode = PickingMode.Position + }; + _confirmButton.style.height = 58f; + _confirmButton.style.minHeight = 58f; + _confirmButton.style.flexShrink = 0f; + _confirmButton.style.borderTopLeftRadius = 24f; + _confirmButton.style.borderTopRightRadius = 24f; + _confirmButton.style.borderBottomLeftRadius = 24f; + _confirmButton.style.borderBottomRightRadius = 24f; + _confirmButton.style.backgroundColor = Dark; + _confirmButton.style.justifyContent = Justify.Center; + _confirmButton.style.alignItems = Align.Center; + _confirmButton.RegisterCallback(_ => _confirmButton.style.backgroundColor = InkSoft); + _confirmButton.RegisterCallback(_ => _confirmButton.style.backgroundColor = Dark); + _confirmButton.RegisterCallback(_ => + { + _confirmButton.style.backgroundColor = Ink; + _confirmButton.style.marginTop = 1f; + }); + _confirmButton.RegisterCallback(_ => + { + _confirmButton.style.backgroundColor = InkSoft; + _confirmButton.style.marginTop = 0f; + }); + _confirmButton.RegisterCallback(_ => BeginClose()); + _confirmButton.RegisterCallback(HandleButtonKeyDown); + _panel.Add(_confirmButton); + + _confirmLabel = new Label(); + _confirmLabel.style.color = Color.white; + _confirmLabel.style.fontSize = 18f; + _confirmLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _confirmLabel.style.unityTextAlign = TextAnchor.MiddleCenter; + _confirmLabel.style.height = 58f; + _confirmButton.Add(_confirmLabel); + } + + private void Update() + { + if (_visible && _root != null) + { + ApplySafeAreaPadding(); + ApplyResponsiveLayout(); + } + } + + private void BeginClose() + { + if (!_visible || _root == null) + { + return; + } + + StartDialogAnimation(PlayCloseAnimation()); + } + private void OnGUI() { if (!_visible) @@ -53,36 +344,379 @@ internal sealed class BriskDefaultErrorDialog : MonoBehaviour return; } - var overlayRect = new Rect(0, 0, Screen.width, Screen.height); + DrawLegacyOverlay(); + } + + private void DrawLegacyOverlay() + { + EnsureLegacyStyles(); + + var previousDepth = GUI.depth; + var previousMatrix = GUI.matrix; var previousColor = GUI.color; - GUI.color = new Color(0f, 0f, 0f, 0.65f); - GUI.Box(overlayRect, GUIContent.none); - GUI.color = previousColor; + GUI.depth = -10000; + GUI.matrix = Matrix4x4.identity; - 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); + DrawLegacyRect(new Rect(0f, 0f, Screen.width, Screen.height), new Color(0.02f, 0.02f, 0.024f, 0.58f)); - 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(); + var scale = GetReadableScale(); + ApplyLegacyScale(scale); - if (GUILayout.Button(_confirmText, GUILayout.Height(36f))) + var margin = GetDialogMargin(scale); + var padding = GetDialogPadding(scale); + var buttonHeight = GetDialogButtonHeight(scale); + var width = GetDialogWidth(margin); + var contentWidth = width - padding * 2f; + var titleHeight = Mathf.Max(34f, _legacyTitleStyle.CalcHeight(new GUIContent(_titleLabel.text), contentWidth)); + var messageHeight = Mathf.Max(48f, _legacyMessageStyle.CalcHeight(new GUIContent(_messageLabel.text), contentWidth)); + var minHeight = GetDialogMinHeight(scale, margin); + var contentHeight = padding * 2f + 18f * scale + 12f * scale + titleHeight + 16f * scale + messageHeight + 24f * scale + 1f + 22f * scale + buttonHeight; + var height = Mathf.Clamp(contentHeight, minHeight, Mathf.Max(minHeight, Screen.height - margin * 2f)); + var rect = new Rect((Screen.width - width) * 0.5f, (Screen.height - height) * 0.5f, width, height); + + DrawLegacyPanel(rect); + + var x = rect.x + padding; + var y = rect.y + padding; + GUI.Label(new Rect(x, y, contentWidth, 18f * scale), "BRISK SERVICE", _legacyEyebrowStyle); + y += 30f * scale; + GUI.Label(new Rect(x, y, contentWidth, titleHeight), _titleLabel.text, _legacyTitleStyle); + y += titleHeight + 16f * scale; + GUI.Label(new Rect(x, y, contentWidth, messageHeight), _messageLabel.text, _legacyMessageStyle); + + DrawLegacyRect(new Rect(x, rect.yMax - padding - buttonHeight - 22f * scale, contentWidth, 1f), Line); + var buttonRect = new Rect(x, rect.yMax - padding - buttonHeight, contentWidth, buttonHeight); + if (GUI.Button(buttonRect, _confirmLabel.text, _legacyButtonStyle)) { - var callback = _onConfirm; - _visible = false; - _onConfirm = null; - callback?.Invoke(); + BeginClose(); } - GUILayout.Space(8f); - GUILayout.EndArea(); + ConsumeBlockingEvent(); + + GUI.color = previousColor; + GUI.matrix = previousMatrix; + GUI.depth = previousDepth; + } + + private IEnumerator PlayOpenAnimation() + { + _root.style.display = DisplayStyle.Flex; + _root.style.opacity = 0f; + _stage.style.opacity = 0f; + _stage.style.marginTop = 14f; + + yield return null; + _root.Focus(); + + var elapsed = 0f; + while (elapsed < OpenDuration) + { + elapsed += Time.unscaledDeltaTime; + var eased = EaseOutCubic(Mathf.Clamp01(elapsed / OpenDuration)); + _root.style.opacity = eased; + _stage.style.opacity = eased; + _stage.style.marginTop = Mathf.Lerp(14f, 0f, eased); + yield return null; + } + + _root.style.opacity = 1f; + _stage.style.opacity = 1f; + _stage.style.marginTop = 0f; + } + + private IEnumerator PlayCloseAnimation() + { + var callback = _onConfirm; + _onConfirm = null; + + var elapsed = 0f; + while (elapsed < CloseDuration) + { + elapsed += Time.unscaledDeltaTime; + var eased = EaseInCubic(Mathf.Clamp01(elapsed / CloseDuration)); + var alpha = 1f - eased; + _root.style.opacity = alpha; + _stage.style.opacity = alpha; + _stage.style.marginTop = Mathf.Lerp(0f, 10f, eased); + yield return null; + } + + _root.style.display = DisplayStyle.None; + _visible = false; + callback?.Invoke(); + } + + private void StartDialogAnimation(IEnumerator animation) + { + if (_animation != null) + { + StopCoroutine(_animation); + } + + _animation = StartCoroutine(animation); + } + + private void ApplySafeAreaPadding() + { + var safeArea = Screen.safeArea; + var basePadding = Mathf.Max(20f, Mathf.Min(Screen.width, Screen.height) * 0.04f); + _root.style.paddingLeft = Mathf.Max(basePadding, safeArea.xMin + basePadding); + _root.style.paddingRight = Mathf.Max(basePadding, Screen.width - safeArea.xMax + basePadding); + _root.style.paddingTop = Mathf.Max(basePadding, Screen.height - safeArea.yMax + basePadding); + _root.style.paddingBottom = Mathf.Max(basePadding, safeArea.yMin + basePadding); + } + + private void ApplyResponsiveLayout() + { + if (_panel == null) + { + return; + } + + var scale = GetReadableScale(); + var margin = GetDialogMargin(scale); + var maxWidth = Mathf.Min(900f, Mathf.Max(320f, Screen.width - margin * 2f)); + var minHeight = GetDialogMinHeight(scale, margin); + var padding = GetDialogPadding(scale); + var buttonHeight = GetDialogButtonHeight(scale); + + _stage.style.width = Length.Percent(82f); + _stage.style.maxWidth = maxWidth; + _panel.style.minHeight = minHeight; + _panel.style.paddingTop = padding; + _panel.style.paddingRight = padding; + _panel.style.paddingBottom = padding; + _panel.style.paddingLeft = padding; + _panel.style.borderTopLeftRadius = 20f * scale; + _panel.style.borderTopRightRadius = 20f * scale; + _panel.style.borderBottomLeftRadius = 20f * scale; + _panel.style.borderBottomRightRadius = 20f * scale; + + _eyebrowLabel.style.fontSize = 12f * scale; + _eyebrowLabel.style.height = 18f * scale; + _eyebrowLabel.style.marginBottom = 12f * scale; + _titleLabel.style.fontSize = 28f * scale; + _titleLabel.style.minHeight = 40f * scale; + _titleLabel.style.marginBottom = 16f * scale; + _messageLabel.style.fontSize = 20f * scale; + _messageLabel.style.minHeight = 64f * scale; + _messageLabel.style.marginBottom = 22f * scale; + _spacer.style.minHeight = 18f * scale; + _divider.style.marginBottom = 22f * scale; + _confirmButton.style.height = buttonHeight; + _confirmButton.style.minHeight = buttonHeight; + _confirmButton.style.borderTopLeftRadius = buttonHeight * 0.5f; + _confirmButton.style.borderTopRightRadius = buttonHeight * 0.5f; + _confirmButton.style.borderBottomLeftRadius = buttonHeight * 0.5f; + _confirmButton.style.borderBottomRightRadius = buttonHeight * 0.5f; + _confirmLabel.style.fontSize = 17f * scale; + _confirmLabel.style.height = buttonHeight; + } + + private void HandleRootKeyDown(KeyDownEvent evt) + { + if (_closeOnEscape && evt.keyCode == KeyCode.Escape) + { + BeginClose(); + evt.StopPropagation(); + return; + } + + evt.StopPropagation(); + } + + private void HandleButtonKeyDown(KeyDownEvent evt) + { + if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter || evt.keyCode == KeyCode.Space) + { + BeginClose(); + evt.StopPropagation(); + } + } + + private void OnDestroy() + { + if (s_instance == this) + { + s_instance = null; + } + + if (_panelSettings != null) + { + Destroy(_panelSettings); + _panelSettings = null; + } + } + + private void EnsureLegacyStyles() + { + if (_legacyTitleStyle != null) + { + return; + } + + _legacyButtonTexture = CreateTexture(Dark); + _legacyButtonHoverTexture = CreateTexture(InkSoft); + + _legacyEyebrowStyle = new GUIStyle + { + fontStyle = FontStyle.Bold, + normal = { textColor = Muted } + }; + + _legacyTitleStyle = new GUIStyle + { + fontStyle = FontStyle.Bold, + wordWrap = true, + normal = { textColor = Ink } + }; + + _legacyMessageStyle = new GUIStyle + { + wordWrap = true, + normal = { textColor = Muted } + }; + + _legacyButtonStyle = new GUIStyle + { + alignment = TextAnchor.MiddleCenter, + fontStyle = FontStyle.Bold, + border = new RectOffset(24, 24, 24, 24), + normal = + { + background = _legacyButtonTexture, + textColor = Color.white + }, + hover = + { + background = _legacyButtonHoverTexture, + textColor = Color.white + }, + active = + { + background = _legacyButtonHoverTexture, + textColor = Color.white + } + }; + } + + private void ApplyLegacyScale(float scale) + { + _legacyEyebrowStyle.fontSize = Mathf.RoundToInt(12f * scale); + _legacyTitleStyle.fontSize = Mathf.RoundToInt(28f * scale); + _legacyMessageStyle.fontSize = Mathf.RoundToInt(20f * scale); + _legacyButtonStyle.fontSize = Mathf.RoundToInt(17f * scale); + } + + private void DrawLegacyPanel(Rect rect) + { + DrawLegacyRect(new Rect(rect.x, rect.y + 12f, rect.width, rect.height), new Color(0f, 0f, 0f, 0.08f)); + DrawLegacyRect(rect, Paper); + DrawLegacyRect(new Rect(rect.x, rect.y, rect.width, 1f), Line); + DrawLegacyRect(new Rect(rect.x, rect.yMax - 1f, rect.width, 1f), Line); + DrawLegacyRect(new Rect(rect.x, rect.y, 1f, rect.height), Line); + DrawLegacyRect(new Rect(rect.xMax - 1f, rect.y, 1f, rect.height), Line); + + var segment = rect.width / 3f; + DrawLegacyRect(new Rect(rect.x, rect.y, segment, 2f), Blue); + DrawLegacyRect(new Rect(rect.x + segment, rect.y, segment, 2f), Teal); + DrawLegacyRect(new Rect(rect.x + segment * 2f, rect.y, rect.width - segment * 2f, 2f), Violet); + } + + private static void ConsumeBlockingEvent() + { + var evt = Event.current; + if (evt == null) + { + return; + } + + switch (evt.type) + { + case EventType.MouseDown: + case EventType.MouseUp: + case EventType.MouseDrag: + case EventType.ScrollWheel: + case EventType.KeyDown: + case EventType.KeyUp: + evt.Use(); + break; + } + } + + private static Texture2D CreateTexture(Color color) + { + var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false) + { + hideFlags = HideFlags.HideAndDontSave + }; + texture.SetPixel(0, 0, color); + texture.Apply(); + return texture; + } + + private static void DrawLegacyRect(Rect rect, Color color) + { + var previousColor = GUI.color; + GUI.color = color; + GUI.DrawTexture(rect, Texture2D.whiteTexture); + GUI.color = previousColor; + } + + private static void AddAccentSegment(VisualElement parent, Color color) + { + var segment = new VisualElement(); + segment.style.flexGrow = 1f; + segment.style.backgroundColor = color; + parent.Add(segment); + } + + private static float GetReadableScale() + { + return Mathf.Clamp(Mathf.Min(Screen.width, Screen.height) / 720f, 1f, 1.22f); + } + + private static float GetDialogMargin(float scale) + { + return Mathf.Max(24f * scale, Mathf.Min(Screen.width, Screen.height) * 0.06f); + } + + private static float GetDialogPadding(float scale) + { + return 34f * scale; + } + + private static float GetDialogButtonHeight(float scale) + { + return 56f * scale; + } + + private static float GetDialogWidth(float margin) + { + return Mathf.Clamp(Screen.width * 0.82f, 320f, Mathf.Max(320f, Mathf.Min(900f, Screen.width - margin * 2f))); + } + + private static float GetDialogMinHeight(float scale, float margin) + { + var availableHeight = Mathf.Max(320f, Screen.height - margin * 2f); + return Mathf.Min(availableHeight, Mathf.Clamp(Screen.height * 0.24f, 360f * scale, 620f)); + } + + private static float EaseOutCubic(float t) + { + return 1f - Mathf.Pow(1f - t, 3f); + } + + private static float EaseInCubic(float t) + { + return t * t * t; + } + + private static Color ColorFromHex(int rgb) + { + var r = ((rgb >> 16) & 0xff) / 255f; + var g = ((rgb >> 8) & 0xff) / 255f; + var b = (rgb & 0xff) / 255f; + return new Color(r, g, b, 1f); } } diff --git a/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs index c5781a8..3faaf25 100644 --- a/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs +++ b/Assets/BriskSdk/Runtime/Core/BriskDefaultErrorPresenter.cs @@ -13,18 +13,22 @@ public sealed class BriskDefaultErrorPresenter : IBriskErrorPresenter public void ShowBlockingError(BriskBlockingException exception) { - var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message) - ? exception.Message - : "A blocking error occurred."; + var message = GetBlockingMessage(exception); BriskDefaultErrorDialog.Show( title: GetBlockingTitle(exception), message: message, - confirmText: "OK", + confirmText: "知道了", onConfirm: () => { - var exitHandler = Brisk.IsInitialized ? Brisk.Options?.ExitHandler : null; - exitHandler?.Invoke(); + var exitHandler = Brisk.IsInitialized ? Brisk.GetRequiredContext().ExitHandler : null; + if (exitHandler != null) + { + exitHandler.Invoke(); + return; + } + + Application.Quit(); }); Debug.LogError("[Brisk] Blocking error: " + message); @@ -32,35 +36,63 @@ public sealed class BriskDefaultErrorPresenter : IBriskErrorPresenter public void ShowAuthExpired(BriskAuthExpiredException exception) { - var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message) - ? exception.Message - : "Login expired. Please sign in again."; + var message = GetAuthExpiredMessage(exception); BriskDefaultErrorDialog.Show( - title: "Login Expired", + title: "登录已过期", message: message, - confirmText: "OK"); + confirmText: "知道了"); - Debug.LogWarning("[Brisk] Auth expired: " + message); + Debug.LogWarning("[Brisk] Auth expired: " + (exception != null ? exception.Message : message)); } private static string GetBlockingTitle(BriskBlockingException exception) { if (exception is BriskMaintenanceException) { - return "Maintenance"; + return "服务维护中"; } if (exception is BriskAccountBannedException) { - return "Account Restricted"; + return "账号受限"; } if (exception is BriskClientUpdateRequiredException) { - return "Update Required"; + return "需要更新"; } - return "Service Unavailable"; + return "服务暂不可用"; + } + + private static string GetBlockingMessage(BriskBlockingException exception) + { + if (exception is BriskMaintenanceException) + { + return "服务正在维护,请稍后再试。"; + } + + if (exception is BriskAccountBannedException) + { + return "当前账号暂时无法使用该服务。"; + } + + if (exception is BriskClientUpdateRequiredException) + { + return "当前版本过低,请更新后继续。"; + } + + return "服务暂时不可用,请稍后再试。"; + } + + private static string GetAuthExpiredMessage(BriskAuthExpiredException exception) + { + if (exception != null && exception.Message != null && exception.Message.IndexOf("missing", StringComparison.OrdinalIgnoreCase) >= 0) + { + return "当前登录状态不可用,请重新登录。"; + } + + return "登录状态已失效,请重新登录后继续。"; } } diff --git a/Assets/BriskSdk/Runtime/Core/BriskOptions.cs b/Assets/BriskSdk/Runtime/Core/BriskOptions.cs index 6bc6211..4c850c6 100644 --- a/Assets/BriskSdk/Runtime/Core/BriskOptions.cs +++ b/Assets/BriskSdk/Runtime/Core/BriskOptions.cs @@ -30,6 +30,10 @@ public sealed class BriskOptions /// public bool ValidateSessionOnInitialize = true; /// + /// 初始化恢复的本地会话已过期或失效时,是否使用本地保存的登录身份静默换取新会话。 + /// + public bool AutoReloginOnInitialize = true; + /// /// 自定义登录态持久化实现。 /// public IBriskTokenStore TokenStore; diff --git a/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs b/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs index 8581813..dbeeeb4 100644 --- a/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs +++ b/Assets/BriskSdk/Samples/QuickStart/BriskQuickStartSample.cs @@ -112,6 +112,8 @@ public sealed class BriskQuickStartSample : MonoBehaviour private GUIStyle _statusStyle; private GUIStyle _listButtonStyle; private GUIStyle _modalStyle; + private PropertyInfo _defaultDialogVisibleProperty; + private MethodInfo _defaultDialogRenderMethod; private void OnEnable() { @@ -142,6 +144,12 @@ public sealed class BriskQuickStartSample : MonoBehaviour private void OnGUI() { + if (ShouldRouteInputToDefaultDialog()) + { + RenderDefaultErrorDialogTopmost(); + return; + } + EnsureStyles(); var scale = Mathf.Min(Screen.width / DesignWidth, Screen.height / DesignHeight); @@ -167,6 +175,54 @@ public sealed class BriskQuickStartSample : MonoBehaviour } GUI.matrix = previousMatrix; + + RenderDefaultErrorDialogTopmost(); + } + + private bool ShouldRouteInputToDefaultDialog() + { + if (!IsDefaultErrorDialogVisible() || Event.current == null) + { + return false; + } + + switch (Event.current.type) + { + case EventType.Layout: + case EventType.Repaint: + return false; + default: + return true; + } + } + + private bool IsDefaultErrorDialogVisible() + { + EnsureDefaultDialogReflection(); + return _defaultDialogVisibleProperty != null && (bool)_defaultDialogVisibleProperty.GetValue(null, null); + } + + private void RenderDefaultErrorDialogTopmost() + { + EnsureDefaultDialogReflection(); + _defaultDialogRenderMethod?.Invoke(null, null); + } + + private void EnsureDefaultDialogReflection() + { + if (_defaultDialogVisibleProperty != null && _defaultDialogRenderMethod != null) + { + return; + } + + var dialogType = typeof(Brisk).Assembly.GetType("BriskDefaultErrorDialog"); + if (dialogType == null) + { + return; + } + + _defaultDialogVisibleProperty = dialogType.GetProperty("IsVisible", BindingFlags.Static | BindingFlags.NonPublic); + _defaultDialogRenderMethod = dialogType.GetMethod("RenderTopmostLegacyOverlay", BindingFlags.Static | BindingFlags.NonPublic); } private void DrawCurrentPage() @@ -219,6 +275,19 @@ public sealed class BriskQuickStartSample : MonoBehaviour GUILayout.Label("失败会停留在当前页,并把错误展示在底部状态栏。", _bodyStyle); }); + DrawCard("默认弹窗外观预览", () => + { + GUILayout.Label("这些按钮只展示 SDK 默认弹窗外观,不会修改登录状态,也不会请求服务器。", _bodyStyle); + DrawInlineButtons( + new ButtonSpec("登录过期", PreviewAuthExpiredDialogAsync, false, true), + new ButtonSpec("缺少 Token", PreviewMissingTokenDialogAsync, false, true)); + GUILayout.Space(8f); + DrawInlineButtons( + new ButtonSpec("服务维护", PreviewMaintenanceDialogAsync, false, true), + new ButtonSpec("账号受限", PreviewAccountBannedDialogAsync, false, true), + new ButtonSpec("需要更新", PreviewUpdateRequiredDialogAsync, false, true)); + }); + GUILayout.FlexibleSpace(); GUILayout.EndScrollView(); GUILayout.EndArea(); @@ -563,6 +632,36 @@ public sealed class BriskQuickStartSample : MonoBehaviour _page = SamplePage.Home; } + private Task PreviewAuthExpiredDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("invalid token")); + return Task.CompletedTask; + } + + private Task PreviewMissingTokenDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("Missing access token.")); + return Task.CompletedTask; + } + + private Task PreviewMaintenanceDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskMaintenanceException("maintenance")); + return Task.CompletedTask; + } + + private Task PreviewAccountBannedDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskAccountBannedException("account banned")); + return Task.CompletedTask; + } + + private Task PreviewUpdateRequiredDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskClientUpdateRequiredException("client update required")); + return Task.CompletedTask; + } + private async Task RefreshHomeAsync() { _me = await Brisk.Player.GetMeAsync(); diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/CHANGELOG.md b/PackageSource/com.foldcc.cc-framework.brisk-game-server/CHANGELOG.md index 4eacc02..118a0f3 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/CHANGELOG.md +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.4.1 + +- Startup restore now silently clears locally expired or server-rejected sessions without showing the default auth-expired dialog +- Added silent initialization relogin from the stored `login_provider` and `login_user_id` when available +- Localized the default blocking/auth-expired dialogs to Chinese and stopped showing raw server messages to players +- Rebuilt the default dialog with a larger mobile-readable Runtime UI Toolkit layout and topmost legacy IMGUI bridge +- Default dialogs no longer close from backdrop or Escape; blocking dialogs now invoke the configured exit handler or fall back to `Application.Quit()` + ## 0.4.0 - Added daily-cycle space like fields and result semantics to the Unity SDK models diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/README.md b/PackageSource/com.foldcc.cc-framework.brisk-game-server/README.md index c1b0548..25f4462 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/README.md +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/README.md @@ -22,7 +22,7 @@ Brisk Unity SDK 的 UPM 发布目录。 4. 输入: ```text -http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.0 +http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.1 ``` ### 方式二:修改 `Packages/manifest.json` @@ -30,12 +30,12 @@ http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path ```json { "dependencies": { - "com.foldcc.cc-framework.brisk-game-server": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.0" + "com.foldcc.cc-framework.brisk-game-server": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.1" } } ``` -如果需要跟随主分支最新代码,可将末尾的 `#v0.4.0` 改成 `#main`;正式环境建议固定到发布 tag。 +如果需要跟随主分支最新代码,可将末尾的 `#v0.4.1` 改成 `#main`;正式环境建议固定到发布 tag。 ## 开发态源码位置 diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/Brisk.cs b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/Brisk.cs index ff77830..3e824c0 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/Brisk.cs +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/Brisk.cs @@ -198,7 +198,16 @@ public static class Brisk internal static void NotifyAuthExpired(BriskAuthExpiredException exception) { - s_context?.ErrorPresenter?.ShowAuthExpired(exception); + NotifyAuthExpired(exception, true); + } + + internal static void NotifyAuthExpired(BriskAuthExpiredException exception, bool showDefaultPresenter) + { + if (showDefaultPresenter) + { + s_context?.ErrorPresenter?.ShowAuthExpired(exception); + } + OnAuthExpired?.Invoke(exception); } @@ -241,6 +250,15 @@ public static class Brisk return; } + if (storedSession.ExpiresAt.HasValue && storedSession.ExpiresAt.Value <= DateTimeOffset.UtcNow) + { + await HandleExpiredStartupSessionAsync( + context, + storedSession, + new BriskAuthExpiredException("Stored session expired before initialization.")); + return; + } + context.Session.Update( storedSession.AccessToken, storedSession.ExpiresAt, @@ -268,9 +286,7 @@ public static class Brisk } catch (BriskAuthExpiredException exception) { - context.Session.Clear(); - await context.TokenStore.ClearAsync(); - NotifyAuthExpired(exception); + await HandleExpiredStartupSessionAsync(context, storedSession, exception); } catch (BriskBlockingException exception) { @@ -278,4 +294,36 @@ public static class Brisk throw; } } + + private static async Task HandleExpiredStartupSessionAsync(BriskContext context, BriskStoredSession storedSession, BriskAuthExpiredException exception) + { + context.Session.Clear(); + await context.TokenStore.ClearAsync(); + NotifyAuthExpired(exception, false); + + if (!context.Options.AutoReloginOnInitialize || !HasStoredLoginIdentity(storedSession)) + { + return; + } + + try + { + await Auth.LoginWithUserIdAsync(storedSession.LoginProvider, storedSession.LoginUserId); + } + catch (BriskBlockingException) + { + throw; + } + catch + { + // 初始化期静默重登失败时,交给业务层后续第三方登录流程重新调用 Brisk.Auth。 + } + } + + private static bool HasStoredLoginIdentity(BriskStoredSession storedSession) + { + return storedSession != null + && !string.IsNullOrWhiteSpace(storedSession.LoginProvider) + && !string.IsNullOrWhiteSpace(storedSession.LoginUserId); + } } diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorDialog.cs b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorDialog.cs index cad93d4..1840523 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorDialog.cs +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorDialog.cs @@ -1,26 +1,74 @@ using System; +using System.Collections; using UnityEngine; +using UnityEngine.UIElements; +[DefaultExecutionOrder(10000)] internal sealed class BriskDefaultErrorDialog : MonoBehaviour { private const string HostName = "BriskDefaultErrorDialog"; + private const float OpenDuration = 0.24f; + private const float CloseDuration = 0.2f; + + private static readonly Color Ink = ColorFromHex(0x05060a); + private static readonly Color InkSoft = ColorFromHex(0x1b1d24); + private static readonly Color Muted = ColorFromHex(0x5f6572); + private static readonly Color Line = ColorFromHex(0xdde2ea); + private static readonly Color Paper = ColorFromHex(0xffffff); + private static readonly Color Soft = ColorFromHex(0xf7f8fb); + private static readonly Color Blue = ColorFromHex(0x2563ff); + private static readonly Color Teal = ColorFromHex(0x23d6c8); + private static readonly Color Violet = ColorFromHex(0x8b5cff); + private static readonly Color Dark = ColorFromHex(0x050506); private static BriskDefaultErrorDialog s_instance; - private string _title; - private string _message; - private string _confirmText; + private UIDocument _document; + private PanelSettings _panelSettings; + private VisualElement _root; + private VisualElement _stage; + private VisualElement _panel; + private Label _eyebrowLabel; + private Label _titleLabel; + private Label _messageLabel; + private VisualElement _spacer; + private VisualElement _divider; + private VisualElement _confirmButton; + private Label _confirmLabel; private Action _onConfirm; + private Coroutine _animation; private bool _visible; + private bool _closeOnBackdrop; + private bool _closeOnEscape; + private GUIStyle _legacyTitleStyle; + private GUIStyle _legacyMessageStyle; + private GUIStyle _legacyEyebrowStyle; + private GUIStyle _legacyButtonStyle; + private Texture2D _legacyButtonTexture; + private Texture2D _legacyButtonHoverTexture; - public static void Show(string title, string message, string confirmText, Action onConfirm = null) + internal static bool IsVisible => s_instance != null && s_instance._visible; + + internal static void RenderTopmostLegacyOverlay() + { + if (s_instance == null || !s_instance._visible) + { + return; + } + + s_instance.DrawLegacyOverlay(); + } + + public static void Show(string title, string message, string confirmText, Action onConfirm = null, bool closeOnBackdrop = false, bool closeOnEscape = false) { 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; + host.ShowInternal( + string.IsNullOrWhiteSpace(title) ? "Brisk" : title, + string.IsNullOrWhiteSpace(message) ? "服务暂时不可用,请稍后再试。" : message, + string.IsNullOrWhiteSpace(confirmText) ? "知道了" : confirmText, + onConfirm, + closeOnBackdrop, + closeOnEscape); } private static BriskDefaultErrorDialog EnsureInstance() @@ -46,6 +94,249 @@ internal sealed class BriskDefaultErrorDialog : MonoBehaviour return s_instance; } + private void ShowInternal(string title, string message, string confirmText, Action onConfirm, bool closeOnBackdrop, bool closeOnEscape) + { + EnsureDocument(); + ApplySafeAreaPadding(); + + _titleLabel.text = title; + _messageLabel.text = message; + _confirmLabel.text = confirmText; + _onConfirm = onConfirm; + _closeOnBackdrop = closeOnBackdrop; + _closeOnEscape = closeOnEscape; + _visible = true; + + StartDialogAnimation(PlayOpenAnimation()); + } + + private void EnsureDocument() + { + if (_document != null) + { + ApplyResponsiveLayout(); + return; + } + + _panelSettings = ScriptableObject.CreateInstance(); + _panelSettings.name = HostName + "PanelSettings"; + _panelSettings.scaleMode = PanelScaleMode.ConstantPixelSize; + _panelSettings.scale = 1f; + _panelSettings.sortingOrder = 32767f; + + _document = gameObject.AddComponent(); + _document.panelSettings = _panelSettings; + _document.sortingOrder = 32767f; + + BuildVisualTree(_document.rootVisualElement); + ApplyResponsiveLayout(); + } + + private void BuildVisualTree(VisualElement documentRoot) + { + documentRoot.Clear(); + documentRoot.pickingMode = PickingMode.Ignore; + documentRoot.style.position = Position.Absolute; + documentRoot.style.left = 0f; + documentRoot.style.top = 0f; + documentRoot.style.right = 0f; + documentRoot.style.bottom = 0f; + documentRoot.style.width = Length.Percent(100f); + documentRoot.style.height = Length.Percent(100f); + documentRoot.style.flexGrow = 1f; + + _root = new VisualElement + { + pickingMode = PickingMode.Position, + focusable = true + }; + _root.style.position = Position.Absolute; + _root.style.left = 0; + _root.style.top = 0; + _root.style.right = 0; + _root.style.bottom = 0; + _root.style.display = DisplayStyle.None; + _root.style.opacity = 0f; + _root.style.backgroundColor = new Color(0.02f, 0.02f, 0.024f, 0.58f); + _root.style.justifyContent = Justify.Center; + _root.style.alignItems = Align.Center; + _root.RegisterCallback(HandleRootKeyDown); + _root.RegisterCallback(evt => + { + if (_closeOnBackdrop && evt.target == _root) + { + BeginClose(); + } + + evt.StopPropagation(); + }); + documentRoot.Add(_root); + + _stage = new VisualElement(); + _stage.pickingMode = PickingMode.Ignore; + _stage.style.position = Position.Relative; + _stage.style.width = Length.Percent(82f); + _stage.style.maxWidth = 900f; + _stage.style.opacity = 0f; + _stage.style.marginTop = 14f; + _root.Add(_stage); + + var shadow = new VisualElement(); + shadow.pickingMode = PickingMode.Ignore; + shadow.style.position = Position.Absolute; + shadow.style.left = 0f; + shadow.style.right = 0f; + shadow.style.top = 10f; + shadow.style.bottom = -10f; + shadow.style.backgroundColor = new Color(0f, 0f, 0f, 0.09f); + shadow.style.borderTopLeftRadius = 18f; + shadow.style.borderTopRightRadius = 18f; + shadow.style.borderBottomLeftRadius = 18f; + shadow.style.borderBottomRightRadius = 18f; + _stage.Add(shadow); + + _panel = new VisualElement + { + pickingMode = PickingMode.Position + }; + _panel.style.width = Length.Percent(100f); + _panel.style.minHeight = 360f; + _panel.style.paddingTop = 36f; + _panel.style.paddingRight = 36f; + _panel.style.paddingBottom = 36f; + _panel.style.paddingLeft = 36f; + _panel.style.backgroundColor = Paper; + _panel.style.borderTopWidth = 1f; + _panel.style.borderRightWidth = 1f; + _panel.style.borderBottomWidth = 1f; + _panel.style.borderLeftWidth = 1f; + _panel.style.borderTopColor = Line; + _panel.style.borderRightColor = Line; + _panel.style.borderBottomColor = Line; + _panel.style.borderLeftColor = Line; + _panel.style.borderTopLeftRadius = 18f; + _panel.style.borderTopRightRadius = 18f; + _panel.style.borderBottomLeftRadius = 18f; + _panel.style.borderBottomRightRadius = 18f; + _panel.style.overflow = Overflow.Hidden; + _stage.Add(_panel); + + var accent = new VisualElement(); + accent.pickingMode = PickingMode.Ignore; + accent.style.position = Position.Absolute; + accent.style.top = 0f; + accent.style.left = 0f; + accent.style.right = 0f; + accent.style.height = 2f; + accent.style.flexDirection = FlexDirection.Row; + _panel.Add(accent); + AddAccentSegment(accent, Blue); + AddAccentSegment(accent, Teal); + AddAccentSegment(accent, Violet); + + _eyebrowLabel = new Label("BRISK SERVICE"); + _eyebrowLabel.style.color = Muted; + _eyebrowLabel.style.fontSize = 13f; + _eyebrowLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _eyebrowLabel.style.letterSpacing = 1.4f; + _eyebrowLabel.style.height = 18f; + _eyebrowLabel.style.flexShrink = 0f; + _eyebrowLabel.style.marginBottom = 12f; + _panel.Add(_eyebrowLabel); + + _titleLabel = new Label(); + _titleLabel.style.color = Ink; + _titleLabel.style.fontSize = 30f; + _titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _titleLabel.style.whiteSpace = WhiteSpace.Normal; + _titleLabel.style.unityTextAlign = TextAnchor.MiddleLeft; + _titleLabel.style.minHeight = 40f; + _titleLabel.style.flexShrink = 0f; + _titleLabel.style.marginBottom = 16f; + _panel.Add(_titleLabel); + + _messageLabel = new Label(); + _messageLabel.style.color = Muted; + _messageLabel.style.fontSize = 21f; + _messageLabel.style.whiteSpace = WhiteSpace.Normal; + _messageLabel.style.unityTextAlign = TextAnchor.UpperLeft; + _messageLabel.style.minHeight = 64f; + _messageLabel.style.flexShrink = 0f; + _messageLabel.style.marginBottom = 22f; + _panel.Add(_messageLabel); + + _spacer = new VisualElement(); + _spacer.pickingMode = PickingMode.Ignore; + _spacer.style.flexGrow = 1f; + _spacer.style.minHeight = 18f; + _panel.Add(_spacer); + + _divider = new VisualElement(); + _divider.style.height = 1f; + _divider.style.flexShrink = 0f; + _divider.style.backgroundColor = Line; + _divider.style.marginBottom = 22f; + _panel.Add(_divider); + + _confirmButton = new VisualElement + { + focusable = true, + pickingMode = PickingMode.Position + }; + _confirmButton.style.height = 58f; + _confirmButton.style.minHeight = 58f; + _confirmButton.style.flexShrink = 0f; + _confirmButton.style.borderTopLeftRadius = 24f; + _confirmButton.style.borderTopRightRadius = 24f; + _confirmButton.style.borderBottomLeftRadius = 24f; + _confirmButton.style.borderBottomRightRadius = 24f; + _confirmButton.style.backgroundColor = Dark; + _confirmButton.style.justifyContent = Justify.Center; + _confirmButton.style.alignItems = Align.Center; + _confirmButton.RegisterCallback(_ => _confirmButton.style.backgroundColor = InkSoft); + _confirmButton.RegisterCallback(_ => _confirmButton.style.backgroundColor = Dark); + _confirmButton.RegisterCallback(_ => + { + _confirmButton.style.backgroundColor = Ink; + _confirmButton.style.marginTop = 1f; + }); + _confirmButton.RegisterCallback(_ => + { + _confirmButton.style.backgroundColor = InkSoft; + _confirmButton.style.marginTop = 0f; + }); + _confirmButton.RegisterCallback(_ => BeginClose()); + _confirmButton.RegisterCallback(HandleButtonKeyDown); + _panel.Add(_confirmButton); + + _confirmLabel = new Label(); + _confirmLabel.style.color = Color.white; + _confirmLabel.style.fontSize = 18f; + _confirmLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + _confirmLabel.style.unityTextAlign = TextAnchor.MiddleCenter; + _confirmLabel.style.height = 58f; + _confirmButton.Add(_confirmLabel); + } + + private void Update() + { + if (_visible && _root != null) + { + ApplySafeAreaPadding(); + ApplyResponsiveLayout(); + } + } + + private void BeginClose() + { + if (!_visible || _root == null) + { + return; + } + + StartDialogAnimation(PlayCloseAnimation()); + } + private void OnGUI() { if (!_visible) @@ -53,36 +344,379 @@ internal sealed class BriskDefaultErrorDialog : MonoBehaviour return; } - var overlayRect = new Rect(0, 0, Screen.width, Screen.height); + DrawLegacyOverlay(); + } + + private void DrawLegacyOverlay() + { + EnsureLegacyStyles(); + + var previousDepth = GUI.depth; + var previousMatrix = GUI.matrix; var previousColor = GUI.color; - GUI.color = new Color(0f, 0f, 0f, 0.65f); - GUI.Box(overlayRect, GUIContent.none); - GUI.color = previousColor; + GUI.depth = -10000; + GUI.matrix = Matrix4x4.identity; - 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); + DrawLegacyRect(new Rect(0f, 0f, Screen.width, Screen.height), new Color(0.02f, 0.02f, 0.024f, 0.58f)); - 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(); + var scale = GetReadableScale(); + ApplyLegacyScale(scale); - if (GUILayout.Button(_confirmText, GUILayout.Height(36f))) + var margin = GetDialogMargin(scale); + var padding = GetDialogPadding(scale); + var buttonHeight = GetDialogButtonHeight(scale); + var width = GetDialogWidth(margin); + var contentWidth = width - padding * 2f; + var titleHeight = Mathf.Max(34f, _legacyTitleStyle.CalcHeight(new GUIContent(_titleLabel.text), contentWidth)); + var messageHeight = Mathf.Max(48f, _legacyMessageStyle.CalcHeight(new GUIContent(_messageLabel.text), contentWidth)); + var minHeight = GetDialogMinHeight(scale, margin); + var contentHeight = padding * 2f + 18f * scale + 12f * scale + titleHeight + 16f * scale + messageHeight + 24f * scale + 1f + 22f * scale + buttonHeight; + var height = Mathf.Clamp(contentHeight, minHeight, Mathf.Max(minHeight, Screen.height - margin * 2f)); + var rect = new Rect((Screen.width - width) * 0.5f, (Screen.height - height) * 0.5f, width, height); + + DrawLegacyPanel(rect); + + var x = rect.x + padding; + var y = rect.y + padding; + GUI.Label(new Rect(x, y, contentWidth, 18f * scale), "BRISK SERVICE", _legacyEyebrowStyle); + y += 30f * scale; + GUI.Label(new Rect(x, y, contentWidth, titleHeight), _titleLabel.text, _legacyTitleStyle); + y += titleHeight + 16f * scale; + GUI.Label(new Rect(x, y, contentWidth, messageHeight), _messageLabel.text, _legacyMessageStyle); + + DrawLegacyRect(new Rect(x, rect.yMax - padding - buttonHeight - 22f * scale, contentWidth, 1f), Line); + var buttonRect = new Rect(x, rect.yMax - padding - buttonHeight, contentWidth, buttonHeight); + if (GUI.Button(buttonRect, _confirmLabel.text, _legacyButtonStyle)) { - var callback = _onConfirm; - _visible = false; - _onConfirm = null; - callback?.Invoke(); + BeginClose(); } - GUILayout.Space(8f); - GUILayout.EndArea(); + ConsumeBlockingEvent(); + + GUI.color = previousColor; + GUI.matrix = previousMatrix; + GUI.depth = previousDepth; + } + + private IEnumerator PlayOpenAnimation() + { + _root.style.display = DisplayStyle.Flex; + _root.style.opacity = 0f; + _stage.style.opacity = 0f; + _stage.style.marginTop = 14f; + + yield return null; + _root.Focus(); + + var elapsed = 0f; + while (elapsed < OpenDuration) + { + elapsed += Time.unscaledDeltaTime; + var eased = EaseOutCubic(Mathf.Clamp01(elapsed / OpenDuration)); + _root.style.opacity = eased; + _stage.style.opacity = eased; + _stage.style.marginTop = Mathf.Lerp(14f, 0f, eased); + yield return null; + } + + _root.style.opacity = 1f; + _stage.style.opacity = 1f; + _stage.style.marginTop = 0f; + } + + private IEnumerator PlayCloseAnimation() + { + var callback = _onConfirm; + _onConfirm = null; + + var elapsed = 0f; + while (elapsed < CloseDuration) + { + elapsed += Time.unscaledDeltaTime; + var eased = EaseInCubic(Mathf.Clamp01(elapsed / CloseDuration)); + var alpha = 1f - eased; + _root.style.opacity = alpha; + _stage.style.opacity = alpha; + _stage.style.marginTop = Mathf.Lerp(0f, 10f, eased); + yield return null; + } + + _root.style.display = DisplayStyle.None; + _visible = false; + callback?.Invoke(); + } + + private void StartDialogAnimation(IEnumerator animation) + { + if (_animation != null) + { + StopCoroutine(_animation); + } + + _animation = StartCoroutine(animation); + } + + private void ApplySafeAreaPadding() + { + var safeArea = Screen.safeArea; + var basePadding = Mathf.Max(20f, Mathf.Min(Screen.width, Screen.height) * 0.04f); + _root.style.paddingLeft = Mathf.Max(basePadding, safeArea.xMin + basePadding); + _root.style.paddingRight = Mathf.Max(basePadding, Screen.width - safeArea.xMax + basePadding); + _root.style.paddingTop = Mathf.Max(basePadding, Screen.height - safeArea.yMax + basePadding); + _root.style.paddingBottom = Mathf.Max(basePadding, safeArea.yMin + basePadding); + } + + private void ApplyResponsiveLayout() + { + if (_panel == null) + { + return; + } + + var scale = GetReadableScale(); + var margin = GetDialogMargin(scale); + var maxWidth = Mathf.Min(900f, Mathf.Max(320f, Screen.width - margin * 2f)); + var minHeight = GetDialogMinHeight(scale, margin); + var padding = GetDialogPadding(scale); + var buttonHeight = GetDialogButtonHeight(scale); + + _stage.style.width = Length.Percent(82f); + _stage.style.maxWidth = maxWidth; + _panel.style.minHeight = minHeight; + _panel.style.paddingTop = padding; + _panel.style.paddingRight = padding; + _panel.style.paddingBottom = padding; + _panel.style.paddingLeft = padding; + _panel.style.borderTopLeftRadius = 20f * scale; + _panel.style.borderTopRightRadius = 20f * scale; + _panel.style.borderBottomLeftRadius = 20f * scale; + _panel.style.borderBottomRightRadius = 20f * scale; + + _eyebrowLabel.style.fontSize = 12f * scale; + _eyebrowLabel.style.height = 18f * scale; + _eyebrowLabel.style.marginBottom = 12f * scale; + _titleLabel.style.fontSize = 28f * scale; + _titleLabel.style.minHeight = 40f * scale; + _titleLabel.style.marginBottom = 16f * scale; + _messageLabel.style.fontSize = 20f * scale; + _messageLabel.style.minHeight = 64f * scale; + _messageLabel.style.marginBottom = 22f * scale; + _spacer.style.minHeight = 18f * scale; + _divider.style.marginBottom = 22f * scale; + _confirmButton.style.height = buttonHeight; + _confirmButton.style.minHeight = buttonHeight; + _confirmButton.style.borderTopLeftRadius = buttonHeight * 0.5f; + _confirmButton.style.borderTopRightRadius = buttonHeight * 0.5f; + _confirmButton.style.borderBottomLeftRadius = buttonHeight * 0.5f; + _confirmButton.style.borderBottomRightRadius = buttonHeight * 0.5f; + _confirmLabel.style.fontSize = 17f * scale; + _confirmLabel.style.height = buttonHeight; + } + + private void HandleRootKeyDown(KeyDownEvent evt) + { + if (_closeOnEscape && evt.keyCode == KeyCode.Escape) + { + BeginClose(); + evt.StopPropagation(); + return; + } + + evt.StopPropagation(); + } + + private void HandleButtonKeyDown(KeyDownEvent evt) + { + if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter || evt.keyCode == KeyCode.Space) + { + BeginClose(); + evt.StopPropagation(); + } + } + + private void OnDestroy() + { + if (s_instance == this) + { + s_instance = null; + } + + if (_panelSettings != null) + { + Destroy(_panelSettings); + _panelSettings = null; + } + } + + private void EnsureLegacyStyles() + { + if (_legacyTitleStyle != null) + { + return; + } + + _legacyButtonTexture = CreateTexture(Dark); + _legacyButtonHoverTexture = CreateTexture(InkSoft); + + _legacyEyebrowStyle = new GUIStyle + { + fontStyle = FontStyle.Bold, + normal = { textColor = Muted } + }; + + _legacyTitleStyle = new GUIStyle + { + fontStyle = FontStyle.Bold, + wordWrap = true, + normal = { textColor = Ink } + }; + + _legacyMessageStyle = new GUIStyle + { + wordWrap = true, + normal = { textColor = Muted } + }; + + _legacyButtonStyle = new GUIStyle + { + alignment = TextAnchor.MiddleCenter, + fontStyle = FontStyle.Bold, + border = new RectOffset(24, 24, 24, 24), + normal = + { + background = _legacyButtonTexture, + textColor = Color.white + }, + hover = + { + background = _legacyButtonHoverTexture, + textColor = Color.white + }, + active = + { + background = _legacyButtonHoverTexture, + textColor = Color.white + } + }; + } + + private void ApplyLegacyScale(float scale) + { + _legacyEyebrowStyle.fontSize = Mathf.RoundToInt(12f * scale); + _legacyTitleStyle.fontSize = Mathf.RoundToInt(28f * scale); + _legacyMessageStyle.fontSize = Mathf.RoundToInt(20f * scale); + _legacyButtonStyle.fontSize = Mathf.RoundToInt(17f * scale); + } + + private void DrawLegacyPanel(Rect rect) + { + DrawLegacyRect(new Rect(rect.x, rect.y + 12f, rect.width, rect.height), new Color(0f, 0f, 0f, 0.08f)); + DrawLegacyRect(rect, Paper); + DrawLegacyRect(new Rect(rect.x, rect.y, rect.width, 1f), Line); + DrawLegacyRect(new Rect(rect.x, rect.yMax - 1f, rect.width, 1f), Line); + DrawLegacyRect(new Rect(rect.x, rect.y, 1f, rect.height), Line); + DrawLegacyRect(new Rect(rect.xMax - 1f, rect.y, 1f, rect.height), Line); + + var segment = rect.width / 3f; + DrawLegacyRect(new Rect(rect.x, rect.y, segment, 2f), Blue); + DrawLegacyRect(new Rect(rect.x + segment, rect.y, segment, 2f), Teal); + DrawLegacyRect(new Rect(rect.x + segment * 2f, rect.y, rect.width - segment * 2f, 2f), Violet); + } + + private static void ConsumeBlockingEvent() + { + var evt = Event.current; + if (evt == null) + { + return; + } + + switch (evt.type) + { + case EventType.MouseDown: + case EventType.MouseUp: + case EventType.MouseDrag: + case EventType.ScrollWheel: + case EventType.KeyDown: + case EventType.KeyUp: + evt.Use(); + break; + } + } + + private static Texture2D CreateTexture(Color color) + { + var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false) + { + hideFlags = HideFlags.HideAndDontSave + }; + texture.SetPixel(0, 0, color); + texture.Apply(); + return texture; + } + + private static void DrawLegacyRect(Rect rect, Color color) + { + var previousColor = GUI.color; + GUI.color = color; + GUI.DrawTexture(rect, Texture2D.whiteTexture); + GUI.color = previousColor; + } + + private static void AddAccentSegment(VisualElement parent, Color color) + { + var segment = new VisualElement(); + segment.style.flexGrow = 1f; + segment.style.backgroundColor = color; + parent.Add(segment); + } + + private static float GetReadableScale() + { + return Mathf.Clamp(Mathf.Min(Screen.width, Screen.height) / 720f, 1f, 1.22f); + } + + private static float GetDialogMargin(float scale) + { + return Mathf.Max(24f * scale, Mathf.Min(Screen.width, Screen.height) * 0.06f); + } + + private static float GetDialogPadding(float scale) + { + return 34f * scale; + } + + private static float GetDialogButtonHeight(float scale) + { + return 56f * scale; + } + + private static float GetDialogWidth(float margin) + { + return Mathf.Clamp(Screen.width * 0.82f, 320f, Mathf.Max(320f, Mathf.Min(900f, Screen.width - margin * 2f))); + } + + private static float GetDialogMinHeight(float scale, float margin) + { + var availableHeight = Mathf.Max(320f, Screen.height - margin * 2f); + return Mathf.Min(availableHeight, Mathf.Clamp(Screen.height * 0.24f, 360f * scale, 620f)); + } + + private static float EaseOutCubic(float t) + { + return 1f - Mathf.Pow(1f - t, 3f); + } + + private static float EaseInCubic(float t) + { + return t * t * t; + } + + private static Color ColorFromHex(int rgb) + { + var r = ((rgb >> 16) & 0xff) / 255f; + var g = ((rgb >> 8) & 0xff) / 255f; + var b = (rgb & 0xff) / 255f; + return new Color(r, g, b, 1f); } } diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorPresenter.cs b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorPresenter.cs index c5781a8..3faaf25 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorPresenter.cs +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskDefaultErrorPresenter.cs @@ -13,18 +13,22 @@ public sealed class BriskDefaultErrorPresenter : IBriskErrorPresenter public void ShowBlockingError(BriskBlockingException exception) { - var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message) - ? exception.Message - : "A blocking error occurred."; + var message = GetBlockingMessage(exception); BriskDefaultErrorDialog.Show( title: GetBlockingTitle(exception), message: message, - confirmText: "OK", + confirmText: "知道了", onConfirm: () => { - var exitHandler = Brisk.IsInitialized ? Brisk.Options?.ExitHandler : null; - exitHandler?.Invoke(); + var exitHandler = Brisk.IsInitialized ? Brisk.GetRequiredContext().ExitHandler : null; + if (exitHandler != null) + { + exitHandler.Invoke(); + return; + } + + Application.Quit(); }); Debug.LogError("[Brisk] Blocking error: " + message); @@ -32,35 +36,63 @@ public sealed class BriskDefaultErrorPresenter : IBriskErrorPresenter public void ShowAuthExpired(BriskAuthExpiredException exception) { - var message = exception != null && !string.IsNullOrWhiteSpace(exception.Message) - ? exception.Message - : "Login expired. Please sign in again."; + var message = GetAuthExpiredMessage(exception); BriskDefaultErrorDialog.Show( - title: "Login Expired", + title: "登录已过期", message: message, - confirmText: "OK"); + confirmText: "知道了"); - Debug.LogWarning("[Brisk] Auth expired: " + message); + Debug.LogWarning("[Brisk] Auth expired: " + (exception != null ? exception.Message : message)); } private static string GetBlockingTitle(BriskBlockingException exception) { if (exception is BriskMaintenanceException) { - return "Maintenance"; + return "服务维护中"; } if (exception is BriskAccountBannedException) { - return "Account Restricted"; + return "账号受限"; } if (exception is BriskClientUpdateRequiredException) { - return "Update Required"; + return "需要更新"; } - return "Service Unavailable"; + return "服务暂不可用"; + } + + private static string GetBlockingMessage(BriskBlockingException exception) + { + if (exception is BriskMaintenanceException) + { + return "服务正在维护,请稍后再试。"; + } + + if (exception is BriskAccountBannedException) + { + return "当前账号暂时无法使用该服务。"; + } + + if (exception is BriskClientUpdateRequiredException) + { + return "当前版本过低,请更新后继续。"; + } + + return "服务暂时不可用,请稍后再试。"; + } + + private static string GetAuthExpiredMessage(BriskAuthExpiredException exception) + { + if (exception != null && exception.Message != null && exception.Message.IndexOf("missing", StringComparison.OrdinalIgnoreCase) >= 0) + { + return "当前登录状态不可用,请重新登录。"; + } + + return "登录状态已失效,请重新登录后继续。"; } } diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskOptions.cs b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskOptions.cs index 6bc6211..4c850c6 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskOptions.cs +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Runtime/Core/BriskOptions.cs @@ -30,6 +30,10 @@ public sealed class BriskOptions /// public bool ValidateSessionOnInitialize = true; /// + /// 初始化恢复的本地会话已过期或失效时,是否使用本地保存的登录身份静默换取新会话。 + /// + public bool AutoReloginOnInitialize = true; + /// /// 自定义登录态持久化实现。 /// public IBriskTokenStore TokenStore; diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Samples~/QuickStart/BriskQuickStartSample.cs b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Samples~/QuickStart/BriskQuickStartSample.cs index 8581813..dbeeeb4 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/Samples~/QuickStart/BriskQuickStartSample.cs +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/Samples~/QuickStart/BriskQuickStartSample.cs @@ -112,6 +112,8 @@ public sealed class BriskQuickStartSample : MonoBehaviour private GUIStyle _statusStyle; private GUIStyle _listButtonStyle; private GUIStyle _modalStyle; + private PropertyInfo _defaultDialogVisibleProperty; + private MethodInfo _defaultDialogRenderMethod; private void OnEnable() { @@ -142,6 +144,12 @@ public sealed class BriskQuickStartSample : MonoBehaviour private void OnGUI() { + if (ShouldRouteInputToDefaultDialog()) + { + RenderDefaultErrorDialogTopmost(); + return; + } + EnsureStyles(); var scale = Mathf.Min(Screen.width / DesignWidth, Screen.height / DesignHeight); @@ -167,6 +175,54 @@ public sealed class BriskQuickStartSample : MonoBehaviour } GUI.matrix = previousMatrix; + + RenderDefaultErrorDialogTopmost(); + } + + private bool ShouldRouteInputToDefaultDialog() + { + if (!IsDefaultErrorDialogVisible() || Event.current == null) + { + return false; + } + + switch (Event.current.type) + { + case EventType.Layout: + case EventType.Repaint: + return false; + default: + return true; + } + } + + private bool IsDefaultErrorDialogVisible() + { + EnsureDefaultDialogReflection(); + return _defaultDialogVisibleProperty != null && (bool)_defaultDialogVisibleProperty.GetValue(null, null); + } + + private void RenderDefaultErrorDialogTopmost() + { + EnsureDefaultDialogReflection(); + _defaultDialogRenderMethod?.Invoke(null, null); + } + + private void EnsureDefaultDialogReflection() + { + if (_defaultDialogVisibleProperty != null && _defaultDialogRenderMethod != null) + { + return; + } + + var dialogType = typeof(Brisk).Assembly.GetType("BriskDefaultErrorDialog"); + if (dialogType == null) + { + return; + } + + _defaultDialogVisibleProperty = dialogType.GetProperty("IsVisible", BindingFlags.Static | BindingFlags.NonPublic); + _defaultDialogRenderMethod = dialogType.GetMethod("RenderTopmostLegacyOverlay", BindingFlags.Static | BindingFlags.NonPublic); } private void DrawCurrentPage() @@ -219,6 +275,19 @@ public sealed class BriskQuickStartSample : MonoBehaviour GUILayout.Label("失败会停留在当前页,并把错误展示在底部状态栏。", _bodyStyle); }); + DrawCard("默认弹窗外观预览", () => + { + GUILayout.Label("这些按钮只展示 SDK 默认弹窗外观,不会修改登录状态,也不会请求服务器。", _bodyStyle); + DrawInlineButtons( + new ButtonSpec("登录过期", PreviewAuthExpiredDialogAsync, false, true), + new ButtonSpec("缺少 Token", PreviewMissingTokenDialogAsync, false, true)); + GUILayout.Space(8f); + DrawInlineButtons( + new ButtonSpec("服务维护", PreviewMaintenanceDialogAsync, false, true), + new ButtonSpec("账号受限", PreviewAccountBannedDialogAsync, false, true), + new ButtonSpec("需要更新", PreviewUpdateRequiredDialogAsync, false, true)); + }); + GUILayout.FlexibleSpace(); GUILayout.EndScrollView(); GUILayout.EndArea(); @@ -563,6 +632,36 @@ public sealed class BriskQuickStartSample : MonoBehaviour _page = SamplePage.Home; } + private Task PreviewAuthExpiredDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("invalid token")); + return Task.CompletedTask; + } + + private Task PreviewMissingTokenDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("Missing access token.")); + return Task.CompletedTask; + } + + private Task PreviewMaintenanceDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskMaintenanceException("maintenance")); + return Task.CompletedTask; + } + + private Task PreviewAccountBannedDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskAccountBannedException("account banned")); + return Task.CompletedTask; + } + + private Task PreviewUpdateRequiredDialogAsync() + { + BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskClientUpdateRequiredException("client update required")); + return Task.CompletedTask; + } + private async Task RefreshHomeAsync() { _me = await Brisk.Player.GetMeAsync(); diff --git a/PackageSource/com.foldcc.cc-framework.brisk-game-server/package.json b/PackageSource/com.foldcc.cc-framework.brisk-game-server/package.json index 7401a67..7fcbfa8 100644 --- a/PackageSource/com.foldcc.cc-framework.brisk-game-server/package.json +++ b/PackageSource/com.foldcc.cc-framework.brisk-game-server/package.json @@ -1,7 +1,7 @@ { "name": "com.foldcc.cc-framework.brisk-game-server", "displayName": "FoldCC Brisk Game Server SDK", - "version": "0.4.0", + "version": "0.4.1", "unity": "2021.3", "description": "A lightweight Unity SDK for Brisk game services, including bootstrap, auth, announcements, leaderboard, archive, and player space modules.", "keywords": [ diff --git a/README.md b/README.md index 41ec8ea..07a532a 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Brisk Unity SDK 原始开发工程。 外部项目 Git Package 接入示例: ```text -http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.0 +http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.1 ``` ## 如何引入到项目 @@ -169,12 +169,12 @@ http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path ```json { "dependencies": { - "com.foldcc.cc-framework.brisk-game-server": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.0" + "com.foldcc.cc-framework.brisk-game-server": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.BriskGameServer.git?path=/PackageSource/com.foldcc.cc-framework.brisk-game-server#v0.4.1" } } ``` -如果要跟随主分支最新代码,可把末尾的 `#v0.4.0` 改成 `#main`;正式项目仍建议固定到发布 tag。 +如果要跟随主分支最新代码,可把末尾的 `#v0.4.1` 改成 `#main`;正式项目仍建议固定到发布 tag。 ## 文档位置