UI换了很多,spine主视觉停用
This commit is contained in:
@@ -176,7 +176,7 @@ public class AnimationController : MonoBehaviour
|
||||
// Next try common names if tag not used
|
||||
if (hit_particular_object == null)
|
||||
{
|
||||
var byName = GameObject.Find("HitParticleSource") ?? GameObject.Find("hit_particular_object") ?? GameObject.Find("Hit_Particle_Source");
|
||||
var byName = SceneObjectLookupCache.Find("HitParticleSource") ?? SceneObjectLookupCache.Find("hit_particular_object") ?? SceneObjectLookupCache.Find("Hit_Particle_Source");
|
||||
if (byName != null)
|
||||
{
|
||||
hit_particular_object = byName;
|
||||
|
||||
@@ -6,7 +6,7 @@ using TMPro;
|
||||
|
||||
public class BeatmapManager : MonoBehaviour
|
||||
{
|
||||
private const bool VerboseLogs = false;
|
||||
private static readonly bool VerboseLogs = false;
|
||||
public static BeatmapManager Instance { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
@@ -339,7 +339,7 @@ public class BeatmapManager : MonoBehaviour
|
||||
if (VerboseLogs) Debug.Log("BeatmapManager.Start: chart parsed from SongData, attempting to assign audio and pause system");
|
||||
|
||||
// try to assign audio to GameManager.musicSource
|
||||
var gm = FindAnyObjectByType<GameManager>();
|
||||
var gm = SceneObjectLookupCache.FindAny<GameManager>();
|
||||
if (gm != null && gm.musicSource != null)
|
||||
{
|
||||
if (assignedSongData != null && assignedSongData.audioFile != null)
|
||||
@@ -406,18 +406,18 @@ public class BeatmapManager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Pause the system to show pause overlay (PauseManager will enable overlayRoot)
|
||||
var pauseMgr = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
if (pauseMgr != null)
|
||||
{
|
||||
pauseMgr.Pause(true);
|
||||
// Pause the system to show pause overlay (PauseManager will enable overlayRoot)
|
||||
var pauseMgr = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pauseMgr != null)
|
||||
{
|
||||
pauseMgr.Pause(true);
|
||||
if (VerboseLogs) Debug.Log("System paused after loading chart and audio to allow player to start");
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VerboseLogs) Debug.Log("PauseManager not found in scene; cannot pause system automatically");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear pending so it doesn't run again
|
||||
pendingSongData = null;
|
||||
@@ -895,7 +895,7 @@ public class BeatmapManager : MonoBehaviour
|
||||
go = ui.GetAllyObjectBySlot(slotIndex);
|
||||
if (go == null)
|
||||
{
|
||||
var byName = GameObject.Find($"ally_0{slotIndex + 1}");
|
||||
var byName = SceneObjectLookupCache.Find($"ally_0{slotIndex + 1}");
|
||||
if (byName != null) go = byName;
|
||||
}
|
||||
if (go == null) return null;
|
||||
|
||||
@@ -117,7 +117,7 @@ public class GameManager : MonoBehaviour
|
||||
{
|
||||
if (pauseSubscribed) return;
|
||||
// prefer the singleton, but try to find in scene if null
|
||||
var pm = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
var pm = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pm != null)
|
||||
{
|
||||
pm.OnPauseStateChanged += HandlePauseStateChanged;
|
||||
@@ -128,7 +128,7 @@ public class GameManager : MonoBehaviour
|
||||
private void UnsubscribeFromPauseManager()
|
||||
{
|
||||
if (!pauseSubscribed) return;
|
||||
var pm = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
var pm = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pm != null)
|
||||
{
|
||||
pm.OnPauseStateChanged -= HandlePauseStateChanged;
|
||||
@@ -503,16 +503,16 @@ public class GameManager : MonoBehaviour
|
||||
HoldNote.PrewarmRuntimeCaches();
|
||||
|
||||
var judgeFx = Animation_GenerateJudgementSituationPrefab.Instance
|
||||
?? FindAnyObjectByType<Animation_GenerateJudgementSituationPrefab>();
|
||||
?? SceneObjectLookupCache.FindAny<Animation_GenerateJudgementSituationPrefab>();
|
||||
judgeFx?.PrewarmJudgePrefabs(1);
|
||||
|
||||
var fxEvent = effectEventController.Instance ?? FindAnyObjectByType<effectEventController>();
|
||||
var fxEvent = effectEventController.Instance ?? SceneObjectLookupCache.FindAny<effectEventController>();
|
||||
if (fxEvent != null && fxEvent.isActiveAndEnabled)
|
||||
{
|
||||
// Cache target and pre-create SmoothShake via OnEnable/Awake path already present.
|
||||
}
|
||||
|
||||
var globalFx = globalNoteEffect.Instance ?? FindAnyObjectByType<globalNoteEffect>();
|
||||
var globalFx = globalNoteEffect.Instance ?? SceneObjectLookupCache.FindAny<globalNoteEffect>();
|
||||
globalFx?.PrewarmRuntime();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
@@ -1138,7 +1138,7 @@ public class GameManager : MonoBehaviour
|
||||
StartCoroutine(StopWarmupAfterFrame());
|
||||
|
||||
// After loading audio, enable overlay and pause via PauseManager
|
||||
var pm = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
var pm = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pm != null)
|
||||
{
|
||||
pm.Pause(true);
|
||||
@@ -1279,12 +1279,10 @@ public class GameManager : MonoBehaviour
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
gNotice.recommendation.display("游戏开始!");
|
||||
gNotice.recommendation.display(LocalizationService.Get("gameplay.notice.game_start", "游戏开始!"));
|
||||
|
||||
// Resume using PauseManager
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
// yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning using the parsed beatmap
|
||||
if (beatmapManager.beatmap != null)
|
||||
@@ -1340,12 +1338,10 @@ public class GameManager : MonoBehaviour
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
gNotice.recommendation.display("游戏开始!");
|
||||
gNotice.recommendation.display(LocalizationService.Get("gameplay.notice.game_start", "游戏开始!"));
|
||||
|
||||
// Resume
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
// yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning using the existing parsed beatmap
|
||||
if (beatmapManager.beatmap != null)
|
||||
@@ -1381,7 +1377,7 @@ public class GameManager : MonoBehaviour
|
||||
PlayMusicWithDelay(delay);
|
||||
|
||||
// ensure overlay shows and pause logic runs after loading the clip
|
||||
var pm2 = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
var pm2 = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pm2 != null)
|
||||
{
|
||||
pm2.Pause(true);
|
||||
@@ -1440,12 +1436,10 @@ public class GameManager : MonoBehaviour
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
gNotice.recommendation.display("游戏开始!");
|
||||
gNotice.recommendation.display(LocalizationService.Get("gameplay.notice.game_start", "游戏开始!"));
|
||||
|
||||
// Resume
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
// yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning
|
||||
if (beatmapManager.beatmap != null)
|
||||
@@ -1475,7 +1469,7 @@ public class GameManager : MonoBehaviour
|
||||
float delay = beatmapManager.globalDelaySeconds;
|
||||
PlayMusicWithDelay(delay);
|
||||
|
||||
var pm3 = PauseManager.Instance ?? FindAnyObjectByType<PauseManager>();
|
||||
var pm3 = PauseManager.Instance ?? SceneObjectLookupCache.FindAny<PauseManager>();
|
||||
if (pm3 != null)
|
||||
{
|
||||
pm3.Pause(true);
|
||||
@@ -1524,7 +1518,7 @@ public class GameManager : MonoBehaviour
|
||||
private IEnumerator TriggerOnGameStartNextFrame()
|
||||
{
|
||||
yield return null;
|
||||
var builder = FindAnyObjectByType<SkillBuilder>();
|
||||
var builder = SceneObjectLookupCache.FindAny<SkillBuilder>();
|
||||
if (builder == null)
|
||||
{
|
||||
Debug.LogWarning("[GameManager] TriggerOnGameStartSkills: SkillBuilder not found.");
|
||||
|
||||
@@ -84,7 +84,7 @@ public class HoldNote : BaseNote
|
||||
if (allyCache == null || allyCache.Length < 10) allyCache = new AllyCombatant[10];
|
||||
if (allyCache[trackIndex] != null) return allyCache[trackIndex];
|
||||
|
||||
var allyGo = GameObject.Find($"ally_0{trackIndex + 1}");
|
||||
var allyGo = SceneObjectLookupCache.Find($"ally_0{trackIndex + 1}");
|
||||
if (allyGo != null)
|
||||
{
|
||||
allyCache[trackIndex] = allyGo.GetComponent<AllyCombatant>();
|
||||
@@ -95,7 +95,7 @@ public class HoldNote : BaseNote
|
||||
private static BeatmapManager GetBeatmapManagerCached()
|
||||
{
|
||||
if (beatmapManagerCache != null) return beatmapManagerCache;
|
||||
beatmapManagerCache = Object.FindAnyObjectByType<BeatmapManager>();
|
||||
beatmapManagerCache = SceneObjectLookupCache.FindAny<BeatmapManager>();
|
||||
return beatmapManagerCache;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ public class HoldNote : BaseNote
|
||||
anim = GetComponent<AnimationController>();
|
||||
if (anim == null)
|
||||
{
|
||||
anim = FindAnyObjectByType<AnimationController>();
|
||||
anim = SceneObjectLookupCache.FindAny<AnimationController>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Note : BaseNote
|
||||
if (allyCache == null || allyCache.Length < 10) allyCache = new AllyCombatant[10];
|
||||
if (allyCache[trackIndex] != null) return allyCache[trackIndex];
|
||||
|
||||
var allyGo = GameObject.Find($"ally_0{trackIndex + 1}");
|
||||
var allyGo = SceneObjectLookupCache.Find($"ally_0{trackIndex + 1}");
|
||||
if (allyGo != null)
|
||||
{
|
||||
allyCache[trackIndex] = allyGo.GetComponent<AllyCombatant>();
|
||||
@@ -39,7 +39,7 @@ public class Note : BaseNote
|
||||
private static BeatmapManager GetBeatmapManagerCached()
|
||||
{
|
||||
if (beatmapManagerCache != null) return beatmapManagerCache;
|
||||
beatmapManagerCache = Object.FindAnyObjectByType<BeatmapManager>();
|
||||
beatmapManagerCache = SceneObjectLookupCache.FindAny<BeatmapManager>();
|
||||
return beatmapManagerCache;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GamePlay; // for PoolItem
|
||||
using System.Collections;
|
||||
@@ -112,7 +112,7 @@ public class NotePool : MonoBehaviour
|
||||
}
|
||||
|
||||
// Also ensure particle and judge prefab warm-up if AnimationController exists in scene.
|
||||
var anim = AnimationController.Global ?? FindAnyObjectByType<AnimationController>();
|
||||
var anim = AnimationController.Global ?? SceneObjectLookupCache.FindAny<AnimationController>();
|
||||
if (anim != null)
|
||||
{
|
||||
anim.PrewarmParticles(2);
|
||||
|
||||
@@ -160,10 +160,10 @@ public static class SkillTriggerFeedUI
|
||||
{
|
||||
if (HasBoundText()) return;
|
||||
|
||||
GameObject skillRoot = GameObject.Find("score/Skill");
|
||||
GameObject skillRoot = SceneObjectLookupCache.Find("score/Skill");
|
||||
if (skillRoot == null)
|
||||
{
|
||||
var score = GameObject.Find("score");
|
||||
var score = SceneObjectLookupCache.Find("score");
|
||||
if (score == null)
|
||||
{
|
||||
if (!s_warnedMissingScore)
|
||||
@@ -271,7 +271,7 @@ public static class SkillTriggerFeedUI
|
||||
|
||||
private static void TryHideLegacyFeedRoot()
|
||||
{
|
||||
GameObject feed = GameObject.Find("score/Skill");
|
||||
GameObject feed = SceneObjectLookupCache.Find("score/Skill");
|
||||
if (feed != null && feed.activeSelf)
|
||||
feed.SetActive(false);
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ public class effectEventController : MonoBehaviour
|
||||
|
||||
public static void RefreshPlayerSkillCameraOffset()
|
||||
{
|
||||
effectEventController controller = Instance != null ? Instance : FindAnyObjectByType<effectEventController>();
|
||||
effectEventController controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<effectEventController>();
|
||||
if (controller == null)
|
||||
return;
|
||||
|
||||
@@ -258,7 +258,7 @@ public class effectEventController : MonoBehaviour
|
||||
|
||||
public static void TryTriggerMultiNoteShake()
|
||||
{
|
||||
effectEventController controller = Instance != null ? Instance : FindAnyObjectByType<effectEventController>();
|
||||
effectEventController controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<effectEventController>();
|
||||
if (controller == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ public class globalNoteEffect : MonoBehaviour
|
||||
if (Instance != null)
|
||||
return Instance;
|
||||
|
||||
globalNoteEffect found = FindAnyObjectByType<globalNoteEffect>();
|
||||
globalNoteEffect found = SceneObjectLookupCache.FindAny<globalNoteEffect>();
|
||||
if (found != null)
|
||||
{
|
||||
Instance = found;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -135,7 +135,7 @@ namespace GameServer.Client
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void TriggerStartupLeaveOnce()
|
||||
{
|
||||
if (Instance == null || _startupLeaveScheduled || _startupLeaveCompleted)
|
||||
if (Instance == null || _startupLeaveScheduled || _startupLeaveCompleted || OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -155,10 +155,14 @@ namespace GameServer.Client
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
SceneManager.sceneLoaded += HandleSceneLoaded;
|
||||
OnlineModeSettings.ModeChanged += HandleOnlineModeChanged;
|
||||
if (!_startupSocialConnectStarted)
|
||||
{
|
||||
_startupSocialConnectStarted = true;
|
||||
StartCoroutine(StartupSocialConnectCoroutine());
|
||||
if (!OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
StartCoroutine(StartupSocialConnectCoroutine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +191,7 @@ namespace GameServer.Client
|
||||
_ = _client?.Close();
|
||||
_ = _socialClient?.Close();
|
||||
SceneManager.sceneLoaded -= HandleSceneLoaded;
|
||||
OnlineModeSettings.ModeChanged -= HandleOnlineModeChanged;
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
@@ -275,6 +280,11 @@ namespace GameServer.Client
|
||||
|
||||
private IEnumerator StartupLeaveRoomCoroutine()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
float timeout = Time.realtimeSinceStartup + 8f;
|
||||
while ((NetworkManager.Instance == null || string.IsNullOrWhiteSpace(NetworkManager.Instance.SteamId))
|
||||
&& Time.realtimeSinceStartup < timeout)
|
||||
@@ -297,6 +307,11 @@ namespace GameServer.Client
|
||||
|
||||
private async Task LeaveAnyRoomAtStartupAsync()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_startupLeaveCompleted)
|
||||
{
|
||||
return;
|
||||
@@ -750,6 +765,11 @@ namespace GameServer.Client
|
||||
|
||||
private async Task EnsureConnected()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw new InvalidOperationException("Arena online features are disabled in local-only mode.");
|
||||
}
|
||||
|
||||
if (_client != null && _client.IsConnected)
|
||||
{
|
||||
return;
|
||||
@@ -805,6 +825,11 @@ namespace GameServer.Client
|
||||
|
||||
private async Task EnsureSocialConnected()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw new InvalidOperationException("Social online features are disabled in local-only mode.");
|
||||
}
|
||||
|
||||
if (_socialClient != null && _socialClient.IsConnected)
|
||||
{
|
||||
return;
|
||||
@@ -3413,6 +3438,11 @@ namespace GameServer.Client
|
||||
|
||||
private IEnumerator StartupSocialConnectCoroutine()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
float timeout = Time.realtimeSinceStartup + 12f;
|
||||
while ((NetworkManager.Instance == null || string.IsNullOrWhiteSpace(NetworkManager.Instance.SteamId))
|
||||
&& Time.realtimeSinceStartup < timeout)
|
||||
@@ -3437,6 +3467,32 @@ namespace GameServer.Client
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOnlineModeChanged(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
_manualClose = true;
|
||||
_manualSocialClose = true;
|
||||
_heartbeatCts?.Cancel();
|
||||
_socialHeartbeatCts?.Cancel();
|
||||
StopArenaReconnectLoop();
|
||||
StopSocialReconnectLoop();
|
||||
_ = _client?.Close();
|
||||
_ = _socialClient?.Close();
|
||||
_client = null;
|
||||
_socialClient = null;
|
||||
_isConnecting = false;
|
||||
_isSocialConnecting = false;
|
||||
_joinedSocialRoomCode = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_startupSocialConnectStarted && _socialReconnectCoroutine == null && _socialClient == null && Application.isPlaying)
|
||||
{
|
||||
StartCoroutine(StartupSocialConnectCoroutine());
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshLocalPresenceSilently()
|
||||
{
|
||||
_ = RefreshLocalPresenceSilentlyAsync();
|
||||
@@ -3528,7 +3584,7 @@ namespace GameServer.Client
|
||||
yield break;
|
||||
}
|
||||
|
||||
SongDetailsUI ui = FindAnyObjectByType<SongDetailsUI>();
|
||||
SongDetailsUI ui = SceneObjectLookupCache.FindAny<SongDetailsUI>();
|
||||
if (ui != null)
|
||||
{
|
||||
ui.OpenRoomDetailsForCurrentRoom();
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace GameServer.Client
|
||||
public enum ConnectionState
|
||||
{
|
||||
Disconnected,
|
||||
LocalOnly,
|
||||
Connecting,
|
||||
Handshaking,
|
||||
LoadingProfile,
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
@@ -22,7 +21,7 @@ namespace GameServer.Client
|
||||
{
|
||||
public class NetworkManager : MonoBehaviour
|
||||
{
|
||||
private const bool VerboseLogs = false;
|
||||
private static readonly bool VerboseLogs = false;
|
||||
public static NetworkManager Instance { get; private set; }
|
||||
private static string _startupSteamId = string.Empty;
|
||||
private static string _startupSteamDisplayName = string.Empty;
|
||||
@@ -57,6 +56,7 @@ public class NetworkManager : MonoBehaviour
|
||||
private bool _startupHandshakeStarted;
|
||||
private bool _startupHandshakeCompleted;
|
||||
private Task<string> _avatarUploadTask;
|
||||
private bool _isRefreshingSteamIdentity;
|
||||
|
||||
private struct CachedRemoteIdentity
|
||||
{
|
||||
@@ -69,6 +69,8 @@ public class NetworkManager : MonoBehaviour
|
||||
public bool IsReady => _state == ConnectionState.Ready;
|
||||
public bool IsConnectedAndHandshaked => _state == ConnectionState.Ready;
|
||||
public string ServerUrl => serverUrl;
|
||||
public bool OnlineFeaturesEnabled => OnlineModeSettings.IsOnlineEnabled;
|
||||
public bool IsLocalOnlyMode => OnlineModeSettings.IsLocalOnlyMode;
|
||||
public string SteamId
|
||||
{
|
||||
get
|
||||
@@ -134,12 +136,20 @@ public class NetworkManager : MonoBehaviour
|
||||
}
|
||||
RefreshSteamIdentity(false);
|
||||
CacheIdentity(steamId, steamDisplayName, steamAvatarUrl);
|
||||
OnlineModeSettings.ModeChanged += HandleOnlineModeChanged;
|
||||
ApplyRuntimeModeState();
|
||||
if (VerboseLogs) Debug.Log("[NetworkManager] HTTP lazy mode singleton initialized");
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
RefreshSteamIdentity(false);
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(StartupHandshakeRoutine());
|
||||
if (VerboseLogs) Debug.Log($"[NetworkManager] Startup handshake enabled. Settlement submit will use {BuildApiUrl("/api/submit")}");
|
||||
}
|
||||
@@ -152,6 +162,7 @@ public class NetworkManager : MonoBehaviour
|
||||
private void OnDestroy()
|
||||
{
|
||||
_requestCts?.Cancel();
|
||||
OnlineModeSettings.ModeChanged -= HandleOnlineModeChanged;
|
||||
if (!_isApplicationQuitting && Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
@@ -160,6 +171,12 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
_requestCts?.Cancel();
|
||||
_requestCts = new CancellationTokenSource();
|
||||
ConnectWithErrorHandling(_requestCts.Token);
|
||||
@@ -178,6 +195,12 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task SendHandshake()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
await PerformHandshakeAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -206,6 +229,11 @@ public class NetworkManager : MonoBehaviour
|
||||
throw new ArgumentException("targetSteamId is empty", nameof(targetSteamId));
|
||||
}
|
||||
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return BuildLocalOnlyProfile(targetSteamId);
|
||||
}
|
||||
|
||||
ProfileData data = await GetJson<ProfileData>(BuildApiUrl($"/api/profile/{targetSteamId}"), CancellationToken.None);
|
||||
CacheIdentity(targetSteamId, data != null ? data.display_name : null, data != null ? data.avatar_url : null);
|
||||
return data;
|
||||
@@ -218,6 +246,16 @@ public class NetworkManager : MonoBehaviour
|
||||
throw new ArgumentOutOfRangeException(nameof(uid), "uid must be positive");
|
||||
}
|
||||
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new ProfileData
|
||||
{
|
||||
success = false,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Online profile lookup is unavailable in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
ProfileData data = await GetJson<ProfileData>(BuildApiUrl($"/api/players/uid/{uid}"), CancellationToken.None);
|
||||
string identitySteamId = data != null ? data.steam_id : null;
|
||||
CacheIdentity(identitySteamId, data != null ? data.display_name : null, data != null ? data.avatar_url : null);
|
||||
@@ -226,6 +264,18 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task<SteamFriendsImportResponse> ImportSteamFriends(IEnumerable<string> steamFriends)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new SteamFriendsImportResponse
|
||||
{
|
||||
success = false,
|
||||
added = 0,
|
||||
skipped = 0,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Steam friend import is unavailable in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
var unique = new HashSet<string>(StringComparer.Ordinal);
|
||||
var normalized = new List<string>();
|
||||
if (steamFriends != null)
|
||||
@@ -253,6 +303,19 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task<SocialFriendsApiResponse> GetFriendsList(IEnumerable<string> steamFriends)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new SocialFriendsApiResponse
|
||||
{
|
||||
success = true,
|
||||
friends = new List<SocialFriendEntry>(),
|
||||
added = 0,
|
||||
skipped = 0,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Online friends are disabled in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
var unique = new HashSet<string>(StringComparer.Ordinal);
|
||||
var normalized = new List<string>();
|
||||
if (steamFriends != null)
|
||||
@@ -294,12 +357,22 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task<MailApiVersionResponse> GetMailVersion()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return BuildLocalOnlyMailVersionResponse();
|
||||
}
|
||||
|
||||
string steamIdQuery = Uri.EscapeDataString(SteamId ?? string.Empty);
|
||||
return await GetJson<MailApiVersionResponse>(BuildApiUrl($"/api/mail/version?steam_id={steamIdQuery}"), CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task<MailApiListResponse> GetMailList()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return BuildLocalOnlyMailListResponse();
|
||||
}
|
||||
|
||||
string steamIdQuery = Uri.EscapeDataString(SteamId ?? string.Empty);
|
||||
return await GetJson<MailApiListResponse>(BuildApiUrl($"/api/mail/list?steam_id={steamIdQuery}"), CancellationToken.None);
|
||||
}
|
||||
@@ -311,6 +384,18 @@ public class NetworkManager : MonoBehaviour
|
||||
throw new ArgumentOutOfRangeException(nameof(mailId), "mailId must be positive");
|
||||
}
|
||||
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new MailClaimResponse
|
||||
{
|
||||
success = false,
|
||||
mail_id = mailId,
|
||||
steam_id = SteamId,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Online mail sync is unavailable in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
var req = new MailClaimRequest
|
||||
{
|
||||
steam_id = SteamId,
|
||||
@@ -322,12 +407,24 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public IEnumerator GetMailVersionRoutine(Action<MailApiVersionResponse, Exception> onCompleted)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
onCompleted?.Invoke(BuildLocalOnlyMailVersionResponse(), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
string steamIdQuery = Uri.EscapeDataString(SteamId ?? string.Empty);
|
||||
yield return GetJsonRoutine(BuildApiUrl($"/api/mail/version?steam_id={steamIdQuery}"), onCompleted);
|
||||
}
|
||||
|
||||
public IEnumerator GetMailListRoutine(Action<MailApiListResponse, Exception> onCompleted)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
onCompleted?.Invoke(BuildLocalOnlyMailListResponse(), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
string steamIdQuery = Uri.EscapeDataString(SteamId ?? string.Empty);
|
||||
yield return GetJsonRoutine(BuildApiUrl($"/api/mail/list?steam_id={steamIdQuery}"), onCompleted);
|
||||
}
|
||||
@@ -422,6 +519,12 @@ public class NetworkManager : MonoBehaviour
|
||||
int chartScore, int idolScore, long totalScore, string grade,
|
||||
double runtimeSeconds, string playedAt, string hmac = "")
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return "LOCAL_ONLY";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await PerformHandshakeAsync(CancellationToken.None);
|
||||
@@ -469,6 +572,16 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task<ArenaCreateResponse> CreateArenaRoom(string songId, string difficulty, string password = null)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new ArenaCreateResponse
|
||||
{
|
||||
success = false,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Arena rooms are unavailable in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
var req = new ArenaCreateRequest
|
||||
{
|
||||
steam_id = SteamId,
|
||||
@@ -483,6 +596,17 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task<ArenaJoinResponse> JoinArenaRoom(string roomCode, string password = null)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return new ArenaJoinResponse
|
||||
{
|
||||
success = false,
|
||||
room_code = roomCode,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Arena rooms are unavailable in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
var req = new ArenaJoinRequest
|
||||
{
|
||||
steam_id = SteamId,
|
||||
@@ -496,6 +620,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task StartArenaGame()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw CreateLocalOnlyException("Arena gameplay start is unavailable in local-only mode.");
|
||||
}
|
||||
|
||||
var req = new ArenaStartRequest
|
||||
{
|
||||
steam_id = SteamId
|
||||
@@ -509,6 +638,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task SubmitArenaScore(string roomCode, long totalScore, string grade)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw CreateLocalOnlyException("Arena score submit is unavailable in local-only mode.");
|
||||
}
|
||||
|
||||
var req = new ArenaSubmitRequest
|
||||
{
|
||||
steam_id = SteamId,
|
||||
@@ -529,6 +663,12 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public async Task SendHeartbeat()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
HeartbeatResponse resp = await GetJson<HeartbeatResponse>(BuildApiUrl("/api/ping"), CancellationToken.None);
|
||||
if (VerboseLogs) Debug.Log($"[NetworkManager] Ping: {resp.message}");
|
||||
}
|
||||
@@ -540,6 +680,14 @@ public class NetworkManager : MonoBehaviour
|
||||
throw new ArgumentException("songId is required", nameof(songId));
|
||||
}
|
||||
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
LeaderboardData localData = BuildLocalOnlyLeaderboard(songId);
|
||||
OnLeaderboardLoaded?.Invoke(localData);
|
||||
return localData;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SetState(ConnectionState.LoadingLeaderboard);
|
||||
@@ -562,6 +710,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
internal Task<LeaderboardData> FetchLeaderboardFromServer(string songId)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
return Task.FromResult(BuildLocalOnlyLeaderboard(songId));
|
||||
}
|
||||
|
||||
return GetJson<LeaderboardData>(BuildApiUrl($"/api/leaderboard/{songId}"), CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -585,6 +738,19 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
private async Task PerformHandshakeAsync(CancellationToken token)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
OnHandshakeResult?.Invoke(new HandshakeResponse
|
||||
{
|
||||
success = false,
|
||||
steam_id = SteamId,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Online handshake is disabled in local-only mode."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
HandshakeRequest req = await PrepareHandshakeRequestAsync(token, true);
|
||||
SetState(ConnectionState.Handshaking);
|
||||
|
||||
@@ -626,6 +792,13 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
private IEnumerator StartupHandshakeRoutine()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
_startupHandshakeStarted = true;
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (_startupHandshakeStarted || _startupHandshakeCompleted)
|
||||
{
|
||||
yield break;
|
||||
@@ -684,36 +857,49 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
public bool RefreshSteamIdentity(bool logWarnings)
|
||||
{
|
||||
bool refreshed = TryReadSteamIdentity(out string currentSteamId, out string currentDisplayName, logWarnings);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(currentSteamId))
|
||||
if (_isRefreshingSteamIdentity)
|
||||
{
|
||||
steamId = currentSteamId;
|
||||
_startupSteamId = currentSteamId;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(steamId))
|
||||
{
|
||||
steamId = _startupSteamId;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsUsableDisplayName(currentDisplayName, steamId))
|
||||
_isRefreshingSteamIdentity = true;
|
||||
try
|
||||
{
|
||||
steamDisplayName = currentDisplayName;
|
||||
_startupSteamDisplayName = currentDisplayName;
|
||||
bool refreshed = TryReadSteamIdentity(out string currentSteamId, out string currentDisplayName, logWarnings);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(currentSteamId))
|
||||
{
|
||||
steamId = currentSteamId;
|
||||
_startupSteamId = currentSteamId;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(steamId))
|
||||
{
|
||||
steamId = _startupSteamId;
|
||||
}
|
||||
|
||||
if (IsUsableDisplayName(currentDisplayName, steamId))
|
||||
{
|
||||
steamDisplayName = currentDisplayName;
|
||||
_startupSteamDisplayName = currentDisplayName;
|
||||
}
|
||||
else if (!IsUsableDisplayName(steamDisplayName, steamId))
|
||||
{
|
||||
steamDisplayName = ResolveDisplayName(_startupSteamDisplayName, steamId, false);
|
||||
}
|
||||
|
||||
if (!IsUsableAvatarUrl(steamAvatarUrl))
|
||||
{
|
||||
steamAvatarUrl = BuildSteamAvatarUrl(steamId);
|
||||
}
|
||||
|
||||
CacheIdentity(steamId, steamDisplayName, steamAvatarUrl);
|
||||
|
||||
return refreshed;
|
||||
}
|
||||
else if (!IsUsableDisplayName(steamDisplayName, steamId))
|
||||
finally
|
||||
{
|
||||
steamDisplayName = ResolveDisplayName(_startupSteamDisplayName, steamId, false);
|
||||
_isRefreshingSteamIdentity = false;
|
||||
}
|
||||
|
||||
if (!IsUsableAvatarUrl(steamAvatarUrl))
|
||||
{
|
||||
steamAvatarUrl = BuildSteamAvatarUrl(steamId);
|
||||
}
|
||||
|
||||
CacheIdentity(steamId, steamDisplayName, steamAvatarUrl);
|
||||
|
||||
return refreshed;
|
||||
}
|
||||
|
||||
private async Task<string> EnsureAvatarUploadedAsync(CancellationToken token)
|
||||
@@ -780,11 +966,12 @@ public class NetworkManager : MonoBehaviour
|
||||
NetworkManager instance = Instance;
|
||||
if (instance != null)
|
||||
{
|
||||
instance.RefreshSteamIdentity(false);
|
||||
if (string.Equals(instance.steamId, normalizedSteamId, StringComparison.Ordinal)
|
||||
&& IsUsableDisplayName(instance.steamDisplayName, normalizedSteamId))
|
||||
string instanceSteamId = instance.steamId;
|
||||
string instanceDisplayName = instance.steamDisplayName;
|
||||
if (string.Equals(instanceSteamId, normalizedSteamId, StringComparison.Ordinal)
|
||||
&& IsUsableDisplayName(instanceDisplayName, normalizedSteamId))
|
||||
{
|
||||
return instance.steamDisplayName.Trim();
|
||||
return instanceDisplayName.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1238,6 +1425,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
private async Task<AvatarUploadResponse> UploadAvatarAsync(string targetSteamId, byte[] avatarBytes, CancellationToken token)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw CreateLocalOnlyException("Avatar upload is disabled in local-only mode.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetSteamId))
|
||||
{
|
||||
throw new ArgumentException("targetSteamId is empty", nameof(targetSteamId));
|
||||
@@ -1277,6 +1469,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
private async Task<T> PostJson<T>(string url, object payload, CancellationToken token)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw CreateLocalOnlyException($"HTTP POST is disabled in local-only mode. url={url}");
|
||||
}
|
||||
|
||||
string json = JsonConvert.SerializeObject(payload);
|
||||
using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||
{
|
||||
@@ -1308,6 +1505,11 @@ public class NetworkManager : MonoBehaviour
|
||||
|
||||
private async Task<T> GetJson<T>(string url, CancellationToken token)
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
throw CreateLocalOnlyException($"HTTP GET is disabled in local-only mode. url={url}");
|
||||
}
|
||||
|
||||
using (UnityWebRequest request = UnityWebRequest.Get(url))
|
||||
{
|
||||
request.timeout = 15;
|
||||
@@ -1331,6 +1533,89 @@ public class NetworkManager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOnlineModeChanged(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
_requestCts?.Cancel();
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state == ConnectionState.LocalOnly)
|
||||
{
|
||||
SetState(ConnectionState.Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRuntimeModeState()
|
||||
{
|
||||
if (OnlineModeSettings.IsLocalOnlyMode)
|
||||
{
|
||||
SetState(ConnectionState.LocalOnly);
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception CreateLocalOnlyException(string message)
|
||||
{
|
||||
return new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
private ProfileData BuildLocalOnlyProfile(string targetSteamId)
|
||||
{
|
||||
string normalizedSteamId = string.IsNullOrWhiteSpace(targetSteamId) ? string.Empty : targetSteamId.Trim();
|
||||
bool isSelf = string.Equals(normalizedSteamId, SteamId, StringComparison.Ordinal);
|
||||
string displayName = ResolveDisplayName(string.Empty, normalizedSteamId, false);
|
||||
string avatarUrl = ResolveAvatarUrl(null, normalizedSteamId);
|
||||
|
||||
return new ProfileData
|
||||
{
|
||||
success = isSelf,
|
||||
error_code = isSelf ? null : "LOCAL_ONLY_MODE",
|
||||
message = isSelf ? "Loaded local profile in local-only mode." : "Remote profiles are unavailable in local-only mode.",
|
||||
steam_id = normalizedSteamId,
|
||||
display_name = displayName,
|
||||
avatar_url = avatarUrl
|
||||
};
|
||||
}
|
||||
|
||||
private static MailApiVersionResponse BuildLocalOnlyMailVersionResponse()
|
||||
{
|
||||
return new MailApiVersionResponse
|
||||
{
|
||||
success = true,
|
||||
revision = "local-only",
|
||||
count = 0,
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Server mail is disabled in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
private static MailApiListResponse BuildLocalOnlyMailListResponse()
|
||||
{
|
||||
return new MailApiListResponse
|
||||
{
|
||||
success = true,
|
||||
revision = "local-only",
|
||||
count = 0,
|
||||
mails = new List<MailApiEntry>(),
|
||||
deletions = new List<MailApiDeletionEntry>(),
|
||||
error_code = "LOCAL_ONLY_MODE",
|
||||
message = "Server mail is disabled in local-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
private static LeaderboardData BuildLocalOnlyLeaderboard(string songId)
|
||||
{
|
||||
return new LeaderboardData
|
||||
{
|
||||
song_id = songId,
|
||||
cycle_id = 0,
|
||||
is_locked = false,
|
||||
rankings = new List<LeaderboardEntry>()
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerator GetJsonRoutine<T>(string url, Action<T, Exception> onCompleted)
|
||||
{
|
||||
using (UnityWebRequest request = UnityWebRequest.Get(url))
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameServer.Client
|
||||
{
|
||||
public static class OnlineModeSettings
|
||||
{
|
||||
public const string PlayerPrefsKey = "bansonic_online_enabled";
|
||||
private const int DefaultOnlineEnabled = 1;
|
||||
private static bool _initialized;
|
||||
|
||||
public static event Action<bool> ModeChanged;
|
||||
|
||||
public static bool IsOnlineEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return PlayerPrefs.GetInt(PlayerPrefsKey, DefaultOnlineEnabled) != 0;
|
||||
}
|
||||
set => SetOnlineEnabled(value);
|
||||
}
|
||||
|
||||
public static bool IsLocalOnlyMode => !IsOnlineEnabled;
|
||||
|
||||
public static void SetOnlineEnabled(bool enabled)
|
||||
{
|
||||
EnsureInitialized();
|
||||
bool previous = IsOnlineEnabled;
|
||||
int persistedValue = enabled ? 1 : 0;
|
||||
if (PlayerPrefs.GetInt(PlayerPrefsKey, DefaultOnlineEnabled) == persistedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerPrefs.SetInt(PlayerPrefsKey, persistedValue);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
if (previous != enabled)
|
||||
{
|
||||
ModeChanged?.Invoke(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
if (!PlayerPrefs.HasKey(PlayerPrefsKey))
|
||||
{
|
||||
PlayerPrefs.SetInt(PlayerPrefsKey, DefaultOnlineEnabled);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a38c03061babdb24b9208ec991bde237
|
||||
@@ -42,7 +42,7 @@ namespace GameServer.Client
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
// 跳过系统代理
|
||||
_ws.Options.Proxy = GlobalProxySelection.GetEmptyWebProxy();
|
||||
_ws.Options.Proxy = null;
|
||||
_ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(20);
|
||||
|
||||
try
|
||||
@@ -205,4 +205,4 @@ namespace GameServer.Client
|
||||
_ws?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -970,7 +970,7 @@ public class iBudeffPrefabController : MonoBehaviour
|
||||
|
||||
private static GameObject FindEnemyPutInScene()
|
||||
{
|
||||
var direct = GameObject.Find("budeffGrid");
|
||||
var direct = SceneObjectLookupCache.Find("budeffGrid");
|
||||
if (direct != null) return direct;
|
||||
try
|
||||
{
|
||||
@@ -1065,7 +1065,7 @@ public class iBudeffPrefabController : MonoBehaviour
|
||||
|
||||
private void TryAutoBindPuts()
|
||||
{
|
||||
var ui = teamUIController.Instance != null ? teamUIController.Instance : FindAnyObjectByType<teamUIController>();
|
||||
var ui = teamUIController.Instance != null ? teamUIController.Instance : SceneObjectLookupCache.FindAny<teamUIController>();
|
||||
if (ui == null) return;
|
||||
|
||||
if (ally01_put == null) ally01_put = FindPutUnder(ui.objectFather_ally01);
|
||||
|
||||
+3
-3
@@ -65,14 +65,14 @@ public class iNumberPrefabController : MonoBehaviour
|
||||
{
|
||||
if (ally_number_prefab == null)
|
||||
{
|
||||
var foundAlly = GameObject.Find("player_instant_prefab_ally");
|
||||
if (foundAlly == null) foundAlly = GameObject.Find("player_instant_prefab");
|
||||
var foundAlly = SceneObjectLookupCache.Find("player_instant_prefab_ally");
|
||||
if (foundAlly == null) foundAlly = SceneObjectLookupCache.Find("player_instant_prefab");
|
||||
if (foundAlly != null && foundAlly.GetComponent<playerInstantNumbersPrefab>() != null) ally_number_prefab = foundAlly;
|
||||
}
|
||||
|
||||
if (enemy_number_prefab == null)
|
||||
{
|
||||
var foundEnemy = GameObject.Find("player_instant_prefab_enemy");
|
||||
var foundEnemy = SceneObjectLookupCache.Find("player_instant_prefab_enemy");
|
||||
if (foundEnemy != null && foundEnemy.GetComponent<playerInstantNumbersPrefab>() != null) enemy_number_prefab = foundEnemy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using UnityEngine.UI;
|
||||
|
||||
public class rankingListPrefab : MonoBehaviour
|
||||
{
|
||||
private const string RoomRankingServerText = "根据房间成绩动态排名,所在服务端:雅莉梦璃高新产业园";
|
||||
private static string RoomRankingServerText => LocalizationService.Get("ranking.room_server", "根据房间成绩动态排名,所在服务端:雅莉梦璃高新产业园");
|
||||
|
||||
[Header("shutbutton")]
|
||||
public Button shutbutton;
|
||||
@@ -94,7 +94,7 @@ public class rankingListPrefab : MonoBehaviour
|
||||
|
||||
if (roomRankingData == null || roomRankingData.rankings == null || roomRankingData.rankings.Count == 0)
|
||||
{
|
||||
SetEmptyState(true, "房间排行榜暂无数据");
|
||||
SetEmptyState(true, LocalizationService.Get("ranking.room_no_data", "房间排行榜暂无数据"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class rankingListPrefab : MonoBehaviour
|
||||
NetworkManager nm = NetworkManager.Instance;
|
||||
if (nm == null)
|
||||
{
|
||||
SetEmptyState(true, "排行榜服务不可用");
|
||||
SetEmptyState(true, LocalizationService.Get("ranking.service_unavailable", "排行榜服务不可用"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,13 +124,13 @@ public class rankingListPrefab : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEmptyState(false, "正在拉取排行榜...");
|
||||
SetEmptyState(false, LocalizationService.Get("ranking.loading", "正在拉取排行榜..."));
|
||||
data = await nm.GetLeaderboard(currentSongId);
|
||||
}
|
||||
|
||||
if (data == null || data.rankings == null || data.rankings.Count == 0)
|
||||
{
|
||||
SetEmptyState(true, "排行榜暂无数据");
|
||||
SetEmptyState(true, LocalizationService.Get("ranking.no_data", "排行榜暂无数据"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class rankingListPrefab : MonoBehaviour
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[rankingListPrefab] Failed to load leaderboard: {ex.Message}");
|
||||
SetEmptyState(true, "拉取排行榜失败");
|
||||
SetEmptyState(true, LocalizationService.Get("ranking.load_failed", "拉取排行榜失败"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -239,7 +239,9 @@ public class rankingListPrefab : MonoBehaviour
|
||||
{
|
||||
if (rankingListType != null)
|
||||
{
|
||||
rankingListType.text = isRoomRanking ? "房间排行榜" : "世界排行榜";
|
||||
rankingListType.text = isRoomRanking
|
||||
? LocalizationService.Get("ranking.room_title", "房间排行榜")
|
||||
: LocalizationService.Get("ranking.world_title", "世界排行榜");
|
||||
}
|
||||
|
||||
if (versionandServer != null)
|
||||
@@ -260,7 +262,7 @@ public class rankingListPrefab : MonoBehaviour
|
||||
|
||||
if (data == null || data.rankings == null || data.rankings.Count == 0 || string.IsNullOrWhiteSpace(roomRankingLocalSteamId))
|
||||
{
|
||||
yourPositionText.text = "您的得分为--,目前排名第<color=green>--</color>位";
|
||||
yourPositionText.text = LocalizationService.Get("ranking.your_position_empty", "您的得分为--,目前排名第<color=green>--</color>位");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -277,12 +279,11 @@ public class rankingListPrefab : MonoBehaviour
|
||||
|
||||
if (localEntry == null)
|
||||
{
|
||||
yourPositionText.text = "您的得分为--,目前排名第<color=green>--</color>位";
|
||||
yourPositionText.text = LocalizationService.Get("ranking.your_position_empty", "您的得分为--,目前排名第<color=green>--</color>位");
|
||||
return;
|
||||
}
|
||||
|
||||
yourPositionText.text =
|
||||
$"您的得分为{localEntry.total_score},目前排名第<color=green>{localEntry.rank}</color>位";
|
||||
yourPositionText.text = LocalizationService.GetFormat("ranking.your_position_value", localEntry.total_score, localEntry.rank);
|
||||
}
|
||||
|
||||
private static string FormatSubmitTime(LeaderboardEntry entry)
|
||||
|
||||
@@ -89,7 +89,7 @@ public class settleChart : MonoBehaviour
|
||||
|
||||
BeatmapManager beatmapManager = BeatmapManager.Instance != null
|
||||
? BeatmapManager.Instance
|
||||
: FindAnyObjectByType<BeatmapManager>();
|
||||
: SceneObjectLookupCache.FindAny<BeatmapManager>();
|
||||
|
||||
NoteData[] notes = beatmapManager != null && beatmapManager.beatmap != null
|
||||
? beatmapManager.beatmap.notes
|
||||
@@ -401,13 +401,13 @@ public class settleChart : MonoBehaviour
|
||||
}
|
||||
|
||||
string lowerName = button.name.ToLowerInvariant();
|
||||
if (previousPageButton == null && (lowerName.Contains("prev") || lowerName.Contains("previous") || lowerName.Contains("lastpage") || lowerName.Contains("uppage") || lowerName.Contains("ÉÏÒ»")))
|
||||
if (previousPageButton == null && (lowerName.Contains("prev") || lowerName.Contains("previous") || lowerName.Contains("lastpage") || lowerName.Contains("uppage") || lowerName.Contains("��һ")))
|
||||
{
|
||||
previousPageButton = button;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextPageButton == null && (lowerName.Contains("next") || lowerName.Contains("downpage") || lowerName.Contains("nextpage") || lowerName.Contains("ÏÂÒ»")))
|
||||
if (nextPageButton == null && (lowerName.Contains("next") || lowerName.Contains("downpage") || lowerName.Contains("nextpage") || lowerName.Contains("��һ")))
|
||||
{
|
||||
nextPageButton = button;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%YAML 1.1
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &118873712428923354
|
||||
GameObject:
|
||||
@@ -208,8 +208,8 @@ MonoBehaviour:
|
||||
reward_icon: {fileID: 6251169732777221245}
|
||||
reward_name: {fileID: 267584174794072845}
|
||||
reward_count: {fileID: 6866410370984929931}
|
||||
coin_sprite: {fileID: 21300000, guid: 1836bfb842a045f4f9a4a38b08b395ef, type: 3}
|
||||
jiyisuipian: {fileID: 21300000, guid: 27adfaee3f8bee14280e634021049f50, type: 3}
|
||||
coin_sprite: {fileID: 21300000, guid: 16f0765cc15fce04f828ecd44d1dbf91, type: 3}
|
||||
jiyisuipian: {fileID: 21300000, guid: 6fd4dd33d986d824d95f7b01579acc4d, type: 3}
|
||||
player_exp: {fileID: 21300000, guid: af3ce18394be1b448a9e38602526eb80, type: 3}
|
||||
--- !u!1 &7159171791523732843
|
||||
GameObject:
|
||||
|
||||
@@ -437,7 +437,7 @@ public class settlementController : MonoBehaviour
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
GameObject go = GameObject.Find($"ally_0{i + 1}");
|
||||
GameObject go = SceneObjectLookupCache.Find($"ally_0{i + 1}");
|
||||
if (go != null)
|
||||
{
|
||||
AllyCombatant ac = go.GetComponent<AllyCombatant>();
|
||||
@@ -550,7 +550,7 @@ public class settlementController : MonoBehaviour
|
||||
getThisSong_info();
|
||||
|
||||
// --- Statistics: Record total play time at settlement ---
|
||||
GameManager runtimeGameManager = gm != null ? gm : FindAnyObjectByType<GameManager>();
|
||||
GameManager runtimeGameManager = gm != null ? gm : SceneObjectLookupCache.FindAny<GameManager>();
|
||||
float sessionDurationForTasks = runtimeGameManager != null ? runtimeGameManager.GetPendingSessionDurationSeconds() : 0f;
|
||||
if (runtimeGameManager != null) runtimeGameManager.RecordTotalPlayTime();
|
||||
|
||||
@@ -733,7 +733,7 @@ public class settlementController : MonoBehaviour
|
||||
if (settlementTeamLoader == null)
|
||||
{
|
||||
// try to auto-locate loader in scene if not assigned in inspector
|
||||
settlementTeamLoader = FindAnyObjectByType<loadSettlementTeamPrefab>();
|
||||
settlementTeamLoader = SceneObjectLookupCache.FindAny<loadSettlementTeamPrefab>();
|
||||
}
|
||||
|
||||
if (settlementTeamLoader != null)
|
||||
@@ -741,7 +741,7 @@ public class settlementController : MonoBehaviour
|
||||
// ensure loader has key references
|
||||
if (settlementTeamLoader.sm == null) settlementTeamLoader.sm = sm ?? ScoreManager.Instance;
|
||||
if (settlementTeamLoader.tuic == null) settlementTeamLoader.tuic = teamUIController.Instance;
|
||||
if (settlementTeamLoader.gm == null) settlementTeamLoader.gm = gm ?? FindAnyObjectByType<GameManager>();
|
||||
if (settlementTeamLoader.gm == null) settlementTeamLoader.gm = gm ?? SceneObjectLookupCache.FindAny<GameManager>();
|
||||
try { settlementTeamLoader.PopulateSettlementCards(); }
|
||||
catch (System.Exception ex) { Debug.LogWarning("Failed to PopulateSettlementCards: " + ex); }
|
||||
}
|
||||
@@ -996,7 +996,7 @@ public class settlementController : MonoBehaviour
|
||||
|
||||
private bool IsEarlySettlement(int noteCountRaw)
|
||||
{
|
||||
NoteSpawner spawner = FindAnyObjectByType<NoteSpawner>();
|
||||
NoteSpawner spawner = SceneObjectLookupCache.FindAny<NoteSpawner>();
|
||||
if (spawner != null && spawner.IsImmediateSettlementTriggered)
|
||||
{
|
||||
return true;
|
||||
@@ -2078,7 +2078,7 @@ public class settlementController : MonoBehaviour
|
||||
// fallback: read AllyCombatant.currentScore from scene
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var go = GameObject.Find($"ally_0{ i+1 }");
|
||||
var go = SceneObjectLookupCache.Find($"ally_0{ i+1 }");
|
||||
if (go != null)
|
||||
{
|
||||
var ac = go.GetComponent<AllyCombatant>();
|
||||
|
||||
Reference in New Issue
Block a user