diff --git a/Assets/PerfectWorld/Scripts/GameData/GameRunConfig.cs b/Assets/PerfectWorld/Scripts/GameData/GameRunConfig.cs index 00da34eb39..48f97a5adc 100644 --- a/Assets/PerfectWorld/Scripts/GameData/GameRunConfig.cs +++ b/Assets/PerfectWorld/Scripts/GameData/GameRunConfig.cs @@ -18,4 +18,4 @@ namespace BrewMonster public GameObject NpcServerPrefab => npcServerPrefab.gameObject; public GameObject TestVfxPrefab => testVfxPrefab; } -} \ No newline at end of file +} diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs index aff1af0f7e..73d1204849 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs @@ -87,6 +87,8 @@ namespace PerfectWorld.Scripts.Managers OnMsgMatterDisappear(Msg); break; } + case EC_MsgDef.MSG_MM_MATTEROUTOFVIEW: OnMsgMatterOutOfView(Msg); break; + case EC_MsgDef.MSG_MM_INVALIDOBJECT: OnMsgInvalidObject(Msg); break; } } @@ -346,5 +348,18 @@ namespace PerfectWorld.Scripts.Managers { return null; } + + bool OnMsgMatterOutOfView(ECMSG Msg) + { + MatterLeave(Convert.ToInt32(Msg.dwParam1)); + return true; + } + + bool OnMsgInvalidObject(ECMSG Msg) + { + cmd_invalid_object pCmd = GPDataTypeHelper.FromBytes((byte[])Msg.dwParam1); + MatterLeave(pCmd.id); + return true; + } } } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs b/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs index 55048b1302..d679e9ccc8 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs @@ -87,7 +87,15 @@ namespace PerfectWorld.Scripts.Managers case EC_MsgDef.MSG_PM_PLAYEREXTPROP: OnMsgPlayerExtProp(Msg); break; - + case EC_MsgDef.MSG_PM_PLAYERDISAPPEAR: + OnMsgPlayerDisappear(Msg); + break; + case EC_MsgDef.MSG_PM_PLAYEROUTOFVIEW: + OnMsgPlayerOutOfView(Msg); + break; + case EC_MsgDef.MSG_PM_INVALIDOBJECT: + OnMsgInvalidObject(Msg); + break; case int value when value == EC_MsgDef.MSG_PM_PLAYERDUELOPT: // PLAYER_DUEL_START (229): server may send to both duelists; update host state if host is one of them // dwParam2 is ushort (pCmdHeader from GameSession); use Convert to avoid InvalidCastException when unboxing @@ -525,7 +533,11 @@ namespace PerfectWorld.Scripts.Managers private EC_ElsePlayer CreateElsePlayer(info_player_1 info, int iAppearFlag) { GameObject ob = CECGameRun.Instance.InitCharacter(info); - EC_ElsePlayer elsePlayer = ob.AddComponent(); + EC_ElsePlayer elsePlayer; + if (!ob.TryGetComponent(out elsePlayer)) + { + elsePlayer = ob.AddComponent(); + } elsePlayer.Init(info, iAppearFlag); return elsePlayer; @@ -618,7 +630,11 @@ namespace PerfectWorld.Scripts.Managers } // Destroy the player GameObject if (pPlayer.gameObject != null) - UnityEngine.Object.Destroy(pPlayer.gameObject); + { + PoolManager.Instance.Despawn(pPlayer.m_pPlayerCECModel.m_pPlayerModel); + PrefabPoolManager.Instance.Despawn(pPlayer.gameObject); + GameObject.Destroy(pPlayer); + } } } @@ -917,6 +933,32 @@ namespace PerfectWorld.Scripts.Managers ElsePlayerLeave(pCmd.id, true); return true; } + + bool OnMsgPlayerOutOfView(ECMSG Msg) + { + int id = Convert.ToInt32(Msg.dwParam1); + ElsePlayerLeave(id, false); + return true; + } + + bool OnMsgPlayerDisappear(ECMSG Msg) + { + var pCmd = Convert.ToInt32(Msg.dwParam1); + ElsePlayerLeave(pCmd, false); + return true; + } + + bool OnMsgInvalidObject(ECMSG Msg) + { + cmd_invalid_object pCmd = GPDataTypeHelper.FromBytes((byte[])Msg.dwParam1); + // Remove the player if it exists + EC_ElsePlayer pPlayer = GetElsePlayer(pCmd.id); + if (pPlayer) + { + ElsePlayerLeave(pCmd.id, false); + } + return true; + } } } public struct EC_PLAYERLOADRESULT diff --git a/Assets/PerfectWorld/Scripts/Managers/NPCManager.cs b/Assets/PerfectWorld/Scripts/Managers/NPCManager.cs index ecbde51b2f..a14c55d1b1 100644 --- a/Assets/PerfectWorld/Scripts/Managers/NPCManager.cs +++ b/Assets/PerfectWorld/Scripts/Managers/NPCManager.cs @@ -103,17 +103,17 @@ namespace BrewMonster.Scripts.Managers public async Task GetModelPlayer(byte profession, byte gender) { - var prefab = await AddressableManager.Instance.LoadPrefabAsync(_playerModelPaths[profession * GENDER.NUM_GENDER + gender]); + var playerOb = await PoolManager.Instance.SpawnAsync(_playerModelPaths[profession * GENDER.NUM_GENDER + gender], Vector3.zero, Quaternion.identity, memoryReleaseTTL: 0f); BMLogger.LogError("_playerModelPaths[profession * GENDER.NUM_GENDER + gender] = " + _playerModelPaths[profession * GENDER.NUM_GENDER + gender]); - if (prefab == null) + if (playerOb == null) { BMLogger.LogError( $" [NPC Manager] Not found Prefab from Addressable with profession : {profession} gender : {gender}"); return null; } - var player = Instantiate(prefab); - player.SetActive(true); - return player; + //var player = Instantiate(prefab); + playerOb.SetActive(true); + return playerOb; } public async Task GetDummyModel(int iShape) { diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index 08d6361b9b..a01ef31d2e 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -103,7 +103,7 @@ namespace BrewMonster // 需要是可能 || Need is possible protected bool m_bHangerOn = false; protected int m_iCurAction; - protected bool m_bAboutToDie = false; + protected bool m_bAboutToDie = false; public bool m_bCandHangerOn = false; public bool m_bPetInSanctuary = false; // true, the pet pet of the player is in sanctuary //The ID of the currently summoned pet @@ -3445,7 +3445,7 @@ namespace BrewMonster } try { - m_PetModelVisual = await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(szPetPath.ToLower(), true), Vector3.zero, Quaternion.identity, 15f); + m_PetModelVisual = await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(szPetPath.ToLower(), true), Vector3.zero, Quaternion.identity, memoryReleaseTTL: 0f); if(m_PetModelVisual == null) { m_PetModelVisual = GameObject.CreatePrimitive(PrimitiveType.Capsule); diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs index ddcdeb5fc4..098711dff1 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs @@ -66,8 +66,6 @@ public class CECNPC : CECObject [SerializeField] protected bool isDebug; [SerializeField] protected NPCVisual npcVisual; GameObject m_modelVisual = null; - float m_fDistShowModelVisual = 25f; // Display distance model NPC - float m_fDistHideModelVisual = 40f; // Hide/display distance model NPC protected static CECStringTab m_ActionNames; /* public string NameNPC => m_strName; @@ -638,11 +636,11 @@ public class CECNPC : CECObject if(m_modelVisual != null) { - if(m_fDistToHostH < m_fDistShowModelVisual) + if(m_fDistToHostH < AddressResourceConfig.m_fDistShowModelVisual) { m_modelVisual.SetActive(true); } - else if(m_fDistToHostH > m_fDistHideModelVisual) + else if(m_fDistToHostH > AddressResourceConfig.m_fDistHideModelVisual) { m_modelVisual.SetActive(false); } diff --git a/Assets/PerfectWorld/Scripts/NPC/NPCBuilder.cs b/Assets/PerfectWorld/Scripts/NPC/NPCBuilder.cs index 4b8bd0afe8..15dc18c80c 100644 --- a/Assets/PerfectWorld/Scripts/NPC/NPCBuilder.cs +++ b/Assets/PerfectWorld/Scripts/NPC/NPCBuilder.cs @@ -30,7 +30,7 @@ public class NPCBuilder : MonoSingleton public async Task GetModelByPath(string path) { //return await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(path)); - return await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(path), Vector3.zero, Quaternion.identity, memoryReleaseTTL: 15f, autoDespawnTime: 0f); + return await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(path), Vector3.zero, Quaternion.identity, memoryReleaseTTL: 0f, autoDespawnTime: 0f); } #if UNITY_EDITOR diff --git a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs index a7e2754672..769a82f63a 100644 --- a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs +++ b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs @@ -16,6 +16,7 @@ public class NPCVisual : MonoBehaviour private const FadeMode FadeMode = Animancer.FadeMode.FixedDuration; public CECNPC.INFO GetNPCINFO => m_NPCInfo; + public bool TryPlayAction(string animationName, CECAttackEvent cECAttackEvent, bool isHit = false, bool bRestart = true) { // BMLogger.LogMono(this, "HoangDev: TryPlayAction: " + animationName); @@ -50,6 +51,7 @@ public class NPCVisual : MonoBehaviour BMLogger.LogWarning("animancer == null"); return; } + namedAnimancer.Animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; EventBus.SubscribeChannel(m_NPCInfo.nid, OnQueueAction); EventBus.SubscribeChannel(m_NPCInfo.nid, OnClearComActFlagEvent); } diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs index b92ddf8b37..0012496751 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs @@ -3261,4 +3261,10 @@ namespace CSNetwork.GPDataType public int exp; public int sp; }; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct cmd_invalid_object + { + public int id; + }; } diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 5d2449ed24..8de0c8bf4f 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -808,7 +808,7 @@ namespace CSNetwork m_CmdCache.RemoveAllCmds(); // int iFlag; - // switch (p->result) + // switch (p.result) // { // case _PLAYER_LOGOUT_FULL: iFlag = 0; break; // case _PLAYER_LOGOUT_HALF: iFlag = 1; break; @@ -822,13 +822,13 @@ namespace CSNetwork default: iFlag = 2; break; } - // g_pGame->GetGameRun()->SetLogoutFlag(iFlag); + // g_pGame.GetGameRun().SetLogoutFlag(iFlag); EC_Game.GetGameRun().SetLogoutFlag(iFlag); - // if (!IsConnected() && g_pGame->GetGameRun()->GetLogoutFlag() == 1) + // if (!IsConnected() && g_pGame.GetGameRun().GetLogoutFlag() == 1) // { // a_LogOutput(1, "CECGameSession::OnPrtcPlayerLogout, LogoutFlag=1 replaced by 2."); - // g_pGame->GetGameRun()->SetLogoutFlag(2); + // g_pGame.GetGameRun().SetLogoutFlag(2); // } // Keep half logout as role-select even if the server closes the socket. // LoginScene will reconnect with saved credentials if needed. @@ -1021,7 +1021,7 @@ namespace CSNetwork case CommandID.EQUIP_DATA: case CommandID.EQUIP_DATA_CHANGED: // BMLogger.Log($"### EQUIP_DATA: CMDID {pCmdHeader}"); - // MSG_PM_PLAYEREQUIPDATA, MAN_PLAYER, -1, (DWORD)pDataBuf, pCmdHeader->cmd, dwDataSize); + // MSG_PM_PLAYEREQUIPDATA, MAN_PLAYER, -1, (DWORD)pDataBuf, pCmdHeader.cmd, dwDataSize); EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREQUIPDATA, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader); break; case CommandID.PLAYER_EQUIP_DETAIL: @@ -1548,9 +1548,20 @@ namespace CSNetwork } case CommandID.HOST_NOTIFY_ROOT: case CommandID.HOST_DISPEL_ROOT: - EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ROOTNOTIFY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); break; + case CommandID.INVALID_OBJECT: + { + cmd_invalid_object pCmd = GPDataTypeHelper.FromBytes((byte[])pDataBuf); + if (ISPLAYERID(pCmd.id)) + EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_INVALIDOBJECT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader); + else if (ISNPCID(pCmd.id)) + EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_INVALIDOBJECT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader); + else if (ISMATTERID(pCmd.id)) + EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_INVALIDOBJECT, MANAGER_INDEX.MAN_MATTER, 0, pDataBuf, pCmdHeader); + + break; + } default: #if UNITY_EDITOR if (isDebug) @@ -1569,7 +1580,7 @@ namespace CSNetwork var callback = _selectRoleCallback; PostToUnityContext(() => callback?.Invoke(_selectedRole)); - // in C++: we call CECGameRun() via pLoginUIMan->LaunchLoading(); + // 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); } @@ -2621,15 +2632,15 @@ namespace CSNetwork if (!pBuf) return; - ((cmd_header*)pBuf)->cmd = C2S::GET_OTHER_EQUIP; + ((cmd_header*)pBuf).cmd = C2S::GET_OTHER_EQUIP; cmd_get_other_equip* pCmd = (cmd_get_other_equip*)(pBuf + sizeof (cmd_header)); - pCmd->size = (WORD)iNumSend; + pCmd.size = (WORD)iNumSend; for (int i=0; i < iNumSend; i++) - pCmd->idlist[i] = aIDs[iCount+i]; + pCmd.idlist[i] = aIDs[iCount+i]; - g_pGame->GetGameSession()->SendGameData(pBuf, iSize); + g_pGame.GetGameSession().SendGameData(pBuf, iSize); a_freetemp(pBuf); //*/ diff --git a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs index e3a79ecc6d..5776c5bd88 100644 --- a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs +++ b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs @@ -162,7 +162,7 @@ namespace PerfectWorld.Scripts string filePath = ByteToStringUtils.ByteArrayToCP936String((byte[])fileMatterValue); //var matterPrefab = await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(filePath.ToLower(), true)); - var matterObject = await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(filePath.ToLower(), true), Vector3.zero, Quaternion.identity, 15f); + var matterObject = await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(filePath.ToLower(), true), Vector3.zero, Quaternion.identity, memoryReleaseTTL: 0f); if (matterObject != null) { //var matterObject = Instantiate(matterPrefab); diff --git a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs index c9c0ad150c..c9cf45fca8 100644 --- a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs +++ b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs @@ -447,6 +447,17 @@ namespace BrewMonster { m_fDistToHost = CalcDist(pHost.GetPos(), true); m_fDistToHostH = CalcDist(pHost.GetPos(), false); + if (m_pPlayerModel != null) + { + if (m_fDistToHostH < AddressResourceConfig.m_fDistShowModelVisual) + { + m_pPlayerModel.SetActive(true); + } + else if (m_fDistToHostH > AddressResourceConfig.m_fDistHideModelVisual) + { + m_pPlayerModel.SetActive(false); + } + } } m_pEPWorkMan?.Tick(Time.deltaTime); } diff --git a/Assets/PerfectWorld/Scripts/Utility/AddressResourceConfig.cs b/Assets/PerfectWorld/Scripts/Utility/AddressResourceConfig.cs index 62dd12ee72..cd58a08dea 100644 --- a/Assets/PerfectWorld/Scripts/Utility/AddressResourceConfig.cs +++ b/Assets/PerfectWorld/Scripts/Utility/AddressResourceConfig.cs @@ -9,5 +9,9 @@ namespace BrewMonster public static string TestVfxPrefab = "Prefabs/Vfx/TestVfx"; public static string PetServerPrefab = "Pet/PetPrefab"; public static string PetMountServerPrefab = "Pet/PetMount/PetMountPrefab"; + + + public static float m_fDistShowModelVisual = 25f; // Display distance model NPC + public static float m_fDistHideModelVisual = 40f; // Hide/display distance model NPC } } diff --git a/Assets/Scripts/EC_GameRun.cs b/Assets/Scripts/EC_GameRun.cs index 9fc1acbdef..6ddffd5f04 100644 --- a/Assets/Scripts/EC_GameRun.cs +++ b/Assets/Scripts/EC_GameRun.cs @@ -145,6 +145,7 @@ public partial class CECGameRun : ITickable { BMLogger.LogWarning("CECGameRun::LoadPrefabs, Loading prefabs from Resources. Consider using Addressables for better performance and memory management."); _playerPrefab = Resources.Load(AddressResourceConfig.PlayerPrefab); + PrefabPoolManager.Instance.InitPool(_playerPrefab); _monsterPrefab = Resources.Load(AddressResourceConfig.MonsterPrefab); PrefabPoolManager.Instance.InitPool(_monsterPrefab, defaultCapacity: 200, maxSize: 250); _npcServerPrefab = Resources.Load(AddressResourceConfig.NpcServerPrefab); @@ -386,7 +387,7 @@ public partial class CECGameRun : ITickable } CECPlayer.InitStaticRes(); - m_pHostPlayer = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true).AddComponent(); + m_pHostPlayer = PrefabPoolManager.Instance.Spawn(_playerPrefab, Vector3.zero, Quaternion.identity, ObjectSpawner.Instance.transform).AddComponent(); m_pHostPlayer.InitCharacter(info); if (m_pHostPlayer != null) @@ -453,7 +454,7 @@ public partial class CECGameRun : ITickable Debug.LogError("null prefab"); return null; } - GameObject character = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true); + GameObject character = PrefabPoolManager.Instance.Spawn(_playerPrefab, Vector3.zero, Quaternion.identity, ObjectSpawner.Instance.transform); return character.gameObject; } diff --git a/Assets/Scripts/PlayerVisual.cs b/Assets/Scripts/PlayerVisual.cs index 121e6e0d86..5dfd0ace41 100644 --- a/Assets/Scripts/PlayerVisual.cs +++ b/Assets/Scripts/PlayerVisual.cs @@ -263,6 +263,12 @@ namespace BrewMonster { UnregisterPlayerEventHandlers(); } + + private void OnDisable() + { + UnregisterPlayerEventHandlers(); + } + public bool IsAnimationExist(string animationName) { var exists = namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false; @@ -395,4 +401,4 @@ namespace BrewMonster return true; } } -} \ No newline at end of file +}