Add logic update time and show time in minimap

This commit is contained in:
CuongNV
2026-05-13 10:15:29 +07:00
parent 6cebb9079d
commit 258679dd7c
7 changed files with 194 additions and 27 deletions
@@ -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; }
/// <summary>
/// 与 PC CECGameRun::CreateWorld / CECWorld::InitNatureObjects 末尾一致:按当前服务器钟面刷新昼夜相位 // Match PC after world load / nature init
/// </summary>
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}");
}
/// <summary>
/// 将昼夜相位写入 SunMoon(与 PC GetWorld()->GetSunMoon()->SetTimeOfTheDay 用法一致) // Push day/night phase to SunMoon (same role as PC)
/// </summary>
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()
@@ -92,4 +92,4 @@ namespace CSNetwork
_instance = null;
}
}
}
}
@@ -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<cmd_server_time>(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:
@@ -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)
@@ -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,
}
/// <summary>十二时辰名(表缺失时回退,与 PC 1330+i 顺序一致:子丑寅…) // Fallback 12 double-hours if string table missing (same order as PC 1330+i)</summary>
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();
}
/// <summary>
@@ -97,14 +122,58 @@ namespace PerfectWorld.UI.MiniMap
}
/// <summary>
/// Get the Host Player instance.
/// PC DlgMiniMap.cpp Render: nTimeIndex, Format(604,...), int(v*24), FixFrame(nTimeItem).
/// </summary>
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}");
}
/// <summary>
/// Get the Host Player instance.
/// </summary>
/// <returns></returns>
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
}
}
}
@@ -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;
/// <summary>
/// 设置一天中的时刻(与 PC 版 CECSunMoon::SetTimeOfTheDay 对齐) // Set time-of-day phase [0,1), aligned with PC CECSunMoon::SetTimeOfTheDay
/// </summary>
public bool SetTimeOfTheDay(float vTime)
{
while (vTime < 0f)
vTime += 1f;
while (vTime > 1f)
vTime -= 1f;
m_vTimeOfTheDay = vTime;
RefreshDayNightFactorsFromPhase();
return true;
}
/// <summary>
/// PC EC_SunMoon::UpdateWithTime 中昼夜因子段(供 minimap 与场景一致) // DN factor block from PC EC_SunMoon::UpdateWithTime
/// </summary>
void RefreshDayNightFactorsFromPhase()
{
float v = (float)m_vTimeOfTheDay;
// update day night factor — same branches as PC EC_SunMoon.cpp UpdateWithTime (lines 894918)
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;
}
}
}
}
}
+7 -6
View File
@@ -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<int, int> m_CommonDataTab = new Dictionary<int, int>();
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;
}
}
}