diff --git a/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs b/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs index 6a50b72b10..9266651585 100644 --- a/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs +++ b/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs @@ -1,21 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.AsyncOperations; -using UnityEngine.SceneManagement; using UnityEngine.U2D; namespace BrewMonster.Scripts { /// - /// Scene game (Bootstrap): chờ gate chỉ khi cùng scene. - /// Nếu đã chạy scene GameContentBootstrap trước → . + /// Scene Bootstrap (index 1): Addressables luôn được init thẳng vì + /// đã chạy xong ở scene 0 ( = true). /// [DefaultExecutionOrder(-1990)] public class AddressableManager : MonoSingleton @@ -38,14 +36,6 @@ namespace BrewMonster.Scripts private Dictionary _releaseAssetTimestamps = new(); [SerializeField]private float _releaseAssetTimeout = 10f; - [Header("Bootstrap gate")] - [Tooltip("Max seconds to wait for GameContentBootstrap before initializing Addressables anyway.")] - [SerializeField] - float _bootstrapGateWaitTimeoutSeconds = 50f; - - [SerializeField] - bool _verboseBootstrapWaitDebug = true; - public event Action OnDispose; /// Get the count of currently loaded assets. @@ -65,13 +55,7 @@ namespace BrewMonster.Scripts protected override void Awake() { - if (_verboseBootstrapWaitDebug) - { - Debug.Log( - $"[Cuong] AddressableManager: Awake | id={GetInstanceID()} scene={SceneManager.GetActiveScene().name} " + - $"frame={Time.frameCount} bootstrapGate={GameContentBootstrap.GetGateDebugState()}"); - } - + Debug.Log($"[Cuong] AddressableManager: Awake | id={GetInstanceID()} frame={Time.frameCount}"); base.Awake(); } @@ -80,35 +64,11 @@ namespace BrewMonster.Scripts base.Initialize(); _isInitialized = false; _initializationTcs = new UniTaskCompletionSource(); - StartAddressablesInitAfterBootstrapGate().Forget(); + StartAddressablesInitAsync().Forget(); } - /// - /// Waits for (version HTTP + optional URL rewrite) when that gate is active. - /// - async UniTaskVoid StartAddressablesInitAfterBootstrapGate() + async UniTaskVoid StartAddressablesInitAsync() { - if (GameContentBootstrapSession.IsContentReady || AddressablesInitService.IsInitialized) - { - Debug.Log( - $"[Cuong] AddressableManager: Content bootstrap đã chạy ở scene trước — init Addressables (scene={SceneManager.GetActiveScene().name})."); - } - else - { - var gateState = GameContentBootstrap.GetGateDebugState(); - Debug.Log( - $"[Cuong] AddressableManager: Đang chờ GameContentBootstrap (version / URL rewrite)... | " + - $"id={GetInstanceID()} scene={SceneManager.GetActiveScene().name} gate={gateState}"); - - var waited = await WaitForBootstrapGateWithTimeoutAsync(); - if (!waited) - { - Debug.LogWarning( - $"[Cuong] AddressableManager: Bootstrap gate timeout ({_bootstrapGateWaitTimeoutSeconds:F0}s) — " + - "InitializeAsync anyway. Nên dùng scene GameContentBootstrap riêng (index 0)."); - } - } - Debug.Log("[Cuong] AddressableManager: Đang InitializeAsync Addressables..."); try { @@ -125,21 +85,6 @@ namespace BrewMonster.Scripts } } - async UniTask WaitForBootstrapGateWithTimeoutAsync() - { - var timeoutSec = Mathf.Max(5f, _bootstrapGateWaitTimeoutSeconds); - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSec)); - try - { - await GameContentBootstrap.WaitForPreAddressablesSetupIfAnyAsync(cts.Token); - return true; - } - catch (OperationCanceledException) - { - return false; - } - } - #region Unity lifecycle private List _assetToForceRelease = new(); private void Update() diff --git a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs index 44187e9adf..729ac69e95 100644 --- a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs +++ b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs @@ -34,9 +34,12 @@ namespace BrewMonster.Network shiftedUnix = 0L; var serverLocalUtc = DateTimeOffset.FromUnixTimeSeconds(shiftedUnix).UtcDateTime; int nTimeInDay = serverLocalUtc.Hour * 3600 + serverLocalUtc.Minute * 60 + serverLocalUtc.Second; - float phase = nTimeInDay / (4f * 3600f); - SetTimeOfTheDay(phase); - BMLogger.Log($"[Cuong] SyncSunMoon server-local {serverLocalUtc.Hour:D2}:{serverLocalUtc.Minute:D2}:{serverLocalUtc.Second:D2}, phase={phase}"); + // C++ formula: nTimeInDay / (4f*3600f) — drives 4h sun/moon animation cycle (matches EC_Game.cpp SetServerTime) + float phase4h = nTimeInDay / (4f * 3600f); + // Real 24h phase: maps server time directly to [0,1) — used for nightControl shader (noon=day, midnight=night) + float phase24h = nTimeInDay / (24f * 3600f); + SetTimeOfTheDayFull(phase4h, phase24h); + BMLogger.Log($"[Cuong] SyncSunMoon server-local {serverLocalUtc.Hour:D2}:{serverLocalUtc.Minute:D2}:{serverLocalUtc.Second:D2}, phase4h={phase4h:F3} phase24h={phase24h:F3}"); } /// @@ -54,6 +57,18 @@ namespace BrewMonster.Network BMLogger.Log($"[Cuong] EC_Game.SetTimeOfTheDay vTime(in)={vTime}, m_vTimeOfTheDay={sunMoon.m_vTimeOfTheDay}"); } + static void SetTimeOfTheDayFull(float phase4h, float phase24h) + { + var sunMoon = CECSunMoon.Instance; + if (sunMoon == null) + { + BMLogger.LogWarning("[Cuong] EC_Game.SetTimeOfTheDayFull: CECSunMoon.Instance is null."); + return; + } + sunMoon.SetTimeOfTheDayFull(phase4h, phase24h); + BMLogger.Log($"[Cuong] EC_Game.SetTimeOfTheDayFull phase4h={phase4h:F3} phase24h={phase24h:F3}, m_vTimeOfTheDay={sunMoon.m_vTimeOfTheDay:F3} m_vRealPhase={sunMoon.m_vRealPhase:F3}"); + } + // 设置时间误差 // Set time error public static void SetServerTime(int iSevTime, int iTimeZoneBias) { diff --git a/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs b/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs index e92db4588f..780e3f062c 100644 --- a/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs +++ b/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs @@ -11,7 +11,8 @@ namespace BrewMonster.Scripts private const float DAY_NIGHT_START = (18.0f / 24.0f); private const float DAY_NIGHT_END = (21.0f / 24.0f); - public double m_vTimeOfTheDay; // time of the day 0.0f means 00:00, 1.0f means 24:00 + public double m_vTimeOfTheDay; // time of the day 0.0f means 00:00, 1.0f means 24:00 (4h-cycle phase, matches C++ CECSunMoon) + public double m_vRealPhase; // 真实服务器时间对应的24h相位(用于 nightControl shader)// real 24h phase from server time, used for nightControl shader (not the 4h-wrapped cycle) public float m_fDNFactor; // day or night factor public float m_fDNFactorDest; // day or night factor dest @@ -28,9 +29,15 @@ namespace BrewMonster.Scripts public void Update() { - m_vTimeOfTheDay += Time.deltaTime / 3600.0 / 24.0 * TIME_SCALE; + double advance = Time.deltaTime / 3600.0 / 24.0; + // 4h cycle — same as C++ Tick(): drives sun/moon visual animation + m_vTimeOfTheDay += advance * TIME_SCALE; while (m_vTimeOfTheDay > 1.0) m_vTimeOfTheDay -= 1.0; + // 24h real-time phase — used for nightControl shader (not TIME_SCALE-sped-up) + m_vRealPhase += advance; + while (m_vRealPhase > 1.0) + m_vRealPhase -= 1.0; RefreshDayNightFactorsFromPhase(); } @@ -40,6 +47,7 @@ namespace BrewMonster.Scripts /// /// 设置一天中的时刻(与 PC 版 CECSunMoon::SetTimeOfTheDay 对齐) // Set time-of-day phase [0,1), aligned with PC CECSunMoon::SetTimeOfTheDay + /// 仅设置4h动画相位;用 SetTimeOfTheDayFull 同时设置真实24h相位 // Sets 4h animation phase only; use SetTimeOfTheDayFull to also set the real 24h phase /// public bool SetTimeOfTheDay(float vTime) { @@ -52,14 +60,34 @@ namespace BrewMonster.Scripts return true; } + /// + /// 同时设置4h动画相位(C++公式)和24h真实相位(shader nightControl用) // Set both the 4h animation phase (C++ formula) and the real 24h phase (for shader nightControl) + /// + /// nTimeInDay / (4f*3600f) — wrap to [0,1), drives sun/moon animation + /// nTimeInDay / (24f*3600f) — [0,1), drives nightControl shader (0=day 1=night based on real server time) + public bool SetTimeOfTheDayFull(float phase4h, float phase24h) + { + while (phase4h < 0f) phase4h += 1f; + while (phase4h > 1f) phase4h -= 1f; + m_vTimeOfTheDay = phase4h; + + phase24h = Mathf.Clamp01(phase24h); + m_vRealPhase = phase24h; + + RefreshDayNightFactorsFromPhase(); + return true; + } + /// /// PC EC_SunMoon::UpdateWithTime 中昼夜因子段(供 minimap 与场景一致) // DN factor block from PC EC_SunMoon::UpdateWithTime + /// m_fDNFactor 由4h相位驱动(与C++一致,用于太阳/月亮视觉); nightControl 由24h真实相位驱动(shader正确反映服务器白天/黑夜) + /// m_fDNFactor is driven by the 4h phase (C++ compatible, for sun/moon visuals); nightControl is driven by the real 24h phase (shader correctly reflects server day/night) /// void RefreshDayNightFactorsFromPhase() { float v = (float)m_vTimeOfTheDay; - // update day night factor — same branches as PC EC_SunMoon.cpp UpdateWithTime (lines 894–918) + // m_fDNFactor — 4h cycle phase, same as C++ EC_SunMoon::UpdateWithTime lines 894-918 if (v < NIGHT_DAY_START) { m_fDNFactor = 1.0f; @@ -87,7 +115,22 @@ namespace BrewMonster.Scripts m_fDNFactorDest = 1.0f; } - _globalShaderVariables?.Apply(m_fDNFactor); + // nightControl shader — driven by m_vRealPhase (24h real server time, 0=day 1=night) + // 用真实24h相位计算 nightControl,确保正午=白天(0),午夜=黑夜(1),不受4h周期wrap影响 + // Real 24h phase ensures noon=day(0), midnight=night(1), unaffected by 4h cycle wrapping + _globalShaderVariables?.Apply(ComputeNightControlFromRealPhase((float)m_vRealPhase)); + } + + /// + /// 用24h真实相位计算 nightControl(与相同阈值,但基于实际服务器时间) // Compute nightControl from real 24h phase using same thresholds + /// + static float ComputeNightControlFromRealPhase(float r) + { + if (r < NIGHT_DAY_START) return 1.0f; + if (r < NIGHT_DAY_END) return 1.0f - (r - NIGHT_DAY_START) / (NIGHT_DAY_END - NIGHT_DAY_START); + if (r < DAY_NIGHT_START) return 0.0f; + if (r < DAY_NIGHT_END) return (r - DAY_NIGHT_START) / (DAY_NIGHT_END - DAY_NIGHT_START); + return 1.0f; } } }