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)