From bc743a8e89c3c10e5ac9345746dfae2131df0091 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 9 Mar 2026 14:52:55 +0700 Subject: [PATCH] Add CreateGetOtherEquipDetailCmd and update player option UI prefab --- .../Prefab/UI/PlayerOptionPopup.prefab | 2 +- .../CSNetwork/C2SCommand/C2SCommand.cs | 13 ++++ .../CSNetwork/C2SCommand/C2SCommandFactory.cs | 16 +++++ .../Scripts/Network/CSNetwork/GPDataType.cs | 52 +++++++++++++++ .../Scripts/Network/CSNetwork/GameSession.cs | 63 ++++++++++++++++++- .../Scripts/Network/UnityGameSession.cs | 6 ++ .../Scripts/UI/Dialogs/DlgPlayerOptions.cs | 3 +- 7 files changed, 152 insertions(+), 3 deletions(-) diff --git a/Assets/PerfectWorld/Prefab/UI/PlayerOptionPopup.prefab b/Assets/PerfectWorld/Prefab/UI/PlayerOptionPopup.prefab index 0ae7acbd5d..d432d82ba0 100644 --- a/Assets/PerfectWorld/Prefab/UI/PlayerOptionPopup.prefab +++ b/Assets/PerfectWorld/Prefab/UI/PlayerOptionPopup.prefab @@ -116,7 +116,7 @@ MonoBehaviour: m_PressedTrigger: Pressed m_SelectedTrigger: Selected m_DisabledTrigger: Disabled - m_Interactable: 0 + m_Interactable: 1 m_TargetGraphic: {fileID: 369226366953714584} m_OnClick: m_PersistentCalls: diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs index f018981f06..8fb7db980a 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs @@ -457,6 +457,19 @@ namespace CSNetwork.C2SCommand public int[] idList; // Variable length array } + // Get other player equip/profile detail (view other player profile) — same shape as CMD_GetOtherEquip + public struct CMD_GetOtherEquipDetail + { + public ushort size; + public int[] idList; // Variable length array (role IDs) + } + + // Single-role variant: body is only roleId (4 bytes). Some servers expect this instead of size+idList. + public struct CMD_GetOtherEquipDetailSingle + { + public int roleId; + } + // Team set pickup command public struct CMD_TeamSetPickup { diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs index 3f7b3eef44..21f10d901e 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs @@ -377,6 +377,22 @@ namespace CSNetwork.C2SCommand return SerializeCommand(CommandID.SELECT_TARGET, cmd); } + /// Create C2S GET_OTHER_EQUIP_DETAIL command (view other player profile/equip). Sends cmd + roleId only (4-byte body). + public static Octets CreateGetOtherEquipDetailCmd(int roleId) + { + var cmd = new CMD_GetOtherEquipDetailSingle { roleId = roleId }; + return SerializeCommand(CommandID.GET_OTHER_EQUIP_DETAIL, cmd); + } + + /// Create C2S GET_OTHER_EQUIP_DETAIL command for multiple role IDs (size + idList format). + public static Octets CreateGetOtherEquipDetailCmd(int[] roleIds) + { + if (roleIds == null || roleIds.Length == 0) + throw new ArgumentException("roleIds cannot be null or empty.", nameof(roleIds)); + var cmd = new CMD_GetOtherEquipDetail { size = (ushort)roleIds.Length, idList = roleIds }; + return SerializeCommand(CommandID.GET_OTHER_EQUIP_DETAIL, cmd); + } + public static Octets CreateDropIvtrItem(byte index, int amount) { var cmd = new CMD_DropIvtrItem diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs index 5ee7b11205..1b0a2530d1 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs @@ -1363,6 +1363,34 @@ namespace CSNetwork.GPDataType public uint count_equip; } + /// S2C PLAYER_EQUIP_DETAIL: one equipment slot entry (slot index + item template id). + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct s2c_player_equip_detail_slot + { + public byte slot_index; + public int tid; + } + + /// Parsed S2C PLAYER_EQUIP_DETAIL payload for readable use. + public class PlayerEquipDetailData + { + public int RoleId { get; set; } + public List<(byte SlotIndex, int Tid)> Slots { get; } = new List<(byte, int)>(); + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append($"[PlayerEquipDetail] RoleId={RoleId}, Slots={Slots.Count}: "); + for (int i = 0; i < Slots.Count; i++) + { + var (slot, tid) = Slots[i]; + if (i > 0) sb.Append(", "); + sb.Append($"[{slot}]=tid{tid}"); + } + return sb.ToString(); + } + } + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct cmd_move_equip_item { @@ -1701,6 +1729,30 @@ namespace CSNetwork.GPDataType return (id & 0xC0000000) == 0xC0000000; } + /// Parse S2C PLAYER_EQUIP_DETAIL payload into readable data. Layout: roleId (4), count (2), then count × (slot byte, tid int). + public static PlayerEquipDetailData ParsePlayerEquipDetail(byte[] data) + { + var result = new PlayerEquipDetailData(); + if (data == null || data.Length < 6) + return result; + + int pos = 0; + result.RoleId = BitConverter.ToInt32(data, pos); + pos += 4; + ushort count = BitConverter.ToUInt16(data, pos); + pos += 2; + + int slotSize = Marshal.SizeOf(); + for (int i = 0; i < count && pos + slotSize <= data.Length; i++) + { + var slot = FromBytes(data, pos); + result.Slots.Add((slot.slot_index, slot.tid)); + pos += slotSize; + } + + return result; + } + public static A3DVECTOR3 g_vOrigin = new A3DVECTOR3(0.0f); public static A3DVECTOR3 g_vAxisX = new A3DVECTOR3(1.0f, 0.0f, 0.0f); public static A3DVECTOR3 g_vAxisY = new A3DVECTOR3(0.0f, 1.0f, 0.0f); diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 902c16efd5..b7d606bb35 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -15,6 +15,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using BrewMonster.Scripts; +using BrewMonster.Scripts.Managers; using UnityEngine; using CommandID = CSNetwork.GPDataType.CommandID; @@ -639,7 +640,39 @@ namespace CSNetwork EC_Game.GetGameRun().SetLogoutFlag(2); } } - + + /// Format parsed PLAYER_EQUIP_DETAIL with real game data: slot names and item names from element data. + private static string FormatPlayerEquipDetailReadable(PlayerEquipDetailData parsed) + { + var sb = new StringBuilder(); + sb.Append($"RoleId={parsed.RoleId}"); + if (parsed.Slots.Count == 0) + { + sb.Append(", no slots"); + return sb.ToString(); + } + sb.Append(" | "); + for (int i = 0; i < parsed.Slots.Count; i++) + { + var (slotIndex, tid) = parsed.Slots[i]; + string slotName = GetEquipSlotName(slotIndex); + string itemName = tid <= 0 ? "(empty)" : (EC_IvtrItemUtils.Instance.ResolveItemName(tid) ?? $"(tid{tid})"); + if (string.IsNullOrEmpty(itemName)) itemName = $"(tid{tid})"; + if (i > 0) sb.Append(", "); + sb.Append($"{slotName}: {itemName}"); + } + return sb.ToString(); + } + + private static string GetEquipSlotName(byte slotIndex) + { + if (Enum.IsDefined(typeof(IndexOfIteminEquipmentInventory), slotIndex)) + { + var name = Enum.GetName(typeof(IndexOfIteminEquipmentInventory), slotIndex); + if (!string.IsNullOrEmpty(name)) return name; + } + return $"Slot{slotIndex}"; + } private void HandleServerDataSend(gamedatasend protocol) { @@ -783,6 +816,26 @@ namespace CSNetwork EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ITEMOPERATION, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); break; + case CommandID.PLAYER_EQUIP_DETAIL: + // View other player profile/equip detail — parse and log with real game data (item names, slot names) + if (pDataBuf != null && pDataBuf.Length > 0) + { + try + { + var parsed = GPDataTypeHelper.ParsePlayerEquipDetail(pDataBuf); + string readable = FormatPlayerEquipDetailReadable(parsed); + Debug.Log($"[PLAYER_EQUIP_DETAIL] {readable}"); + if (parsed.Slots.Count == 0 && pDataBuf.Length > 6) + Debug.Log($"[PLAYER_EQUIP_DETAIL] Raw(hex): {BitConverter.ToString(pDataBuf)}"); + } + catch (Exception ex) + { + Debug.LogWarning($"[PLAYER_EQUIP_DETAIL] Parse failed: {ex.Message}. Raw length={pDataBuf.Length}, hex: {BitConverter.ToString(pDataBuf, 0, Math.Min(64, pDataBuf.Length))}..."); + } + } + else + Debug.Log("[PLAYER_EQUIP_DETAIL] Server sent empty payload."); + break; case CommandID.PLAYER_CASH: { EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, @@ -1824,6 +1877,14 @@ namespace CSNetwork // } } + /// Request other player profile/equip detail (C2S GET_OTHER_EQUIP_DETAIL). Server responds with PLAYER_EQUIP_DETAIL. + public void c2s_SendCmdGetOtherEquipDetail(int roleId) + { + gamedatasend gamedatasend = new gamedatasend(); + gamedatasend.Data = C2SCommandFactory.CreateGetOtherEquipDetailCmd(roleId); + SendProtocol(gamedatasend); + } + public void c2s_SendCmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem) { gamedatasend gamedatasend = new gamedatasend(); diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index 513ed79758..01a1120f31 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -732,6 +732,12 @@ namespace BrewMonster.Network Instance._gameSession.CmdCache.SendCmdExtProps(); } + /// Request other player profile/equip detail (C2S GET_OTHER_EQUIP_DETAIL). Server responds with PLAYER_EQUIP_DETAIL; response is currently logged via Debug.Log. + public static void c2s_SendCmdGetOtherEquipDetail(int roleId) + { + Instance._gameSession.c2s_SendCmdGetOtherEquipDetail(roleId); + } + /// Send C2S::SET_STATUS_POINT (attribute point allocation). public static void c2s_CmdSetStatusPts(int vitality, int energy, int strength, int agility) { diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPlayerOptions.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPlayerOptions.cs index 1f2a8c2b04..0d341b9901 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPlayerOptions.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgPlayerOptions.cs @@ -60,7 +60,8 @@ namespace BrewMonster.UI { Debug.Log("OnViewInfo: " + characterId); var list = new List { characterId }; - UnityGameSession.GetRoleBaseInfo(1, list); + //UnityGameSession.GetRoleBaseInfo(1, list); + UnityGameSession.c2s_SendCmdGetOtherEquipDetail(characterId); } void OnTeamInvite(int characterId)