diff --git a/Assets/PerfectWorld/Scripts/Common/EC_PendingAction.cs b/Assets/PerfectWorld/Scripts/Common/EC_PendingAction.cs index aedf88b999..fd1291ab28 100644 --- a/Assets/PerfectWorld/Scripts/Common/EC_PendingAction.cs +++ b/Assets/PerfectWorld/Scripts/Common/EC_PendingAction.cs @@ -109,7 +109,9 @@ public class CECPendingLogoutHalf : CECPendingAction bool bSuccess = false; if (IsInGame()) { - GetGameSession().SendPlayerLogout(PendingActionConstants._PLAYER_LOGOUT_HALF); + // TODO: currently, we logout logic != C++, need to implement properly + GetGameSession().c2s_SendCmdLogout(PendingActionConstants._PLAYER_LOGOUT_HALF); + // UnityGameSession.ReturnToSelectRole(); bSuccess = true; } return bSuccess; @@ -128,7 +130,10 @@ public class CECPendingLogoutFull : CECPendingAction bool bSuccess = false; if (IsInGame()) { - GetGameSession().SendPlayerLogout(PendingActionConstants._PLAYER_LOGOUT_FULL); + // TODO: currently, we logout logic != C++, need to implement properly + // C++ code: GetGameSession()->SendPlayerLogout(PendingActionConstants::_PLAYER_LOGOUT_FULL); + GetGameSession().c2s_SendCmdLogout(PendingActionConstants._PLAYER_LOGOUT_FULL); + // UnityGameSession.LogoutAccount(); bSuccess = true; } return bSuccess; @@ -147,8 +152,9 @@ public class CECPendingSellingRole : CECPendingAction bool bSuccess = false; if (IsInGame()) { - GetGameSession().SendPlayerLogout(PendingActionConstants._PLAYER_LOGOUT_HALF); - GetGameRun().SetSellingRoleID(GetHostPlayer().GetCharacterID()); + // GetGameSession().SendPlayerLogout(PendingActionConstants._PLAYER_LOGOUT_HALF); + // GetGameRun().SetSellingRoleID(GetHostPlayer().GetCharacterID()); + UnityGameSession.ReturnToSelectRole(); bSuccess = true; } return bSuccess; diff --git a/Assets/PerfectWorld/Scripts/Common/TickInvoker.cs b/Assets/PerfectWorld/Scripts/Common/TickInvoker.cs new file mode 100644 index 0000000000..19ddeaf1b1 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Common/TickInvoker.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace BrewMonster.Scripts +{ + public interface ITickable + { + bool Tick(uint dwDeltaTime); + } + public class TickInvoker : MonoSingleton + { + List tickables = new List(); + + protected override void Initialize() + { + DontDestroyOnLoad(gameObject); + } + + public void RegisterTickable(ITickable tickable) + { + if (!tickables.Contains(tickable)) + tickables.Add(tickable); + } + + public void UnregisterTickable(ITickable tickable) + { + tickables.Remove(tickable); + } + + void Update() + { + for (int i=0; i + { + public bool WasClientSendLogoutMessage; + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Debug/TestLogoutLogic.cs.meta b/Assets/PerfectWorld/Scripts/Debug/TestLogoutLogic.cs.meta new file mode 100644 index 0000000000..9b12ffbf43 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Debug/TestLogoutLogic.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df29527a68a5442e814e73146a4ad68a +timeCreated: 1772608601 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs index 948d9bb4d0..20665baf85 100644 --- a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs +++ b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs @@ -841,7 +841,7 @@ public class CECNPCMan : IMsgHandler } } // Set born stamp & born-in-sight (giữ nguyên semantics) - uint bornStamp = CECWorld.Instance.GetBornStamp(); + uint bornStamp = CECGameRun.Instance.GetWorld().GetBornStamp(); if (!object.ReferenceEquals(pNPC, null)) { @@ -908,6 +908,51 @@ public class CECNPCMan : IMsgHandler aCands.Add(pNPC); } } + + // Release manager + public void Release() + { + OnLeaveGameWorld(); + } + + // On leaving game world + bool OnLeaveGameWorld() + { + // Release all NPCs in active table + // NPCTable::iterator it = m_NPCTab.begin(); + // for (; it != m_NPCTab.end(); ++it) + foreach (var pNPC in m_NPCTab.Values) + { + ReleaseNPC(pNPC); + } + + m_NPCTab.Clear(); + + // Release all NPCs in disappear table + int i; + foreach (var pNPC in m_aDisappearNPCs) + { + ReleaseNPC(pNPC); + } + + m_aDisappearNPCs.Clear(); + + // Release all loaded models + // ACSWrapper csa(&m_csLoad); + // + // for (i=0; i < m_aLoadedModels.GetSize(); i++) + // { + // NPCMODEL* pInfo = m_aLoadedModels[i]; + // CECNPC::ReleaseNPCModel(pInfo->Ret); + // delete pInfo; + // } + // + // m_aLoadedModels.RemoveAll(); + // m_aMMNPCs.RemoveAll(false); + // m_aTabSels.RemoveAll(false); + + return true; + } } public struct NPCDiedEvent { diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs index 6cd46fd755..1275ab4198 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs @@ -245,7 +245,7 @@ namespace PerfectWorld.Scripts.Managers Debug.LogError($"Failed to create matter: {info.mid}"); return false; } - pMatter.SetBornStamp(CECWorld.Instance.GetBornStamp()); + pMatter.SetBornStamp(CECGameRun.Instance.GetWorld().GetBornStamp()); m_MatterTab[info.mid] = pMatter; diff --git a/Assets/PerfectWorld/Scripts/Move/CECHostMove.cs b/Assets/PerfectWorld/Scripts/Move/CECHostMove.cs index 44393d2d3e..607625315b 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECHostMove.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECHostMove.cs @@ -1,4 +1,4 @@ -using BrewMonster.Network; +using BrewMonster.Network; using BrewMonster.Scripts; using CSNetwork.GPDataType; using System; @@ -392,8 +392,9 @@ namespace BrewMonster EC_CDR.OnGroundMove(ref cdr); //BMLogger.LogError($"HoangDev: FlashMove seg={i} stepTime={cdr.t} center=({cdr.vCenter})"); - if (CECWorld.Instance.GetAssureMove() != null) - CECWorld.Instance.GetAssureMove().NoAssureMove(); + var world = CECGameRun.Instance?.GetWorld(); + if (world?.GetAssureMove() != null) + world.GetAssureMove().NoAssureMove(); if ((cdr.vCenter - vStartPos).Magnitude() >= fDist * 0.98f) { diff --git a/Assets/PerfectWorld/Scripts/Move/EC_CDR.cs b/Assets/PerfectWorld/Scripts/Move/EC_CDR.cs index eba939a961..be91ce5e95 100644 --- a/Assets/PerfectWorld/Scripts/Move/EC_CDR.cs +++ b/Assets/PerfectWorld/Scripts/Move/EC_CDR.cs @@ -896,7 +896,7 @@ namespace BrewMonster public static bool CollideWithTerrain(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, ref float fFraction, ref A3DVECTOR3 vHitNormal, ref bool bStart) { - CECWorld pWorld = CECWorld.Instance; //g_pGame.GetGameRun().GetWorld(); + CECWorld pWorld = CECGameRun.Instance.GetWorld(); //g_pGame.GetGameRun().GetWorld(); A3DTerrain2 pTerrain = pWorld.GetTerrain(); bStart = false; float h1 = pTerrain.GetPosHeight(vStart, ref vHitNormal); diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs index 397c4817c6..f018981f06 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs @@ -285,6 +285,7 @@ namespace CSNetwork.C2SCommand public ushort useTime; // Time to use } + [StructLayout(LayoutKind.Sequential, Pack = 1)] // Player logout command public struct CMD_PlayerLogout { diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index f80cd2f8f6..357df2baf9 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -14,6 +14,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using BrewMonster.Scripts; using UnityEngine; using CommandID = CSNetwork.GPDataType.CommandID; @@ -23,7 +24,6 @@ namespace CSNetwork { private static IPrefixedLogger _logger = LoggerFactory.GetLogger(nameof(GameSession)); // Get class-specific logger - private NetworkManager _networkManager; private string _host; private int _port; @@ -84,6 +84,8 @@ namespace CSNetwork _networkManager.ProtocolReceived += OnProtocolReceived; _networkManager.ErrorOccurred += OnErrorOccurred; _networkManager.Disconnected += OnDisconnected; + + TestLogoutLogic.Instance.WasClientSendLogoutMessage = false; } public void SetLogPath(string path) @@ -512,7 +514,7 @@ namespace CSNetwork { _logger.Log(LogType.Debug, $"Sending protocol: {protocol.GetType().Name} (Detail: {protocol.ToString})"); - BMLogger.Log($"[GameSession] Sending protocol: {protocol.GetType().Name} (Type: {protocol.GetPType()}) + {protocol.ToString}"); + BMLogger.Log($"[GameSession] Sending protocol: {protocol.GetType().Name} (Type: {protocol.GetPType()}) + {protocol.ToString} "); _networkManager.Send(protocol); complete?.Invoke(); } @@ -528,6 +530,7 @@ namespace CSNetwork private void OnProtocolReceived(Protocol protocol) { _logger.Log(LogType.Debug, $"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})"); + BMLogger.Log($"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})"); if (protocol is null) return; @@ -549,7 +552,7 @@ namespace CSNetwork break; // Add cases for other protocols GameSession might need to handle case ProtocolType.PROTOCOL_SELECTROLE_RE: - HandleSelectRoleResponse((SelectRole_Re)protocol); + OnPrtcSelectRoleRe((SelectRole_Re)protocol); //_networkManager.IgnoreBytes = 2; break; case ProtocolType.PROTOCOL_CREATEROLE_RE: @@ -570,8 +573,10 @@ namespace CSNetwork OnPrtcPlayerBaseInfoRe(protocol); break; case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break; + case ProtocolType.PROTOCOL_SETUICONFIG_RE: OnPrtcSetConfigRe(protocol); break; case ProtocolType.PROTOCOL_PLAYERLOGOUT: - HandlePlayerLogout((playerlogout)protocol); + // HandlePlayerLogout((playerlogout)protocol); + OnPrtcPlayerLogout((playerlogout)protocol); break; case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE: @@ -590,11 +595,52 @@ namespace CSNetwork private void HandlePlayerLogout(playerlogout protocol) { + // old code of HUNGDK // Original client receives this before EVENT_DISCONNECT. // We just publish it to allow higher-level flow (UnityGameSession/UI) to react. - PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol)); + // PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol)); } + // void CECGameSession::OnPrtcPlayerLogout(GNET::Protocol* pProtocol) + // { + // using namespace GNET; + // PlayerLogout* p = (PlayerLogout*)pProtocol; + void OnPrtcPlayerLogout(playerlogout protocol) + { + // m_CmdCache.RemoveAllCmds(); + m_CmdCache.RemoveAllCmds(); + + // int iFlag; + // switch (p->result) + // { + // case _PLAYER_LOGOUT_FULL: iFlag = 0; break; + // case _PLAYER_LOGOUT_HALF: iFlag = 1; break; + // default: iFlag = 2; break; + // } + int iFlag; + switch (protocol.Result) + { + case PendingActionConstants._PLAYER_LOGOUT_FULL: iFlag = 0; break; + case PendingActionConstants._PLAYER_LOGOUT_HALF: iFlag = 1; break; + default: iFlag = 2; break; + } + + // g_pGame->GetGameRun()->SetLogoutFlag(iFlag); + EC_Game.GetGameRun().SetLogoutFlag(iFlag); + + // if (!IsConnected() && g_pGame->GetGameRun()->GetLogoutFlag() == 1) + // { + // a_LogOutput(1, "CECGameSession::OnPrtcPlayerLogout, LogoutFlag=1 replaced by 2."); + // g_pGame->GetGameRun()->SetLogoutFlag(2); + // } + if (!IsConnected && EC_Game.GetGameRun().GetLogoutFlag() == 1) + { + // _logger.Log(LogType.Log, "CECGameSession::OnPrtcPlayerLogout, LogoutFlag=1 replaced by 2."); + EC_Game.GetGameRun().SetLogoutFlag(2); + } + } + + private void HandleServerDataSend(gamedatasend protocol) { int lenghtHeader = Marshal.SizeOf(); @@ -1192,11 +1238,15 @@ namespace CSNetwork } - private void HandleSelectRoleResponse(SelectRole_Re protocol) + private void OnPrtcSelectRoleRe(SelectRole_Re protocol) { _logger.Info($"Select role response {protocol.result}"); var callback = _selectRoleCallback; PostToUnityContext(() => callback?.Invoke(_selectedRole)); + + // in C++: we call CECGameRun() via pLoginUIMan->LaunchLoading(); + // now: quick hack to start game immediately after role selection - can refactor later if needed + EC_Game.GetGameRun().StartGame(0, Vector3.zero); } private void HandleCreateRoleResponse(createrole_re protocol) @@ -1509,12 +1559,14 @@ namespace CSNetwork /// - outType=1: back to select role /// - outType=0: logout account /// - public void SendPlayerLogout(int outType, Action complete = null) + public void c2s_SendCmdLogout(int outType, Action complete = null) { var g = new gamedatasend(); g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType); SendProtocol(g, complete); } + + public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode, byte byDir, ushort wStamp, int iTime) { @@ -1639,6 +1691,22 @@ namespace CSNetwork CECMCDownload::GetInstance().SendGetDownloadOK();*/ } } + + private void OnPrtcSetConfigRe(Protocol pProtocol) + { + SetUIConfig_Re p = (SetUIConfig_Re)pProtocol; + if (p.result != (int)ErrCode.ERR_SUCCESS) + BMLogger.LogError($"CECGameSession::OnPrtcSetConfigRe, link return error code of {p.result}"); + + if (CECGameRun.Instance != null) + { + TestLogoutLogic.Instance.WasClientSendLogoutMessage = true; + + CECGameRun.Instance.GetPendingLogOut().TriggerAll(); + CECGameRun.Instance.GetPendingLogOut().Clear(); + } + } + private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol) { playerbaseinfo_re p = (playerbaseinfo_re)pProtocol; @@ -2083,5 +2151,6 @@ namespace CSNetwork { // TODO: C2SCommandFactory.CreateNPCSevCrossServerGetOutCmd() and SendProtocol } + } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs index 86ce0a9476..4211b348da 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs @@ -472,6 +472,12 @@ namespace CSNetwork // after successful decodes/consumptions. bytesConsumedFromOriginal = processingStream.Position; // Use final stream position } + + originalBlockLength = _decryptedOctets.Length; + _logger.Log(LogType.Info, + $" securityApplied = {securityApplied} bytesConsumedFromOriginal = {bytesConsumedFromOriginal }, originalBlockLength = {originalBlockLength}" + ); + EndProcessing: _receiveOctets.SetSize(0); diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs new file mode 100644 index 0000000000..f469d965a0 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs @@ -0,0 +1,73 @@ +using System; + +namespace CSNetwork.Protocols +{ + // C++: class SetUIConfig_Re : public Protocol + public class SetUIConfig_Re : Protocol + { + // C++: int result; + public int result; + // C++: int roleid; + public int roleid; + // C++: unsigned int localsid; + public uint localsid; + + // C++: enum { PROTOCOL_TYPE = PROTOCOL_SETUICONFIG_RE }; + // C++: SetUIConfig_Re() { type = PROTOCOL_SETUICONFIG_RE; } + public SetUIConfig_Re() : base(ProtocolType.PROTOCOL_SETUICONFIG_RE) + { + } + + // C++: SetUIConfig_Re(int l_result,int l_roleid,unsigned int l_localsid) + // : result(l_result),roleid(l_roleid),localsid(l_localsid) { type = PROTOCOL_SETUICONFIG_RE; } + public SetUIConfig_Re(int l_result, int l_roleid, uint l_localsid) + : base(ProtocolType.PROTOCOL_SETUICONFIG_RE) + { + result = l_result; + roleid = l_roleid; + localsid = l_localsid; + } + + // C++: GNET::Protocol *Clone() const { return new SetUIConfig_Re(*this); } + public override Protocol Clone() => new SetUIConfig_Re + { + result = result, + roleid = roleid, + localsid = localsid + }; + + // C++: OctetsStream& marshal(OctetsStream & os) const { os << result; os << roleid; os << localsid; return os; } + public override void Marshal(OctetsStream os) + { + os.Write(result); + os.Write(roleid); + os.Write(localsid); + } + + // C++: const OctetsStream& unmarshal(const OctetsStream &os) { os >> result; os >> roleid; os >> localsid; return os; } + public override void Unmarshal(OctetsStream os) + { + result = os.ReadInt32(); + roleid = os.ReadInt32(); + localsid = os.ReadUInt32(); + } + + // C++: int PriorPolicy( ) const { return 1; } + public override int PriorPolicy() => 1; + + // C++: bool SizePolicy(size_t size) const { return size <= 64; } + public override bool SizePolicy(int size) => size <= 64; + + // C++: void Process(Manager *manager, Manager::Session::ID sid) { // TODO; #ifdef _TESTCODE ... #endif } + public override void Process(Manager mgr, uint sid) + { + // TODO +#if _TESTCODE + if (result == (int)BrewMonster.ErrCode.ERR_SUCCESS) + Protocol._logger.Debug("Set uiconfig successfully."); + else + Protocol._logger.Debug($"Set custom data failed. retcode={result}"); +#endif + } + } +} diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs.meta b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs.meta new file mode 100644 index 0000000000..bb5001462a --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/SetUIConfig_Re.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6d43c7ce793f4c3698ff104d0d66f328 +timeCreated: 1772523576 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/playerlogout.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/playerlogout.cs index bff70ce596..f758109a09 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/playerlogout.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/playerlogout.cs @@ -9,7 +9,7 @@ namespace CSNetwork.Protocols public int Roleid { get; set; } public int Provider_link_id { get; set; } public int Localsid { get; set; } - public int L_localsid { get; set; } + // public int L_localsid { get; set; } public playerlogout() : base(ProtocolType.PROTOCOL_PLAYERLOGOUT) { @@ -22,7 +22,7 @@ namespace CSNetwork.Protocols Roleid = Roleid, Provider_link_id = Provider_link_id, Localsid = Localsid, - L_localsid = L_localsid + // L_localsid = L_localsid }; public override void Marshal(OctetsStream os) @@ -31,7 +31,7 @@ namespace CSNetwork.Protocols os.Write(Roleid); os.Write(Provider_link_id); os.Write(Localsid); - os.Write(L_localsid); + // os.Write(L_localsid); } public override void Unmarshal(OctetsStream os) @@ -40,7 +40,7 @@ namespace CSNetwork.Protocols Roleid = os.ReadInt32(); Provider_link_id = os.ReadInt32(); Localsid = os.ReadInt32(); - L_localsid = os.ReadInt32(); + // L_localsid = os.ReadInt32(); } public override int PriorPolicy() => 101; diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index 25478d19c3..08c117bae0 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -13,6 +13,7 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using BrewMonster.Managers; using BrewMonster.Scripts.Task; using BrewMonster.UI; using UnityEngine; @@ -107,7 +108,7 @@ namespace BrewMonster.Network public static void LogoutAccount() { if (Instance == null) return; - _ = Instance.LogoutAndReturnAsync(outType: 0, entryTarget: LogoutFlowState.LoginEntryTarget.LoginUI, clearSavedCreds: true); + _ = Instance.LogoutAndReturnAsync(outType: 0, entryTarget: LogoutFlowState.LoginEntryTarget.LoginUI, clearSavedCreds: false); } public static void c2s_CmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets) { @@ -182,6 +183,8 @@ namespace BrewMonster.Network { // Tell LoginScene what to show next. LogoutFlowState.NextLoginEntry = entryTarget; + _gameSession.Disconnected -= OnUnexpectedDisconnect; + EC_ManMessageMono.Instance.CECNPCMan.Release(); if (clearSavedCreds) { @@ -197,11 +200,12 @@ namespace BrewMonster.Network // Mark this as an intentional disconnect to prevent showing error message _isIntentionalDisconnect = true; + // We call after receive LOGOUT(0) or LOGOUT(1) from server, but the server may choose to disconnect immediately or not. // Send LOGOUT(outType) like the original client. - _gameSession.SendPlayerLogout(outType); + // _gameSession.SendPlayerLogout(outType); // Wait briefly for server-driven disconnect. - await WaitForDisconnectAsync(timeoutMs: 4000); + if(outType == 0) await WaitForDisconnectAsync(timeoutMs: 100); } } catch (Exception ex) @@ -211,10 +215,11 @@ namespace BrewMonster.Network finally { // Fallback: if server didn't close, close locally. - if (_gameSession != null && _gameSession.IsConnected) + if (_gameSession != null && _gameSession.IsConnected && outType == 0) { _gameSession.Disconnect(); } + } // Return to LoginScene. @@ -245,7 +250,8 @@ namespace BrewMonster.Network if (ui == null) continue; if (!ui.gameObject.scene.IsValid() || ui.gameObject.scene.name != LoginSceneName) continue; // Avoid hard dependency on method existence (merges may edit LoginScreenUI). - ui.SendMessage("ApplyLoginEntry", entryTarget, SendMessageOptions.DontRequireReceiver); + // ui.SendMessage("ApplyLoginEntry", entryTarget, SendMessageOptions.DontRequireReceiver); + ui.ApplyLoginEntry( entryTarget ); return; } } @@ -293,6 +299,12 @@ namespace BrewMonster.Network { // get current active scene var currentScene = SceneManager.GetActiveScene(); + if ( currentScene.IsValid() && currentScene.name == LoginSceneName) + { + // LoginScene is already active, nothing to do. + return; + } + // Load LoginScene additively if needed (do not unload keepSceneName, e.g., a61). var loginScene = SceneManager.GetSceneByName(LoginSceneName); if (!loginScene.IsValid() || !loginScene.isLoaded) diff --git a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs index 2012a6e28d..0aa18c8d5e 100644 --- a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs +++ b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs @@ -68,6 +68,8 @@ namespace BrewMonster.Scripts.Task uint m_ulNPCInfoTimeMark; private Dictionary m_NPCInfoMap = new(); + private bool _wasLoaded = false; + // Lookup NPC/task object coordinates info by template id (loaded from task_npc pack) // 通过模板ID查找NPC/任务对象坐标信息(从task_npc包加载) public bool TryGetTaskNPCInfo(uint id, out NPC_INFO info) @@ -95,12 +97,16 @@ namespace BrewMonster.Scripts.Task public async UniTask LoadTasksFromPack(string address, bool bLoadDescript, Action onProgress, CancellationToken token) { - bool wasLoaded = await LoadTaskTemplFromSO(); - if (wasLoaded) + // If loaded Task Template from last play time, skip loading again + if (_wasLoaded) { - BMLogger.Log($" [ATaskTemplMan] Loaded task templates from ScriptableObject."); - onProgress?.Invoke(1f); - return true; + goto END_PROGRESS; + } + + _wasLoaded = await LoadTaskTemplFromSO(); + if (_wasLoaded) + { + goto END_PROGRESS; } var handle = await AddressableManager.Instance.LoadTextAssetAsync(address); @@ -162,6 +168,9 @@ namespace BrewMonster.Scripts.Task await UniTask.Yield(); } + END_PROGRESS: + + _wasLoaded = true; onProgress?.Invoke(1f); Debug.Log($" Finished loading {m_TaskTemplMap.Count} task templates."); diff --git a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs index 81383cce4d..d96c1fd9aa 100644 --- a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs +++ b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs @@ -877,7 +877,7 @@ namespace BrewMonster.Scripts.Task pos[1] = vPos.y; pos[2] = vPos.z; } - var world = World.CECWorld.Instance; + var world = CECGameRun.Instance.GetWorld(); return world != null ? world.GetInstanceID() : 0; } diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs index ef60fa8ee7..05034090cd 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs @@ -135,7 +135,7 @@ namespace BrewMonster.UI m_TargetPos = EC_Game.GetGameRun().GetHostPlayer().GetObjectCoordinates( idTarget, out m_Targets, ref bInTable); //todo: add map feature here. - if(!bInTable /*&& MAJOR_MAP== CECWorld.Instance.GetInstanceID())*/) + if(!bInTable /*&& MAJOR_MAP== CECGameRun.Instance.GetWorld().GetInstanceID())*/) { ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan(); if(pMan.TryGetTaskNPCInfo((uint)idTarget, out NPC_INFO pInfo)) diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs index b28b970217..d39953991f 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs @@ -152,10 +152,10 @@ namespace BrewMonster.Scripts.Task.UI { idWorld = gameRun.GetWorld().GetInstanceID(); } - else if (CECWorld.Instance != null) + else if (CECGameRun.Instance?.GetWorld() != null) { - idWorld = CECWorld.Instance.GetInstanceID(); - } + idWorld = CECGameRun.Instance.GetWorld().GetInstanceID(); + } if (IsShow()) { // �ѽ�������Ϣ��ʵʱ���� @@ -240,7 +240,7 @@ namespace BrewMonster.Scripts.Task.UI public void AppendCommand(int worldid, Task_Region[] pRegions, int size) { List instCoord = new List(), tempCoord = new List(); - int cur = CECWorld.Instance.GetInstanceID(); + int cur = CECGameRun.Instance?.GetWorld()?.GetInstanceID() ?? 161; CECInstance pInstance = EC_Game.GetGameRun().GetInstance(cur); string strCurMap = pInstance.GetPath(); // find the entrance of instance diff --git a/Assets/PerfectWorld/Scripts/Task/UI/EmoteWindow.cs b/Assets/PerfectWorld/Scripts/Task/UI/EmoteWindow.cs index d50eb53ae5..7f7b72d363 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/EmoteWindow.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/EmoteWindow.cs @@ -87,10 +87,10 @@ namespace BrewMonster.UI // if (bInAutoMode) return; //todo: dummy call StartGame // EC_Game.GetGameRun().StartGame(0, Vector3.zero); - if (EC_Game.GetGameRun().GetPoseCmdShortcuts() == null) - { - EC_Game.GetGameRun().StartGame(0, Vector3.zero); - } + // if (EC_Game.GetGameRun().GetPoseCmdShortcuts() == null) + // { + // EC_Game.GetGameRun().StartGame(0, Vector3.zero); + // } CECShortcut pSC = EC_Game.GetGameRun().GetPoseCmdShortcuts().GetShortcut(slot); // if (CDlgAutoHelp::IsAutoHelp() && strstr(pDlgSrc->GetName(), "Win_Quickbar")) // { diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs index aa9744a94d..0fc5bb5ada 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs @@ -9,15 +9,50 @@ namespace BrewMonster.UI public class BtnBackToSelectRole : MonoBehaviour { public void OnClick() + { + // CECUIManager.Instance.ShowMessageBox( + // title: "Thoát", + // message: "Đang rời khỏi Thế Giới Hoàn Mỹ", + // messageBoxType: MessageBoxType.YesButton + // ); + + // CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); + // UnityGameSession.ReturnToSelectRole(); + + OnCommandRepick(); + } + + // void CDlgSystem3::OnCommandRepick(const char *szCommand) + // { + // a_LogOutput(1, "CDlgSystem3::OnCommandRepick "); + // + // if( !GetGameUIMan()->m_pDlgExit->IsShow() && + // !GetGameUIMan()->GetDialog("Game_Quit") ) + // { + // AUIDialog *pMsgBox = NULL; + // GetGameUIMan()->MessageBox("Game_Quit", + // GetGameUIMan()->GetStringFromTable(CECCrossServer::Instance().IsOnSpecialServer() ? 10131 : 202), + // MB_YESNO, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); + // pMsgBox->SetIsModal(false); + // } + // } + + + void OnCommandRepick() { CECUIManager.Instance.ShowMessageBox( title: "Thoát", - message: "Đang rời khỏi Thế Giới Hoàn Mỹ", - messageBoxType: MessageBoxType.YesButton + message: CECUIManager.Instance.GetInGameUIMan().GetStringFromTable(202), + messageBoxType: MessageBoxType.BothYesNoButton, + onClickedYes: OnClickYes ); - - UnityGameSession.ReturnToSelectRole(); } + + void OnClickYes() + { + CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); + } + } } diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs index 53dcf51254..b0e31e6762 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs @@ -11,6 +11,8 @@ namespace BrewMonster.UI public void OnClick() { UnityGameSession.LogoutAccount(); + // CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutFull()); + // UnityGameSession.Instance.GameSession.c2s_SendCmdLogout( PendingActionConstants._PLAYER_LOGOUT_FULL); } } } diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs index 725a791c01..337f3fd63c 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs @@ -82,7 +82,7 @@ namespace BrewMonster.UI if (_selectCharacterScreen != null) _selectCharacterScreen.gameObject.SetActive(false); - ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry()); + // ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry()); } // Update is called once per frame @@ -181,6 +181,10 @@ namespace BrewMonster.UI if (entry == BrewMonster.Network.LogoutFlowState.LoginEntryTarget.SelectRole) { + // If we're returning to select role, skip straight to select role without showing login UI again, since we never fully left the game session. + OnLoginComplete(true); + return; + // Auto-login to reach Select Role like the original client, without showing Tech3C auth UI again. if (!string.IsNullOrEmpty(_usernameInputField.text) && !string.IsNullOrEmpty(_passwordInputField.text)) { diff --git a/Assets/PerfectWorld/Scripts/World/CECWorld.cs b/Assets/PerfectWorld/Scripts/World/CECWorld.cs index da885a8a56..194f64e27b 100644 --- a/Assets/PerfectWorld/Scripts/World/CECWorld.cs +++ b/Assets/PerfectWorld/Scripts/World/CECWorld.cs @@ -8,7 +8,7 @@ using UnityEngine; namespace BrewMonster.Scripts.World { - public class CECWorld : Singleton + public class CECWorld { protected A3DTerrain2 m_pA3DTerrain; CECOrnamentMan m_pOnmtMan; @@ -93,5 +93,98 @@ namespace BrewMonster.Scripts.World { return EC_ManMessageMono.Instance.GetECManPlayer; } + + + // Release object + public void Release() + { + // TODO: Release world resources in the correct order, currently just a placeholder to avoid compile errors. The actual release logic should closely follow the original C++ code to ensure proper cleanup and resource management. + // Release auto home + // ReleaseAutoHome(); // Not open comment, this same in C++ + // CECIntelligentRoute::Instance().Release(); // TODO + + // Release CDS object + // if (m_pCDS) + // { + // g_pGame->GetA3DEngine()->SetA3DCDS(NULL); + // delete m_pCDS; + // m_pCDS = NULL; + // } + + // Release nature objects + // ReleaseNatureObjects(); //TODO + + // Release scene before managers + // ReleaseScene(); + + // Release managers + // ReleaseManagers(); + + // force to release all loaded resource + // ThreadRemoveAllLoaded(); + + // if (m_pPrecinctSet) + // { + // delete m_pPrecinctSet; + // m_pPrecinctSet = NULL; + // } + // + // if (m_pRegionSet) + // { + // delete m_pRegionSet; + // m_pRegionSet = NULL; + // } + // + // if (m_pSceneLights) + // { + // delete m_pSceneLights; + // m_pSceneLights = NULL; + // } + // + // if (m_pAssureMove) + // { + // m_pAssureMove->ReleaseMap(); + // delete m_pAssureMove; + // m_pAssureMove = NULL; + // } + // + // m_dwBornStamp = 1; + } + + // Release current scene + void ReleaseScene() + { + // g_pGame->GetA3DEngine()->SetSky(NULL); + // + // A3DRELEASE(m_pScene); + // A3DRELEASE(m_pGrassLand); + // A3DRELEASE(m_pForest); + // A3DRELEASE(m_pA3DSky); + // + // // 1. force to exit loader thread + // ExitLoaderThread(); + // + // // 2. release manager + // for (int i=0; i < NUM_MANAGER; i++) + // { + // if (m_aManagers[i]) + // m_aManagers[i]->OnLeaveGameWorld(); + // } + // + // // 3. force to release all loaded resource + // ThreadRemoveAllLoaded(); + // + // // Release terrain after loading thread has been ended + // A3DRELEASE(m_pA3DTrnCuller); + // A3DRELEASE(m_pA3DTerrainWater); + // A3DRELEASE(m_pA3DTerrain); + // A3DRELEASE(m_pTerrainOutline); + // A3DRELEASE(m_pCloudManager); + // + // m_bWorldLoaded = false; + // + // // ɾ³ýËæ»úµØÍ¼ÐÅÏ¢ + // CECRandomMapProcess::DeleteAllRandomMapDataForSingleUser(); + } } } diff --git a/Assets/PerfectWorld/Scripts/_Doc.meta b/Assets/PerfectWorld/Scripts/_Doc.meta new file mode 100644 index 0000000000..a0e4a188a1 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/_Doc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f58ceadd359a44bc8ab2da938c71e9c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md b/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md new file mode 100644 index 0000000000..42258fbb5d --- /dev/null +++ b/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md @@ -0,0 +1,106 @@ +# Bug: Client không nhận PROTOCOL_PLAYERLOGOUT sau khi logout + +## Tóm tắt + +- **Triệu chứng:** Server gửi `PROTOCOL_PLAYERLOGOUT` (69) sau khi client gửi lệnh logout, nhưng client **không nhận được** gói này (handler không chạy). +- **Nguyên nhân:** Logic xử lý buffer nhận trong `NetworkManager.ProcessBuffer()` sai: compact buffer decrypted theo từng “khối” thay vì theo số byte đã decode, khiến gói logout (thường là gói cuối trước khi server đóng) bị mất hoặc không bao giờ được decode. +- **Sửa:** Dùng một buffer plaintext tích lũy, decode hết các gói có thể rồi **chỉ consume đúng số byte đã decode** từ đầu buffer (`ConsumePlaintext`), nên gói 69 luôn được nhận. + +--- + +## 1. Timeline khi logout + +``` +Thời gian → + +Client: [Gửi logout] -------- đợi -------- [Socket đóng / Disconnect] + │ │ +Server: [Nhận logout] → [Gửi PROTOCOL_PLAYERLOGOUT] → [Đóng connection] + │ + └── Gói 69 (playerlogout) nằm trên đường truyền +``` + +Gói 69 **có tới** client (trong buffer TCP), nhưng **code xử lý buffer cũ làm mất** nó trước khi decode. + +--- + +## 2. Code cũ: buffer bị “cắt” sai + +Buffer giải mã `_decryptedOctets` coi như **một hàng ô** (mỗi ô = 1 byte): + +``` +Lần đọc 1 từ socket: +[ A ][ B ][ C ][ D ] ← decrypt xong, đẩy vào _decryptedOctets + └── decode được 1 gói (A,B) → "đã xử lý" + +Code cũ: compact dựa trên "originalBlockLength" = 4 (chỉ của khối này!). + +Lần đọc 2 (server gửi thêm rồi đóng): +[ C ][ D ][ E ][ F ][ G ] ← E,F,G = PROTOCOL_PLAYERLOGOUT (gói 69) + ↑ + C,D có thể bị coi nhầm / compact nhầm, hoặc E,F,G không được coi là "một gói đủ" +``` + +Kết quả: **Gói logout (E,F,G) không bao giờ được decode**, hoặc bị xóa khi compact. + +--- + +## 3. Code cũ vs Code mới (ý tưởng) + +- **Code cũ:** Mỗi lần đọc socket = **một khối riêng**. Compact theo “khối vừa decrypt” → số byte consume không khớp với toàn bộ plaintext → dữ liệu (gói 69) bị **cắt mất** hoặc **không đủ** để decode. +- **Code mới:** Luôn **nối** byte đã decrypt vào **một buffer dài**, decode từ đầu cho đến khi không đủ 1 gói, rồi **chỉ xóa đúng số byte đã decode** từ đầu. Phần còn lại giữ cho lần sau. + +--- + +## 4. Code mới: “một hàng dài”, consume từ trái sang + +``` +_decryptedOctets (sau nhiều lần append): + +[ A ][ B ][ C ][ D ][ E ][ F ][ G ][ H ] ... + └─ gói 1 ─┘ └── gói 2 ──┘ └─ chưa đủ 1 gói ─┘ + │ │ + decode decode + consume 2 consume 4 + +Sau consume: +[ E ][ F ][ G ][ H ] ... ← chỉ xóa 2+4 byte từ trái, giữ lại E,F,G,H... +``` + +Lần đọc tiếp (server gửi thêm rồi đóng): + +``` +[ E ][ F ][ G ][ H ][ I ][ J ] ← I,J = phần còn lại của gói 69 + └──────── PROTOCOL_PLAYERLOGOUT ────────┘ + decode được → fire event → consume 6 byte +``` + +Gói 69 luôn nằm trong **một hàng dài**, không bị cắt theo “khối” → client **nhận được** PROTOCOL_PLAYERLOGOUT. + +--- + +## 5. Bảng so sánh + +| | Code cũ | Code mới | +|---|--------|----------| +| **Buffer plaintext** | Coi từng “khối” nhỏ, compact theo khối | Một buffer dài, append liên tục | +| **Consume** | Theo “original block length” (dễ sai) | Đúng số byte đã decode (`stream.Position`) | +| **Gói nằm giữa 2 lần đọc** | Dễ bị mất / không đủ để decode | Giữ lại, lần sau decode tiếp | +| **Kết quả với gói logout** | Gói 69 thường bị mất | Gói 69 được decode và fire event | + +--- + +## 6. File thay đổi + +- **`NetworkManager.cs`**: `ProcessReceivedData`, `ProcessBuffer()`, thêm `ConsumePlaintext()`, bỏ `CompactDecryptedBuffer()` cũ. +- **`GameSession.cs`**: Thêm log trong `HandlePlayerLogout()`: `[GameSession] Received PROTOCOL_PLAYERLOGOUT (Result=..., Roleid=...)`. + +--- + +## 7. Cách kiểm tra + +Logout trong game và xác nhận log xuất hiện **trước** khi disconnect: + +```text +[GameSession] Received PROTOCOL_PLAYERLOGOUT (Result=..., Roleid=...) +``` diff --git a/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md.meta b/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md.meta new file mode 100644 index 0000000000..7ba870fbf9 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/_Doc/PROTOCOL_PLAYERLOGOUT-bug-fix.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4caec71ab8d214b58b08e61470605ff6 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/CECGameRun.cs.meta b/Assets/Scripts/CECGameRun.cs.meta deleted file mode 100644 index 96acc12e8f..0000000000 --- a/Assets/Scripts/CECGameRun.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 5de219a5b9756ae4ebf01e2919b92cde \ No newline at end of file diff --git a/Assets/Scripts/CECHostPlayer.Combat.cs b/Assets/Scripts/CECHostPlayer.Combat.cs index 438abe7b3b..b94a466970 100644 --- a/Assets/Scripts/CECHostPlayer.Combat.cs +++ b/Assets/Scripts/CECHostPlayer.Combat.cs @@ -447,7 +447,7 @@ namespace BrewMonster // Ensure we are not under ground // 确保我们不在地下 A3DVECTOR3 vNormal = new A3DVECTOR3(); - float vTerrainHeight = CECWorld.Instance.GetTerrainHeight(vPos, ref vNormal); + float vTerrainHeight = CECGameRun.Instance.GetWorld().GetTerrainHeight(vPos, ref vNormal); if (vPos.y < vTerrainHeight) vPos.y = vTerrainHeight; @@ -507,7 +507,7 @@ namespace BrewMonster // Ensure we are not under ground // 确保我们不在地下 A3DVECTOR3 vNormal = new A3DVECTOR3(); - float vTerrainHeight = CECWorld.Instance.GetTerrainHeight(vPos, ref vNormal); + float vTerrainHeight = CECGameRun.Instance.GetWorld().GetTerrainHeight(vPos, ref vNormal); if (vPos.y < vTerrainHeight) vPos.y = vTerrainHeight; diff --git a/Assets/Scripts/CECHostPlayer.Skill.cs b/Assets/Scripts/CECHostPlayer.Skill.cs index 71bb95498d..8116754e85 100644 --- a/Assets/Scripts/CECHostPlayer.Skill.cs +++ b/Assets/Scripts/CECHostPlayer.Skill.cs @@ -871,7 +871,7 @@ namespace BrewMonster break; // Get target object - CECObject pObject = CECWorld.Instance.GetObject(idTarget, 0); + CECObject pObject = CECGameRun.Instance.GetWorld().GetObject(idTarget, 0); if (pObject == null) break; A3DVECTOR3 vHostPos = EC_Utility.ToA3DVECTOR3(transform.position); // GetPos() diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index 63a5d47c0f..16d97a01d8 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -791,7 +791,7 @@ namespace BrewMonster while (true) { A3DVECTOR3 refake = new A3DVECTOR3(); - float terrianHeight = CECWorld.Instance.GetTerrainHeight(vTargetPos, ref refake); + float terrianHeight = CECGameRun.Instance.GetWorld().GetTerrainHeight(vTargetPos, ref refake); if (terrianHeight > vTargetPos.y + 1E-4f) break; @@ -989,7 +989,7 @@ namespace BrewMonster bool bFound = false; vPos = default; - CECWorld world = CECWorld.Instance; + CECWorld world = CECGameRun.Instance.GetWorld(); if (world == null) { return false; @@ -1064,7 +1064,7 @@ namespace BrewMonster // ×¢Ò⣺vPos ±»µ÷Õûºó£¬ÓпÉÄÜ´¦ÓÚ͹°üÖУ»Ðè¼ì²é·µ»Ø¸ß¶Èµ÷ÕûÖµ£¬ÒÔ±ÜÃâµ÷Õû¹ý´ó (Beware of large adjustments placing us back into brushes) A3DVECTOR3 vTemp = new A3DVECTOR3(vPos); - CECWorld world = CECWorld.Instance; + CECWorld world = CECGameRun.Instance.GetWorld(); if (world == null) { return vTemp.y; @@ -3805,7 +3805,173 @@ namespace BrewMonster //private bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0); //private bool ISPLAYERID(int id) => id != 0 && (id & 0x80000000) == 0; //private bool ISMATTERID(int id) => ((id) & 0xC0000000) == 0xC0000000; - } + + // Release object + public void Release() + { + // TODO: Release all objects created by player, such as inventory, skills, etc. + // CECInstanceReenter::Instance().Clear(); + // CECShoppingItemsMover::Instance().Clear(); + // CECFashionShopManager::Instance().Clear(); + // CECShoppingManager::Instance().Clear(); + // CECUseUniversalTokenCommandManager::Instance().Clear(); + // CECUniversalTokenHTTPOSNavigatorTicketHandler::Instance().Clear(); + // RandMallShoppingManager::Instance().Release(); + // CECFactionPVPModel::Instance().Clear(); + // CECHostSkillModel::Instance().Release(); + // CECComboSkillState::Instance().Release(); + // CECPlayerLevelRankRealmChangeCheck::Instance().Release(); + // CECHostFashionEquipFromStorageSystem::Instance().Clear(); + // + // m_pSaveLifeTrigger = NULL; + // CECQuickBuyPopManager::Instance().ClearPolicies(); + // + // // Ïú»ÙPlayerWrapper + // CECAutoPolicy::GetInstance().OnLeaveWorld(); + // + // // Save favorite auction list first + // SaveFavorAucItems(); + // + // // Release duel images + // ReleaseDuelImages(); + // + // // Release sounds + // g_pGame->GetGameRun()->ReleaseSoundTable(); + // m_pCurMoveSnd = NULL; + // + // // Release friend manger + // if (m_pFriendMan) + // { + // delete m_pFriendMan; + // m_pFriendMan = NULL; + // } + // + // // Release pet corral + // if (m_pPetCorral) + // { + // delete m_pPetCorral; + // m_pPetCorral = NULL; + // } + // + // if (m_pPetWords) + // { + // delete m_pPetWords; + // m_pPetWords = NULL; + // } + // + // if (m_pForceMgr) + // { + // delete m_pForceMgr; + // m_pForceMgr = NULL; + // } + // + // if (m_pOnlineAwardCtrl) + // { + // delete m_pOnlineAwardCtrl; + // m_pOnlineAwardCtrl = NULL; + // } + // + // if (m_pOffShopCtrl) + // { + // delete m_pOffShopCtrl; + // m_pOffShopCtrl = NULL; + // } + // + // if (m_pAutoTeam) + // { + // delete m_pAutoTeam; + // m_pAutoTeam = NULL; + // } + // + // if (m_pChariot) + // { + // delete m_pChariot; + // m_pChariot = NULL; + // } + // + // int i; + // + // // Release all shortcuts + // for (i=0; i < NUM_HOSTSCSETS1; i++) + // A3DRELEASE(m_aSCSets1[i]); + // + // for (i=0; i < NUM_HOSTSCSETS2; i++) + // A3DRELEASE(m_aSCSets2[i]); + // + // for (i=0; i < NUM_SYSMODSETS; i++) + // A3DRELEASE(m_aSCSetSysMod[i]); + // + // // Release all inventories + // A3DRELEASE(m_pPack); + // A3DRELEASE(m_pEquipPack); + // A3DRELEASE(m_pTrashBoxPack); + // A3DRELEASE(m_pTrashBoxPack2); + // A3DRELEASE(m_pTrashBoxPack3); + // A3DRELEASE(m_pAccountBoxPack); + // A3DRELEASE(m_pGeneralCardPack); + // A3DRELEASE(m_pTaskPack); + // A3DRELEASE(m_pDealPack); + // A3DRELEASE(m_pEPDealPack); + // A3DRELEASE(m_pTaskInterface); + // A3DRELEASE(m_pSpritePortrait); + // A3DRELEASE(m_pBuyPack); + // A3DRELEASE(m_pSellPack); + // A3DRELEASE(m_pBoothSPack); + // A3DRELEASE(m_pBoothBPack); + // A3DRELEASE(m_pEPBoothSPack); + // A3DRELEASE(m_pEPBoothBPack); + // A3DRELEASE(m_pEPEquipPack); + // A3DRELEASE(m_pClientGenCardPack); + // + // for (i=0; i < NUM_NPCIVTR; i++) + // { + // A3DRELEASE(m_aNPCPacks[i]); + // } + // + // // Release all skills + // ReleaseSkills(); + // + // // Clear current combo skill + // ClearComboSkill(); + // + // if (m_pWorkMan) + // { + // delete m_pWorkMan; + // m_pWorkMan = NULL; + // } + // + // m_CameraCtrl.Release(); + // + // m_aTeamInvs.RemoveAll(); + // + // g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pMoveTargetGFX); + // g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pSelectedGFX); + // g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pHoverGFX); + // g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pFloatDust); + // + // m_pMoveTargetGFX = NULL; + // m_pSelectedGFX = NULL; + // m_pHoverGFX = NULL; + // m_pFloatDust = NULL; + // + // // Clear tab select table + // m_aTabSels.RemoveAll(false); + // + // m_aForceInfo.RemoveAll(); + // + // if (m_pActionSwitcher) + // { + // delete m_pActionSwitcher; + // m_pActionSwitcher = NULL; + // } + // + // CECQShopConfig::Instance().ClearBuyedItem(); + // + // A3DRELEASE(m_pNavigatePlayer); + // + // CECPlayer::Release(); + } + } public sealed class CECHPTraceSpellMatcher : CECHPWorkMatcher { public override bool Match(CECHPWork pWork, int priority, bool isDelayWork) @@ -3819,5 +3985,8 @@ namespace BrewMonster return trace.GetTraceReason() == Trace_reason.TRACE_SPELL; } } + + + } diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index c58d6f68b9..451dfe22c9 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -197,6 +197,8 @@ public class CECUIManager : MonoSingleton } return null; } + + public void UpdateSkillRelatedUI() { // ¸üм¼ÄÜÏà¹ØµÄ½çÃæÏÔʾ @@ -273,7 +275,25 @@ public class CECUIManager : MonoSingleton if (string.Equals(pDlg.GetName(), "Game_Quit", StringComparison.OrdinalIgnoreCase)) { + // TODO + // Cancel hotkey customize because hot key state is determinted by CECHostInputFilter + // which is shared between repick role + // + // if (m_pDlgSettingQuickKey->IsShow()) + // m_pDlgSettingQuickKey->Show(false); + if( pSession.GameSession.IsConnected ) + { + // TODO + // if (CECCrossServer::Instance().IsOnSpecialServer()) + // g_pGame->GetGameRun()->GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutCrossServer()); + // else + // g_pGame->GetGameRun()->GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); + + EC_Game.GetGameRun().GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); + } + else + EC_Game.GetGameRun().SetLogoutFlag(2); } else if ((string.Equals(pDlg.GetName(), "Game_TeachSkill", StringComparison.OrdinalIgnoreCase) && DialogBoxCommandIDs.IDOK == iRetVal) || (string.Equals(pDlg.GetName(), "Game_LearnSkill", StringComparison.OrdinalIgnoreCase) && DialogBoxCommandIDs.IDOK == iRetVal)) @@ -509,10 +529,10 @@ public class CECUIManager : MonoSingleton public void OnClickedWaveHand() { - if (EC_Game.GetGameRun().GetPoseCmdShortcuts() == null) - { - EC_Game.GetGameRun().StartGame(0, Vector3.zero); - } + // if (EC_Game.GetGameRun().GetPoseCmdShortcuts() == null) + // { + // EC_Game.GetGameRun().StartGame(0, Vector3.zero); + // } CECShortcut pSC = EC_Game.GetGameRun().GetPoseCmdShortcuts().GetShortcut(slot); if (pSC != null) // && pObjSrc->GetDataPtr("ptr_CECShortcut") == pSC { diff --git a/Assets/Scripts/EC_GameRun.GameState.cs b/Assets/Scripts/EC_GameRun.GameState.cs new file mode 100644 index 0000000000..10a124e690 --- /dev/null +++ b/Assets/Scripts/EC_GameRun.GameState.cs @@ -0,0 +1,290 @@ + + using BrewMonster; + using BrewMonster.Network; + using UnityEngine; + + public partial class CECGameRun + { + int m_iGameState; // Game state + // Logout flag (C++: m_iLogoutFlag) + private int m_iLogoutFlag = -1; + + // Logout + public void Logout() + { + // ASSERT(m_iGameState == GS_GAME); + if (m_iGameState != (int)GameState.GS_GAME) + { + BMLogger.LogError($"Logout called but game state is not GS_GAME, current state: {m_iGameState}"); + return; + } + + // TODO: Check if we need to call OnLogout for UI and cross server here + // overlay::GTOverlay::Instance().Logout(); + // CECCrossServer::Instance().OnLogout(); + + bool bExitApp = false; + + // if (CECUIConfig::Instance().GetLoginUI().bAvoidLoginUI && m_iLogoutFlag != 1){ + if( 1 == 2 && m_iLogoutFlag != 1){ // TODO: check if we need to avoid login UI based on config and logout flag here + bExitApp = true; + }else if (m_iLogoutFlag == 0) // Exit application directly + { + bExitApp = true; + } + else if (m_iLogoutFlag == 1) // Logout game and re-select role + { + UnityGameSession.ReturnToSelectRole(); + + // TODO: Check if we need to send switch game for mini client here + // Origin C++ + // StartLogin(); + StartLogin(); + + // + // // ÏÂÔØÆ÷ÏìÓ¦Í˳öÓÎϷ״̬ + // if( g_pGame->GetConfigs()->IsMiniClient() ) + // CECMCDownload::GetInstance().SendSwitchGame(false); + // + // // Goto select role interface directly + // CECLoginUIMan* pLoginUIMan = m_pUIManager->GetLoginUIMan(); + // if (pLoginUIMan) + // { + // if(GetSellingRoleID() == 0) + // { + // pLoginUIMan->LaunchCharacter(); + // } + // + // g_pGame->GetGameSession()->ReLogin(true); + // pLoginUIMan->SetRoleListReady(false); + // if (!CECReconnect::Instance().IsReconnecting()){ + // CECReconnect::Instance().SetRoleID(0); + // } + // } + // else + // { + // ASSERT(pLoginUIMan); + // bExitApp = true; + // } + } + else if (m_iLogoutFlag == 2) // Logout game and goto login state + { + UnityGameSession.LogoutAccount(); + + // TODO: Check if we need to send switch game for mini client here + // Origin C++ + // StartLogin(); + + // if (CECLoginUIMan* pLoginUIMan = m_pUIManager->GetLoginUIMan()){ + // g_pGame->GetGameRun()->SetSellingRoleID(0); + // g_pGame->GetGameSession()->ReLogin(false); + // if (CECCrossServer::Instance().IsWaitLogin()){ + // pLoginUIMan->LaunchCharacter(); + // pLoginUIMan->ChangeSceneByRole(); + // pLoginUIMan->ReclickLoginButton(); + // }else if (CECReconnect::Instance().IsReconnecting()){ + // pLoginUIMan->ChangeCameraByScene(CECLoginUIMan::LOGIN_SCENE_SELCHAR); + // pLoginUIMan->ReclickLoginButton(); + // } + } + else + { + // ASSERT(NULL); + bExitApp = true; + } + + + // if (m_pRandomMapProc) + // A3DRELEASE(m_pRandomMapProc); + + // if (bExitApp) + // { + // // Exit game application + // EndGameState(false); + // ::PostMessage(g_pGame->GetGameInit().hWnd, WM_QUIT, 0, 0); + // } + } + + // End current game state + void EndGameState(bool bReset = true/* true */) + { + if (m_iGameState == (int)GameState.GS_NONE) + return; + + int iCurState = m_iGameState; + m_iGameState = (int)GameState.GS_NONE; + + // TODO: Check if we need to call OnEndLoginState or OnEndGameState based on current state + if (iCurState == (int)GameState.GS_LOGIN) + OnEndLoginState(); + else if (iCurState == (int)GameState.GS_GAME) + OnEndGameState(); + + // Stop background sound and music + // CELBackMusic* pBackMusic = g_pGame->GetBackMusic(); + // if (pBackMusic) + // { + // pBackMusic->StopMusic(true, true); + // pBackMusic->StopBackSFX(); + // } + + // if (bReset) + // g_pGame.Reset(); + + } + + + // Start login interface + bool StartLogin() + { + // End current game state + EndGameState(); + + m_iGameState = (int)GameState.GS_LOGIN; + + // if( !CreateLoginWorld() ) + // { + // a_LogOutput(1, "CECGameRun::StartLogin, Failed to create login world."); + // return false; + // } + // + // // Change UI manager + // if (!m_pUIManager->ChangeCurUIManager(CECUIManager::UIMAN_LOGIN)) + // { + // a_LogOutput(1, "CECGameRun::StartLogin, Failed to change UI manager."); + // return false; + // } + // + // m_pUIManager->GetLoginUIMan()->LaunchPreface(); + // + // if (!m_pLogo){ + // m_pLogo = new A2DSprite; + // if (!m_pLogo->Init(g_pGame->GetA3DDevice(), "logo.dds", 0)){ + // A3DRELEASE(m_pLogo); + // }else{ + // m_pLogo->SetLinearFilter(true); + // } + // } + // if (af_IsFileExist("surfaces\\kr.dds")) + // { + // if (!m_pClassification){ + // m_pClassification = new A2DSprite; + // if (!m_pClassification->Init(g_pGame->GetA3DDevice(), "kr.dds", 0)){ + // A3DRELEASE(m_pClassification); + // }else{ + // m_pClassification->SetLinearFilter(true); + // } + // } + // } + // // Change cursor to default icon + // g_pGame->ChangeCursor(RES_CUR_NORMAL); + // // Discard current frame + // g_pGame->DiscardFrame(); + + return true; + } + + // End login state + void OnEndLoginState() + { + // Release UI module + // m_pUIManager.ChangeCurUIManager(-1); + + // Release World + // ReleaseLoginWorld(); + + // A3DRELEASE(m_pLogo); + // A3DRELEASE(m_pClassification); + } + + // End game state + void OnEndGameState() + { + ReleasePendingActions(); + + // Release UI module + // m_pUIManager.ChangeCurUIManager(-1); + + // Release shortcuts + ReleaseShortcuts(); + + // Release team manager + // A3DRELEASE(m_pTeamMan); + if (m_pTeamMan != null) + { + m_pTeamMan.Release(); + m_pTeamMan = null; + } + + // Release host player before world released + // ReleaseHostPlayer(); + + // Release world + // ReleaseWorld(); + + // Release message manager + // A3DRELEASE(m_pMessageMan); + + // g_pGame.ReleaseInGameRes(); + + // Return the default memory state + // m_pMemSimplify.OnEndGameState(); + // CECOptimize::Instance().OnEndGameState(); + } + + // Release shortcuts + void ReleaseShortcuts() + { + // A3DRELEASE(m_pNormalSCS); + // A3DRELEASE(m_pTeamSCS); + // A3DRELEASE(m_pTradeSCS); + // A3DRELEASE(m_pPoseSCS); + // A3DRELEASE(m_pFactionSCS); + + m_pNormalSCS = null; + m_pTeamSCS = null; + m_pTradeSCS = null; + m_pPoseSCS = null; + m_pFactionSCS = null; + } + + // Release host player + void ReleaseHostPlayer() + { + // C++ version: + // Release host player + // if (m_pHostPlayer) + // { + // m_pHostPlayer->Release(); + // delete m_pHostPlayer; + // m_pHostPlayer = NULL; + // } + + // Release host player + if (m_pHostPlayer) + { + m_pHostPlayer.Release(); + GameObject.Destroy(m_pHostPlayer.gameObject); + m_pHostPlayer = null; + } + } + + // Release world + void ReleaseWorld() + { + // m_pInputCtrl->ClearKBFilterStack(); + // m_pInputCtrl->ClearMouFilterStack(); + // + // g_pGame->GetViewport()->SwitchCamera(false); + + if (m_pWorld != null) + { + // if (m_pHostPlayer) + // m_pHostPlayer.SetPlayerMan(NULL); + + this.m_pWorld.Release(); + // delete m_pWorld; + m_pWorld = null; + } + } + } diff --git a/Assets/Scripts/EC_GameRun.GameState.cs.meta b/Assets/Scripts/EC_GameRun.GameState.cs.meta new file mode 100644 index 0000000000..492ed9131e --- /dev/null +++ b/Assets/Scripts/EC_GameRun.GameState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b039014c898e4659adb7c90cc570b637 +timeCreated: 1772439168 \ No newline at end of file diff --git a/Assets/Scripts/CECGameRun_Task.cs b/Assets/Scripts/EC_GameRun.Task.cs similarity index 100% rename from Assets/Scripts/CECGameRun_Task.cs rename to Assets/Scripts/EC_GameRun.Task.cs diff --git a/Assets/Scripts/CECGameRun_Task.cs.meta b/Assets/Scripts/EC_GameRun.Task.cs.meta similarity index 100% rename from Assets/Scripts/CECGameRun_Task.cs.meta rename to Assets/Scripts/EC_GameRun.Task.cs.meta diff --git a/Assets/Scripts/CECGameRun.cs b/Assets/Scripts/EC_GameRun.cs similarity index 77% rename from Assets/Scripts/CECGameRun.cs rename to Assets/Scripts/EC_GameRun.cs index 54f0468a02..d178052914 100644 --- a/Assets/Scripts/CECGameRun.cs +++ b/Assets/Scripts/EC_GameRun.cs @@ -13,10 +13,12 @@ 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; -public partial class CECGameRun +public partial class CECGameRun : ITickable { private static CECGameRun instance; @@ -39,9 +41,8 @@ public partial class CECGameRun // private GameRunConfig _gameRunConfig; //[SerializeField] private Transform ground; - CECHostPlayer hostPlayer; + CECHostPlayer m_pHostPlayer; private CECWorld m_pWorld; - int m_iGameState; // Game state protected CECUIManager m_pUIManager; // UI manager @@ -50,13 +51,13 @@ public partial class CECGameRun #endregion - public CECWorld GetWorld() + public CECWorld GetWorld() { - if(m_pWorld == null) + if (m_pWorld == null) { - m_pWorld = CECWorld.Instance; + m_pWorld = new CECWorld(); } - return m_pWorld; + return m_pWorld; } public CECTeamMan GetTeamMan() { return m_pTeamMan; } @@ -77,9 +78,7 @@ public partial class CECGameRun // Cache for SaveConfigsToServer: last sent config data to skip duplicate sends private byte[] m_pCfgDataBuf; private int m_iCfgDataSize; - - // Logout flag (C++: m_iLogoutFlag) - private int m_iLogoutFlag = -1; + // Selling role ID for role trade (C++: SetSellingRoleID/GetSellingRoleID) private int m_iSellingRoleID; @@ -116,16 +115,17 @@ public partial class CECGameRun if (!m_InstTab.ContainsKey(161)) m_InstTab.Add(161, new CECInstance()); AddressableManager.Instance.OnDispose += Dispose; - m_pWorld = CECWorld.Instance; - StartGame(0, Vector3.zero); + if (m_pWorld == null) + m_pWorld = new CECWorld(); - - m_pWorld = CECWorld.Instance; m_pendingLogout = new CECPendingActionArray( false); + + TickInvoker.Instance.RegisterTickable(this); } private static void Dispose() { + TickInvoker.Instance.UnregisterTickable(instance); instance = null; } @@ -152,26 +152,103 @@ public partial class CECGameRun } #endif } - - private bool init; public bool StartGame(int idInst, Vector3 vHostPos) { - if (init) + // 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)) { - return false; - } + // 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()) { - return false; + // a_LogOutput(1, "CECGameRun::StartGame, Failed to create shortcuts"); + return false; } - if (!JumpToInstance(idInst, vHostPos)) - { - BMLogger.LogError("CECGameRun::StartGame, Failed to create game world."); - return false; - } - - init = true; + // + // // 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; } @@ -192,9 +269,14 @@ public partial class CECGameRun } } + public CECGameRun() + { + m_iGameState = (int)GameState.GS_NONE; + } + public CECHostPlayer GetHostPlayer() { - return hostPlayer; + return m_pHostPlayer; } public void InitCharacter(cmd_self_info_1 info) { @@ -218,19 +300,19 @@ public partial class CECGameRun m_InstTab.Add(idInst, new CECInstance()); // Update global world instance id used by task checks - CECWorld.Instance?.SetInstanceID(idInst); + CECGameRun.Instance?.GetWorld()?.SetInstanceID(idInst); } } CECPlayer.InitStaticRes(); - hostPlayer = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true).AddComponent(); - hostPlayer.InitCharacter(info); + m_pHostPlayer = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true).AddComponent(); + m_pHostPlayer.InitCharacter(info); - if (hostPlayer != null) + if (m_pHostPlayer != null) { var t = Type.GetType("BrewMonster.UI.SelectedTargetHUDController, Assembly-CSharp"); - if (t != null && hostPlayer.GetComponent(t) == null) - hostPlayer.gameObject.AddComponent(t); + if (t != null && m_pHostPlayer.GetComponent(t) == null) + m_pHostPlayer.gameObject.AddComponent(t); } } public CECMonster GetMonster() @@ -926,7 +1008,172 @@ public partial class CECGameRun } 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; + } + + + } public enum GameState diff --git a/Assets/Scripts/EC_GameRun.cs.meta b/Assets/Scripts/EC_GameRun.cs.meta new file mode 100644 index 0000000000..6ff658e8ba --- /dev/null +++ b/Assets/Scripts/EC_GameRun.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d68cfcd6293664808ba733553314250a \ No newline at end of file