From 258679dd7c80612fc82f9dd71ad2f9299e743ee9 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Wed, 13 May 2026 10:15:29 +0700 Subject: [PATCH] Add logic update time and show time in minimap --- .../Scripts/MainFiles/EC_Game.Time.cs | 49 ++++++++++-- .../Network/CSNetwork/EC_ManMessage.cs | 2 +- .../Scripts/Network/CSNetwork/GameSession.cs | 9 ++- .../Scripts/UI/Login/LoginScreenUI.cs | 3 + .../Scripts/UI/MiniMap/CDlgMiniMap.cs | 79 +++++++++++++++++-- .../PerfectWorld/Scripts/World/CECSunMoon.cs | 66 ++++++++++++++-- Assets/Scripts/EC_GameRun.Task.cs | 13 +-- 7 files changed, 194 insertions(+), 27 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs index f434bd3933..44187e9adf 100644 --- a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs +++ b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs @@ -1,5 +1,6 @@ using UnityEngine; using System; +using BrewMonster.Scripts; //TODO: [DUCK] EC_Game shouldn't be in BrewMonster.Network namespace, it should be in BrewMonster.Scripts namespace. @@ -16,20 +17,52 @@ namespace BrewMonster.Network private static float m_dwRealTickTime; // Real tick time public static int GetTimeZoneBias() { return m_iTimeZoneBias; } + + /// + /// 与 PC CECGameRun::CreateWorld / CECWorld::InitNatureObjects 末尾一致:按当前服务器钟面刷新昼夜相位 // Match PC after world load / nature init + /// + public static void SyncSunMoonTimeOfDayFromServerClock() + { + int serverGmt = GetServerGMTTime(); + ApplySunMoonPhaseFromServerGmtUnix(serverGmt); + } + + static void ApplySunMoonPhaseFromServerGmtUnix(int serverGmtUnixSeconds) + { + long shiftedUnix = (long)serverGmtUnixSeconds - (long)m_iTimeZoneBias * 60L; + if (shiftedUnix < 0L) + 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}"); + } + + /// + /// 将昼夜相位写入 SunMoon(与 PC GetWorld()->GetSunMoon()->SetTimeOfTheDay 用法一致) // Push day/night phase to SunMoon (same role as PC) + /// + public static void SetTimeOfTheDay(float vTime) + { + var sunMoon = CECSunMoon.Instance; + if (sunMoon == null) + { + BMLogger.LogWarning("[Cuong] EC_Game.SetTimeOfTheDay: CECSunMoon.Instance is null."); + return; + } + sunMoon.SetTimeOfTheDay(vTime); + BMLogger.Log($"[Cuong] EC_Game.SetTimeOfTheDay vTime(in)={vTime}, m_vTimeOfTheDay={sunMoon.m_vTimeOfTheDay}"); + } + // 设置时间误差 // Set time error public static void SetServerTime(int iSevTime, int iTimeZoneBias) { - Debug.Log($"SetServerTime, iSevTime = {iSevTime}, iTimeZoneBias = {iTimeZoneBias}"); - int iOldTimeError = m_iTimeError; + BMLogger.Log($"[Cuong] SetServerTime iSevTime={iSevTime}, iTimeZoneBias={iTimeZoneBias}"); int nowUnix = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(); m_iTimeError = iSevTime - nowUnix; // 记录与本机的时间差 // store delta with local m_iTimeZoneBias = iTimeZoneBias; // 记录服务器时区偏移 // store server timezone bias - // 计算服务器本地时间并设置昼夜 // Compute server local time and set time of day - var serverLocal = DateTimeOffset.FromUnixTimeSeconds((long)iSevTime + iTimeZoneBias); - int nTimeInDay = serverLocal.Hour * 3600 + serverLocal.Minute * 60 + serverLocal.Second; - // GetGameRun()->GetWorld()->GetSunMoon()->SetTimeOfTheDay(nTimeInDay / (4.0f * 3600.0f)); - // 设置昼夜时间(原逻辑保留为注释) // Set time of day (original call left commented) + ApplySunMoonPhaseFromServerGmtUnix(iSevTime); // 防沉迷时长修正 // Anti-wallow playtime adjust // S2C::player_wallow_info wallowinfo = GetGameRun()->GetWallowInfo(); @@ -43,7 +76,7 @@ namespace BrewMonster.Network m_AbsTimeStart = iSevTime; m_AbsTickStart = (uint)(Time.realtimeSinceStartup * 1000.0f); m_bServerTimeInited = true; - Debug.Log($"timeGetTime(), TickStart = {m_AbsTickStart}"); + BMLogger.Log($"[Cuong] SetServerTime TickStart (ms) = {m_AbsTickStart}"); } public static int GetServerAbsTime() diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/EC_ManMessage.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/EC_ManMessage.cs index af6e478b9f..17450e7c63 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/EC_ManMessage.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/EC_ManMessage.cs @@ -92,4 +92,4 @@ namespace CSNetwork _instance = null; } } -} \ No newline at end of file +} diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 5d2449ed24..67ba513533 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -1290,8 +1290,15 @@ namespace CSNetwork break; case CommandID.SERVER_TIME: { + // NetworkManager receives on a background Task (ProcessReceivedData); never touch Unity / EC_ManMessage queue here. cmd_server_time pcmd_server_time = GPDataTypeHelper.FromBytes(pDataBuf); - EC_ManMessage.PostMessage(EC_MsgDef.MSG_SERVERTIME, -1, 0, pcmd_server_time.time, pcmd_server_time.timebias); + int st = pcmd_server_time.time; + int sb = pcmd_server_time.timebias; + PostToUnityContext(() => + { + EC_Game.SetServerTime(st, sb); + EC_ManMessage.PostMessage(EC_MsgDef.MSG_SERVERTIME, -1, 0, st, sb); + }); break; } case CommandID.SCENE_SERVICE_NPC_LIST: diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs index ecfa569367..74d3cd91a3 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs @@ -391,6 +391,9 @@ namespace BrewMonster.UI private async void OnEnterWorldComplete() { + // PC CECGameRun::CreateWorld: SunMoon phase from GetServerLocalTime after map load + EC_Game.SyncSunMoonTimeOfDayFromServerClock(); + // initialize the mini map CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan(); if (pGameUI != null) diff --git a/Assets/PerfectWorld/Scripts/UI/MiniMap/CDlgMiniMap.cs b/Assets/PerfectWorld/Scripts/UI/MiniMap/CDlgMiniMap.cs index 90eccde575..41c92587f3 100644 --- a/Assets/PerfectWorld/Scripts/UI/MiniMap/CDlgMiniMap.cs +++ b/Assets/PerfectWorld/Scripts/UI/MiniMap/CDlgMiniMap.cs @@ -29,7 +29,7 @@ namespace PerfectWorld.UI.MiniMap this.mapID = mapID; } } - + [SerializeField] private Vector3 _debugHostPlayerPos; [SerializeField] private RectTransform _hostPlayerIcon; @@ -62,6 +62,30 @@ namespace PerfectWorld.UI.MiniMap private int m_nMode; // TODO: currently, there is only get logic, not set logic + // PC DlgMiniMap.cpp: GetStringFromTable(604), GetStringFromTable(1330 + nTimeIndex), FixFrame(nTimeItem) + private const int StrIdSystemTimeFormat = 604; + private const int StrIdShichenBase = 1330; + + private enum MinimapTimeSprite + { + TIME_DAY = 0, + TIME_MORNING, + TIME_DUSK, + TIME_NIGHT, + } + + /// 十二时辰名(表缺失时回退,与 PC 1330+i 顺序一致:子丑寅…) // Fallback 12 double-hours if string table missing (same order as PC 1330+i) + static readonly string[] s_FallbackShichenNames = + { + "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥", // Zi, Chou, ... + }; + + [Header("Set data show time in mini map")] + [SerializeField] private TMP_Text _txtSystemTime; + [SerializeField] private Image _imgSystemTime; + [Tooltip("PC order: TIME_DAY, TIME_MORNING, TIME_DUSK, TIME_NIGHT (DlgMiniMap enum)")] + [SerializeField] private Sprite[] _systemTimeSprites; + private void Awake() { // LoadAllMiniMapTextures(); @@ -72,6 +96,7 @@ namespace PerfectWorld.UI.MiniMap void Update() { UpdateMiniMap(); + UpdateSystemClockFromPcMiniMapLogic(); } /// @@ -97,14 +122,58 @@ namespace PerfectWorld.UI.MiniMap } /// - /// Get the Host Player instance. + /// PC DlgMiniMap.cpp Render: nTimeIndex, Format(604,...), int(v*24), FixFrame(nTimeItem). + /// + void UpdateSystemClockFromPcMiniMapLogic() + { + var sun = CECSunMoon.Instance; + if (sun == null) + return; + + var inGame = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan(); + + float fDNFactor = sun.GetDNFactor(); + float fDNFactorDest = sun.GetDNFactorDest(); + float v = sun.GetTimeOfTheDay(); + int nTimeIndex = (int)(12.0f * v + 0.5f) % 12; + + MinimapTimeSprite nTimeItem; + if (fDNFactor == 0.0f) + nTimeItem = MinimapTimeSprite.TIME_DAY; + else if (fDNFactor == 1.0f) + nTimeItem = MinimapTimeSprite.TIME_NIGHT; + else + nTimeItem = fDNFactorDest == 1.0f ? MinimapTimeSprite.TIME_DUSK : MinimapTimeSprite.TIME_MORNING; + + if (_imgSystemTime != null && _systemTimeSprites != null && + _systemTimeSprites.Length > (int)nTimeItem && _systemTimeSprites[(int)nTimeItem] != null) + _imgSystemTime.sprite = _systemTimeSprites[(int)nTimeItem]; + + string shichen = inGame != null ? GetStringFromTable(StrIdShichenBase + nTimeIndex) : null; + if (string.IsNullOrEmpty(shichen)) + shichen = s_FallbackShichenNames[nTimeIndex]; + + int hour = (int)(v * 24.0f); + string fmt = inGame != null ? GetStringFromTable(StrIdSystemTimeFormat) : null; + if (string.IsNullOrEmpty(fmt)) + fmt = "{0}({1}时)"; // 与常见 PC 客户端格式一致 // Typical PC client style: name (hour) + + string strText = FormatPrintf(fmt, shichen, hour); + if (_txtSystemTime != null) + _txtSystemTime.text = strText; + + BMLogger.Log($"[Cuong] {strText}"); + } + + /// + /// Get the Host Player instance. /// /// private CECHostPlayer GetHostPlayer() { return CECGameRun.Instance.GetHostPlayer(); } - + // change radar mode public int GetMode() { return m_nMode; } @@ -185,8 +254,8 @@ namespace PerfectWorld.UI.MiniMap { Vector2 hostPlayerPos = new Vector2(_debugHostPlayerPos.x / 2, _debugHostPlayerPos.z / 2); _transformMiniMapParent.anchoredPosition = -hostPlayerPos; - + } #endif } -} \ No newline at end of file +} diff --git a/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs b/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs index c7585ae3fa..62597e65ae 100644 --- a/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs +++ b/Assets/PerfectWorld/Scripts/World/CECSunMoon.cs @@ -7,12 +7,10 @@ namespace BrewMonster.Scripts { private const float TIME_SCALE = 6f; private const float NIGHT_DAY_START = (3.0f / 24.0f); - private const float NIGHT_SUN_RISE_MIN = (4.0f / 24.0f); private const float NIGHT_DAY_END = (7.0f / 24.0f); private const float DAY_NIGHT_START = (18.0f / 24.0f); - private const float DAY_SUN_SET_MAX = (19.5f / 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 float m_fDNFactor; // day or night factor public float m_fDNFactorDest; // day or night factor dest @@ -23,13 +21,69 @@ namespace BrewMonster.Scripts // keep this alive when scene is loaded DontDestroyOnLoad(gameObject); + RefreshDayNightFactorsFromPhase(); } public void Update() { m_vTimeOfTheDay += Time.deltaTime / 3600.0 / 24.0 * TIME_SCALE; - while( m_vTimeOfTheDay > 1.0 ) - m_vTimeOfTheDay -= 1.0; + while (m_vTimeOfTheDay > 1.0) + m_vTimeOfTheDay -= 1.0; + RefreshDayNightFactorsFromPhase(); + } + + public float GetTimeOfTheDay() => (float)m_vTimeOfTheDay; + public float GetDNFactor() => m_fDNFactor; + public float GetDNFactorDest() => m_fDNFactorDest; + + /// + /// 设置一天中的时刻(与 PC 版 CECSunMoon::SetTimeOfTheDay 对齐) // Set time-of-day phase [0,1), aligned with PC CECSunMoon::SetTimeOfTheDay + /// + public bool SetTimeOfTheDay(float vTime) + { + while (vTime < 0f) + vTime += 1f; + while (vTime > 1f) + vTime -= 1f; + m_vTimeOfTheDay = vTime; + RefreshDayNightFactorsFromPhase(); + return true; + } + + /// + /// PC EC_SunMoon::UpdateWithTime 中昼夜因子段(供 minimap 与场景一致) // DN factor block from PC EC_SunMoon::UpdateWithTime + /// + void RefreshDayNightFactorsFromPhase() + { + float v = (float)m_vTimeOfTheDay; + + // update day night factor — same branches as PC EC_SunMoon.cpp UpdateWithTime (lines 894–918) + if (v < NIGHT_DAY_START) + { + m_fDNFactor = 1.0f; + m_fDNFactorDest = 1.0f; + } + else if (v < NIGHT_DAY_END) + { + m_fDNFactor = 1.0f - (v - NIGHT_DAY_START) / (NIGHT_DAY_END - NIGHT_DAY_START); + m_fDNFactorDest = 0.0f; + } + else if (m_vTimeOfTheDay < DAY_NIGHT_START) + { + // PC 使用 m_vTimeOfTheDay 与 DAY_NIGHT_START 比较 // PC compares m_vTimeOfTheDay to DAY_NIGHT_START (see EC_SunMoon.cpp) + m_fDNFactor = 0.0f; + m_fDNFactorDest = 0.0f; + } + else if (v < DAY_NIGHT_END) + { + m_fDNFactor = (v - DAY_NIGHT_START) / (DAY_NIGHT_END - DAY_NIGHT_START); + m_fDNFactorDest = 1.0f; + } + else + { + m_fDNFactor = 1.0f; + m_fDNFactorDest = 1.0f; + } } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/EC_GameRun.Task.cs b/Assets/Scripts/EC_GameRun.Task.cs index 7e0f7a5f9a..f530249131 100644 --- a/Assets/Scripts/EC_GameRun.Task.cs +++ b/Assets/Scripts/EC_GameRun.Task.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BrewMonster; using BrewMonster.Network; using CSNetwork; using CSNetwork.GPDataType; @@ -7,7 +8,7 @@ using CSNetwork.GPDataType; partial class CECGameRun : IMsgHandler { private readonly Dictionary m_CommonDataTab = new Dictionary(); - + public int GetCommonData(int key) { // Lookup a common data value by key @@ -17,9 +18,9 @@ partial class CECGameRun : IMsgHandler else return 0; } - + private CECCounter l_QueryServerTime = new CECCounter(); // ��ѯ��������ǰʱ�� - + public int HandlerId => -1; public bool ProcessMessage(ECMSG Msg) { @@ -30,9 +31,9 @@ partial class CECGameRun : IMsgHandler EC_Game.SetServerTime((int)Msg.dwParam1, (int)Msg.dwParam2); l_QueryServerTime.Reset(); break; - + } - + return true; } -} \ No newline at end of file +}