using BrewMonster; using BrewMonster.Managers; using BrewMonster.Network; using BrewMonster.Scripts; using BrewMonster.Scripts.Managers; using BrewMonster.Scripts.World; using BrewMonster.UI; using CSNetwork; using CSNetwork.GPDataType; using CSNetwork.Protocols.RPCData; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; using PerfectWorld.Scripts.Shop; using Unity.Cinemachine; using Unity.VisualScripting; using UnityEngine; using BrewMonster.Common; public partial class CECGameRun : ITickable { private static CECGameRun instance; #region PROPERTIES AND FIELDS private const uint USERCFG_VERSION = 3; private GameObject _playerPrefab; private GameObject _monsterPrefab;//CECMonster private GameObject _npcServerPrefab;//CECNPCServer private GameObject _petServerPrefab;//CECPet private GameObject _petMountServerPrefab;//CECPetRider private CECTeamMan m_pTeamMan; // Team manager private CECShortcutSet m_pNormalSCS; // Normal shortcut set private CECShortcutSet m_pTeamSCS; // Team shortcut set private CECShortcutSet m_pTradeSCS; // Trade shortcut set private CECShortcutSet m_pPoseSCS; // Pose shortcut set private CECShortcutSet m_pFactionSCS; // Faction shortcut set // private GameRunConfig _gameRunConfig; //[SerializeField] private Transform ground; CECHostPlayer m_pHostPlayer; private CECWorld m_pWorld; protected CECUIManager m_pUIManager; // UI manager CECPendingActionArray m_pendingLogout; #endregion public CECWorld GetWorld() { if (m_pWorld == null) { m_pWorld = new CECWorld(); } return m_pWorld; } public CECTeamMan GetTeamMan() { return m_pTeamMan; } // Get shortcut sets public CECShortcutSet GetGenCmdShortcuts() { return m_pNormalSCS; } public CECShortcutSet GetTeamCmdShortcuts() { return m_pTeamSCS; } public CECShortcutSet GetTradeCmdShortcuts() { return m_pTradeSCS; } public CECShortcutSet GetPoseCmdShortcuts() { return m_pPoseSCS; } public CECShortcutSet GetFactionCmdShortcuts() { return m_pFactionSCS; } private static Dictionary m_InstTab = new Dictionary(); static RoleInfo l_SelRoleInfo; // Selected character's role info. private int m_iDExpEndTime = 0; // Cache for SaveConfigsToServer: last sent config data to skip duplicate sends private byte[] m_pCfgDataBuf; private int m_iCfgDataSize; // Selling role ID for role trade (C++: SetSellingRoleID/GetSellingRoleID) private int m_iSellingRoleID; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void AfterSceneLoad() { l_SelRoleInfo = null; m_InstTab = new Dictionary(); } public void Init() { #if !UNITY_EDITOR Debug.unityLogger.logEnabled = false; QualitySettings.vSyncCount = 0; Application.targetFrameRate = 60; #endif instance = this; // _gameRunConfig = Resources.Load("GameRunConfig"); // _playerPrefab = _gameRunConfig.PlayerPrefab; // _monsterPrefab = _gameRunConfig.MonsterPrefab; // _npcServerPrefab = _gameRunConfig.NpcServerPrefab; LoadPrefabs(); m_pTeamMan = new CECTeamMan(); // LoadPrefabs(); EC_ManMessage.RegisterHandler(this); // Load instance information //if (!LoadInstanceInfo("Configs\\instance.txt")) //{ // //glb_ErrorOutput(ECERR_FAILEDTOCALL, "CECGameRun::Init", __LINE__); // //return false; //} var instanceOB = Resources.Load("instance"); for(int i = 0; i < instanceOB.items.Length; i++) { if (!m_InstTab.ContainsKey(instanceOB.items[i].m_id)) { var cecInstance = new CECInstance(); cecInstance.Load(instanceOB.items[i]); m_InstTab.Add(instanceOB.items[i].m_id, cecInstance); } } AddressableManager.Instance.OnDispose += Dispose; if (m_pWorld == null) m_pWorld = new CECWorld(); m_pendingLogout = new CECPendingActionArray( false); TickInvoker.Instance.RegisterTickable(this); } private static void Dispose() { TickInvoker.Instance.UnregisterTickable(instance); instance = null; } private void LoadPrefabs() { BMLogger.LogError("CECGameRun::LoadPrefabs, Loading prefabs from Resources. Consider using Addressables for better performance and memory management."); _playerPrefab = Resources.Load(AddressResourceConfig.PlayerPrefab); _monsterPrefab = Resources.Load(AddressResourceConfig.MonsterPrefab); _npcServerPrefab = Resources.Load(AddressResourceConfig.NpcServerPrefab); _petServerPrefab = Resources.Load(AddressResourceConfig.PetServerPrefab); _petMountServerPrefab = Resources.Load(AddressResourceConfig.PetMountServerPrefab); #if UNITY_EDITOR if (_playerPrefab == null) { BMLogger.LogError("CECGameRun::LoadPrefabs, Failed to load player prefab."); } if (_monsterPrefab == null) { BMLogger.LogError("CECGameRun::LoadPrefabs, Failed to load _monsterPrefab prefab."); } if (_npcServerPrefab == null) { BMLogger.LogError("CECGameRun::LoadPrefabs, Failed to load _npcServerPrefab prefab."); } #endif } public bool StartGame(int idInst, Vector3 vHostPos) { // TODO: Implement the rest of the StartGame logic based on the original C++ code, including: // End current game state EndGameState(); // // memset(&m_WallowInfo, 0, sizeof(m_WallowInfo)); m_iGameState = (int)GameState.GS_GAME; // // if (!g_pGame->LoadInGameRes()) // { // a_LogOutput(1, "CECGameRun::StartGame, Failed to call LoadInGameRes()."); // return false; // } // // // Create message manager // if (!(m_pMessageMan = new CECMessageMan(this))) // { // glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECGameRun::StartGame", __LINE__); // return false; // } // // Create default game world if (!JumpToInstance(idInst, vHostPos)) { // a_LogOutput(1, "CECGameRun::StartGame, Failed to create game world."); return false; } // // // ÉèÖÿç·þ³É¹¦±êʶ£¬ÒÔÀûÓÚ CECGameUIMan ¸ù¾Ý¿ç·þ״̬×öÏàÓ¦³õʼ»¯ // if (CECCrossServer::Instance().IsWaitLogin()){ // CECCrossServer::Instance().OnLoginSuccess(); // } // if (CECReconnect::Instance().IsReconnecting()){ // CECReconnect::Instance().OnReconnectSuccess(); // } // // // Create host player // if (!CreateHostPlayer()) // { // a_LogOutput(1, "CECGameRun::StartGame, Failed to create host player."); // return false; // } // // // Create team manager // if (!(m_pTeamMan = new CECTeamMan)) // { // glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECGameRun::StartGame", __LINE__); // return false; // } // // // Reset faction manager // g_pGame->GetFactionMan()->Release(false); // // Create shortcuts if (!CreateShortcuts()) { // a_LogOutput(1, "CECGameRun::StartGame, Failed to create shortcuts"); return false; } // // // Change UI manager // if (!m_pUIManager->ChangeCurUIManager(CECUIManager::UIMAN_INGAME)) // { // a_LogOutput(1, "CECGameRun::StartGame, Failed to change UI manager."); // return false; // } // m_pInputFilter->LoadHotKey(); // // CECGameUIMan* pGameUIMan = m_pUIManager->GetInGameUIMan(); // if (pGameUIMan) // pGameUIMan->ChangeWorldInstance(idInst); // // l_SaveCfgCnt.Reset(); // // // Change cursor to default icon // g_pGame->ChangeCursor(RES_CUR_NORMAL); // // Discard current frame // g_pGame->DiscardFrame(); // // // Clear frame controller // memset(&l_fc, 0, sizeof (l_fc)); // // // ³õʼ»¯ÍøÂçÑÓ³Ù²éѯ // l_bFirstQuery = true; // m_iInGameDelay = 0; // l_DelayQueryCounter.Reset(); // // l_QueryServerTime.Reset(); // // // clear the selling id // m_SellingRoleID = 0; // // // ÏÂÔØÆ÷ÏìÓ¦½øÈëÓÎϷ״̬ // if( g_pGame->GetConfigs()->IsMiniClient() ) // CECMCDownload::GetInstance().SendSwitchGame(true); // return true; } private bool JumpToInstance(int idInst, Vector3 vHostPos, int iParallelWorldID = 0) { return true; } public static CECGameRun Instance { get { if (instance == null) { instance = new CECGameRun(); } return instance; } } public CECGameRun() { m_iGameState = (int)GameState.GS_NONE; } public CECHostPlayer GetHostPlayer() { return m_pHostPlayer; } public void InitCharacter(cmd_self_info_1 info) { if (_playerPrefab == null) { BMLogger.LogError("null _playerPrefab"); return; } // 同步世界/地图ID给任务系统(GetPos 返回的 worldId) // English: Sync map ID to task system (worldId returned by GetPos). // We don't receive mapId in cmd_self_info_1, so use selected RoleInfo.worldtag as current map id. var roleInfo = UnityGameSession.Instance != null ? UnityGameSession.Instance.GetRoleInfo() : null; if (roleInfo != null) { int idInst = roleInfo.worldtag; if (idInst > 0) { // Ensure instance exists in table (minimal stub instance is fine for now) if (!m_InstTab.ContainsKey(idInst)) m_InstTab.Add(idInst, new CECInstance()); // Update global world instance id used by task checks CECGameRun.Instance?.GetWorld()?.SetInstanceID(idInst); } } CECPlayer.InitStaticRes(); m_pHostPlayer = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true).AddComponent(); m_pHostPlayer.InitCharacter(info); if (m_pHostPlayer != null) { var t = Type.GetType("BrewMonster.UI.SelectedTargetHUDController, Assembly-CSharp"); if (t != null && m_pHostPlayer.GetComponent(t) == null) m_pHostPlayer.gameObject.AddComponent(t); } } public CECMonster GetMonster() { return ObjectSpawner.Instance.InstantiateObject(_monsterPrefab, setThisAsParent: true) .GetComponent(); } public RoleInfo GetSelectedRoleInfo() { return l_SelRoleInfo; } public void SetSelectedRoleInfo(RoleInfo Info) { l_SelRoleInfo = Info; } public CECNPCServer GetNPCServer() { return ObjectSpawner.Instance.InstantiateObject(_npcServerPrefab, setThisAsParent: true).GetComponent(); } public async void ShowVfx(string assetAddress, Vector3 position, Transform parent, float timeLife) { var prefab = await AddressableManager.Instance.LoadPrefabAsync(assetAddress); if (prefab == null) { BMLogger.LogError("Failed to load prefab: " + assetAddress); return; } BMLogger.Log($"[GFX Spawn] ShowVfx - Asset: {assetAddress}, Prefab Name: {prefab.name}, Position: {position}, Parent: {(parent != null ? parent.name : "null")}, LifeTime: {timeLife}s"); if (parent == null) { var obj = ObjectSpawner.Instance.InstantiateObject(prefab, setThisAsParent: true); obj.transform.position = position; BMLogger.Log($"[GFX Spawn] ShowVfx - Instantiated object (no parent): {obj.name}"); DestroyTestVfx(obj, timeLife); } else { var obj = ObjectSpawner.Instance.InstantiateObject(prefab, parent, false); obj.transform.position = position; BMLogger.Log($"[GFX Spawn] ShowVfx - Instantiated object (with parent): {obj.name}"); DestroyTestVfx(obj, timeLife); } } public void DestroyTestVfx(GameObject go, float delayTime) { ObjectSpawner.Instance.DestroyGameObject(go, delayTime); } public GameObject InitCharacter(info_player_1 info) { if (_playerPrefab == null) { Debug.LogError("null prefab"); return null; } GameObject character = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true); return character.gameObject; } //private void Update() //{ // if (hostPlayer == null || hostPlayer.transform == null) // return; // bool rightClick = Input.GetMouseButton(1); // if (rightClick) // { // // Bật khả năng xoay khi giữ chuột phải // freeLookCam.m_XAxis.m_MaxSpeed = rotateSpeedX; // freeLookCam.m_YAxis.m_MaxSpeed = rotateSpeedY; // // Ghi giá trị chuột thủ công (nếu muốn override Input System cũ) // float mouseX = Input.GetAxis("Mouse X"); // float mouseY = Input.GetAxis("Mouse Y"); // // Xoay camera theo hướng chuột // freeLookCam.m_XAxis.Value += mouseX * Time.deltaTime * rotateSpeedX; // freeLookCam.m_YAxis.Value -= mouseY * Time.deltaTime * (rotateSpeedY / 100f); // } // else // { // // Khi thả chuột thì khoá xoay // freeLookCam.m_XAxis.m_MaxSpeed = 0; // freeLookCam.m_YAxis.m_MaxSpeed = 0; // } //} /// /// Load necessary user configs (UI, shortcut, accelerate keys) from server /// 从服务器加载必要的用户配置(UI、快捷键、加速键) /// /// Config data buffer / 配置数据缓冲区 /// Data size / 数据大小 /// True if loaded successfully / 加载成功返回true public bool LoadConfigsFromServer(byte[] pDataBuf, int iDataSize) { BMLogger.Log($"MH CECGameRun.LoadConfigsFromServer, iDataSize = {iDataSize}"); if (pDataBuf == null || iDataSize == 0) { BMLogger.LogError("CECGameRun::LoadConfigsFromServer, configs data is empty"); return false; } int offset = 0; // Read version / 读取版本号 uint dwVer = System.BitConverter.ToUInt32(pDataBuf, offset); offset += sizeof(uint); if (dwVer > USERCFG_VERSION) { Debug.LogError($"CECGameRun::LoadConfigsFromServer, version {dwVer} > {USERCFG_VERSION}"); return false; } byte[] pUncompBuf = null; uint dwRealLen = (uint)(iDataSize - sizeof(uint)); if (dwRealLen < 0) { dwRealLen = 0; } byte[] pData = pDataBuf; if (dwVer >= 3) { // Uncompress config data / 解压配置数据 dwRealLen = 4096; pUncompBuf = new byte[dwRealLen]; // Extract compressed data / 提取压缩数据 byte[] compressedData = new byte[iDataSize - sizeof(uint)]; System.Array.Copy(pDataBuf, offset, compressedData, 0, compressedData.Length); // TODO: Implement AFilePackage::Uncompress equivalent int iRes = AFilePackage.Uncompress(compressedData, compressedData.Length, pUncompBuf, ref dwRealLen); if (iRes != 0) { // 解压失败,过程出错 / Decompression failed, process error BMLogger.LogError($"CECGameRun::LoadConfigsFromServer, Failed to uncompress configs data ({iRes}:{iDataSize})"); return false; } pData = pUncompBuf; } try { // Create data reader / 创建数据读取器 CECDataReader dr = new CECDataReader(pData, (int)dwRealLen); // Load host configs / 加载主机配置 CECHostPlayer pHost = GetHostPlayer(); if (pHost != null) { int iSize = dr.ReadInt(); byte[] hostConfigData = dr.ReadData(iSize); if (!pHost.LoadConfigData(hostConfigData)) { BMLogger.LogError("CECGameRun::LoadConfigsFromServer, Failed to load host configs"); return false; } } //TODO: flow in update fix later /* Task.Run(() => { GameSession.Context.Post(_ => { if (m_pUIManager == null) { m_pUIManager = CECUIManager.Instance; } m_pUIManager.GetCDlgQuickBar().UpdateShortcuts(); }, null); });*/ // TODO: Uncomment when UI manager is available // Load UI configs / 加载UI配置 CECGameUIMan pGameUI = m_pUIManager.GetInGameUIMan(); if (pGameUI != null) { int iSize = dr.ReadInt(); byte[] uiConfigData = dr.ReadData(iSize); /*if (!pGameUI.SetUserLayout(uiConfigData, iSize)) { BMLogger.LogError("CECGameRun::LoadConfigsFromServer, Failed to set user layout"); return false; }*/ } // Load user settings / 加载用户设置 if (dwVer >= 2) { // TODO: Uncomment when game configs are available int iSize = dr.ReadInt(); byte[] settingsData = dr.ReadData(iSize); if (!EC_Game.GetConfigs().LoadUserConfigData(settingsData, iSize)) { BMLogger.LogError("CECGameRun::LoadConfigsFromServer, Failed to load user config data"); return false; } } } catch (System.Exception e) { BMLogger.LogError($"CECGameRun::LoadConfigsFromServer, data read error: {e.Message}"); return false; } return true; } public int GetCurStageIndex() { // CECGameRun.unique_data* data = GetUniqueData(0); // if (data) // { // if(data->type ==1) // { // return data->GetValueAsInt(); // } // } return -1; } // Create shortcuts public bool CreateShortcuts() { // // Normal command shortcut set m_pNormalSCS = new CECShortcutSet(); m_pNormalSCS.Init(8); CECSCCommand pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_SITDOWN); m_pNormalSCS.SetShortcut(0, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_WALKRUN); m_pNormalSCS.SetShortcut(1, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_NORMALATTACK); m_pNormalSCS.SetShortcut(2, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_FINDTARGET); m_pNormalSCS.SetShortcut(3, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_ASSISTATTACK); m_pNormalSCS.SetShortcut(3, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_FLY); m_pNormalSCS.SetShortcut(4, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_PICKUP); m_pNormalSCS.SetShortcut(5, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_GATHER); m_pNormalSCS.SetShortcut(6, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_RUSHFLY); m_pNormalSCS.SetShortcut(6, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_BINDBUDDY); m_pNormalSCS.SetShortcut(7, pSC); // Team command shortcut set m_pTeamSCS = new CECShortcutSet(); m_pTeamSCS.Init(2); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_INVITETOTEAM); m_pTeamSCS.SetShortcut(0, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_LEAVETEAM); m_pTeamSCS.SetShortcut(1, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_KICKTEAMMEM); m_pTeamSCS.SetShortcut(2, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_FINDTEAM); m_pTeamSCS.SetShortcut(2, pSC); // Trade command shortcut set m_pTradeSCS = new CECShortcutSet(); m_pTradeSCS.Init(2); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_STARTTRADE); m_pTradeSCS.SetShortcut(0, pSC); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_SELLBOOTH); m_pTradeSCS.SetShortcut(1, pSC); // Pose command shortcut set m_pPoseSCS = new CECShortcutSet(); m_pPoseSCS.Init((int)RoleExpression.NUM_ROLEEXP); for (int i = 0; i < (int)RoleExpression.NUM_ROLEEXP; i++) { pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_PLAYPOSE); pSC.SetParam((uint)i); m_pPoseSCS.SetShortcut(i, pSC); } // Faction command shortcut set m_pFactionSCS = new CECShortcutSet(); m_pFactionSCS.Init(1); pSC = new CECSCCommand((int)CECSCCommand.CommandID.CMD_INVITETOFACTION); m_pFactionSCS.SetShortcut(0, pSC); return true; } // Get UI manager public CECUIManager GetUIManager() { if (m_pUIManager == null) { m_pUIManager = CECUIManager.Instance; } return m_pUIManager; } // Get instance by ID public CECInstance GetInstance(int id) { if (m_InstTab.TryGetValue(id, out CECInstance value)) { return value; } return null; } public string GetProfName(int i) { string szRet = null; if (i >= 0 && i < (int)Profession.NUM_PROFESSION) { int[] s_ProfDesc = { (int)FixedMsg.FIXMSG_PROF_WARRIOR, (int)FixedMsg.FIXMSG_PROF_MAGE, (int)FixedMsg.FIXMSG_PROF_MONK, (int)FixedMsg.FIXMSG_PROF_HAG, (int)FixedMsg.FIXMSG_PROF_ORC, (int)FixedMsg.FIXMSG_PROF_GHOST, (int)FixedMsg.FIXMSG_PROF_ARCHOR, (int)FixedMsg.FIXMSG_PROF_ANGEL, (int)FixedMsg.FIXMSG_PROF_JIANLING, (int)FixedMsg.FIXMSG_PROF_MEILING, (int)FixedMsg.FIXMSG_PROF_YEYING, (int)FixedMsg.FIXMSG_PROF_YUEXIAN, }; CECStringTab pStrTab = EC_Game.GetFixedMsgs(); szRet = pStrTab.GetWideString(s_ProfDesc[i]); } else { //BMLogger.LogError("CECGameRun::GetProfName, i: {0} is not exist", i); return null; } return szRet; } public CECPet GetPet() { return ObjectSpawner.Instance.InstantiateObject(_petServerPrefab, setThisAsParent: true) .GetComponent(); } public GameObject GetPetMount() { return ObjectSpawner.Instance.InstantiateObject(_petMountServerPrefab, setThisAsParent: true); } /// /// Add a fixed message to chat system with optional formatting parameters /// 添加固定消息到聊天系统,支持可选的格式化参数 /// /// Fixed message ID / 固定消息ID /// Optional formatting arguments / 可选的格式化参数 public void AddFixedMessage(int iMsg, params object[] args) { CECStringTab pStrTab = EC_Game.GetFixedMsgs(); if (pStrTab == null) { Debug.LogWarning("[AddFixedMessage] Failed to get fixed message table"); return; } string szFixMsg = pStrTab.GetWideString(iMsg); if (string.IsNullOrEmpty(szFixMsg)) { Debug.LogWarning($"[AddFixedMessage] Fixed message {iMsg} not found"); return; } // Format the message with provided arguments string szFormattedMsg; try { if (args != null && args.Length > 0) { szFormattedMsg = string.Format(szFixMsg, args); } else { szFormattedMsg = szFixMsg; } } catch (System.FormatException ex) { Debug.LogError($"[AddFixedMessage] Format error for message {iMsg}: {ex.Message}"); szFormattedMsg = szFixMsg; // Use unformatted message as fallback } // Try to add to in-game UI chat CECGameUIMan pGameUI = m_pUIManager?.GetInGameUIMan(); if (pGameUI != null) { //pGameUI.AddChatMessage(szFormattedMsg, (int)GP_CHAT.GP_CHAT_SYSTEM); } else { // Fallback to debug output if UI is not available Debug.Log($"[System] {szFormattedMsg}"); } } /// /// Add a fixed message to specific chat channel with optional formatting /// 添加固定消息到指定聊天频道,支持可选的格式化 /// /// Fixed message ID / 固定消息ID /// Chat channel ID / 聊天频道ID /// Optional formatting arguments / 可选的格式化参数 public void AddFixedChannelMsg(int iMsg, int cChannel, params object[] args) { CECStringTab pStrTab = EC_Game.GetFixedMsgs(); if (pStrTab == null) { Debug.LogWarning("[AddFixedChannelMsg] Failed to get fixed message table"); return; } string szFixMsg = pStrTab.GetWideString(iMsg); if (string.IsNullOrEmpty(szFixMsg)) { Debug.LogWarning($"[AddFixedChannelMsg] Fixed message {iMsg} not found"); return; } // Format the message with provided arguments string szFormattedMsg; try { if (args != null && args.Length > 0) { szFormattedMsg = string.Format(szFixMsg, args); } else { szFormattedMsg = szFixMsg; } } catch (System.FormatException ex) { Debug.LogError($"[AddFixedChannelMsg] Format error for message {iMsg}: {ex.Message}"); szFormattedMsg = szFixMsg; // Use unformatted message as fallback } // Try to add to in-game UI chat with specific channel CECGameUIMan pGameUI = m_pUIManager?.GetInGameUIMan(); if (pGameUI != null) { //pGameUI.AddChatMessage(szFormattedMsg, cChannel); } else { // Fallback to debug output if UI is not available Debug.Log($"[Channel {cChannel}] {szFormattedMsg}"); } } /// /// Add a chat message with full parameters (matching C++ signature) /// 添加聊天消息(完整参数版本,匹配C++签名) /// /// Message text / 消息文本 /// Chat channel / 聊天频道 /// Player ID (default -1) / 玩家ID /// Player name (optional) / 玩家名称 /// Message flag (default 0) / 消息标志 /// Emotion ID (default 0) / 表情ID /// Related item (optional) / 相关物品 /// Original message (optional) / 原始消息 public void AddChatMessage(string pszMsg, int cChannel, int idPlayer = -1, string szName = null, byte byFlag = 0, int cEmotion = 0, EC_IvtrItem pItem = null, string pszMsgOrigion = null) { if (string.IsNullOrEmpty(pszMsg)) return; CECGameUIMan pGameUI = m_pUIManager?.GetInGameUIMan(); if (pGameUI != null) { // Call UI manager's AddChatMessage with full parameters //pGameUI.AddChatMessage(pszMsg, cChannel, idPlayer, szName, byFlag, cEmotion, pItem, pszMsgOrigion); } else { // Fallback to debug output if UI is not available Debug.Log($"[Channel {cChannel}] {pszMsg}"); // Note: In C++ original, pItem is deleted here if UI is not available // In C#, we don't need explicit deletion due to garbage collection } } /// /// Get remaining double experience time in seconds /// 获取剩余双倍经验时间(秒) /// public int GetRemainDblExpTime() { int iRemainTime = m_iDExpEndTime - GetServerAbsTime(); if (iRemainTime < 0) iRemainTime = 0; return iRemainTime; } /// /// Get server absolute time /// 获取服务器绝对时间 /// public int GetServerAbsTime() { // TODO: Implement server time synchronization // This should return the synchronized server timestamp return (int)(System.DateTime.UtcNow.Subtract(new System.DateTime(1970, 1, 1))).TotalSeconds; } /// /// Set double experience end time /// 设置双倍经验结束时间 /// public void SetDExpEndTime(int endTime) { m_iDExpEndTime = endTime; } public int GetGameState() { return m_iGameState; } // Logout flag: -1 none, 0 exit app, 1 re-select role, 2 goto login (C++: SetLogoutFlag/GetLogoutFlag) public void SetLogoutFlag(int iFlag) { m_iLogoutFlag = iFlag; } public int GetLogoutFlag() { return m_iLogoutFlag; } // Selling role ID for role trade (C++: SetSellingRoleID) public void SetSellingRoleID(int roleid) { m_iSellingRoleID = roleid; } public int GetSellingRoleID() { return m_iSellingRoleID; } // Save necessary user configs (UI, shortcut, accelerate keys, etc.) to server // ����ֵ: 0(����ʧ��), 1(���������ظ�����), 2(�������������Э��) public uint SaveConfigsToServer() { // if (!m_pWorld || !m_pWorld->GetHostPlayer() || !m_pWorld->GetHostPlayer()->HostIsReady() || !m_pUIManager->GetInGameUIMan()) // return 0; CECHostPlayer pHost = GetHostPlayer(); CECGameUIMan pGameUI = m_pUIManager?.GetInGameUIMan() as CECGameUIMan; if (m_pWorld == null || pHost == null || !pHost.HostIsReady() || pGameUI == null) return 0; // g_pGame->GetConfigs()->SaveBlockedID(); EC_Game.GetConfigs().SaveBlockedID(); // TODO: Check if this is needed here // int iTotalSize = 0; // iTotalSize += sizeof (DWORD); // iTotalSize += sizeof (int); // int iHostSize = 0; // CECHostPlayer* pHost = m_pWorld->GetHostPlayer(); // pHost->SaveConfigData(NULL, &iHostSize); // iTotalSize += iHostSize; // iTotalSize += sizeof (int); int iTotalSize = sizeof(uint); iTotalSize += sizeof(int); int iHostSize = 0; pHost.SaveConfigData(null, ref iHostSize); iTotalSize += sizeof(int) + iHostSize; // DWORD dwUISize = 0; // CECGameUIMan* pGameUI = (CECGameUIMan*)m_pUIManager->GetInGameUIMan(); // pGameUI->GetUserLayout(NULL, dwUISize); // iTotalSize += (int)dwUISize; // iTotalSize += sizeof (int); uint dwUISize = 0; pGameUI.GetUserLayout(null, ref dwUISize); // TODO: Check if this is needed here iTotalSize += sizeof(int) + (int)dwUISize; // int iSettingSize = 0; // g_pGame->GetConfigs()->SaveUserConfigData(NULL, &iSettingSize); // iTotalSize += iSettingSize; int iSettingSize = 0; EC_Game.GetConfigs().SaveUserConfigData(null, out iSettingSize); // iTotalSize += sizeof(int) + iSettingSize; iTotalSize += iSettingSize; // void* pDataBuf = a_malloctemp(iTotalSize); // if (!pDataBuf) { glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECGameRun::SaveConfigsToServer", __LINE__); return 0; } // BYTE* pData = (BYTE*)pDataBuf; byte[] pDataBuf = new byte[iTotalSize]; int offset = 0; // *((DWORD*)pData) = USERCFG_VERSION; // pData += sizeof (DWORD); Buffer.BlockCopy(BitConverter.GetBytes(USERCFG_VERSION), 0, pDataBuf, offset, sizeof(uint)); offset += sizeof(uint); BMLogger.Log($"[SaveConfigsToServer] offset={offset} (after version)"); // *((int*)pData) = iHostSize; // pData += sizeof (int); // pHost->SaveConfigData(pData, &iHostSize); // pData += iHostSize; Buffer.BlockCopy(BitConverter.GetBytes(iHostSize), 0, pDataBuf, offset, sizeof(int)); offset += sizeof(int); pHost.SaveConfigData(pDataBuf, ref iHostSize, offset); // TODO: converted but need to check offset += iHostSize; BMLogger.Log($"[SaveConfigsToServer] offset={offset} (after host config, iHostSize={iHostSize})"); // *((int*)pData) = (int)dwUISize; // pData += sizeof (int); // pGameUI->GetUserLayout(pData, dwUISize); // pData += dwUISize; Buffer.BlockCopy(BitConverter.GetBytes((int)dwUISize), 0, pDataBuf, offset, sizeof(int)); offset += sizeof(int); pGameUI.GetUserLayout(pDataBuf, ref dwUISize); // TODO: Check if this is needed here offset += (int)dwUISize; BMLogger.Log($"[SaveConfigsToServer] offset={offset} (after UI layout, dwUISize={dwUISize})"); // *((int*)pData) = iSettingSize; // pData += sizeof (int); // g_pGame->GetConfigs()->SaveUserConfigData(pData, &iSettingSize); // pData += iSettingSize; Buffer.BlockCopy(BitConverter.GetBytes(iSettingSize), 0, pDataBuf, offset, sizeof(int)); offset += sizeof(int); EC_Game.GetConfigs().SaveUserConfigData(pDataBuf, offset, out iSettingSize); offset += iSettingSize; BMLogger.Log($"[SaveConfigsToServer] offset={offset} (after user config, iSettingSize={iSettingSize}), iTotalSize={iTotalSize}"); // if (m_pCfgDataBuf) { // if (m_iCfgDataSize == iTotalSize && !memcmp(m_pCfgDataBuf, pDataBuf, iTotalSize)) { a_freetemp(pDataBuf); return 1; } // a_free(m_pCfgDataBuf); m_pCfgDataBuf = NULL; m_iCfgDataSize = 0; // } if (m_pCfgDataBuf != null && m_iCfgDataSize == iTotalSize) { bool same = true; for (int i = 0; i < iTotalSize && same; i++) same = (m_pCfgDataBuf[i] == pDataBuf[i]); if (same) return 1; } m_pCfgDataBuf = null; m_iCfgDataSize = 0; // if ((m_pCfgDataBuf = (BYTE*)a_malloc(iTotalSize))) { memcpy(m_pCfgDataBuf, pDataBuf, iTotalSize); m_iCfgDataSize = iTotalSize; } m_pCfgDataBuf = new byte[iTotalSize]; Buffer.BlockCopy(pDataBuf, 0, m_pCfgDataBuf, 0, iTotalSize); m_iCfgDataSize = iTotalSize; // DWORD dwCompLen = iTotalSize * 2; // void* pCompBuf = a_malloctemp(dwCompLen); // if (!pCompBuf) { a_freetemp(pDataBuf); glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECGameRun::SaveConfigsToServer", __LINE__); return 0; } // int iVerLen = sizeof (DWORD); // memcpy(pCompBuf, pDataBuf, iVerLen); // BYTE* pSrc = (BYTE*)pDataBuf + iVerLen; // BYTE* pDst = (BYTE*)pCompBuf + iVerLen; // dwCompLen -= iVerLen; // int iRes = AFilePackage::Compress(pSrc, iTotalSize - iVerLen, pDst, &dwCompLen); int iVerLen = sizeof(uint); // int compLen = (iTotalSize - iVerLen) * 2; int compLen = iTotalSize * 2; byte[] pCompBuf = new byte[iVerLen + compLen]; compLen -= iVerLen; Buffer.BlockCopy(pDataBuf, 0, pCompBuf, 0, iVerLen); int iRes = AFilePackage.Compress(pDataBuf, iVerLen,iTotalSize - iVerLen, pCompBuf, iVerLen, ref compLen); // if (0 == iRes) { g_pGame->GetGameSession()->SaveConfigData(pCompBuf, dwCompLen+iVerLen); iRes = 2; } // else { a_LogOutput(1, "CECGameRun::SaveConfigsToServer, Failed to compress config data (%d:%d)", iRes, iTotalSize); iRes = 0; } // a_freetemp(pDataBuf); // a_freetemp(pCompBuf); // return iRes; if (iRes == 0) { var session = UnityGameSession.Instance?.GameSession; if (session != null) { session.SaveConfigData(pCompBuf, iVerLen + compLen); return 2; } } else BMLogger.LogWarning($"CECGameRun::SaveConfigsToServer, Failed to compress config data ({iRes}:{iTotalSize})"); return 0; } public void ReleasePendingActions() { m_pendingLogout.Clear(); } public CECPendingActionArray GetPendingLogOut(){ return m_pendingLogout; } // Game tick routine public bool Tick(uint dwDeltaTime) { // if (GetWallowInfo().anti_wallow_active && // g_pGame->GetGameSession()->IsConnected() && // GetGameState() == GS_GAME) // { // if (CECUIConfig::Instance().GetGameUI().nWallowHintType != CECUIConfig::GameUI::WHT_KOREA) // { // // ·À³ÁÃÔµ½3Сʱ£¬ÈôAU²»ÌßÈË£¨ÏûÏ¢¶ªÊ§£©£¬Ôò×Ô¶¯ÏÂÏß // int stime = g_pGame->GetServerGMTTime(); // int nTime = stime - GetWallowInfo().play_time; // if (nTime >= 3 * 3600) // { // // ÒѾ­³¬¹ý3Сʱ // g_pGame->GetGameSession()->SetBreakLinkFlag(CECGameSession::LBR_ANTI_WALLOW); // } // } // } // // DWORD dwTickTime = a_GetTime(); // // CECGameSession* pSession = g_pGame->GetGameSession(); // pSession->ProcessNewProtocols(); // DWORD dwRealTime = g_pGame->GetRealTickTime(); if (m_iLogoutFlag >= 0) { Logout(); m_iLogoutFlag = -1; } // CECReconnect::Instance().Tick(); // // if (m_pUIManager) // m_bUIHasCursor = m_pUIManager->UIControlCursor(); // else // m_bUIHasCursor = false; // // // Deal input first // if (m_pInputCtrl) // m_pInputCtrl->Tick(); // // // Tick world // if (!TickGameWorld(dwDeltaTime, g_pGame->GetViewport())) // return false; // Tick UI // if (m_pUIManager) // m_pUIManager.Tick(); // // Tick GFX caster // g_pGame->GetGFXCaster()->Tick(dwDeltaTime); // // // Tick GFX Manager // g_pGame->GetA3DGFXExMan()->Tick(dwDeltaTime); // // // A3DEngine::TickAnimation trigger animation of many objects. // // For example: A3DSky objects, GFX objects etc. // static DWORD dwAnimTime = 0; // dwAnimTime += dwDeltaTime; // while (dwAnimTime >= TIME_TICKANIMATION) // { // dwAnimTime -= TIME_TICKANIMATION; // g_pGame->GetA3DEngine()->TickAnimation(); // } // // // Update ear position so that all 3D sounds' positions are correct // static DWORD dwEarTime = 0; // if ((dwEarTime += dwDeltaTime) >= TIME_UPDATEEAR) // { // dwEarTime -= TIME_UPDATEEAR; // // CECHostPlayer* pHostPlayer = NULL; // if (m_pWorld) // pHostPlayer = m_pWorld->GetHostPlayer(); // // A3DCamera * pCamera = g_pGame->GetViewport()->GetA3DCamera(); // // if (GetGameState() == CECGameRun::GS_GAME && pHostPlayer && pHostPlayer->HostIsReady()) // { // AM3DSoundDevice * pAM3DSoundDevice = g_pGame->GetA3DEngine()->GetAMSoundEngine()->GetAM3DSoundDevice(); // A3DVECTOR3 vecDir = pCamera->GetDirH(); // A3DVECTOR3 vecUp = A3DVECTOR3(0.0f, 1.0f, 0.0f); // // // Now we should adjust the 3d sound device's pos and orientation; // if (pAM3DSoundDevice) // { // pAM3DSoundDevice->SetPosition(pHostPlayer->GetPos() + A3DVECTOR3(0.0f, 0.8f, 0.0f)); // pAM3DSoundDevice->SetOrientation(vecDir, vecUp); // pAM3DSoundDevice->UpdateChanges(); // } // } // else // g_pGame->GetViewport()->GetA3DCamera()->UpdateEar(); // } // // // Tick Run-Time debug information // g_pGame->GetRTDebug()->Tick(dwDeltaTime); // // // Save UI configs when time reached // if (m_iGameState == GS_GAME && l_SaveCfgCnt.IncCounter(dwRealTime)) // { // l_SaveCfgCnt.Reset(); // SaveConfigsToServer(); // } // // l_StatCnt.IncCounter(dwDeltaTime); // // pSession->ClearOldProtocols(); // // DWORD dwCurrentTick = a_GetTime(); // dwTickTime = (dwCurrentTick > dwTickTime) ? (dwCurrentTick - dwTickTime) : 0; // l_Statistic.iTickTime = (int)dwTickTime; // // if (GetGameState() == CECGameRun::GS_GAME && l_fc.iAvgRdTime) // { // // Accumulate tick time // l_fc.iTickCnt++; // l_fc.iTickTime += (int)dwTickTime; // } // // if (GetGameState() == GS_GAME && GetHostPlayer() && GetHostPlayer()->HostIsReady()) // { // if (l_bFirstQuery || l_DelayQueryCounter.IncCounter(dwDeltaTime)) // { // // µÚÒ»´Î²éѯ£¬»òÕß²éѯ¼ÆÊýÆ÷µ½µã // // // ·¢ËÍÍøÂçÓµ¼·²éѯЭÒé // l_iDelayTimeStamp = timeGetTime(); // g_pGame->GetGameSession()->c2s_CmdQueryNetworkDelay(l_iDelayTimeStamp); // l_DelayQueryCounter.Reset(); // l_bFirstQuery = false; // } // // if (l_QueryServerTime.IncCounter(dwDeltaTime)) // { // g_pGame->GetGameSession()->c2s_CmdSendGetServerTime(); // l_QueryServerTime.Reset(); // } // } // // m_pendingLogout.Update(dwDeltaTime); // // // ÓÅ»¯ÄÚ´æµÄÕ¼Óà // m_pMemSimplify->Tick(dwDeltaTime); // // // ¸üÐÂÏÂÔØ×´Ì¬ // CECMCDownload::GetInstance().Tick(dwDeltaTime); // // // ¸üÐÂ×Ô¶¯²ßÂÔ // CECAutoPolicy::GetInstance().Tick(dwDeltaTime); // // if(m_pRandomMapProc) // m_pRandomMapProc->Tick(dwDeltaTime); // // #ifdef _PROFILE_MEMORY // g_TickMemoryHistory(); // #endif return true; } // Load instance information from file bool LoadInstanceInfo(string szFile) { // AWScriptFile sf = new AWScriptFile(); //if (!sf.Open(szFile)) //{ // //a_LogOutput(1, "CECGameRun::LoadInstanceInfo, Failed to load %s", szFile); // return false; //} // CECInstance pInst; // while (sf.PeekNextToken(true)) // { // pInst = new CECInstance(); // if (pInst == null || !pInst.Load(sf)) // { // //a_LogOutput(1, "CECGameRun::LoadInstanceInfo, Failed to read %s near line:%d", szFile, sf.GetCurLine()); // return false; // } // if (!m_InstTab.ContainsKey(pInst.GetID())) // { // // ID collsion ? // m_InstTab.Add(pInst.GetID(), pInst); // } // } // sf.Close(); return true; } } public enum GameState { GS_NONE = 0, // None GS_LOGIN, // Login in state GS_GAME, // In game };