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
+}