From 2b644ba5eb899deac60658d01f8d16bbfad66bc0 Mon Sep 17 00:00:00 2001
From: HungDK <>
Date: Sat, 28 Feb 2026 18:14:10 +0700
Subject: [PATCH 1/3] Add missing purchase item
---
.../Scripts/Managers/EC_Inventory.cs | 39 +
.../Scripts/Network/CSNetwork/GPDataType.cs | 22 +-
.../Scripts/Network/CSNetwork/GameSession.cs | 4108 +++++++++--------
.../Scripts/UI/NPCShopDetailPanel.cs | 16 +-
.../Scripts/UI/NPCShopItemPanel.cs | 6 +-
.../Scripts/UI/NPCShopUIManager.cs | 18 +-
Assets/Scripts/CECHostPlayer.cs | 3 +
7 files changed, 2154 insertions(+), 2058 deletions(-)
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
index 93f3700dce..4240c01bfb 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
@@ -129,6 +129,42 @@ namespace BrewMonster.Scripts.Managers
m_aItems[iSlot2] = tmp;
}
+ ///
+ /// Place or stack item in a specific slot (server-specified slot). Matches C++ expectation that client uses same slot as server.
+ ///
+ public bool PutItemInSlot(int iSlot, int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount)
+ {
+ piLastSlot = -1;
+ piLastAmount = 0;
+ if (iSlot < 0 || iSlot >= m_aItems.Length || iAmount <= 0)
+ return false;
+
+ var slotItem = m_aItems[iSlot];
+ if (slotItem == null)
+ {
+ var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount);
+ if (newItem == null)
+ return false;
+ newItem.Slot = iSlot;
+ newItem.SetCount(iAmount);
+ m_aItems[iSlot] = newItem;
+ piLastSlot = iSlot;
+ piLastAmount = iAmount;
+ return true;
+ }
+ if (slotItem.GetTemplateID() != tid)
+ return false;
+ int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(tid));
+ int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.GetCount()));
+ if (canAdd <= 0)
+ return false;
+ int add = Math.Min(canAdd, iAmount);
+ slotItem.AddAmount(add);
+ piLastSlot = iSlot;
+ piLastAmount = slotItem.GetCount();
+ return true;
+ }
+
public bool MergeItem(int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount)
{
piLastSlot = -1;
@@ -170,6 +206,9 @@ namespace BrewMonster.Scripts.Managers
return false;
}
var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount);
+ if (newItem == null)
+ return false;
+ newItem.Slot = firstEmpty;
newItem.SetCount(iAmount);
m_aItems[firstEmpty] = newItem;
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs
index a2f2c05aea..2fcbaa4fc5 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs
@@ -1315,6 +1315,26 @@ namespace CSNetwork.GPDataType
public byte bySlot;
}
+ /// One item in cmd_purchase_item (buy from NPC/booth). Wire: item_id, expire_date, count, inv_index, booth_slot = 15 bytes.
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct cmd_purchase_item_ITEM
+ {
+ public int item_id;
+ public int expire_date;
+ public uint count;
+ public ushort inv_index;
+ public byte booth_slot;
+ }
+
+ /// Fixed header of cmd_purchase_item. Rest of packet is item_count x cmd_purchase_item_ITEM.
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct cmd_purchase_item_header
+ {
+ public uint cost;
+ public uint yinpiao;
+ public byte flag;
+ public ushort item_count;
+ }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_get_own_money
@@ -1394,7 +1414,7 @@ namespace CSNetwork.GPDataType
public byte index;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
- struct cmd_pickup_item
+ public struct cmd_pickup_item
{
public int tid;
public int expire_date;
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
index dc0a3ac586..2cd0e8486d 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
@@ -1,2043 +1,2067 @@
-using BrewMonster;
-using BrewMonster.Common;
-using BrewMonster.Managers;
-using BrewMonster.Network;
-using BrewMonster.Scripts.Skills;
-using BrewMonster.UI;
-using CSNetwork.C2SCommand;
-using CSNetwork.GPDataType;
-using CSNetwork.Protocols;
-using CSNetwork.Protocols.RPCData;
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using UnityEngine;
-using CommandID = CSNetwork.GPDataType.CommandID;
-
-namespace CSNetwork
-{
- public class GameSession : IDisposable
- {
- private static IPrefixedLogger
- _logger = LoggerFactory.GetLogger(nameof(GameSession)); // Get class-specific logger
-
- private NetworkManager _networkManager;
- private string _host;
- private int _port;
- private string _username;
- private string _password;
- private int _currentUserId = -1; // To store the UserID after successful login
- private uint _localsid = 0; // To store the LocalSID from onlineannounce
- private int m_iCharID;
- private int m_idLastSelTarget = 0; // ID of selected item last time
-
- CECStringTab m_ErrorMsgs;
-
- // State management for async operations and callbacks
- private Action _loginCallback;
- private Action> _roleListCallback;
- private List _accumulatedRoles;
- private Action _selectRoleCallback;
- private Action _createRoleCallback;
- private RoleInfo _selectedRole;
- public bool IsConnected => _networkManager?.IsConnected ?? false;
- public static SynchronizationContext Context;
- private CECC2SCmdCache m_CmdCache; // C2S command cache
-
- /// Raised when server sends PROTOCOL_PLAYERLOGOUT(69).
- public event Action PlayerLogoutReceived;
-
- /// Raised when the underlying network disconnects.
- public event Action Disconnected;
-
- private static void PostToUnityContext(Action action)
- {
- if (action == null) return;
- var ctx = Context;
- if (ctx != null)
- {
- ctx.Post(_ => action(), null);
- }
- else
- {
- action();
- }
- }
-#if UNITY_EDITOR
- public bool isDebug;
- public bool IsDebug
- {
- get => isDebug;
- set => isDebug = value;
- }
-#endif
- public CECC2SCmdCache CmdCache { get => m_CmdCache; }
-
-
- public GameSession()
- {
- _networkManager = new NetworkManager();
- m_CmdCache = new CECC2SCmdCache();
- _networkManager.ProtocolReceived += OnProtocolReceived;
- _networkManager.ErrorOccurred += OnErrorOccurred;
- _networkManager.Disconnected += OnDisconnected;
- }
-
- public void SetLogPath(string path)
- {
- LoggerFactory.SetFileLoggerImplementation(new FileLogger());
- _logger = LoggerFactory.GetCustomLogger(path, nameof(GameSession) + GetHashCode(), LoggerType.File);
- _networkManager.SetLogPath(path);
- }
-
- ///
- /// Connects to the game server asynchronously.
- ///
- /// Server hostname or IP address.
- /// Server port.
- /// Task representing the asynchronous connect operation. Check IsConnected property or handle Disconnected event for status.
- public async Task ConnectAsync(string host, int port)
- {
- if (IsConnected)
- {
- _logger.Log(LogType.Warning, "ConnectAsync called but already connected.");
- return;
- }
-
- _host = host;
- _port = port;
- _logger.Log(LogType.Info, $"Attempting to connect to {_host}:{_port}...");
- try
- {
- await _networkManager.ConnectAsync(_host, _port);
- if (IsConnected)
- {
- _logger.Log(LogType.Info, "Connection established.");
- }
- else
- {
- _logger.Log(LogType.Warning,
- "Connection failed after ConnectAsync completed (check NetworkManager logs/events).");
- }
- }
- catch (Exception ex)
- {
- _logger.Log(LogType.Error, $"Connection exception: {ex.Message}");
- _logger.LogException(ex);
- OnDisconnected();
- }
- }
-
-
- public void Disconnect()
- {
- _networkManager.Disconnect();
- }
-
- ///
- /// Initiates the login process asynchronously.
- ///
- /// Account username.
- /// Account password.
- /// Action invoked with true on successful login (OnlineAnnounce received), false otherwise.
- public void LoginAsync(string username, string password, Action callback)
- {
- if (!IsConnected)
- {
- _logger.Log(LogType.Warning, "LoginAsync called but not connected.");
- callback?.Invoke(false);
- return;
- }
-
- if (_loginCallback != null)
- {
- _logger.Log(LogType.Warning, "LoginAsync called while another login is already in progress.");
- callback?.Invoke(false);
- return;
- }
-
- _username = username;
- _password = password;
- _loginCallback = callback;
- _currentUserId = -1; // Reset user ID
-
- _logger.Log(LogType.Info, $"Initiating login for user '{_username}'...");
- }
-
- ///
- /// Initiates fetching the role list asynchronously. Requires successful login.
- ///
- /// Action invoked with the complete list of roles, or null/empty list on failure.
- public void GetRoleListAsync(Action> callback)
- {
- if (!IsConnected)
- {
- _logger.Log(LogType.Warning, "GetRoleListAsync called but not connected.");
- callback?.Invoke(null);
- return;
- }
-
- if (_currentUserId == -1)
- {
- _logger.Log(LogType.Warning, "GetRoleListAsync called but not logged in.");
- callback?.Invoke(null);
- return;
- }
-
- if (_roleListCallback != null)
- {
- _logger.Log(LogType.Warning,
- "GetRoleListAsync called while another role list retrieval is already in progress.");
- callback?.Invoke(null);
- return;
- }
-
- _roleListCallback = callback;
- _accumulatedRoles = new List();
- _logger.Log(LogType.Info, "Requesting role list...");
- RequestRoleListInternal();
- }
-
- public RoleInfo GetRoleInfo()
- {
- return _selectedRole;
- }
-
- public void SelectRoleAsync(RoleInfo role, Action callback)
- {
- _selectedRole = role;
- _selectRoleCallback = callback;
- SetCharacterID(role.roleid);
- SendProtocol(new selectrole()
- {
- Roleid = role.roleid,
- Flag = 0
- });
- }
-
- public void CreateRoleAsync(RoleInfo roleInfo, Octets referId, Action callback)
- {
- if (!IsConnected)
- {
- callback?.Invoke(null);
- return;
- }
-
- if (_currentUserId == -1)
- {
- callback?.Invoke(null);
- return;
- }
-
- if (_createRoleCallback != null)
- {
- callback?.Invoke(null);
- return;
- }
-
- _createRoleCallback = callback;
-
- createrole createRoleProtocol = new createrole()
- {
- Userid = _currentUserId,
- Localsid = _localsid,
- Roleinfo = roleInfo,
- Referid = referId ?? new Octets()
- };
-
- Debug.Log($"[GameSession] Creating role - UserID: {_currentUserId}, Localsid: {_localsid}, Profession: {roleInfo.occupation}, Gender: {roleInfo.gender}");
- Debug.Log($"[GameSession] RoleInfo details - Name size: {roleInfo.name?.Size ?? 0}, Equipment count: {roleInfo.equipment?.Count ?? 0}, Custom data size: {roleInfo.custom_data?.Size ?? 0}, Race: {roleInfo.race}");
-
- // Log first few bytes of custom_data for debugging
- if (roleInfo.custom_data != null && roleInfo.custom_data.Size > 0)
- {
- byte[] customDataPreview = new byte[Math.Min(16, (int)roleInfo.custom_data.Size)];
- Array.Copy(roleInfo.custom_data.ByteArray, 0, customDataPreview, 0, customDataPreview.Length);
- string hexPreview = BitConverter.ToString(customDataPreview).Replace("-", " ");
- Debug.Log($"[GameSession] Custom_data preview (first 16 bytes): {hexPreview}");
- }
-
- Debug.Log($"[GameSession] Sending createrole protocol (Type: {createRoleProtocol.Type})");
- SendProtocol(createRoleProtocol);
- }
-
- ///
- /// Helper method to create a new RoleInfo for character creation.
- /// Matches C++ NewCharacterImpl behavior.
- ///
- public static RoleInfo CreateNewRoleInfo(string name, int profession, int gender)
- {
- RoleInfo roleInfo = new RoleInfo();
-
- // Set basic info
- roleInfo.occupation = (byte)profession;
- roleInfo.gender = (byte)gender;
- roleInfo.level = 1;
- roleInfo.level2 = 0;
- roleInfo.status = 0; // _ROLE_STATUS_NORMAL
- roleInfo.delete_time = 0;
- roleInfo.create_time = 0; // Server will set this
- roleInfo.lastlogin_time = 0;
- roleInfo.posx = 0.0f;
- roleInfo.posy = 0.0f;
- roleInfo.posz = 0.0f;
- roleInfo.worldtag = 0; // Server will set this
- roleInfo.referrer_role = 0;
- roleInfo.cash_add = 0;
-
- // Set name - C++ uses Unicode encoding (ACHAR = wchar_t)
- if (!string.IsNullOrEmpty(name))
- {
- byte[] nameBytes = Encoding.Unicode.GetBytes(name);
- roleInfo.name = new Octets(nameBytes);
- }
- else
- {
- roleInfo.name = new Octets();
- }
-
- // Initialize equipment list with 29 empty items (IVTRSIZE_EQUIPPACK = 29)
- roleInfo.equipment = new List();
- for (int i = 0; i < 29; i++)
- {
- // Important: GRoleInventory.data must be non-null or Marshal() will fail and the packet won't send.
- roleInfo.equipment.Add(new GRoleInventory
- {
- id = 0,
- pos = i,
- count = 0,
- max_count = 0,
- data = new Octets(),
- proctype = 0,
- expire_date = 0,
- guid1 = 0,
- guid2 = 0,
- mask = 0
- });
- }
-
- // Initialize custom data exactly as C++ does: memset to 0, then set specific values
- // This matches C++ LoadDefaultCustomizeData behavior
- roleInfo.custom_data = CreateDefaultCustomizeData(profession, gender);
-
- // Initialize other empty custom data fields
- roleInfo.custom_status = new Octets();
- roleInfo.charactermode = new Octets();
- roleInfo.reincarnation_data = new Octets();
- roleInfo.realm_data = new Octets();
-
- // Race is typically determined by profession, but we'll leave it at 0 for now
- // Server may set it based on profession
- roleInfo.race = 0;
-
- return roleInfo;
- }
-
- ///
- /// Creates default customize data exactly matching C++ LoadDefaultCustomizeData behavior.
- /// Structure layout - trying multiple sizes due to potential padding:
- /// - DWORD dwVersion (offset 0-3, 4 bytes) = 0x10007001
- /// - FACE_CUSTOMIZEDATA faceData (offset 4-87, 84 bytes) = all zeros
- /// - unsigned short bodyID (offset 88-89, 2 bytes) = 0
- /// - A3DCOLOR colorBody (offset 90-93, 4 bytes) = 0xffffffff
- /// - 6 unsigned char scales (offset 94-99, 6 bytes) = all 128
- /// Note: C++ sizeof might include padding, trying 100, 104, 108 bytes
- ///
- private static Octets CreateDefaultCustomizeData(int profession, int gender)
- {
- // C++ sizeof(PLAYER_CUSTOMIZEDATA) - Based on server response, it expects 176 bytes
- // The C++ code sends: sizeof(CECPlayer::PLAYER_CUSTOMIZEDATA) which appears to be 176 bytes
- // Server response shows: custom_data size=176, so server expects/receives 176 bytes
- // Structure layout (176 bytes total):
- // - DWORD dwVersion (4 bytes, offset 0) = 0x10007001
- // - FACE_CUSTOMIZEDATA faceData (84 bytes, offset 4) = all zeros for now
- // - unsigned short bodyID (2 bytes, offset 88) = 0
- // - A3DCOLOR colorBody (4 bytes, offset 90) = 0xffffffff
- // - 6 unsigned char scales (6 bytes, offset 94) = all 128
- // - Padding/additional fields (76 bytes, offset 100-175) = all zeros
- const int CUSTOMIZE_DATA_SIZE = 176; // Match server expectation (176 bytes from response)
- const uint CUSTOMIZE_DATA_VERSION = 0x10007001; // CUSTOMIZE_DATA_VERSION from C++
- byte[] customDataBytes = new byte[CUSTOMIZE_DATA_SIZE];
-
- // Step 1: memset to 0 (already done by new byte[])
-
- // Step 2: Set dwVersion at offset 0-3 (little-endian)
- byte[] versionBytes = BitConverter.GetBytes(CUSTOMIZE_DATA_VERSION);
- Array.Copy(versionBytes, 0, customDataBytes, 0, 4);
-
- // Step 3: FACE_CUSTOMIZEDATA at offset 4-87 is already zero (84 bytes)
- // (In C++ this would be loaded from INI, but for now we use zeros)
-
- // Step 4: bodyID at offset 88-89 is already zero (2 bytes)
- // Note: There might be 2 bytes padding here if struct is 4-byte aligned
-
- // Step 5: Set colorBody to 0xffffffff
- // Try offset 90 first (no padding), if that doesn't work try 92 (with padding)
- byte[] colorBodyBytes = BitConverter.GetBytes(0xffffffffu);
- Array.Copy(colorBodyBytes, 0, customDataBytes, 90, 4); // Try 90 first
-
- // Step 6: Set all 6 scale fields to 128
- // Try offset 94 first (no padding), if that doesn't work try 96 (with padding)
- for (int i = 94; i < 100; i++)
- {
- customDataBytes[i] = 128;
- }
-
- return new Octets(customDataBytes);
- }
-
- public void EnterWorldAsync(RoleInfo role, Action callback)
- {
- SendProtocol(new enterworld()
- {
- Roleid = _selectedRole.roleid,
- Provider_link_id = 0,
- }, callback);
- }
-
- public void RequestDropIvtrItem(byte index, int amount)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.CreateDropIvtrItem(index, amount);
- SendProtocol(gamedatasendRequest);
- }
-
- public void RequestDropEquipItem(byte index)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.CreateDropEquipItem(index);
- SendProtocol(gamedatasendRequest);
- }
-
- public void RequestPickupItem(int idItem, int tid)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.CreatePickupItem(idItem, tid);
- SendProtocol(gamedatasendRequest);
- }
-
- public void c2s_SendCmdGetIvtrDetailData(byte byPackage, Action callback)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGetIvtrDetailData(byPackage);
- SendProtocol(gamedatasendRequest, callback);
- }
-
- public void c2s_SendCmdQueryCashInfo()
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdQueryCashInfo();
- SendProtocol(gamedatasendRequest);
- }
-
- public void c2s_SendCmdOpenFashionTrash(string password)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdOpenFashionTrash(password);
- SendProtocol(gamedatasendRequest);
- }
-
- public void c2s_SendCmdEquipItem(byte iIvtrIdx, byte iEquipIdx, Action callback)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdEquipItem(iIvtrIdx, iEquipIdx);
- SendProtocol(gamedatasendRequest, callback);
- }
-
- public void c2s_SendCmdReviveVillage(int param = 0)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
- SendProtocol(gamedatasendRequest);
- }
- public void c2s_SendCmdReviveItem(int param = 0)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
- SendProtocol(gamedatasendRequest);
- }
- public void RequestReviveByPlayer(int param = 0)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
- SendProtocol(gamedatasendRequest);
- }
-
-
- public void c2s_SendCmdMallShopping(uint count, CMD_MallShopping.goods[] goodsArray)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetMallShopping(count, goodsArray);
- SendProtocol(gamedatasendRequest);
- }
- public void c2s_SendCmdGatherMaterial(int idMatter, int iToolPack, int idToolIndex, int idTool, int idTask)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGatherMaterial(idMatter, iToolPack, idToolIndex, idTool, idTask);
- SendProtocol(gamedatasendRequest);
- }
-
- public void c2s_SendCmdUseItemWithTarget(byte byPackage, byte bySlot, int tid, byte byPVPMask)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateUseItemWithTarget(byPackage, bySlot, tid, byPVPMask);
- SendProtocol(gamedatasend);
- }
-
- public void RequestOwnItemInfoAsync(
- byte byPackage,
- byte bySlot,
- int type,
- int expire_date,
- int state,
- uint count,
- ushort crc,
- ushort content_length,
- byte[] content,
- Action callback)
- {
- gamedatasend gamedatasendRequest = new gamedatasend();
- gamedatasendRequest.Data = C2SCommandFactory.CreateOwnItemInfo(byPackage, bySlot, type, expire_date, state,
- count, crc, content_length, content);
- SendProtocol(gamedatasendRequest, callback);
- }
-
- // --- Protocol Sending ---
- public void SendProtocol(Protocol protocol, Action complete = null)
- {
- if (IsConnected)
- {
- _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}");
- _networkManager.Send(protocol);
- complete?.Invoke();
- }
- else
- {
- _logger.Log(LogType.Warning, $"Cannot send protocol ({protocol.GetType().Name}), not connected.");
- BMLogger.LogError($"[GameSession] Cannot send protocol ({protocol.GetType().Name}), not connected.");
- }
- }
-
- // --- Event Handlers (from NetworkManager) ---
-
- private void OnProtocolReceived(Protocol protocol)
- {
- _logger.Log(LogType.Debug, $"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})");
- if (protocol is null)
- return;
-
-
- // Route protocol to appropriate handler
- switch (protocol.GetPType())
- {
- case ProtocolType.PROTOCOL_CHALLENGE:
- HandleChallenge((challenge)protocol);
- break;
- case ProtocolType.PROTOCOL_KEYEXCHANGE:
- HandleKeyExchange((KeyExchange)protocol);
- break;
- case ProtocolType.PROTOCOL_ONLINEANNOUNCE:
- HandleOnlineAnnounce((onlineannounce)protocol);
- break;
- case ProtocolType.PROTOCOL_ROLELIST_RE:
- HandleRoleListResponse((RoleListResponse)protocol);
- break;
- // Add cases for other protocols GameSession might need to handle
- case ProtocolType.PROTOCOL_SELECTROLE_RE:
- HandleSelectRoleResponse((SelectRole_Re)protocol);
- //_networkManager.IgnoreBytes = 2;
- break;
- case ProtocolType.PROTOCOL_CREATEROLE_RE:
- HandleCreateRoleResponse((createrole_re)protocol);
- break;
- case ProtocolType.PROTOCOL_ERRORINFO:
- HandleErrorInfo((errorinfo)protocol);
- break;
- case ProtocolType.PROTOCOL_S2CGAMEDATASEND:
- case ProtocolType.PROTOCOL_GAMEDATASEND:
- HandleServerDataSend((gamedatasend)protocol);
- break;
- case ProtocolType.PROTOCOL_CHATMESSAGE:
- _logger.Log(LogType.Warning, $"HoangDev :ProtocolType.PROTOCOL_CHATMESSAGE {protocol.GetPType()}");
- OnPrtcChatMessage(protocol, false);
- break;
- case ProtocolType.PROTOCOL_PLAYERBASEINFO_RE:
- OnPrtcPlayerBaseInfoRe(protocol);
- break;
- case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break;
- case ProtocolType.PROTOCOL_PLAYERLOGOUT:
- HandlePlayerLogout((playerlogout)protocol);
- break;
-
- case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE:
- {
- // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
- // if( pAutoTeam !=null)
- // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
- }
- break;
-
- default:
- _logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
- break;
- }
- }
-
- private void HandlePlayerLogout(playerlogout protocol)
- {
- // Original client receives this before EVENT_DISCONNECT.
- // We just publish it to allow higher-level flow (UnityGameSession/UI) to react.
- PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
- }
-
- private void HandleServerDataSend(gamedatasend protocol)
- {
- int lenghtHeader = Marshal.SizeOf();
- var pDataBuf = new byte[protocol.Data.ByteArray.Length - lenghtHeader];
- var byteArrHeader = new byte[lenghtHeader];
- long dwDataSize = protocol.Data.Size;
-
- if (dwDataSize < Marshal.SizeOf())
- {
- _logger.Error($"### GameDataSend: size invalid {dwDataSize}");
- return;
- }
-
- dwDataSize -= Marshal.SizeOf(); // subtract the header size (ushort)
- for (int i = 0; i < protocol.Data.ByteArray.Length; i++)
- {
- if (i < lenghtHeader)
- {
- byteArrHeader[i] = protocol.Data.ByteArray[i];
- }
- else
- {
- pDataBuf[i - lenghtHeader] = protocol.Data.ByteArray[i];
- }
- }
-
- var pCmdHeader = BitConverter.ToUInt16(byteArrHeader);
- //sss
-#if UNITY_EDITOR
- if (isDebug)
- {
- BMLogger.LogError($"### GameDataSend: CMDID {pCmdHeader}");
- }
-#endif
- int iHostID = _selectedRole.roleid;
- switch (pCmdHeader)
- {
- case CommandID.PLAYER_INFO_2:
- case CommandID.PLAYER_INFO_3:
- case CommandID.PLAYER_INFO_4:
- case CommandID.PLAYER_INFO_2_LIST:
- case CommandID.PLAYER_INFO_3_LIST:
- case CommandID.PLAYER_INFO_23_LIST:
-
- break;
-
- case CommandID.PLAYER_INFO_1:
- case CommandID.PLAYER_ENTER_WORLD:
- case CommandID.PLAYER_ENTER_SLICE:
- case CommandID.PLAYER_INFO_1_LIST:
- case CommandID.PLAYER_INFO_00:
- case CommandID.SELF_INFO_1:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERINFO, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf,
- pCmdHeader, iHostID);
- break;
- case CommandID.OBJECT_MOVE:
- int lenghtDataType = Marshal.SizeOf();
- byte[] arrByteData = GetBytes(pDataBuf, lenghtDataType, 0);
- int idObjMove = BitConverter.ToInt32(arrByteData);
- if (ISPLAYERID(idObjMove))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERMOVE, (int)MANAGER_INDEX.MAN_PLAYER, -1,
- pDataBuf, pCmdHeader, iHostID);
- }
- else if (ISNPCID(idObjMove))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCMOVE, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
- pCmdHeader);
- }
-
- break;
- case CommandID.OBJECT_STOP_MOVE:
- {
- int id1 = GPDataTypeHelper.FromBytes(pDataBuf);
-
- if (ISPLAYERID(id1))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERSTOPMOVE, (int)MANAGER_INDEX.MAN_PLAYER, -1,
- pDataBuf, pCmdHeader);
- }
- else if (ISNPCID(id1))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCSTOPMOVE, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
- pCmdHeader);
- }
-
- break;
- }
- case CommandID.OBJECT_LEAVE_SLICE:
- {
- int id = GPDataTypeHelper.FromBytes(pDataBuf);
- if (ISPLAYERID(id))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERRUNOUT, (int)MANAGER_INDEX.MAN_PLAYER, -1,
- pDataBuf, pCmdHeader);
- }
- else if (ISNPCID(id))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCRUNOUT, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
- pCmdHeader);
- }
-
- break;
- }
- case CommandID.OWN_IVTR_DATA:
- case CommandID.OWN_IVTR_DETAIL_DATA:
- case CommandID.GET_OWN_MONEY:
- case CommandID.CHANGE_IVTR_SIZE:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader, iHostID);
- break;
- case CommandID.EXG_IVTR_ITEM:
- case CommandID.MOVE_IVTR_ITEM:
- case CommandID.PLAYER_DROP_ITEM:
- case CommandID.EXG_EQUIP_ITEM:
- case CommandID.EQUIP_ITEM:
- case CommandID.MOVE_EQUIP_ITEM:
- case CommandID.UNFREEZE_IVTR_SLOT:
- case CommandID.PLAYER_EQUIP_TRASHBOX_ITEM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ITEMOPERATION, (int)MANAGER_INDEX.MAN_PLAYER, 0,
- pDataBuf, pCmdHeader);
- break;
- case CommandID.PLAYER_CASH:
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader, iHostID);
- break;
- }
- case CommandID.MATTER_INFO_LIST:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERINFO, (int)MANAGER_INDEX.MAN_MATTER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.MATTER_ENTER_WORLD:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERENTWORLD, (int)MANAGER_INDEX.MAN_MATTER, 0,
- pDataBuf, pCmdHeader);
- break;
- case CommandID.PICKUP_ITEM:
- case CommandID.HOST_OBTAIN_ITEM:
- case CommandID.PRODUCE_ONCE:
- case CommandID.TASK_DELIVER_ITEM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPITEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.MATTER_PICKUP:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PICKUPMATTER, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.PICKUP_MONEY:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPMONEY, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.HOST_CORRECT_POS:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CORRECTPOS, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader, iHostID);
- break;
- case CommandID.EMPTY_ITEM_SLOT:
- case CommandID.OWN_ITEM_INFO:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_OWNITEMINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader, iHostID);
- break;
- case CommandID.PLAYER_DIED:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDIED, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.HOST_DIED:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_DIED, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.PLAYER_REVIVE:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERREVIVE, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.NOTIFY_HOSTPOS:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_GOTO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.NPC_ENTER_SLICE:
- case CommandID.NPC_INFO_LIST:
- case CommandID.NPC_INFO_00:
- case CommandID.NPC_ENTER_WORLD:
- case CommandID.NPC_VISIBLE_TID_NOTIFY:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCINFO, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
- pCmdHeader, dwDataSize);
- break;
- case CommandID.TASK_DATA:
- case CommandID.TASK_VAR_DATA:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TASKDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader, dwDataSize);
- break;
- case CommandID.BE_HURT:
- case CommandID.HURT_RESULT:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_HURTRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.OBJECT_ATTACK_RESULT:
- //int id = GPDataTypeHelper.FromBytes(pDataBuf);
- cmd_object_atk_result pCmdAtk = GPDataTypeHelper.FromBytes(pDataBuf);
- //BMLogger.LogError($"OBJECT_ATTACK_RESULT: npc ? " + ISNPCID(id));
-
- if (ISPLAYERID(pCmdAtk.attacker_id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERATKRESULT, MANAGER_INDEX.MAN_PLAYER, -1,
- pDataBuf, pCmdHeader);
- else if (ISNPCID(pCmdAtk.attacker_id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCATKRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.HOST_ATTACKRESULT:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATKRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.HOST_ATTACKED:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATTACKED, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
-
- case CommandID.TEAM_JOIN_TEAM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_JOINTEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.TEAM_LEAVE_PARTY:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.TEAM_NEW_MEMBER:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_NEWTEAMMEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.TEAM_MEMBER_DATA:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TEAMMEMBERDATA, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.TEAM_MEMBER_LEAVE:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
-
- case CommandID.ERROR_MESSAGE:
- {
- int errRaw = BitConverter.ToInt32(pDataBuf, 0);
- // Note: _logger may be configured as a file logger via SetLogPath(), so also log to console for visibility.
- _logger.Info($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
-#if UNITY_EDITOR
- BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
-#endif
- cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
-#if UNITY_EDITOR
- BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}");
-#endif
- Debug.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
- if (pCmd.iMessage != 0)
- {
- // string szMsg = m_ErrorMsgs.GetWideString(pCmd.iMessage);
- // if (string.IsNullOrEmpty(szMsg))
- // BMLogger.LogError("SERVER - unknown error !");
- // else
- // {
- // BMLogger.LogError("SERVER - error: "+szMsg);
- // }
- // else if (pCmd.iMessage != 2)
- // g_pGame.GetGameRun().AddChatMessage(szMsg, GP_CHAT_MISC);
- }
-
- if (pCmd.iMessage == 2)
- {
- // Attack target is too far
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- }
- else if (pCmd.iMessage == 20)
- {
- // Failed to cast skill
- //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
- }
- else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
- {
- // deal failed
- //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
- }
- else if (pCmd.iMessage == 158)
- {
- // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ
- //c2s_CmdGetCashMoneyRate();
- }
- else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/)
- {
- /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
- if (pGameUI)
- pGameUI.EndNPCService();*/
- }
- else if
- (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/
- )
- {
- /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
- if (pGameUI)
- pGameUI.EndNPCService();*/
- }
- else if (pCmd.iMessage == 175)
- {
- //c2s_CmdQueryParallelWorld();
- }
- else if (pCmd.iMessage == 6)
- {
- //AP_ActionEvent(AP_EVENT_CANNOTPICKUP);
- }
-
- break;
- }
- case CommandID.SELECT_TARGET:
- case CommandID.UNSELECT:
-
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SELTARGET, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.NPC_DIED:
- case CommandID.NPC_DIED2:
-
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDIED, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.OBJECT_DISAPPEAR:
- {
- int lenghtDataType1 = Marshal.SizeOf();
- byte[] arrByteData1 = GetBytes(pDataBuf, lenghtDataType1, 0);
- int objectId = BitConverter.ToInt32(arrByteData1);
- if (ISPLAYERID(objectId))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDISAPPEAR, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- else if (ISNPCID(objectId))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDISAPPEAR, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
- else if (ISMATTERID(objectId))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERDISAPPEAR, MANAGER_INDEX.MAN_MATTER, 0, pDataBuf, pCmdHeader);
-
- break;
- }
- case CommandID.SELF_INFO_00:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_INFO00, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
- pCmdHeader);
- break;
- case CommandID.NPC_GREETING:
- {
- // If this greeting is from the skill-learn NPC, record it (C++ skill dialog relies on this).
- try
- {
- cmd_npc_greeting greet = GPDataTypeHelper.FromBytes(pDataBuf);
- CECHostSkillModel.Instance.OnNpcGreeting(greet.idObject);
- }
- catch (Exception ex)
- {
- _logger.Log(LogType.Warning, $"Failed to parse NPC_GREETING payload: {ex.Message}");
- }
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_NPCGREETING, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- }
- case CommandID.ACTIVATE_WAYPOINT:
- case CommandID.WAYPOINT_LIST:
-
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_WAYPOINT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.SERVER_TIME:
- {
- cmd_server_time pcmd_server_time = GPDataTypeHelper.FromBytes(pDataBuf);
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_SERVERTIME, -1, 0, pcmd_server_time.time, pcmd_server_time.timebias);
- break;
- }
- case CommandID.SCENE_SERVICE_NPC_LIST:
- {
- CECHostSkillModel.Instance.RecvNPCServiceList(protocol.Data);
- break;
- }
- case CommandID.SKILL_DATA:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.OBJECT_CAST_SKILL:
- case CommandID.OBJECT_CAST_INSTANT_SKILL:
- case CommandID.OBJECT_CAST_POS_SKILL:
- {
- cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf,true);
- if (ISPLAYERID(pCmd2.caster))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- else if (ISNPCID(pCmd2.caster))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCCASTSKILL, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
-
- break;
- }
- case CommandID.LEVEL_UP:
- {
- cmd_level_up pCmdLevelUp = GPDataTypeHelper.FromBytes(pDataBuf); ;
- if (ISPLAYERID(pCmdLevelUp.id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERLEVELUP, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- else if (ISNPCID(pCmdLevelUp.id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCLEVELUP, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
- break;
- }
- case CommandID.HOST_START_ATTACK:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_STARTATTACK, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
- break;
- case CommandID.HOST_STOPATTACK:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_STOPATTACK, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
- break;
- case CommandID.HOST_SKILL_ATTACK_RESULT:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.CHANGE_FACE_START:
- case CommandID.CHANGE_FACE_END:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CHANGEFACE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
- break;
- case CommandID.ENCHANT_RESULT:
- cmd_enchant_result pCmd3 = GPDataTypeHelper.FromBytes(pDataBuf);
- if (ISPLAYERID(pCmd3.caster))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_ENCHANTRESULT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- else if (ISNPCID(pCmd3.caster))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_ENCHANTRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.HOST_STOP_SKILL:
- case CommandID.SELF_SKILL_INTERRUPTED:
- case CommandID.SKILL_PERFORM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.SET_COOLDOWN:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETCOOLTIME, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.HOST_USE_ITEM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_USEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.COMBO_SKILL_PREPARE:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_COMBO_SKILL_PREPARE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
- break;
- case CommandID.PLAYER_EXT_PROP_BASE:
- case CommandID.PLAYER_EXT_PROP_MOVE:
- case CommandID.PLAYER_EXT_PROP_ATK:
- case CommandID.PLAYER_EXT_PROP_DEF:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXTPROP, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.OWN_EXT_PROP:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_OWNEXTPROP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.OBJECT_DO_EMOTE:
- case CommandID.OBJECT_EMOTE_RESTORE:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDOEMOTE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.OUT_OF_SIGHT_LIST:
- {
- cmd_out_of_sight_list pCmd5 = default;
- pCmd5.uCount = GPDataTypeHelper.FromBytes(pDataBuf);
- int offset2 = sizeof(uint);
- pCmd5.idList = new int[pCmd5.uCount];
- for (int i = 0; i < pCmd5.uCount; i++)
- {
- pCmd5.idList[i] = GPDataTypeHelper.FromBytes(pDataBuf, offset2);
- offset2 += 4;//sizeof int;
- }
-
- for (uint n = 0; n < pCmd5.uCount; n++)
- {
- if (ISPLAYERID(pCmd5.idList[n]))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEROUTOFVIEW, MANAGER_INDEX.MAN_PLAYER, -1, pCmd5.idList[n], pCmdHeader);
- else if (ISNPCID(pCmd5.idList[n]))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCOUTOFVIEW, MANAGER_INDEX.MAN_NPC, 0, pCmd5.idList[n], pCmdHeader);
- else if (ISMATTERID(pCmd5.idList[n]))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTEROUTOFVIEW, MANAGER_INDEX.MAN_MATTER, 0, pCmd5.idList[n], pCmdHeader);
- }
-
- break;
- }
- case CommandID.PLAYER_GATHER_START:
- case CommandID.PLAYER_GATHER_STOP:
- case CommandID.MINE_GATHERED:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERGATHER, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.COOLTIME_DATA:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_COOLTIMEDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.OBJECT_TAKEOFF:
- {
- cmd_object_takeoff pCmdTakeOff = GPDataTypeHelper.FromBytes((byte[])pDataBuf);
- if (ISPLAYERID(pCmdTakeOff.object_id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- }
- case CommandID.OBJECT_LANDING:
- {
- cmd_object_landing pCmdLanding = GPDataTypeHelper.FromBytes((byte[])pDataBuf);
- if (ISPLAYERID(pCmdLanding.object_id))
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- }
- case CommandID.HOST_RUSH_FLY:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.FLYSWORD_TIME:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_FLYSWORDTIME, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.PRODUCE_START:
- case CommandID.PRODUCE_END:
- case CommandID.PRODUCE_NULL:
- // Post MSG_HST_PRODUCEITEM message with command ID as parameter (matches C++ behavior)
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRODUCEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
-
-
- case CommandID.LEARN_SKILL:
- BMLogger.LogError("### GameDataSend: LEARN_SKILL");
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEARNSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.GAIN_PET:
- case CommandID.FREE_PET:
- case CommandID.SUMMON_PET:
- case CommandID.RECALL_PET:
- case CommandID.PLAYER_START_PET_OP:
- case CommandID.PLAYER_STOP_PET_OP:
- case CommandID.PET_RECEIVE_EXP:
- case CommandID.PET_LEVELUP:
- case CommandID.PET_ROOM:
- case CommandID.PET_ROOM_CAPACITY:
- case CommandID.PET_HONOR_POINT:
- case CommandID.PET_HUNGER_GAUGE:
- case CommandID.PET_DEAD:
- case CommandID.PET_REVIVE:
- case CommandID.PET_HP_NOTIFY:
- case CommandID.PET_AI_STATE:
- case CommandID.PET_SET_COOLDOWN:
- case CommandID.SUMMON_PLANT_PET:
- case CommandID.PLANT_PET_DISAPPEAR:
- case CommandID.PLANT_PET_HP_NOTIFY:
- case CommandID.PET_PROPERTY:
- case CommandID.PET_REBUILD_INHERIT_START:
- case CommandID.PET_REBUILD_INHERIT_INFO:
- case CommandID.PET_REBUILD_INHERIT_END:
- case CommandID.PET_EVOLUTION_DONE:
- case CommandID.PET_REBUILD_NATURE_START:
- case CommandID.PET_REBUILD_NATURE_INFO:
- case CommandID.PET_REBUILD_NATURE_END:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PETOPT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.SET_PLAYER_LIMIT:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETPLAYERLIMIT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.PLAYER_MOUNTING:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERMOUNT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.DUEL_RECV_REQUEST:
- case CommandID.DUEL_REJECT_REQUEST:
- case CommandID.DUEL_PREPARE:
- case CommandID.DUEL_CANCEL:
- case CommandID.HOST_DUEL_START:
- case CommandID.DUEL_STOP:
- case CommandID.DUEL_RESULT:
- // Origin: host duel events use MSG_PM_DUELOPT (391); handler distinguishes by pCmdHeader (214-220)
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_DUELOPT, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.PLAYER_DUEL_START:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDUELOPT, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- break;
- case CommandID.EMBED_ITEM:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_EMBEDITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.CLEAR_TESSERA:
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CLEARTESSERA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- break;
- case CommandID.PLAYER_LEAVE_WORLD:
- {
- cmd_player_leave_world pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
- if (ISPLAYERID(pCmd.id))
- {
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXIT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
- }
- break;
- }
- default:
-#if UNITY_EDITOR
- if (isDebug)
- {
- BMLogger.LogError($"### GameDataSend: Unhandled CMDID {pCmdHeader} (payloadBytes={pDataBuf?.Length ?? 0})");
- }
-#endif
- break;
- }
- }
-
-
- private void HandleSelectRoleResponse(SelectRole_Re protocol)
- {
- _logger.Info($"Select role response {protocol.result}");
- var callback = _selectRoleCallback;
- PostToUnityContext(() => callback?.Invoke(_selectedRole));
- }
-
- private void HandleCreateRoleResponse(createrole_re protocol)
- {
- Debug.Log($"[GameSession] HandleCreateRoleResponse - result: {protocol.result}, roleid: {protocol.roleid}");
-
- if (protocol.result != (int)ErrCode.ERR_SUCCESS)
- {
- string errorMsg = $"Create role failed with result code: {protocol.result} (ERR_SUCCESS = {(int)ErrCode.ERR_SUCCESS})";
- _logger.Log(LogType.Error, errorMsg);
- Debug.LogError($"[GameSession] {errorMsg}");
- var callback = _createRoleCallback;
- _createRoleCallback = null;
- PostToUnityContext(() => callback?.Invoke(null));
- return;
- }
-
- Debug.Log($"[GameSession] Create role successful! RoleID: {protocol.roleid}");
- var successCallback = _createRoleCallback;
- _createRoleCallback = null;
- PostToUnityContext(() => successCallback?.Invoke(protocol.roleinfo));
- }
-
- private void HandleErrorInfo(errorinfo protocol)
- {
- Debug.LogError($"[GameSession] Server error - Errcode: {protocol.Errcode}");
-
- // If we're waiting for create role response and get an error, fail the callback
- if (_createRoleCallback != null)
- {
- Debug.LogError($"[GameSession] Create role failed due to server error: {protocol.Errcode}");
- var callback = _createRoleCallback;
- _createRoleCallback = null;
- PostToUnityContext(() => callback?.Invoke(null));
- }
- }
-
- private void OnErrorOccurred(string errorMessage)
- {
- _logger.Log(LogType.Error, $"Network Error: {errorMessage}");
- FailLoginInProgress(errorMessage);
- FailRoleListInProgress(errorMessage);
- }
-
- private void OnDisconnected()
- {
- _logger.Log(LogType.Info, "Disconnected from server.");
- _currentUserId = -1;
- FailLoginInProgress("Disconnected");
- FailRoleListInProgress("Disconnected");
- // Clear command cache
- m_CmdCache.RemoveAllCmds();
- PostToUnityContext(() => Disconnected?.Invoke());
- }
-
- private void HandleChallenge(challenge challenge)
- {
- if (_loginCallback == null || string.IsNullOrEmpty(_username))
- {
- _logger.Log(LogType.Warning, "Received Challenge but not expecting it or username not set.");
- return;
- }
-
- _logger.Log(LogType.Info, "Handling Challenge...");
-
- response response = new response();
- byte[] usernameBytes = Encoding.ASCII.GetBytes(_username);
- byte[] passwordBytes = Encoding.ASCII.GetBytes(_password);
- response.identity.Replace(usernameBytes);
- response.Setup(new Octets(usernameBytes), new Octets(passwordBytes), challenge.nonce);
-
- uint clientId = 0xffffffff;
- byte[] clientIdBytes = BitConverter.GetBytes(clientId);
- response.cli_fingerprint.Replace(clientIdBytes);
- response.use_token = 0;
-
- _networkManager.SetNonce(response.response_data);
- SendProtocol(response);
- _logger.Log(LogType.Info, "Sent Response.");
- }
-
- private void HandleKeyExchange(KeyExchange keyExchange)
- {
- if (_loginCallback == null || string.IsNullOrEmpty(_username))
- {
- _logger.Log(LogType.Warning, "Received KeyExchange but not expecting it.");
- return;
- }
-
- _logger.Log(LogType.Info, "Handling KeyExchange...");
- keyExchange.Setup(_networkManager, _username);
- keyExchange.Blkickuser = 1;
- SendProtocol(keyExchange);
- _logger.Log(LogType.Info, "Sent KeyExchange acknowledgment/response.");
- }
-
- private void HandleOnlineAnnounce(onlineannounce announce)
- {
- if (_loginCallback == null)
- {
- _logger.Log(LogType.Warning, "Received OnlineAnnounce but not expecting it.");
- return;
- }
-
- _logger.Log(LogType.Info, $"Login successful! UserID: {announce.Userid}, LocalSID: {announce.Localsid}");
- _currentUserId = announce.Userid;
- _localsid = announce.Localsid;
-
- var callback = _loginCallback;
- _loginCallback = null;
- PostToUnityContext(() => callback?.Invoke(true));
- }
-
- private void RequestRoleListInternal(int lastHandle = -1)
- {
- rolelist rolelistRequest = new rolelist();
- rolelistRequest.Userid = _currentUserId;
- rolelistRequest.Localsid = 0;
- rolelistRequest.Handle = lastHandle;
-
- SendProtocol(rolelistRequest);
-
-
- //gamedatasend gamedatasendRequest = new gamedatasend();
- //gamedatasendRequest.Data = C2SCommandFactory.CreatePlayerMove();
-
- //SendProtocol(gamedatasendRequest);
- }
-
- private void HandleRoleListResponse(RoleListResponse response)
- {
- if (_roleListCallback == null || _accumulatedRoles == null)
- {
- _logger.Log(LogType.Warning, "Received RoleListResponse but not expecting it.");
- return;
- }
-
- _logger.Log(LogType.Debug,
- $"Received RoleListResponse. Handle: {response.handle}, Result: {response.result}, Count: {response.rolelist.Count}");
-
- if (response.result == 0)
- {
- _accumulatedRoles.AddRange(response.rolelist);
-
- foreach (var role in response.rolelist)
- {
- try
- {
- string roleName = Encoding.UTF8.GetString(role.name.ByteArray, 0, role.name.Length);
- _logger.Log(LogType.Info, $" - Role ID: {role.roleid}, Name: {roleName}, Level: {role.level}");
- }
- catch (Exception ex)
- {
- _logger.Log(LogType.Error, $" - Error decoding role name: {ex.Message}");
- _logger.LogException(ex);
- }
- }
-
- if (response.handle != -1)
- {
- _logger.Log(LogType.Debug, $"Requesting next batch of roles (handle: {response.handle})...");
- RequestRoleListInternal(response.handle);
- }
- else
- {
- _logger.Log(LogType.Info, $"Finished fetching roles. Total count: {_accumulatedRoles.Count}");
- var callback = _roleListCallback;
- var result = _accumulatedRoles;
- _roleListCallback = null;
- _accumulatedRoles = null;
- PostToUnityContext(() => callback?.Invoke(result));
- }
- }
- else
- {
- _logger.Log(LogType.Error, $"Role list retrieval failed. Result code: {response.result}");
- FailRoleListInProgress($"Role list retrieval failed (Result: {response.result})");
- }
- }
-
- // --- Helper methods for failure handling ---
- private void FailLoginInProgress(string reason)
- {
- if (_loginCallback != null)
- {
- _logger.Log(LogType.Error, $"Login failed: {reason}");
- var callback = _loginCallback;
- _loginCallback = null;
- PostToUnityContext(() => callback?.Invoke(false));
- }
- }
-
- private void FailRoleListInProgress(string reason)
- {
- if (_roleListCallback != null)
- {
- _logger.Log(LogType.Error, $"Role list retrieval failed: {reason}");
- var callback = _roleListCallback;
- _roleListCallback = null;
- _accumulatedRoles = null;
- PostToUnityContext(() => callback?.Invoke(null));
- }
- }
-
- // --- IDisposable Implementation ---
- private bool disposedValue = false;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- if (_networkManager != null)
- {
- _logger.Log(LogType.Info, "[DUCK] Disposing GameSession and disconnecting...");
- _networkManager.ProtocolReceived -= OnProtocolReceived;
- _networkManager.ErrorOccurred -= OnErrorOccurred;
- _networkManager.Disconnected -= OnDisconnected;
- _networkManager.Disconnect();
- _networkManager.Dispose();
- _networkManager = null;
- }
-
- _loginCallback = null;
- _roleListCallback = null;
- _accumulatedRoles = null;
- }
-
- disposedValue = true;
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- // GC.SuppressFinalize(this);
- }
-
- public bool ISPLAYERID(int id)
- {
- return id != 0 && (id & 0x80000000) == 0;
- }
- public bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0);
- public bool ISMATTERID(int id) => ((id) & 0xC0000000) == 0xC0000000;
- private byte[] GetBytes(byte[] bytes, int length, int index)
- {
- byte[] arrByteData = new byte[length];
- for (int i = 0; i < length; i++)
- {
- arrByteData[i] = bytes[i + index];
- }
-
- return arrByteData;
- }
-
- public void c2s_CmdPlayerMove(in Vector3 vCurPos, in Vector3 vDest,
- int iTime, float fSpeed, int iMoveMode, ushort wStamp)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data =
- C2SCommandFactory.CreatePlayerMove(vCurPos, vDest, (ushort)iTime, fSpeed, (byte)iMoveMode, wStamp);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data =
- C2SCommandFactory.CreatePlayerCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
-
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdCastInstantSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data =
- C2SCommandFactory.CreatePlayerCastInstantSkill(idSkill, byPVPMask, iNumTarget, aTargets);
-
- SendProtocol(gamedatasend);
- }
-
- public void c2s_CmdCastPosSkill(int idSkill, Vector3 vDest, byte byPVPMask, int iNumTarget, int aTargets)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data =
- C2SCommandFactory.CreatePlayerCastPosSkill(idSkill, vDest, byPVPMask, iNumTarget, aTargets);
-
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdNotifyForceAttack(int iForceAttack, byte refuseBless)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNotifyForceAttack(iForceAttack, refuseBless);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdContinueAction()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.CONTINUE_ACTION);
- SendProtocol(gamedatasend);
- }
-
- ///
- /// Client logout request. Mirrors original client behavior:
- /// - outType=1: back to select role
- /// - outType=0: logout account
- ///
- public void SendPlayerLogout(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)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data =
- C2SCommandFactory.CreatePlayerStop(vDest, fSpeed, (byte)iMoveMode, byDir, wStamp, (ushort)iTime);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_CmdSendEnterPKPrecinctint()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.ENTER_PK_PROTECTED);
- SendProtocol(gamedatasend);
- }
- public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
- {
- publicchat publicChat = new publicchat();
- publicChat.Channel = cChannel;
- publicChat.Roleid = m_iCharID;
-
- byte[] unicodeBytes = Encoding.Unicode.GetBytes(szMsg);
- publicChat.Msg.Replace(unicodeBytes);
- _logger.Log(LogType.Warning, $"HoangDev : publicChat {publicChat}");
- SendProtocol(publicChat);
- }
- public void LoadConfigData()
- {
- getuiconfig p = new getuiconfig();
- p.Roleid = m_iCharID;
- SendProtocol(p);
- }
-
- ///
- /// Save config data to server (sends setuiconfig with compressed config blob).
- /// 保存配置数据到服务器(发送 setuiconfig 及压缩后的配置数据)。
- ///
- public void SaveConfigData(byte[] pBuf, int len)
- {
- BMLogger.Log($"[MH] Session.SaveConfigData | len={len}");
-
- if (pBuf == null || len <= 0) return;
- var p = new setuiconfig();
- p.Roleid = m_iCharID;
- p.Localsid = (int)_localsid;
- byte[] slice = new byte[len];
- Buffer.BlockCopy(pBuf, 0, slice, 0, len);
- p.Ui_config = new Octets(slice);
-
- // return;
- SendProtocol(p);
- }
-
- private void SetCharacterID(int iCharID)
- {
- m_iCharID = iCharID;
- }
-
- private void OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
- {
- chatmessage p = (chatmessage)pProtocol;
-
- string strTemp = System.Text.Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
-
- _logger.Log(LogType.Warning, $"HoangDev : OnPrtcChatMessage :{strTemp}");
- EventBus.Publish(new ChatMessageEvent(strTemp));
- }
-
- public struct ChatMessageEvent
- {
- public string context;
-
- public ChatMessageEvent(string context)
- {
- this.context = context;
- }
- }
- public void OnPrtcGetConfigRe(Protocol pProtocol)
- {
- getuiconfig_re p = (getuiconfig_re)pProtocol;
- if (p.Result != (int)ErrCode.ERR_SUCCESS)
- BMLogger.LogError("CECGameSession::OnPrtcGetConfigRe, link return error code of " + p.Result);
- else
- {
- if (!CECGameRun.Instance.LoadConfigsFromServer(p.UiConfig.RawBuffer, p.UiConfig.Size))
- {
- // if load failed then use current setting directly
- //TODO : fix later
- EC_Game.GetConfigs().ApplyUserSetting();
- }
-
- // Now, Get config data request is sent after all host initial data ready.
- // so when we receive this reply, we can do some last work before game
- // really starts. Maybe it's not the best place to do these work, but
- // now we do it here.
- // Enalbe game UI
- CECGameUIMan pGameUI = (CECGameUIMan)EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
- if (pGameUI != null)
- {
- pGameUI.EnableUI(true);
-
- // Get referral name for adding friend or other display
- //TODO: a Hung lam phan select role info di
- /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
- if (info.referrer_role > 0)
- GetPlayerBriefInfo(1, info.referrer_role, 2);*/
- }
-
- CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
- pHost.OnAllInitDataReady();
-
- /* if (pHost.IsGM())
- {
- CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap");
- pDlgCountryMap.GetConfig();
- }
-
- g_pGame.GetConfigs().ApplyOptimizeSetting();
-
- if (g_pGame.GetConfigs().IsMiniClient())
- CECMCDownload::GetInstance().SendGetDownloadOK();*/
- }
- }
- private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
- {
- playerbaseinfo_re p = (playerbaseinfo_re)pProtocol;
- BMLogger.Log($"OnPrtcPlayerBaseInfoRe: {p.Roleid} {p.Player.cls} {p.Player.gender}");
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERBASEINFO, MANAGER_INDEX.MAN_PLAYER, -1, p);
- }
-
- public void c2s_CmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(idTask, idStorage, idRefreshItem);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdGetAllData(bool byPack, bool byEquip, bool byTask)
- {
- gamedatasend gamedatasend = new gamedatasend();
-
- gamedatasend.Data = C2SCommandFactory.CreateGetAllDataCommand(byPack, byEquip, byTask);
- _logger.Log(LogType.Warning, $"[Dat]- SendCmdGetAllData {byPack},{byEquip},{byTask}");
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevHello(int nid)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevHelloDataCommand(nid);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_CmdNormalAttack(byte byPVPMask)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNormalAttackDataCmd(byPVPMask);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdCancelAction()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.CANCEL_ACTION);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_CmdUnselect()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.UNSELECT);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdSelectTarget(int idTarget)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateSelectTarget(idTarget);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevWaypoint()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevWaypointCmd(NPC_service_type.GP_NPCSEV_WAYPOINT, 0);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdNPCSevMakeItem(int idSkill, int idItem, uint dwCount)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevMakeItemCmd(idSkill, idItem, dwCount);
- SendProtocol(gamedatasend);
- }
- public void GetRoleBaseInfo(int iNumRole, List aRoleIDs)
- {
- int iNumLimit = 128;
- playerbaseinfo p = null;
- int iCount = 0;
-
- while (iCount < iNumRole)
- {
- p = new();
- p.Roleid = _selectedRole.roleid;
-
- int iNumSend = iNumLimit;
- if (iCount + iNumLimit > iNumRole)
- iNumSend = iNumRole - iCount;
-
- if (iNumSend > 0)
- {
- p.playerList = new();
- for (int i = 0; i < iNumSend; i++)
- p.playerList.Add(aRoleIDs[iCount + i]);
-
- SendProtocol(p);
- }
-
-
- iCount += iNumSend;
- }
- }
-
- public void c2s_SendCmdGetOtherEquip(int iNumID, List aIDs)
- {
- // int iNumLimit = 250;
- // int iCount = 0;
-
- // while (iCount < iNumID)
- // {
- // int iNumSend = iNumLimit;
- // if (iCount + iNumLimit > iNumID)
- // iNumSend = iNumID - iCount;
-
- // if (iNumSend > 0)
- // {
- // }
- // }
- }
-
- public void c2s_SendCmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(
- idTask,
- idStorage,
- idRefreshItem);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevReturnTask(int idTask, int iChoice)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevReturnTaskCmd(
- idTask,
- iChoice);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevTaskMatter(int idTask)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevTaskMatterCmd(idTask);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevLearnSkill(int idSkill)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevLearnSkillCmd(idSkill);
- BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill gamedatasend.Data : " + gamedatasend.Data.Size);
- BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill idSkill : " + idSkill);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevBuy(int itemNum, C2SCommand.npc_trade_item[] items)
- {
- if (itemNum <= 0 || items == null || items.Length < itemNum)
- return;
-
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevBuyCmd(itemNum, items);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevSell(int itemNum, C2SCommand.npc_sell_item[] items)
- {
- if (itemNum <= 0 || items == null || items.Length < itemNum)
- return;
-
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevSellCmd(itemNum, items);
- SendProtocol(gamedatasend);
- }
-
- public void GetRoleCustomizeData(int iNumRole, List aRoleIDs)
- {
- if (iNumRole <= 0 || aRoleIDs == null || aRoleIDs.Count == 0) return;
-
- int iNumLimit = 240;
- int iCount = 0;
-
- while (iCount < iNumRole)
- {
- getcustomdata p = new();
- p.Roleid = _selectedRole.roleid;
-
- int iNumSend = iNumLimit;
- if (iCount + iNumLimit > iNumRole)
- iNumSend = iNumRole - iCount;
-
- for (int i = 0; i < iNumSend; i++)
- p.playerlist.Add(aRoleIDs[iCount + i]);
-
- SendProtocol(p);
-
- iCount += iNumSend;
- }
- }
- public void c2s_SendCmdEmoteAction(uint wPose)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateEmoteActionCmd((int)wPose);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdTaskNotify(byte[] pData, uint dwDataSize)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd( pData, dwDataSize);
- BMLogger.Log($"[MH Task] c2s_SendCmdTaskNotify Command ID : {pData[0]} Size: {dwDataSize}");
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdStandUp()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.STAND_UP);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdAutoTeamSetGoal(int type, int goal_id, int op)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type,goal_id, op);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdTeamInvite(int idPlayer)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamInviteCommand(idPlayer);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdDuelRequest(int idTarget)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateDuelRequestCommand(idTarget);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdDuelReply(bool accept, int idInviter)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateDuelReplyCommand(accept, idInviter);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamKickMember(int idMember)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamKickMemberCommand(idMember);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamLeaveParty()
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamLeavePartyCommand();
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamDismissParty()
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamDismissPartyCommand();
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamSetPickupFlag(short pickupFlag)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamSetPickupFlagCommand(pickupFlag);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamChangeLeader(int idNewLeader)
- {
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamChangeLeaderCommand(idNewLeader);
- SendProtocol(g);
- }
-
- public void c2s_SendCmdTeamMemberPos(int count, int[] memberIds)
- {
- if (memberIds == null || count <= 0 || count > memberIds.Length) return;
- var g = new gamedatasend();
- g.Data = C2SCommandFactory.CreateTeamMemberPosCommand(count, memberIds);
- SendProtocol(g);
- }
-
- public void c2s_CmdGoto(float x, float y, float z)
- {
- c2s_SendCmdGoto(x, y, z);
- }
-
- // Send C2S::GOTO command data
- void c2s_SendCmdGoto(float x, float y, float z)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateGoToCommed( x, y, z);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdUseItem(byte byPackage, byte bySlot, int tid, byte byCount)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateUseItemCmd(byPackage, bySlot, tid, byCount);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdGetExtProps()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.GET_EXT_PROP);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdGivePresent(int roleid, int mail_id, int goods_id, int goods_index, int goods_slot)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateGivePresentCmd(roleid, mail_id, goods_id, goods_index, goods_slot);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdEnterSanctuary(int id)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateEnterSanctuaryCmd(id);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdEnterInstance(int iTransIdx, int idInst)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateEnterInstanceCmd(iTransIdx, idInst);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdActiveRushFly(bool bActive)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateActiveRushFlyCmd(bActive);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdPetCtrl(int idTarget, int cmd, byte[] pParamBuf, int iParamLen)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreatePetCtrlCmd(idTarget, cmd, pParamBuf, iParamLen);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdQueryFactionPVPInfo(int faction_id)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateQueryFactionPVPInfo(faction_id);
- SendProtocol(gamedatasend);
- }
-
- public void SendCmdPetCtrl(int idTarget, int cmd, object pParamBuf, int iParamLen)
- {
- m_CmdCache.SendCmdPetCtrl(idTarget, cmd, (byte[])pParamBuf, iParamLen);
- }
-
- public void c2s_SendCmdPetSummon(int iPetIdx)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreatePetSummon(iPetIdx);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdNPCSevEmbed(ushort wStoneIdx, ushort wEquipIdx, int tidStone, int tidEquip)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevEmbedCmd(wStoneIdx, wEquipIdx, tidStone, tidEquip);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdGetItemInfo(byte byPackage, byte bySlot)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateGetItemInfoCmd(byPackage, bySlot);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdSetStatusPts(int vitality, int energy, int strength, int agility)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateSetStatusPtCmd(vitality, energy, strength, agility);
- SendProtocol(gamedatasend);
- }
- public void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevClearEmbeddedChipCmd(iEquipIdx, tidEquip);
- SendProtocol(gamedatasend);
- }
-
- public void c2s_SendCmdPetRecall()
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.RECALL_PET);
- SendProtocol(gamedatasend);
- }
- public void c2s_CmdDebug(ushort icmd, int param1 = int.MinValue)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateDebugCmd(icmd, param1);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdNPCSevHatchPet(int iIvtrIdx, int idEgg)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevHatchPetCmd(iIvtrIdx, idEgg);
- SendProtocol(gamedatasend);
- }
- public void c2s_SendCmdNPCSevRestorePet(int iPetIdx)
- {
- gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateNPCSevRestorePetCmd(iPetIdx);
- SendProtocol(gamedatasend);
- }
-
- // Cross-server get in (C++: c2s_CmdNPCSevCrossServerGetIn) — TODO: implement C2S packet when needed
- public void c2s_CmdNPCSevCrossServerGetIn()
- {
- // TODO: C2SCommandFactory.CreateNPCSevCrossServerGetInCmd() and SendProtocol
- }
-
- // Cross-server get out (C++: c2s_CmdNPCSevCrossServerGetOut) — TODO: implement C2S packet when needed
- public void c2s_CmdNPCSevCrossServerGetOut()
- {
- // TODO: C2SCommandFactory.CreateNPCSevCrossServerGetOutCmd() and SendProtocol
- }
- }
+using BrewMonster;
+using BrewMonster.Common;
+using BrewMonster.Managers;
+using BrewMonster.Network;
+using BrewMonster.Scripts.Skills;
+using BrewMonster.UI;
+using CSNetwork.C2SCommand;
+using CSNetwork.GPDataType;
+using CSNetwork.Protocols;
+using CSNetwork.Protocols.RPCData;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEngine;
+using CommandID = CSNetwork.GPDataType.CommandID;
+
+namespace CSNetwork
+{
+ public class GameSession : IDisposable
+ {
+ private static IPrefixedLogger
+ _logger = LoggerFactory.GetLogger(nameof(GameSession)); // Get class-specific logger
+
+ private NetworkManager _networkManager;
+ private string _host;
+ private int _port;
+ private string _username;
+ private string _password;
+ private int _currentUserId = -1; // To store the UserID after successful login
+ private uint _localsid = 0; // To store the LocalSID from onlineannounce
+ private int m_iCharID;
+ private int m_idLastSelTarget = 0; // ID of selected item last time
+
+ CECStringTab m_ErrorMsgs;
+
+ // State management for async operations and callbacks
+ private Action _loginCallback;
+ private Action> _roleListCallback;
+ private List _accumulatedRoles;
+ private Action _selectRoleCallback;
+ private Action _createRoleCallback;
+ private RoleInfo _selectedRole;
+ public bool IsConnected => _networkManager?.IsConnected ?? false;
+ public static SynchronizationContext Context;
+ private CECC2SCmdCache m_CmdCache; // C2S command cache
+
+ /// Raised when server sends PROTOCOL_PLAYERLOGOUT(69).
+ public event Action PlayerLogoutReceived;
+
+ /// Raised when the underlying network disconnects.
+ public event Action Disconnected;
+
+ private static void PostToUnityContext(Action action)
+ {
+ if (action == null) return;
+ var ctx = Context;
+ if (ctx != null)
+ {
+ ctx.Post(_ => action(), null);
+ }
+ else
+ {
+ action();
+ }
+ }
+#if UNITY_EDITOR
+ public bool isDebug;
+ public bool IsDebug
+ {
+ get => isDebug;
+ set => isDebug = value;
+ }
+#endif
+ public CECC2SCmdCache CmdCache { get => m_CmdCache; }
+
+
+ public GameSession()
+ {
+ _networkManager = new NetworkManager();
+ m_CmdCache = new CECC2SCmdCache();
+ _networkManager.ProtocolReceived += OnProtocolReceived;
+ _networkManager.ErrorOccurred += OnErrorOccurred;
+ _networkManager.Disconnected += OnDisconnected;
+ }
+
+ public void SetLogPath(string path)
+ {
+ LoggerFactory.SetFileLoggerImplementation(new FileLogger());
+ _logger = LoggerFactory.GetCustomLogger(path, nameof(GameSession) + GetHashCode(), LoggerType.File);
+ _networkManager.SetLogPath(path);
+ }
+
+ ///
+ /// Connects to the game server asynchronously.
+ ///
+ /// Server hostname or IP address.
+ /// Server port.
+ /// Task representing the asynchronous connect operation. Check IsConnected property or handle Disconnected event for status.
+ public async Task ConnectAsync(string host, int port)
+ {
+ if (IsConnected)
+ {
+ _logger.Log(LogType.Warning, "ConnectAsync called but already connected.");
+ return;
+ }
+
+ _host = host;
+ _port = port;
+ _logger.Log(LogType.Info, $"Attempting to connect to {_host}:{_port}...");
+ try
+ {
+ await _networkManager.ConnectAsync(_host, _port);
+ if (IsConnected)
+ {
+ _logger.Log(LogType.Info, "Connection established.");
+ }
+ else
+ {
+ _logger.Log(LogType.Warning,
+ "Connection failed after ConnectAsync completed (check NetworkManager logs/events).");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Log(LogType.Error, $"Connection exception: {ex.Message}");
+ _logger.LogException(ex);
+ OnDisconnected();
+ }
+ }
+
+
+ public void Disconnect()
+ {
+ _networkManager.Disconnect();
+ }
+
+ ///
+ /// Initiates the login process asynchronously.
+ ///
+ /// Account username.
+ /// Account password.
+ /// Action invoked with true on successful login (OnlineAnnounce received), false otherwise.
+ public void LoginAsync(string username, string password, Action callback)
+ {
+ if (!IsConnected)
+ {
+ _logger.Log(LogType.Warning, "LoginAsync called but not connected.");
+ callback?.Invoke(false);
+ return;
+ }
+
+ if (_loginCallback != null)
+ {
+ _logger.Log(LogType.Warning, "LoginAsync called while another login is already in progress.");
+ callback?.Invoke(false);
+ return;
+ }
+
+ _username = username;
+ _password = password;
+ _loginCallback = callback;
+ _currentUserId = -1; // Reset user ID
+
+ _logger.Log(LogType.Info, $"Initiating login for user '{_username}'...");
+ }
+
+ ///
+ /// Initiates fetching the role list asynchronously. Requires successful login.
+ ///
+ /// Action invoked with the complete list of roles, or null/empty list on failure.
+ public void GetRoleListAsync(Action> callback)
+ {
+ if (!IsConnected)
+ {
+ _logger.Log(LogType.Warning, "GetRoleListAsync called but not connected.");
+ callback?.Invoke(null);
+ return;
+ }
+
+ if (_currentUserId == -1)
+ {
+ _logger.Log(LogType.Warning, "GetRoleListAsync called but not logged in.");
+ callback?.Invoke(null);
+ return;
+ }
+
+ if (_roleListCallback != null)
+ {
+ _logger.Log(LogType.Warning,
+ "GetRoleListAsync called while another role list retrieval is already in progress.");
+ callback?.Invoke(null);
+ return;
+ }
+
+ _roleListCallback = callback;
+ _accumulatedRoles = new List();
+ _logger.Log(LogType.Info, "Requesting role list...");
+ RequestRoleListInternal();
+ }
+
+ public RoleInfo GetRoleInfo()
+ {
+ return _selectedRole;
+ }
+
+ public void SelectRoleAsync(RoleInfo role, Action callback)
+ {
+ _selectedRole = role;
+ _selectRoleCallback = callback;
+ SetCharacterID(role.roleid);
+ SendProtocol(new selectrole()
+ {
+ Roleid = role.roleid,
+ Flag = 0
+ });
+ }
+
+ public void CreateRoleAsync(RoleInfo roleInfo, Octets referId, Action callback)
+ {
+ if (!IsConnected)
+ {
+ callback?.Invoke(null);
+ return;
+ }
+
+ if (_currentUserId == -1)
+ {
+ callback?.Invoke(null);
+ return;
+ }
+
+ if (_createRoleCallback != null)
+ {
+ callback?.Invoke(null);
+ return;
+ }
+
+ _createRoleCallback = callback;
+
+ createrole createRoleProtocol = new createrole()
+ {
+ Userid = _currentUserId,
+ Localsid = _localsid,
+ Roleinfo = roleInfo,
+ Referid = referId ?? new Octets()
+ };
+
+ Debug.Log($"[GameSession] Creating role - UserID: {_currentUserId}, Localsid: {_localsid}, Profession: {roleInfo.occupation}, Gender: {roleInfo.gender}");
+ Debug.Log($"[GameSession] RoleInfo details - Name size: {roleInfo.name?.Size ?? 0}, Equipment count: {roleInfo.equipment?.Count ?? 0}, Custom data size: {roleInfo.custom_data?.Size ?? 0}, Race: {roleInfo.race}");
+
+ // Log first few bytes of custom_data for debugging
+ if (roleInfo.custom_data != null && roleInfo.custom_data.Size > 0)
+ {
+ byte[] customDataPreview = new byte[Math.Min(16, (int)roleInfo.custom_data.Size)];
+ Array.Copy(roleInfo.custom_data.ByteArray, 0, customDataPreview, 0, customDataPreview.Length);
+ string hexPreview = BitConverter.ToString(customDataPreview).Replace("-", " ");
+ Debug.Log($"[GameSession] Custom_data preview (first 16 bytes): {hexPreview}");
+ }
+
+ Debug.Log($"[GameSession] Sending createrole protocol (Type: {createRoleProtocol.Type})");
+ SendProtocol(createRoleProtocol);
+ }
+
+ ///
+ /// Helper method to create a new RoleInfo for character creation.
+ /// Matches C++ NewCharacterImpl behavior.
+ ///
+ public static RoleInfo CreateNewRoleInfo(string name, int profession, int gender)
+ {
+ RoleInfo roleInfo = new RoleInfo();
+
+ // Set basic info
+ roleInfo.occupation = (byte)profession;
+ roleInfo.gender = (byte)gender;
+ roleInfo.level = 1;
+ roleInfo.level2 = 0;
+ roleInfo.status = 0; // _ROLE_STATUS_NORMAL
+ roleInfo.delete_time = 0;
+ roleInfo.create_time = 0; // Server will set this
+ roleInfo.lastlogin_time = 0;
+ roleInfo.posx = 0.0f;
+ roleInfo.posy = 0.0f;
+ roleInfo.posz = 0.0f;
+ roleInfo.worldtag = 0; // Server will set this
+ roleInfo.referrer_role = 0;
+ roleInfo.cash_add = 0;
+
+ // Set name - C++ uses Unicode encoding (ACHAR = wchar_t)
+ if (!string.IsNullOrEmpty(name))
+ {
+ byte[] nameBytes = Encoding.Unicode.GetBytes(name);
+ roleInfo.name = new Octets(nameBytes);
+ }
+ else
+ {
+ roleInfo.name = new Octets();
+ }
+
+ // Initialize equipment list with 29 empty items (IVTRSIZE_EQUIPPACK = 29)
+ roleInfo.equipment = new List();
+ for (int i = 0; i < 29; i++)
+ {
+ // Important: GRoleInventory.data must be non-null or Marshal() will fail and the packet won't send.
+ roleInfo.equipment.Add(new GRoleInventory
+ {
+ id = 0,
+ pos = i,
+ count = 0,
+ max_count = 0,
+ data = new Octets(),
+ proctype = 0,
+ expire_date = 0,
+ guid1 = 0,
+ guid2 = 0,
+ mask = 0
+ });
+ }
+
+ // Initialize custom data exactly as C++ does: memset to 0, then set specific values
+ // This matches C++ LoadDefaultCustomizeData behavior
+ roleInfo.custom_data = CreateDefaultCustomizeData(profession, gender);
+
+ // Initialize other empty custom data fields
+ roleInfo.custom_status = new Octets();
+ roleInfo.charactermode = new Octets();
+ roleInfo.reincarnation_data = new Octets();
+ roleInfo.realm_data = new Octets();
+
+ // Race is typically determined by profession, but we'll leave it at 0 for now
+ // Server may set it based on profession
+ roleInfo.race = 0;
+
+ return roleInfo;
+ }
+
+ ///
+ /// Creates default customize data exactly matching C++ LoadDefaultCustomizeData behavior.
+ /// Structure layout - trying multiple sizes due to potential padding:
+ /// - DWORD dwVersion (offset 0-3, 4 bytes) = 0x10007001
+ /// - FACE_CUSTOMIZEDATA faceData (offset 4-87, 84 bytes) = all zeros
+ /// - unsigned short bodyID (offset 88-89, 2 bytes) = 0
+ /// - A3DCOLOR colorBody (offset 90-93, 4 bytes) = 0xffffffff
+ /// - 6 unsigned char scales (offset 94-99, 6 bytes) = all 128
+ /// Note: C++ sizeof might include padding, trying 100, 104, 108 bytes
+ ///
+ private static Octets CreateDefaultCustomizeData(int profession, int gender)
+ {
+ // C++ sizeof(PLAYER_CUSTOMIZEDATA) - Based on server response, it expects 176 bytes
+ // The C++ code sends: sizeof(CECPlayer::PLAYER_CUSTOMIZEDATA) which appears to be 176 bytes
+ // Server response shows: custom_data size=176, so server expects/receives 176 bytes
+ // Structure layout (176 bytes total):
+ // - DWORD dwVersion (4 bytes, offset 0) = 0x10007001
+ // - FACE_CUSTOMIZEDATA faceData (84 bytes, offset 4) = all zeros for now
+ // - unsigned short bodyID (2 bytes, offset 88) = 0
+ // - A3DCOLOR colorBody (4 bytes, offset 90) = 0xffffffff
+ // - 6 unsigned char scales (6 bytes, offset 94) = all 128
+ // - Padding/additional fields (76 bytes, offset 100-175) = all zeros
+ const int CUSTOMIZE_DATA_SIZE = 176; // Match server expectation (176 bytes from response)
+ const uint CUSTOMIZE_DATA_VERSION = 0x10007001; // CUSTOMIZE_DATA_VERSION from C++
+ byte[] customDataBytes = new byte[CUSTOMIZE_DATA_SIZE];
+
+ // Step 1: memset to 0 (already done by new byte[])
+
+ // Step 2: Set dwVersion at offset 0-3 (little-endian)
+ byte[] versionBytes = BitConverter.GetBytes(CUSTOMIZE_DATA_VERSION);
+ Array.Copy(versionBytes, 0, customDataBytes, 0, 4);
+
+ // Step 3: FACE_CUSTOMIZEDATA at offset 4-87 is already zero (84 bytes)
+ // (In C++ this would be loaded from INI, but for now we use zeros)
+
+ // Step 4: bodyID at offset 88-89 is already zero (2 bytes)
+ // Note: There might be 2 bytes padding here if struct is 4-byte aligned
+
+ // Step 5: Set colorBody to 0xffffffff
+ // Try offset 90 first (no padding), if that doesn't work try 92 (with padding)
+ byte[] colorBodyBytes = BitConverter.GetBytes(0xffffffffu);
+ Array.Copy(colorBodyBytes, 0, customDataBytes, 90, 4); // Try 90 first
+
+ // Step 6: Set all 6 scale fields to 128
+ // Try offset 94 first (no padding), if that doesn't work try 96 (with padding)
+ for (int i = 94; i < 100; i++)
+ {
+ customDataBytes[i] = 128;
+ }
+
+ return new Octets(customDataBytes);
+ }
+
+ public void EnterWorldAsync(RoleInfo role, Action callback)
+ {
+ SendProtocol(new enterworld()
+ {
+ Roleid = _selectedRole.roleid,
+ Provider_link_id = 0,
+ }, callback);
+ }
+
+ public void RequestDropIvtrItem(byte index, int amount)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.CreateDropIvtrItem(index, amount);
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void RequestDropEquipItem(byte index)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.CreateDropEquipItem(index);
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void RequestPickupItem(int idItem, int tid)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.CreatePickupItem(idItem, tid);
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void c2s_SendCmdGetIvtrDetailData(byte byPackage, Action callback)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGetIvtrDetailData(byPackage);
+ SendProtocol(gamedatasendRequest, callback);
+ }
+
+ public void c2s_SendCmdQueryCashInfo()
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdQueryCashInfo();
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void c2s_SendCmdOpenFashionTrash(string password)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdOpenFashionTrash(password);
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void c2s_SendCmdEquipItem(byte iIvtrIdx, byte iEquipIdx, Action callback)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdEquipItem(iIvtrIdx, iEquipIdx);
+ SendProtocol(gamedatasendRequest, callback);
+ }
+
+ public void c2s_SendCmdReviveVillage(int param = 0)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
+ SendProtocol(gamedatasendRequest);
+ }
+ public void c2s_SendCmdReviveItem(int param = 0)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
+ SendProtocol(gamedatasendRequest);
+ }
+ public void RequestReviveByPlayer(int param = 0)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
+ SendProtocol(gamedatasendRequest);
+ }
+
+
+ public void c2s_SendCmdMallShopping(uint count, CMD_MallShopping.goods[] goodsArray)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetMallShopping(count, goodsArray);
+ SendProtocol(gamedatasendRequest);
+ }
+ public void c2s_SendCmdGatherMaterial(int idMatter, int iToolPack, int idToolIndex, int idTool, int idTask)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGatherMaterial(idMatter, iToolPack, idToolIndex, idTool, idTask);
+ SendProtocol(gamedatasendRequest);
+ }
+
+ public void c2s_SendCmdUseItemWithTarget(byte byPackage, byte bySlot, int tid, byte byPVPMask)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateUseItemWithTarget(byPackage, bySlot, tid, byPVPMask);
+ SendProtocol(gamedatasend);
+ }
+
+ public void RequestOwnItemInfoAsync(
+ byte byPackage,
+ byte bySlot,
+ int type,
+ int expire_date,
+ int state,
+ uint count,
+ ushort crc,
+ ushort content_length,
+ byte[] content,
+ Action callback)
+ {
+ gamedatasend gamedatasendRequest = new gamedatasend();
+ gamedatasendRequest.Data = C2SCommandFactory.CreateOwnItemInfo(byPackage, bySlot, type, expire_date, state,
+ count, crc, content_length, content);
+ SendProtocol(gamedatasendRequest, callback);
+ }
+
+ // --- Protocol Sending ---
+ public void SendProtocol(Protocol protocol, Action complete = null)
+ {
+ if (IsConnected)
+ {
+ _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}");
+ _networkManager.Send(protocol);
+ complete?.Invoke();
+ }
+ else
+ {
+ _logger.Log(LogType.Warning, $"Cannot send protocol ({protocol.GetType().Name}), not connected.");
+ BMLogger.LogError($"[GameSession] Cannot send protocol ({protocol.GetType().Name}), not connected.");
+ }
+ }
+
+ // --- Event Handlers (from NetworkManager) ---
+
+ private void OnProtocolReceived(Protocol protocol)
+ {
+ _logger.Log(LogType.Debug, $"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})");
+ if (protocol is null)
+ return;
+
+
+ // Route protocol to appropriate handler
+ switch (protocol.GetPType())
+ {
+ case ProtocolType.PROTOCOL_CHALLENGE:
+ HandleChallenge((challenge)protocol);
+ break;
+ case ProtocolType.PROTOCOL_KEYEXCHANGE:
+ HandleKeyExchange((KeyExchange)protocol);
+ break;
+ case ProtocolType.PROTOCOL_ONLINEANNOUNCE:
+ HandleOnlineAnnounce((onlineannounce)protocol);
+ break;
+ case ProtocolType.PROTOCOL_ROLELIST_RE:
+ HandleRoleListResponse((RoleListResponse)protocol);
+ break;
+ // Add cases for other protocols GameSession might need to handle
+ case ProtocolType.PROTOCOL_SELECTROLE_RE:
+ HandleSelectRoleResponse((SelectRole_Re)protocol);
+ //_networkManager.IgnoreBytes = 2;
+ break;
+ case ProtocolType.PROTOCOL_CREATEROLE_RE:
+ HandleCreateRoleResponse((createrole_re)protocol);
+ break;
+ case ProtocolType.PROTOCOL_ERRORINFO:
+ HandleErrorInfo((errorinfo)protocol);
+ break;
+ case ProtocolType.PROTOCOL_S2CGAMEDATASEND:
+ case ProtocolType.PROTOCOL_GAMEDATASEND:
+ HandleServerDataSend((gamedatasend)protocol);
+ break;
+ case ProtocolType.PROTOCOL_CHATMESSAGE:
+ _logger.Log(LogType.Warning, $"HoangDev :ProtocolType.PROTOCOL_CHATMESSAGE {protocol.GetPType()}");
+ OnPrtcChatMessage(protocol, false);
+ break;
+ case ProtocolType.PROTOCOL_PLAYERBASEINFO_RE:
+ OnPrtcPlayerBaseInfoRe(protocol);
+ break;
+ case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break;
+ case ProtocolType.PROTOCOL_PLAYERLOGOUT:
+ HandlePlayerLogout((playerlogout)protocol);
+ break;
+
+ case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE:
+ {
+ // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
+ // if( pAutoTeam !=null)
+ // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
+ }
+ break;
+
+ default:
+ _logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
+ break;
+ }
+ }
+
+ private void HandlePlayerLogout(playerlogout protocol)
+ {
+ // Original client receives this before EVENT_DISCONNECT.
+ // We just publish it to allow higher-level flow (UnityGameSession/UI) to react.
+ PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
+ }
+
+ private void HandleServerDataSend(gamedatasend protocol)
+ {
+ int lenghtHeader = Marshal.SizeOf();
+ var pDataBuf = new byte[protocol.Data.ByteArray.Length - lenghtHeader];
+ var byteArrHeader = new byte[lenghtHeader];
+ long dwDataSize = protocol.Data.Size;
+
+ if (dwDataSize < Marshal.SizeOf())
+ {
+ _logger.Error($"### GameDataSend: size invalid {dwDataSize}");
+ return;
+ }
+
+ dwDataSize -= Marshal.SizeOf(); // subtract the header size (ushort)
+ for (int i = 0; i < protocol.Data.ByteArray.Length; i++)
+ {
+ if (i < lenghtHeader)
+ {
+ byteArrHeader[i] = protocol.Data.ByteArray[i];
+ }
+ else
+ {
+ pDataBuf[i - lenghtHeader] = protocol.Data.ByteArray[i];
+ }
+ }
+
+ var pCmdHeader = BitConverter.ToUInt16(byteArrHeader);
+ //sss
+ // Note: isDebug is UNITY_EDITOR-only. Use runtime gate so we can see logs in Development Build too.
+ bool logInv = Application.isEditor || Debug.isDebugBuild;
+ if (logInv)
+ {
+ // Only log inventory-related commands to avoid spam.
+ if (pCmdHeader == CommandID.OWN_IVTR_DATA ||
+ pCmdHeader == CommandID.OWN_IVTR_DETAIL_DATA ||
+ pCmdHeader == CommandID.OWN_ITEM_INFO ||
+ pCmdHeader == CommandID.EMPTY_ITEM_SLOT ||
+ pCmdHeader == CommandID.PICKUP_ITEM ||
+ pCmdHeader == CommandID.HOST_OBTAIN_ITEM ||
+ pCmdHeader == CommandID.PRODUCE_ONCE ||
+ pCmdHeader == CommandID.TASK_DELIVER_ITEM ||
+ pCmdHeader == CommandID.PURCHASE_ITEM ||
+ pCmdHeader == CommandID.CHANGE_IVTR_SIZE)
+ {
+ Debug.Log($"[INVNET] S2C cmd={pCmdHeader} payloadBytes={pDataBuf?.Length ?? 0}");
+ }
+ }
+
+#if UNITY_EDITOR
+ if (isDebug)
+ {
+ BMLogger.LogError($"### GameDataSend: CMDID {pCmdHeader}");
+ }
+#endif
+ int iHostID = _selectedRole.roleid;
+ switch (pCmdHeader)
+ {
+ case CommandID.PLAYER_INFO_2:
+ case CommandID.PLAYER_INFO_3:
+ case CommandID.PLAYER_INFO_4:
+ case CommandID.PLAYER_INFO_2_LIST:
+ case CommandID.PLAYER_INFO_3_LIST:
+ case CommandID.PLAYER_INFO_23_LIST:
+
+ break;
+
+ case CommandID.PLAYER_INFO_1:
+ case CommandID.PLAYER_ENTER_WORLD:
+ case CommandID.PLAYER_ENTER_SLICE:
+ case CommandID.PLAYER_INFO_1_LIST:
+ case CommandID.PLAYER_INFO_00:
+ case CommandID.SELF_INFO_1:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERINFO, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf,
+ pCmdHeader, iHostID);
+ break;
+ case CommandID.OBJECT_MOVE:
+ int lenghtDataType = Marshal.SizeOf();
+ byte[] arrByteData = GetBytes(pDataBuf, lenghtDataType, 0);
+ int idObjMove = BitConverter.ToInt32(arrByteData);
+ if (ISPLAYERID(idObjMove))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERMOVE, (int)MANAGER_INDEX.MAN_PLAYER, -1,
+ pDataBuf, pCmdHeader, iHostID);
+ }
+ else if (ISNPCID(idObjMove))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCMOVE, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
+ pCmdHeader);
+ }
+
+ break;
+ case CommandID.OBJECT_STOP_MOVE:
+ {
+ int id1 = GPDataTypeHelper.FromBytes(pDataBuf);
+
+ if (ISPLAYERID(id1))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERSTOPMOVE, (int)MANAGER_INDEX.MAN_PLAYER, -1,
+ pDataBuf, pCmdHeader);
+ }
+ else if (ISNPCID(id1))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCSTOPMOVE, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
+ pCmdHeader);
+ }
+
+ break;
+ }
+ case CommandID.OBJECT_LEAVE_SLICE:
+ {
+ int id = GPDataTypeHelper.FromBytes(pDataBuf);
+ if (ISPLAYERID(id))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERRUNOUT, (int)MANAGER_INDEX.MAN_PLAYER, -1,
+ pDataBuf, pCmdHeader);
+ }
+ else if (ISNPCID(id))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCRUNOUT, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
+ pCmdHeader);
+ }
+
+ break;
+ }
+ case CommandID.OWN_IVTR_DATA:
+ case CommandID.OWN_IVTR_DETAIL_DATA:
+ case CommandID.GET_OWN_MONEY:
+ case CommandID.CHANGE_IVTR_SIZE:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader, iHostID);
+ break;
+ case CommandID.EXG_IVTR_ITEM:
+ case CommandID.MOVE_IVTR_ITEM:
+ case CommandID.PLAYER_DROP_ITEM:
+ case CommandID.EXG_EQUIP_ITEM:
+ case CommandID.EQUIP_ITEM:
+ case CommandID.MOVE_EQUIP_ITEM:
+ case CommandID.UNFREEZE_IVTR_SLOT:
+ case CommandID.PLAYER_EQUIP_TRASHBOX_ITEM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ITEMOPERATION, (int)MANAGER_INDEX.MAN_PLAYER, 0,
+ pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PLAYER_CASH:
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader, iHostID);
+ break;
+ }
+ case CommandID.MATTER_INFO_LIST:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERINFO, (int)MANAGER_INDEX.MAN_MATTER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.MATTER_ENTER_WORLD:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERENTWORLD, (int)MANAGER_INDEX.MAN_MATTER, 0,
+ pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PICKUP_ITEM:
+ case CommandID.HOST_OBTAIN_ITEM:
+ case CommandID.PRODUCE_ONCE:
+ case CommandID.TASK_DELIVER_ITEM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPITEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.PURCHASE_ITEM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PURCHASEITEMS, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.MATTER_PICKUP:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PICKUPMATTER, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PICKUP_MONEY:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPMONEY, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.HOST_CORRECT_POS:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CORRECTPOS, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader, iHostID);
+ break;
+ case CommandID.EMPTY_ITEM_SLOT:
+ case CommandID.OWN_ITEM_INFO:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_OWNITEMINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader, iHostID);
+ break;
+ case CommandID.PLAYER_DIED:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDIED, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.HOST_DIED:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_DIED, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PLAYER_REVIVE:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERREVIVE, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.NOTIFY_HOSTPOS:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_GOTO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.NPC_ENTER_SLICE:
+ case CommandID.NPC_INFO_LIST:
+ case CommandID.NPC_INFO_00:
+ case CommandID.NPC_ENTER_WORLD:
+ case CommandID.NPC_VISIBLE_TID_NOTIFY:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCINFO, (int)MANAGER_INDEX.MAN_NPC, 0, pDataBuf,
+ pCmdHeader, dwDataSize);
+ break;
+ case CommandID.TASK_DATA:
+ case CommandID.TASK_VAR_DATA:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TASKDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader, dwDataSize);
+ break;
+ case CommandID.BE_HURT:
+ case CommandID.HURT_RESULT:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_HURTRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.OBJECT_ATTACK_RESULT:
+ //int id = GPDataTypeHelper.FromBytes(pDataBuf);
+ cmd_object_atk_result pCmdAtk = GPDataTypeHelper.FromBytes(pDataBuf);
+ //BMLogger.LogError($"OBJECT_ATTACK_RESULT: npc ? " + ISNPCID(id));
+
+ if (ISPLAYERID(pCmdAtk.attacker_id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERATKRESULT, MANAGER_INDEX.MAN_PLAYER, -1,
+ pDataBuf, pCmdHeader);
+ else if (ISNPCID(pCmdAtk.attacker_id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCATKRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.HOST_ATTACKRESULT:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATKRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.HOST_ATTACKED:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATTACKED, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+
+ case CommandID.TEAM_JOIN_TEAM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_JOINTEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.TEAM_LEAVE_PARTY:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.TEAM_NEW_MEMBER:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_NEWTEAMMEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.TEAM_MEMBER_DATA:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TEAMMEMBERDATA, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.TEAM_MEMBER_LEAVE:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+
+ case CommandID.ERROR_MESSAGE:
+ {
+ int errRaw = BitConverter.ToInt32(pDataBuf, 0);
+ // Note: _logger may be configured as a file logger via SetLogPath(), so also log to console for visibility.
+ _logger.Info($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
+#if UNITY_EDITOR
+ BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
+#endif
+ cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
+#if UNITY_EDITOR
+ BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}");
+#endif
+ Debug.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
+ if (pCmd.iMessage != 0)
+ {
+ // string szMsg = m_ErrorMsgs.GetWideString(pCmd.iMessage);
+ // if (string.IsNullOrEmpty(szMsg))
+ // BMLogger.LogError("SERVER - unknown error !");
+ // else
+ // {
+ // BMLogger.LogError("SERVER - error: "+szMsg);
+ // }
+ // else if (pCmd.iMessage != 2)
+ // g_pGame.GetGameRun().AddChatMessage(szMsg, GP_CHAT_MISC);
+ }
+
+ if (pCmd.iMessage == 2)
+ {
+ // Attack target is too far
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ }
+ else if (pCmd.iMessage == 20)
+ {
+ // Failed to cast skill
+ //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
+ }
+ else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
+ {
+ // deal failed
+ //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
+ }
+ else if (pCmd.iMessage == 158)
+ {
+ // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ
+ //c2s_CmdGetCashMoneyRate();
+ }
+ else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/)
+ {
+ /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ if (pGameUI)
+ pGameUI.EndNPCService();*/
+ }
+ else if
+ (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/
+ )
+ {
+ /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ if (pGameUI)
+ pGameUI.EndNPCService();*/
+ }
+ else if (pCmd.iMessage == 175)
+ {
+ //c2s_CmdQueryParallelWorld();
+ }
+ else if (pCmd.iMessage == 6)
+ {
+ //AP_ActionEvent(AP_EVENT_CANNOTPICKUP);
+ }
+
+ break;
+ }
+ case CommandID.SELECT_TARGET:
+ case CommandID.UNSELECT:
+
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SELTARGET, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.NPC_DIED:
+ case CommandID.NPC_DIED2:
+
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDIED, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OBJECT_DISAPPEAR:
+ {
+ int lenghtDataType1 = Marshal.SizeOf();
+ byte[] arrByteData1 = GetBytes(pDataBuf, lenghtDataType1, 0);
+ int objectId = BitConverter.ToInt32(arrByteData1);
+ if (ISPLAYERID(objectId))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDISAPPEAR, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ else if (ISNPCID(objectId))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDISAPPEAR, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+ else if (ISMATTERID(objectId))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERDISAPPEAR, MANAGER_INDEX.MAN_MATTER, 0, pDataBuf, pCmdHeader);
+
+ break;
+ }
+ case CommandID.SELF_INFO_00:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_INFO00, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
+ pCmdHeader);
+ break;
+ case CommandID.NPC_GREETING:
+ {
+ // If this greeting is from the skill-learn NPC, record it (C++ skill dialog relies on this).
+ try
+ {
+ cmd_npc_greeting greet = GPDataTypeHelper.FromBytes(pDataBuf);
+ CECHostSkillModel.Instance.OnNpcGreeting(greet.idObject);
+ }
+ catch (Exception ex)
+ {
+ _logger.Log(LogType.Warning, $"Failed to parse NPC_GREETING payload: {ex.Message}");
+ }
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_NPCGREETING, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ }
+ case CommandID.ACTIVATE_WAYPOINT:
+ case CommandID.WAYPOINT_LIST:
+
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_WAYPOINT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.SERVER_TIME:
+ {
+ cmd_server_time pcmd_server_time = GPDataTypeHelper.FromBytes(pDataBuf);
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_SERVERTIME, -1, 0, pcmd_server_time.time, pcmd_server_time.timebias);
+ break;
+ }
+ case CommandID.SCENE_SERVICE_NPC_LIST:
+ {
+ CECHostSkillModel.Instance.RecvNPCServiceList(protocol.Data);
+ break;
+ }
+ case CommandID.SKILL_DATA:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OBJECT_CAST_SKILL:
+ case CommandID.OBJECT_CAST_INSTANT_SKILL:
+ case CommandID.OBJECT_CAST_POS_SKILL:
+ {
+ cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf,true);
+ if (ISPLAYERID(pCmd2.caster))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ else if (ISNPCID(pCmd2.caster))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCCASTSKILL, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+
+ break;
+ }
+ case CommandID.LEVEL_UP:
+ {
+ cmd_level_up pCmdLevelUp = GPDataTypeHelper.FromBytes(pDataBuf); ;
+ if (ISPLAYERID(pCmdLevelUp.id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERLEVELUP, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ else if (ISNPCID(pCmdLevelUp.id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCLEVELUP, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+ break;
+ }
+ case CommandID.HOST_START_ATTACK:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_STARTATTACK, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
+ break;
+ case CommandID.HOST_STOPATTACK:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_STOPATTACK, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
+ break;
+ case CommandID.HOST_SKILL_ATTACK_RESULT:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.CHANGE_FACE_START:
+ case CommandID.CHANGE_FACE_END:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CHANGEFACE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
+ break;
+ case CommandID.ENCHANT_RESULT:
+ cmd_enchant_result pCmd3 = GPDataTypeHelper.FromBytes(pDataBuf);
+ if (ISPLAYERID(pCmd3.caster))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_ENCHANTRESULT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ else if (ISNPCID(pCmd3.caster))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_ENCHANTRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.HOST_STOP_SKILL:
+ case CommandID.SELF_SKILL_INTERRUPTED:
+ case CommandID.SKILL_PERFORM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.SET_COOLDOWN:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETCOOLTIME, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.HOST_USE_ITEM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_USEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.COMBO_SKILL_PREPARE:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_COMBO_SKILL_PREPARE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
+ break;
+ case CommandID.PLAYER_EXT_PROP_BASE:
+ case CommandID.PLAYER_EXT_PROP_MOVE:
+ case CommandID.PLAYER_EXT_PROP_ATK:
+ case CommandID.PLAYER_EXT_PROP_DEF:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXTPROP, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OWN_EXT_PROP:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_OWNEXTPROP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OBJECT_DO_EMOTE:
+ case CommandID.OBJECT_EMOTE_RESTORE:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDOEMOTE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OUT_OF_SIGHT_LIST:
+ {
+ cmd_out_of_sight_list pCmd5 = default;
+ pCmd5.uCount = GPDataTypeHelper.FromBytes(pDataBuf);
+ int offset2 = sizeof(uint);
+ pCmd5.idList = new int[pCmd5.uCount];
+ for (int i = 0; i < pCmd5.uCount; i++)
+ {
+ pCmd5.idList[i] = GPDataTypeHelper.FromBytes(pDataBuf, offset2);
+ offset2 += 4;//sizeof int;
+ }
+
+ for (uint n = 0; n < pCmd5.uCount; n++)
+ {
+ if (ISPLAYERID(pCmd5.idList[n]))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEROUTOFVIEW, MANAGER_INDEX.MAN_PLAYER, -1, pCmd5.idList[n], pCmdHeader);
+ else if (ISNPCID(pCmd5.idList[n]))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCOUTOFVIEW, MANAGER_INDEX.MAN_NPC, 0, pCmd5.idList[n], pCmdHeader);
+ else if (ISMATTERID(pCmd5.idList[n]))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTEROUTOFVIEW, MANAGER_INDEX.MAN_MATTER, 0, pCmd5.idList[n], pCmdHeader);
+ }
+
+ break;
+ }
+ case CommandID.PLAYER_GATHER_START:
+ case CommandID.PLAYER_GATHER_STOP:
+ case CommandID.MINE_GATHERED:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERGATHER, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.COOLTIME_DATA:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_COOLTIMEDATA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.OBJECT_TAKEOFF:
+ {
+ cmd_object_takeoff pCmdTakeOff = GPDataTypeHelper.FromBytes((byte[])pDataBuf);
+ if (ISPLAYERID(pCmdTakeOff.object_id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ }
+ case CommandID.OBJECT_LANDING:
+ {
+ cmd_object_landing pCmdLanding = GPDataTypeHelper.FromBytes((byte[])pDataBuf);
+ if (ISPLAYERID(pCmdLanding.object_id))
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ }
+ case CommandID.HOST_RUSH_FLY:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERFLY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.FLYSWORD_TIME:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_FLYSWORDTIME, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PRODUCE_START:
+ case CommandID.PRODUCE_END:
+ case CommandID.PRODUCE_NULL:
+ // Post MSG_HST_PRODUCEITEM message with command ID as parameter (matches C++ behavior)
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRODUCEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+
+
+ case CommandID.LEARN_SKILL:
+ BMLogger.LogError("### GameDataSend: LEARN_SKILL");
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEARNSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.GAIN_PET:
+ case CommandID.FREE_PET:
+ case CommandID.SUMMON_PET:
+ case CommandID.RECALL_PET:
+ case CommandID.PLAYER_START_PET_OP:
+ case CommandID.PLAYER_STOP_PET_OP:
+ case CommandID.PET_RECEIVE_EXP:
+ case CommandID.PET_LEVELUP:
+ case CommandID.PET_ROOM:
+ case CommandID.PET_ROOM_CAPACITY:
+ case CommandID.PET_HONOR_POINT:
+ case CommandID.PET_HUNGER_GAUGE:
+ case CommandID.PET_DEAD:
+ case CommandID.PET_REVIVE:
+ case CommandID.PET_HP_NOTIFY:
+ case CommandID.PET_AI_STATE:
+ case CommandID.PET_SET_COOLDOWN:
+ case CommandID.SUMMON_PLANT_PET:
+ case CommandID.PLANT_PET_DISAPPEAR:
+ case CommandID.PLANT_PET_HP_NOTIFY:
+ case CommandID.PET_PROPERTY:
+ case CommandID.PET_REBUILD_INHERIT_START:
+ case CommandID.PET_REBUILD_INHERIT_INFO:
+ case CommandID.PET_REBUILD_INHERIT_END:
+ case CommandID.PET_EVOLUTION_DONE:
+ case CommandID.PET_REBUILD_NATURE_START:
+ case CommandID.PET_REBUILD_NATURE_INFO:
+ case CommandID.PET_REBUILD_NATURE_END:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PETOPT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.SET_PLAYER_LIMIT:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETPLAYERLIMIT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PLAYER_MOUNTING:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERMOUNT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.DUEL_RECV_REQUEST:
+ case CommandID.DUEL_REJECT_REQUEST:
+ case CommandID.DUEL_PREPARE:
+ case CommandID.DUEL_CANCEL:
+ case CommandID.HOST_DUEL_START:
+ case CommandID.DUEL_STOP:
+ case CommandID.DUEL_RESULT:
+ // Origin: host duel events use MSG_PM_DUELOPT (391); handler distinguishes by pCmdHeader (214-220)
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_DUELOPT, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PLAYER_DUEL_START:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDUELOPT, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.EMBED_ITEM:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_EMBEDITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.CLEAR_TESSERA:
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CLEARTESSERA, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ break;
+ case CommandID.PLAYER_LEAVE_WORLD:
+ {
+ cmd_player_leave_world pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
+ if (ISPLAYERID(pCmd.id))
+ {
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXIT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
+ }
+ break;
+ }
+ default:
+#if UNITY_EDITOR
+ if (isDebug)
+ {
+ BMLogger.LogError($"### GameDataSend: Unhandled CMDID {pCmdHeader} (payloadBytes={pDataBuf?.Length ?? 0})");
+ }
+#endif
+ break;
+ }
+ }
+
+
+ private void HandleSelectRoleResponse(SelectRole_Re protocol)
+ {
+ _logger.Info($"Select role response {protocol.result}");
+ var callback = _selectRoleCallback;
+ PostToUnityContext(() => callback?.Invoke(_selectedRole));
+ }
+
+ private void HandleCreateRoleResponse(createrole_re protocol)
+ {
+ Debug.Log($"[GameSession] HandleCreateRoleResponse - result: {protocol.result}, roleid: {protocol.roleid}");
+
+ if (protocol.result != (int)ErrCode.ERR_SUCCESS)
+ {
+ string errorMsg = $"Create role failed with result code: {protocol.result} (ERR_SUCCESS = {(int)ErrCode.ERR_SUCCESS})";
+ _logger.Log(LogType.Error, errorMsg);
+ Debug.LogError($"[GameSession] {errorMsg}");
+ var callback = _createRoleCallback;
+ _createRoleCallback = null;
+ PostToUnityContext(() => callback?.Invoke(null));
+ return;
+ }
+
+ Debug.Log($"[GameSession] Create role successful! RoleID: {protocol.roleid}");
+ var successCallback = _createRoleCallback;
+ _createRoleCallback = null;
+ PostToUnityContext(() => successCallback?.Invoke(protocol.roleinfo));
+ }
+
+ private void HandleErrorInfo(errorinfo protocol)
+ {
+ Debug.LogError($"[GameSession] Server error - Errcode: {protocol.Errcode}");
+
+ // If we're waiting for create role response and get an error, fail the callback
+ if (_createRoleCallback != null)
+ {
+ Debug.LogError($"[GameSession] Create role failed due to server error: {protocol.Errcode}");
+ var callback = _createRoleCallback;
+ _createRoleCallback = null;
+ PostToUnityContext(() => callback?.Invoke(null));
+ }
+ }
+
+ private void OnErrorOccurred(string errorMessage)
+ {
+ _logger.Log(LogType.Error, $"Network Error: {errorMessage}");
+ FailLoginInProgress(errorMessage);
+ FailRoleListInProgress(errorMessage);
+ }
+
+ private void OnDisconnected()
+ {
+ _logger.Log(LogType.Info, "Disconnected from server.");
+ _currentUserId = -1;
+ FailLoginInProgress("Disconnected");
+ FailRoleListInProgress("Disconnected");
+ // Clear command cache
+ m_CmdCache.RemoveAllCmds();
+ PostToUnityContext(() => Disconnected?.Invoke());
+ }
+
+ private void HandleChallenge(challenge challenge)
+ {
+ if (_loginCallback == null || string.IsNullOrEmpty(_username))
+ {
+ _logger.Log(LogType.Warning, "Received Challenge but not expecting it or username not set.");
+ return;
+ }
+
+ _logger.Log(LogType.Info, "Handling Challenge...");
+
+ response response = new response();
+ byte[] usernameBytes = Encoding.ASCII.GetBytes(_username);
+ byte[] passwordBytes = Encoding.ASCII.GetBytes(_password);
+ response.identity.Replace(usernameBytes);
+ response.Setup(new Octets(usernameBytes), new Octets(passwordBytes), challenge.nonce);
+
+ uint clientId = 0xffffffff;
+ byte[] clientIdBytes = BitConverter.GetBytes(clientId);
+ response.cli_fingerprint.Replace(clientIdBytes);
+ response.use_token = 0;
+
+ _networkManager.SetNonce(response.response_data);
+ SendProtocol(response);
+ _logger.Log(LogType.Info, "Sent Response.");
+ }
+
+ private void HandleKeyExchange(KeyExchange keyExchange)
+ {
+ if (_loginCallback == null || string.IsNullOrEmpty(_username))
+ {
+ _logger.Log(LogType.Warning, "Received KeyExchange but not expecting it.");
+ return;
+ }
+
+ _logger.Log(LogType.Info, "Handling KeyExchange...");
+ keyExchange.Setup(_networkManager, _username);
+ keyExchange.Blkickuser = 1;
+ SendProtocol(keyExchange);
+ _logger.Log(LogType.Info, "Sent KeyExchange acknowledgment/response.");
+ }
+
+ private void HandleOnlineAnnounce(onlineannounce announce)
+ {
+ if (_loginCallback == null)
+ {
+ _logger.Log(LogType.Warning, "Received OnlineAnnounce but not expecting it.");
+ return;
+ }
+
+ _logger.Log(LogType.Info, $"Login successful! UserID: {announce.Userid}, LocalSID: {announce.Localsid}");
+ _currentUserId = announce.Userid;
+ _localsid = announce.Localsid;
+
+ var callback = _loginCallback;
+ _loginCallback = null;
+ PostToUnityContext(() => callback?.Invoke(true));
+ }
+
+ private void RequestRoleListInternal(int lastHandle = -1)
+ {
+ rolelist rolelistRequest = new rolelist();
+ rolelistRequest.Userid = _currentUserId;
+ rolelistRequest.Localsid = 0;
+ rolelistRequest.Handle = lastHandle;
+
+ SendProtocol(rolelistRequest);
+
+
+ //gamedatasend gamedatasendRequest = new gamedatasend();
+ //gamedatasendRequest.Data = C2SCommandFactory.CreatePlayerMove();
+
+ //SendProtocol(gamedatasendRequest);
+ }
+
+ private void HandleRoleListResponse(RoleListResponse response)
+ {
+ if (_roleListCallback == null || _accumulatedRoles == null)
+ {
+ _logger.Log(LogType.Warning, "Received RoleListResponse but not expecting it.");
+ return;
+ }
+
+ _logger.Log(LogType.Debug,
+ $"Received RoleListResponse. Handle: {response.handle}, Result: {response.result}, Count: {response.rolelist.Count}");
+
+ if (response.result == 0)
+ {
+ _accumulatedRoles.AddRange(response.rolelist);
+
+ foreach (var role in response.rolelist)
+ {
+ try
+ {
+ string roleName = Encoding.UTF8.GetString(role.name.ByteArray, 0, role.name.Length);
+ _logger.Log(LogType.Info, $" - Role ID: {role.roleid}, Name: {roleName}, Level: {role.level}");
+ }
+ catch (Exception ex)
+ {
+ _logger.Log(LogType.Error, $" - Error decoding role name: {ex.Message}");
+ _logger.LogException(ex);
+ }
+ }
+
+ if (response.handle != -1)
+ {
+ _logger.Log(LogType.Debug, $"Requesting next batch of roles (handle: {response.handle})...");
+ RequestRoleListInternal(response.handle);
+ }
+ else
+ {
+ _logger.Log(LogType.Info, $"Finished fetching roles. Total count: {_accumulatedRoles.Count}");
+ var callback = _roleListCallback;
+ var result = _accumulatedRoles;
+ _roleListCallback = null;
+ _accumulatedRoles = null;
+ PostToUnityContext(() => callback?.Invoke(result));
+ }
+ }
+ else
+ {
+ _logger.Log(LogType.Error, $"Role list retrieval failed. Result code: {response.result}");
+ FailRoleListInProgress($"Role list retrieval failed (Result: {response.result})");
+ }
+ }
+
+ // --- Helper methods for failure handling ---
+ private void FailLoginInProgress(string reason)
+ {
+ if (_loginCallback != null)
+ {
+ _logger.Log(LogType.Error, $"Login failed: {reason}");
+ var callback = _loginCallback;
+ _loginCallback = null;
+ PostToUnityContext(() => callback?.Invoke(false));
+ }
+ }
+
+ private void FailRoleListInProgress(string reason)
+ {
+ if (_roleListCallback != null)
+ {
+ _logger.Log(LogType.Error, $"Role list retrieval failed: {reason}");
+ var callback = _roleListCallback;
+ _roleListCallback = null;
+ _accumulatedRoles = null;
+ PostToUnityContext(() => callback?.Invoke(null));
+ }
+ }
+
+ // --- IDisposable Implementation ---
+ private bool disposedValue = false;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ if (_networkManager != null)
+ {
+ _logger.Log(LogType.Info, "[DUCK] Disposing GameSession and disconnecting...");
+ _networkManager.ProtocolReceived -= OnProtocolReceived;
+ _networkManager.ErrorOccurred -= OnErrorOccurred;
+ _networkManager.Disconnected -= OnDisconnected;
+ _networkManager.Disconnect();
+ _networkManager.Dispose();
+ _networkManager = null;
+ }
+
+ _loginCallback = null;
+ _roleListCallback = null;
+ _accumulatedRoles = null;
+ }
+
+ disposedValue = true;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ // GC.SuppressFinalize(this);
+ }
+
+ public bool ISPLAYERID(int id)
+ {
+ return id != 0 && (id & 0x80000000) == 0;
+ }
+ public bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0);
+ public bool ISMATTERID(int id) => ((id) & 0xC0000000) == 0xC0000000;
+ private byte[] GetBytes(byte[] bytes, int length, int index)
+ {
+ byte[] arrByteData = new byte[length];
+ for (int i = 0; i < length; i++)
+ {
+ arrByteData[i] = bytes[i + index];
+ }
+
+ return arrByteData;
+ }
+
+ public void c2s_CmdPlayerMove(in Vector3 vCurPos, in Vector3 vDest,
+ int iTime, float fSpeed, int iMoveMode, ushort wStamp)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data =
+ C2SCommandFactory.CreatePlayerMove(vCurPos, vDest, (ushort)iTime, fSpeed, (byte)iMoveMode, wStamp);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data =
+ C2SCommandFactory.CreatePlayerCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
+
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdCastInstantSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data =
+ C2SCommandFactory.CreatePlayerCastInstantSkill(idSkill, byPVPMask, iNumTarget, aTargets);
+
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_CmdCastPosSkill(int idSkill, Vector3 vDest, byte byPVPMask, int iNumTarget, int aTargets)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data =
+ C2SCommandFactory.CreatePlayerCastPosSkill(idSkill, vDest, byPVPMask, iNumTarget, aTargets);
+
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdNotifyForceAttack(int iForceAttack, byte refuseBless)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNotifyForceAttack(iForceAttack, refuseBless);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdContinueAction()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.CONTINUE_ACTION);
+ SendProtocol(gamedatasend);
+ }
+
+ ///
+ /// Client logout request. Mirrors original client behavior:
+ /// - outType=1: back to select role
+ /// - outType=0: logout account
+ ///
+ public void SendPlayerLogout(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)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data =
+ C2SCommandFactory.CreatePlayerStop(vDest, fSpeed, (byte)iMoveMode, byDir, wStamp, (ushort)iTime);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_CmdSendEnterPKPrecinctint()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.ENTER_PK_PROTECTED);
+ SendProtocol(gamedatasend);
+ }
+ public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
+ {
+ publicchat publicChat = new publicchat();
+ publicChat.Channel = cChannel;
+ publicChat.Roleid = m_iCharID;
+
+ byte[] unicodeBytes = Encoding.Unicode.GetBytes(szMsg);
+ publicChat.Msg.Replace(unicodeBytes);
+ _logger.Log(LogType.Warning, $"HoangDev : publicChat {publicChat}");
+ SendProtocol(publicChat);
+ }
+ public void LoadConfigData()
+ {
+ getuiconfig p = new getuiconfig();
+ p.Roleid = m_iCharID;
+ SendProtocol(p);
+ }
+
+ ///
+ /// Save config data to server (sends setuiconfig with compressed config blob).
+ /// 保存配置数据到服务器(发送 setuiconfig 及压缩后的配置数据)。
+ ///
+ public void SaveConfigData(byte[] pBuf, int len)
+ {
+ BMLogger.Log($"[MH] Session.SaveConfigData | len={len}");
+
+ if (pBuf == null || len <= 0) return;
+ var p = new setuiconfig();
+ p.Roleid = m_iCharID;
+ p.Localsid = (int)_localsid;
+ byte[] slice = new byte[len];
+ Buffer.BlockCopy(pBuf, 0, slice, 0, len);
+ p.Ui_config = new Octets(slice);
+
+ // return;
+ SendProtocol(p);
+ }
+
+ private void SetCharacterID(int iCharID)
+ {
+ m_iCharID = iCharID;
+ }
+
+ private void OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
+ {
+ chatmessage p = (chatmessage)pProtocol;
+
+ string strTemp = System.Text.Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
+
+ _logger.Log(LogType.Warning, $"HoangDev : OnPrtcChatMessage :{strTemp}");
+ EventBus.Publish(new ChatMessageEvent(strTemp));
+ }
+
+ public struct ChatMessageEvent
+ {
+ public string context;
+
+ public ChatMessageEvent(string context)
+ {
+ this.context = context;
+ }
+ }
+ public void OnPrtcGetConfigRe(Protocol pProtocol)
+ {
+ getuiconfig_re p = (getuiconfig_re)pProtocol;
+ if (p.Result != (int)ErrCode.ERR_SUCCESS)
+ BMLogger.LogError("CECGameSession::OnPrtcGetConfigRe, link return error code of " + p.Result);
+ else
+ {
+ if (!CECGameRun.Instance.LoadConfigsFromServer(p.UiConfig.RawBuffer, p.UiConfig.Size))
+ {
+ // if load failed then use current setting directly
+ //TODO : fix later
+ EC_Game.GetConfigs().ApplyUserSetting();
+ }
+
+ // Now, Get config data request is sent after all host initial data ready.
+ // so when we receive this reply, we can do some last work before game
+ // really starts. Maybe it's not the best place to do these work, but
+ // now we do it here.
+ // Enalbe game UI
+ CECGameUIMan pGameUI = (CECGameUIMan)EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
+ if (pGameUI != null)
+ {
+ pGameUI.EnableUI(true);
+
+ // Get referral name for adding friend or other display
+ //TODO: a Hung lam phan select role info di
+ /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
+ if (info.referrer_role > 0)
+ GetPlayerBriefInfo(1, info.referrer_role, 2);*/
+ }
+
+ CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
+ pHost.OnAllInitDataReady();
+
+ /* if (pHost.IsGM())
+ {
+ CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap");
+ pDlgCountryMap.GetConfig();
+ }
+
+ g_pGame.GetConfigs().ApplyOptimizeSetting();
+
+ if (g_pGame.GetConfigs().IsMiniClient())
+ CECMCDownload::GetInstance().SendGetDownloadOK();*/
+ }
+ }
+ private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
+ {
+ playerbaseinfo_re p = (playerbaseinfo_re)pProtocol;
+ BMLogger.Log($"OnPrtcPlayerBaseInfoRe: {p.Roleid} {p.Player.cls} {p.Player.gender}");
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERBASEINFO, MANAGER_INDEX.MAN_PLAYER, -1, p);
+ }
+
+ public void c2s_CmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(idTask, idStorage, idRefreshItem);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdGetAllData(bool byPack, bool byEquip, bool byTask)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+
+ gamedatasend.Data = C2SCommandFactory.CreateGetAllDataCommand(byPack, byEquip, byTask);
+ _logger.Log(LogType.Warning, $"[Dat]- SendCmdGetAllData {byPack},{byEquip},{byTask}");
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevHello(int nid)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevHelloDataCommand(nid);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_CmdNormalAttack(byte byPVPMask)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNormalAttackDataCmd(byPVPMask);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdCancelAction()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.CANCEL_ACTION);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_CmdUnselect()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.UNSELECT);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdSelectTarget(int idTarget)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateSelectTarget(idTarget);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevWaypoint()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevWaypointCmd(NPC_service_type.GP_NPCSEV_WAYPOINT, 0);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdNPCSevMakeItem(int idSkill, int idItem, uint dwCount)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevMakeItemCmd(idSkill, idItem, dwCount);
+ SendProtocol(gamedatasend);
+ }
+ public void GetRoleBaseInfo(int iNumRole, List aRoleIDs)
+ {
+ int iNumLimit = 128;
+ playerbaseinfo p = null;
+ int iCount = 0;
+
+ while (iCount < iNumRole)
+ {
+ p = new();
+ p.Roleid = _selectedRole.roleid;
+
+ int iNumSend = iNumLimit;
+ if (iCount + iNumLimit > iNumRole)
+ iNumSend = iNumRole - iCount;
+
+ if (iNumSend > 0)
+ {
+ p.playerList = new();
+ for (int i = 0; i < iNumSend; i++)
+ p.playerList.Add(aRoleIDs[iCount + i]);
+
+ SendProtocol(p);
+ }
+
+
+ iCount += iNumSend;
+ }
+ }
+
+ public void c2s_SendCmdGetOtherEquip(int iNumID, List aIDs)
+ {
+ // int iNumLimit = 250;
+ // int iCount = 0;
+
+ // while (iCount < iNumID)
+ // {
+ // int iNumSend = iNumLimit;
+ // if (iCount + iNumLimit > iNumID)
+ // iNumSend = iNumID - iCount;
+
+ // if (iNumSend > 0)
+ // {
+ // }
+ // }
+ }
+
+ public void c2s_SendCmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(
+ idTask,
+ idStorage,
+ idRefreshItem);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevReturnTask(int idTask, int iChoice)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevReturnTaskCmd(
+ idTask,
+ iChoice);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevTaskMatter(int idTask)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevTaskMatterCmd(idTask);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevLearnSkill(int idSkill)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevLearnSkillCmd(idSkill);
+ BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill gamedatasend.Data : " + gamedatasend.Data.Size);
+ BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill idSkill : " + idSkill);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevBuy(int itemNum, C2SCommand.npc_trade_item[] items)
+ {
+ if (itemNum <= 0 || items == null || items.Length < itemNum)
+ return;
+
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevBuyCmd(itemNum, items);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevSell(int itemNum, C2SCommand.npc_sell_item[] items)
+ {
+ if (itemNum <= 0 || items == null || items.Length < itemNum)
+ return;
+
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevSellCmd(itemNum, items);
+ SendProtocol(gamedatasend);
+ }
+
+ public void GetRoleCustomizeData(int iNumRole, List aRoleIDs)
+ {
+ if (iNumRole <= 0 || aRoleIDs == null || aRoleIDs.Count == 0) return;
+
+ int iNumLimit = 240;
+ int iCount = 0;
+
+ while (iCount < iNumRole)
+ {
+ getcustomdata p = new();
+ p.Roleid = _selectedRole.roleid;
+
+ int iNumSend = iNumLimit;
+ if (iCount + iNumLimit > iNumRole)
+ iNumSend = iNumRole - iCount;
+
+ for (int i = 0; i < iNumSend; i++)
+ p.playerlist.Add(aRoleIDs[iCount + i]);
+
+ SendProtocol(p);
+
+ iCount += iNumSend;
+ }
+ }
+ public void c2s_SendCmdEmoteAction(uint wPose)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateEmoteActionCmd((int)wPose);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdTaskNotify(byte[] pData, uint dwDataSize)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd( pData, dwDataSize);
+ BMLogger.Log($"[MH Task] c2s_SendCmdTaskNotify Command ID : {pData[0]} Size: {dwDataSize}");
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdStandUp()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.STAND_UP);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdAutoTeamSetGoal(int type, int goal_id, int op)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type,goal_id, op);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdTeamInvite(int idPlayer)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamInviteCommand(idPlayer);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdDuelRequest(int idTarget)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateDuelRequestCommand(idTarget);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdDuelReply(bool accept, int idInviter)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateDuelReplyCommand(accept, idInviter);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamKickMember(int idMember)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamKickMemberCommand(idMember);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamLeaveParty()
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamLeavePartyCommand();
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamDismissParty()
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamDismissPartyCommand();
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamSetPickupFlag(short pickupFlag)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamSetPickupFlagCommand(pickupFlag);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamChangeLeader(int idNewLeader)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamChangeLeaderCommand(idNewLeader);
+ SendProtocol(g);
+ }
+
+ public void c2s_SendCmdTeamMemberPos(int count, int[] memberIds)
+ {
+ if (memberIds == null || count <= 0 || count > memberIds.Length) return;
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreateTeamMemberPosCommand(count, memberIds);
+ SendProtocol(g);
+ }
+
+ public void c2s_CmdGoto(float x, float y, float z)
+ {
+ c2s_SendCmdGoto(x, y, z);
+ }
+
+ // Send C2S::GOTO command data
+ void c2s_SendCmdGoto(float x, float y, float z)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateGoToCommed( x, y, z);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdUseItem(byte byPackage, byte bySlot, int tid, byte byCount)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateUseItemCmd(byPackage, bySlot, tid, byCount);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdGetExtProps()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.GET_EXT_PROP);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdGivePresent(int roleid, int mail_id, int goods_id, int goods_index, int goods_slot)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateGivePresentCmd(roleid, mail_id, goods_id, goods_index, goods_slot);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdEnterSanctuary(int id)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateEnterSanctuaryCmd(id);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdEnterInstance(int iTransIdx, int idInst)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateEnterInstanceCmd(iTransIdx, idInst);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdActiveRushFly(bool bActive)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateActiveRushFlyCmd(bActive);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdPetCtrl(int idTarget, int cmd, byte[] pParamBuf, int iParamLen)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreatePetCtrlCmd(idTarget, cmd, pParamBuf, iParamLen);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdQueryFactionPVPInfo(int faction_id)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateQueryFactionPVPInfo(faction_id);
+ SendProtocol(gamedatasend);
+ }
+
+ public void SendCmdPetCtrl(int idTarget, int cmd, object pParamBuf, int iParamLen)
+ {
+ m_CmdCache.SendCmdPetCtrl(idTarget, cmd, (byte[])pParamBuf, iParamLen);
+ }
+
+ public void c2s_SendCmdPetSummon(int iPetIdx)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreatePetSummon(iPetIdx);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdNPCSevEmbed(ushort wStoneIdx, ushort wEquipIdx, int tidStone, int tidEquip)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevEmbedCmd(wStoneIdx, wEquipIdx, tidStone, tidEquip);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdGetItemInfo(byte byPackage, byte bySlot)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateGetItemInfoCmd(byPackage, bySlot);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdSetStatusPts(int vitality, int energy, int strength, int agility)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateSetStatusPtCmd(vitality, energy, strength, agility);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevClearEmbeddedChipCmd(iEquipIdx, tidEquip);
+ SendProtocol(gamedatasend);
+ }
+
+ public void c2s_SendCmdPetRecall()
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.RECALL_PET);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_CmdDebug(ushort icmd, int param1 = int.MinValue)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateDebugCmd(icmd, param1);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdNPCSevHatchPet(int iIvtrIdx, int idEgg)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevHatchPetCmd(iIvtrIdx, idEgg);
+ SendProtocol(gamedatasend);
+ }
+ public void c2s_SendCmdNPCSevRestorePet(int iPetIdx)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevRestorePetCmd(iPetIdx);
+ SendProtocol(gamedatasend);
+ }
+
+ // Cross-server get in (C++: c2s_CmdNPCSevCrossServerGetIn) — TODO: implement C2S packet when needed
+ public void c2s_CmdNPCSevCrossServerGetIn()
+ {
+ // TODO: C2SCommandFactory.CreateNPCSevCrossServerGetInCmd() and SendProtocol
+ }
+
+ // Cross-server get out (C++: c2s_CmdNPCSevCrossServerGetOut) — TODO: implement C2S packet when needed
+ public void c2s_CmdNPCSevCrossServerGetOut()
+ {
+ // TODO: C2SCommandFactory.CreateNPCSevCrossServerGetOutCmd() and SendProtocol
+ }
+ }
}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/UI/NPCShopDetailPanel.cs b/Assets/PerfectWorld/Scripts/UI/NPCShopDetailPanel.cs
index 80e2d2c712..0955fa0986 100644
--- a/Assets/PerfectWorld/Scripts/UI/NPCShopDetailPanel.cs
+++ b/Assets/PerfectWorld/Scripts/UI/NPCShopDetailPanel.cs
@@ -24,6 +24,7 @@ public class NPCShopDetailPanel : MonoBehaviour
private GShopItem currentItem;
private NPCShopUIManager shopManager;
+ private int shopItemIndex;
void Start()
{
@@ -39,10 +40,11 @@ public class NPCShopDetailPanel : MonoBehaviour
buyButton.onClick.AddListener(OnBuyButtonClicked);
}
- public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager)
+ public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager, int index)
{
currentItem = item;
shopManager = manager;
+ shopItemIndex = index;
UpdateDisplay();
}
@@ -189,17 +191,19 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
- // Create npc_trade_item array for buying from NPC
- // The tid is the item template ID, index is shop item index (0 for now), count is quantity to buy
+ // Server requires SEVNPC_HELLO with NPC id before buy, and the correct shop slot index
+ if (shopManager != null && shopManager.CurrentNPCID != 0)
+ UnityGameSession.c2s_CmdNPCSevHello((int)shopManager.CurrentNPCID);
+
+ // Create npc_trade_item: tid = template ID, index = shop slot (server validates this), count = quantity
npc_trade_item[] items = new npc_trade_item[1];
items[0] = new npc_trade_item
{
tid = (int)currentItem.id,
- index = 0, // Shop item index - may need to be determined from shop item position
- count = 1 // Quantity to buy
+ index = (uint)shopItemIndex,
+ count = 1
};
- // Send the buy command
UnityGameSession.c2s_CmdNPCSevBuy(1, items);
Debug.Log($"[NPCShopDetailPanel] Sent buy command for item {currentItem.id}, price {price}");
diff --git a/Assets/PerfectWorld/Scripts/UI/NPCShopItemPanel.cs b/Assets/PerfectWorld/Scripts/UI/NPCShopItemPanel.cs
index 5ccd57d557..e112a3e4ea 100644
--- a/Assets/PerfectWorld/Scripts/UI/NPCShopItemPanel.cs
+++ b/Assets/PerfectWorld/Scripts/UI/NPCShopItemPanel.cs
@@ -19,6 +19,7 @@ public class NPCShopItemPanel : MonoBehaviour
private GShopItem itemData;
private Coroutine iconLoadCoroutine;
private NPCShopUIManager shopManager;
+ private int shopItemIndex;
void Start()
{
@@ -54,10 +55,11 @@ public class NPCShopItemPanel : MonoBehaviour
}
}
- public void SetupItem(GShopItem item, NPCShopUIManager manager)
+ public void SetupItem(GShopItem item, NPCShopUIManager manager, int index)
{
itemData = item;
shopManager = manager;
+ shopItemIndex = index;
UpdateDisplay();
}
@@ -65,7 +67,7 @@ public class NPCShopItemPanel : MonoBehaviour
{
if (shopManager != null && itemData.id != 0)
{
- shopManager.ShowItemDetail(itemData);
+ shopManager.ShowItemDetail(itemData, shopItemIndex);
}
}
diff --git a/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs b/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs
index 4c61a35673..d12ff02f9b 100644
--- a/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs
+++ b/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs
@@ -35,6 +35,9 @@ public class NPCShopUIManager : MonoBehaviour
private int currentTabIndex = 0;
private uint currentNPCID = 0;
private NPC_SELL_SERVICE? cachedSellService = null;
+
+ /// Current NPC id for this shop session. Send SEVNPC_HELLO with this before buy.
+ public uint CurrentNPCID => currentNPCID;
private NPCShopDetailPanel detailPanelScript;
void Start()
@@ -278,8 +281,9 @@ public class NPCShopUIManager : MonoBehaviour
if (elementDataMan == null)
return;
- foreach (var good in page.goods)
+ for (int i = 0; i < page.goods.Length; i++)
{
+ var good = page.goods[i];
if (good.id == 0)
continue;
@@ -293,8 +297,8 @@ public class NPCShopUIManager : MonoBehaviour
// Create GShopItem
GShopItem shopItem = CreateShopItemFromGood(good, itemData, itemDataType);
- // Create panel
- CreateItemPanel(shopItem);
+ // Create panel with shop slot index (server expects this in npc_trade_item.index)
+ CreateItemPanel(shopItem, i);
}
}
}
@@ -372,7 +376,7 @@ public class NPCShopUIManager : MonoBehaviour
return shopItem;
}
- void CreateItemPanel(GShopItem item)
+ void CreateItemPanel(GShopItem item, int shopItemIndex)
{
if (itemPanelPrefab == null || itemContainer == null)
return;
@@ -386,7 +390,7 @@ public class NPCShopUIManager : MonoBehaviour
if (itemPanelScript != null)
{
- itemPanelScript.SetupItem(item, this);
+ itemPanelScript.SetupItem(item, this, shopItemIndex);
}
else
{
@@ -406,7 +410,7 @@ public class NPCShopUIManager : MonoBehaviour
currentItemPanels.Clear();
}
- public void ShowItemDetail(GShopItem item)
+ public void ShowItemDetail(GShopItem item, int shopItemIndex)
{
if (item.id == 0)
return;
@@ -428,7 +432,7 @@ public class NPCShopUIManager : MonoBehaviour
if (detailPanelScript != null)
{
npcShopDetailPanel.SetActive(true);
- detailPanelScript.SetupDetailPanel(item, this);
+ detailPanelScript.SetupDetailPanel(item, this, shopItemIndex);
}
else
{
diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs
index 5dfb12b25d..a534d0d973 100644
--- a/Assets/Scripts/CECHostPlayer.cs
+++ b/Assets/Scripts/CECHostPlayer.cs
@@ -565,6 +565,9 @@ namespace BrewMonster
case EC_MsgDef.MSG_HST_PICKUPITEM:
OnMsgHstPickupItem(Msg);
break;
+ case EC_MsgDef.MSG_HST_PURCHASEITEMS:
+ OnMsgHstPurchaseItems(Msg);
+ break;
case EC_MsgDef.MSG_HST_PRODUCEITEM:
OnMsgHstProduceItem(Msg);
break;
From c848b5421166d9a34633125fd2b0495aceac5c05 Mon Sep 17 00:00:00 2001
From: HungDK <>
Date: Sat, 28 Feb 2026 18:14:46 +0700
Subject: [PATCH 2/3] Clean code, refactor function
---
Assets/Scripts/CECHostPlayer.Inventory.cs | 506 +++++++++++++---------
1 file changed, 301 insertions(+), 205 deletions(-)
diff --git a/Assets/Scripts/CECHostPlayer.Inventory.cs b/Assets/Scripts/CECHostPlayer.Inventory.cs
index 64e371fc10..86c64f28ad 100644
--- a/Assets/Scripts/CECHostPlayer.Inventory.cs
+++ b/Assets/Scripts/CECHostPlayer.Inventory.cs
@@ -1,4 +1,4 @@
-using BrewMonster.Managers;
+using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
@@ -23,11 +23,82 @@ namespace BrewMonster
int cmd = Convert.ToInt32(Msg.dwParam2);
int hostId = Convert.ToInt32(Msg.dwParam3);
+ if (Application.isEditor || Debug.isDebugBuild)
+ {
+ if (cmd == CommandID.OWN_IVTR_DATA || cmd == CommandID.OWN_IVTR_DETAIL_DATA || cmd == CommandID.CHANGE_IVTR_SIZE)
+ Debug.Log($"[INVNET] HST_IVTRINFO cmd={cmd} bytes={data?.Length ?? 0} hostId={hostId}");
+ }
+
switch (cmd)
{
case CommandID.OWN_IVTR_DATA:
{
LogInventoryPacket("OWN_IVTR_DATA", data, hostId);
+
+ // C++: pPack->ResetItems(*pCmd) where cmd_own_ivtr_info.content is a compact stream:
+ // for each slot [0..ivtr_size): tid (int); if tid>=0 then expire_date (int) and amount (int).
+ if (data != null && data.Length >= 6)
+ {
+ byte byPackage = data[0];
+ byte ivtrSize = data[1];
+ uint contentLength = BitConverter.ToUInt32(data, 2);
+ int index = 6;
+ int remaining = data.Length - index;
+ int contentBytes = remaining;
+ if (contentLength < (uint)remaining)
+ contentBytes = (int)contentLength;
+
+ var inv = GetInventory(byPackage);
+ if (inv != null)
+ {
+ inv.Resize(ivtrSize);
+ inv.RemoveAllItems();
+
+ int end = index + Math.Max(0, contentBytes);
+ for (int slot = 0; slot < ivtrSize; slot++)
+ {
+ if (index + 4 > end)
+ break;
+ int tid = BitConverter.ToInt32(data, index);
+ index += 4;
+ if (tid < 0)
+ {
+ inv.SetItem(slot, null);
+ continue;
+ }
+ if (index + 8 > end)
+ break;
+ int expireDate = BitConverter.ToInt32(data, index);
+ index += 4;
+ int amount = BitConverter.ToInt32(data, index);
+ index += 4;
+
+ if (amount > 0)
+ {
+ var item = EC_IvtrItem.CreateItem(tid, expireDate, amount);
+ if (item != null)
+ {
+ item.Package = byPackage;
+ item.Slot = slot;
+ item.SetCount(amount);
+ inv.SetItem(slot, item);
+ }
+ }
+ else
+ {
+ inv.SetItem(slot, null);
+ }
+ }
+ }
+
+ if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
+ {
+ UpdateEquipSkins();
+ }
+
+ var ui = GameObject.FindFirstObjectByType();
+ ui?.RefreshAll();
+ }
break;
}
case CommandID.OWN_IVTR_DETAIL_DATA:
@@ -71,6 +142,19 @@ namespace BrewMonster
break;
}
+ case CommandID.CHANGE_IVTR_SIZE:
+ {
+ // C++: resize pack (normal inventory)
+ if (data != null && data.Length >= 4)
+ {
+ int newSize = BitConverter.ToInt32(data, 0);
+ if (m_pPack != null)
+ m_pPack.Resize(newSize);
+ var ui = GameObject.FindFirstObjectByType();
+ ui?.RefreshAll();
+ }
+ break;
+ }
case CommandID.GET_OWN_MONEY:
{
if (data != null)
@@ -168,14 +252,13 @@ namespace BrewMonster
{
case CommandID.OWN_ITEM_INFO:
{
- //Debug.Log("[Inventory] OWN_ITEM_INFO received");
- //var data = Msg.dwParam1 as byte[];
- //int hostId = Convert.ToInt32(Msg.dwParam3);
- //LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
+ if (Application.isEditor || Debug.isDebugBuild)
+ Debug.Log($"[INVNET] HST_OWNITEMINFO cmd=OWN_ITEM_INFO bytes={(Msg.dwParam1 as byte[])?.Length ?? 0}");
- //Handmade
+ // Match C++ cmd_own_item_info layout and behavior: update an existing item in place.
+ // If the slot is missing (can happen if client missed OWN_IVTR_DATA), create it to match server state.
var data = Msg.dwParam1 as byte[];
- if (data == null || data.Length == 0)
+ if (data == null || data.Length < 22)
return;
byte byPackage = data[0];
@@ -184,7 +267,6 @@ namespace BrewMonster
int expire_date = BitConverter.ToInt32(data, 6);
int state = BitConverter.ToInt32(data, 10);
uint count = BitConverter.ToUInt32(data, 14);
- ushort crc = BitConverter.ToUInt16(data, 18);
ushort content_length = BitConverter.ToUInt16(data, 20);
byte[] content = null;
@@ -192,38 +274,45 @@ namespace BrewMonster
{
content = new byte[content_length];
Buffer.BlockCopy(data, 22, content, 0, content_length);
-
- string hexDebug = BitConverter.ToString(content);
- //Debug.Log($"[OWN_ITEM_INFO] Full Content Hex ({content_length} bytes): {hexDebug}");
}
- //Debug.Log($"[OWN_ITEM_INFO] Parsed: package={byPackage}, slot={bySlot}, tid={type}, count={count}, content_len={content_length}");
-
EC_Inventory pInventory = GetInventory(byPackage);
- EC_IvtrItem newItem = EC_IvtrItem.CreateItem(type, expire_date, (int)count);
+ if (pInventory == null)
+ return;
- if (newItem != null)
+ if (bySlot >= pInventory.GetSize())
+ pInventory.Resize(bySlot + 1);
+
+ var pItem = pInventory.GetItem(bySlot, false);
+ if (pItem == null)
{
- newItem.SetProcType(state);
-
- newItem.GetDetailDataFromLocal();
- if (content != null && content.Length > 0)
- {
- newItem.SetItemInfo(content, content_length);
- }
-
- pInventory.SetItem(bySlot, newItem);
-
- //Debug.Log($"[OWN_ITEM_INFO] Fixed Update: Pack {byPackage} Slot {bySlot} - Type {type}");
+ pItem = EC_IvtrItem.CreateItem(type, expire_date, (int)count);
+ if (pItem == null)
+ return;
+ pItem.Package = byPackage;
+ pItem.Slot = bySlot;
+ pInventory.SetItem(bySlot, pItem);
}
+ pItem.SetExpireDate(expire_date);
+ pItem.SetProcType(state);
+ pItem.SetAmount((int)count);
+ if (content != null && content.Length > 0)
+ pItem.SetItemInfo(content, content.Length);
+ else
+ pItem.SetItemInfo(null, 0);
+
+#if UNITY_EDITOR
+ Debug.Log($"[Inventory] OWN_ITEM_INFO pkg={byPackage} slot={bySlot} tid={type} count={count} contentLen={content_length}");
+#endif
+
if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
{
UpdateEquipSkins();
}
else if (byPackage == InventoryConst.IVTRTYPE_PACK)
{
- if (newItem.IsEquipment())
+ if (pItem != null && pItem.IsEquipment())
{
// TODO
}
@@ -342,7 +431,7 @@ namespace BrewMonster
}
// Trigger UI refresh if an EC_InventoryUI is present in scene
- var ui = GameObject.FindObjectOfType();
+ var ui = GameObject.FindFirstObjectByType();
if (ui != null)
{
ui.RefreshAll();
@@ -354,208 +443,215 @@ namespace BrewMonster
}
}
+ ///
+ /// Message MSG_HST_PICKUPITEM handler. Matches C++ flow: switch only fills idItem/iExpireDate/iAmount/iCmdLastSlot/iCmdSlotAmount/iPack/iMsg;
+ /// then single common path: MergeItem (or PutItemInSlot to match server slot), GetItemInfo request for equipment, notifications, RefreshAll.
+ ///
public void OnMsgHstPickupItem(in ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
+ if (data == null)
+ return;
+
+ if (Application.isEditor || Debug.isDebugBuild)
+ Debug.Log($"[INVNET] HST_PICKUPITEM cmd={cmd} bytes={data.Length}");
bool bDoOther = false;
- int idItem, iExpireDate = 0, iAmount, iCmdLastSlot, iCmdSlotAmount, iPack, iMsg = -1;
+ int idItem = 0, iExpireDate = 0, iAmount = 0, iCmdLastSlot = 0, iCmdSlotAmount = 0, iPack = 0, iMsg = -1;
switch (cmd)
{
- case CommandID.HOST_OBTAIN_ITEM:
- {
- // Parse cmd_host_obtain_item struct data
- int type = BitConverter.ToInt32(data, 0);
- int expire_date = BitConverter.ToInt32(data, 4);
- uint amount = BitConverter.ToUInt32(data, 8);
- uint slot_amount = BitConverter.ToUInt32(data, 12);
- byte where = data[16]; // Package index
- byte index = data[17]; // Slot index in that package
- var newItem = EC_IvtrItem.CreateItem(type, expire_date, (int)amount);
-
- // Add item to inventory
- var ivt = GetInventory(where);
- if (newItem.Content != null && newItem.Content.Length > 0)
- {
- newItem.SetItemInfo(newItem.Content, newItem.Content.Length);
- }
- ivt.SetItem(index, newItem);
-
- Debug.Log(
- $"[HOST_OBTAIN_ITEM] Successfully added item {type} to package {where}, slot {index} with count {amount}");
-
- // Trigger UI refresh if an EC_InventoryUI is present in scene
- var ui = GameObject.FindFirstObjectByType();
- if (ui != null)
- {
- ui.RefreshAll();
- }
-
- UpdateEquipSkins();
- }
- break;
case CommandID.PICKUP_ITEM:
{
- int tid = BitConverter.ToInt32(data, 0);
- int expire_date = BitConverter.ToInt32(data, 4);
- iAmount = (int)BitConverter.ToUInt32(data, 8);
- uint iSlotAmount = BitConverter.ToUInt32(data, 12);
- byte byPackage = data[16];
- byte bySlot = data[17];
-
- //Debug.Log($"[Inventory] PICKUP_ITEM: tid={tid}, expire_date={expire_date}, iAmount={iAmount}, iSlotAmount={iSlotAmount}, byPackage={byPackage}, bySlot={bySlot}");
-
- // Notify pickupItem script about successful pickup
- pickupItem pickupScript = pickupItem.Instance;
- if (pickupScript != null)
- {
- //Debug.Log($"[Inventory] PICKUP_ITEM: tid={tid}, expire_date={expire_date}, iAmount={iAmount}, iSlotAmount={iSlotAmount}, byPackage={byPackage}, bySlot={bySlot}");
-
- // Notify pickupItem script about successful pickup
- pickupScript = UnityEngine.Object.FindFirstObjectByType();
- if (pickupScript != null)
- {
- pickupScript.OnPickupSuccess(tid);
- }
-
- // Create new inventory item data
- var newItem = EC_IvtrItem.CreateItem(tid, expire_date, (int)iAmount);
-
- // Add item to inventory
- var ivt = GetInventory(byPackage);
- ivt.SetItem(bySlot, newItem);
-
- //Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
-
- // Trigger UI refresh if an EC_InventoryUI is present in scene
- var ui = GameObject.FindFirstObjectByType();
- if (ui != null)
- {
- ui.RefreshAll();
- }
- }
- else
- {
- Debug.LogWarning("[Inventory] PICKUP_ITEM: Invalid data length");
- }
-
+ var pCmdPickup = GPDataTypeHelper.FromBytes(data);
+ idItem = pCmdPickup.tid;
+ iExpireDate = pCmdPickup.expire_date;
+ iAmount = (int)pCmdPickup.iAmount;
+ iCmdLastSlot = pCmdPickup.bySlot;
+ iCmdSlotAmount = (int)pCmdPickup.iSlotAmount;
+ iPack = pCmdPickup.byPackage;
+ iMsg = (int)FixedMsg.FIXMSG_PICKUPITEM;
+ break;
+ }
+ case CommandID.HOST_OBTAIN_ITEM:
+ {
+ var pCmdObtain = GPDataTypeHelper.FromBytes(data);
+ idItem = pCmdObtain.type;
+ iExpireDate = pCmdObtain.expire_date;
+ iAmount = (int)pCmdObtain.amount;
+ iCmdLastSlot = pCmdObtain.index;
+ iCmdSlotAmount = (int)pCmdObtain.slot_amount;
+ iPack = pCmdObtain.where;
+ iMsg = (int)FixedMsg.FIXMSG_GAINITEM;
+ break;
+ }
+ case CommandID.PRODUCE_ONCE:
+ {
+ var pCmdProduce = GPDataTypeHelper.FromBytes(data);
+ idItem = pCmdProduce.type;
+ iExpireDate = 0;
+ iAmount = (int)pCmdProduce.amount;
+ iCmdLastSlot = pCmdProduce.index;
+ iCmdSlotAmount = (int)pCmdProduce.slot_amount;
+ iPack = pCmdProduce.where;
+ iMsg = (int)FixedMsg.FIXMSG_PRODUCEITEM;
+ var dlgProduce = UnityEngine.Object.FindFirstObjectByType();
+ dlgProduce?.OnProduceOnce(pCmdProduce);
break;
}
case CommandID.TASK_DELIVER_ITEM:
- cmd_task_deliver_item pCmd = GPDataTypeHelper.FromBytes(data);
- // ASSERT(pCmd);
-
- idItem = pCmd.type;
- iExpireDate = pCmd.expire_date;
- iAmount = (int)pCmd.amount;
- iCmdLastSlot = pCmd.index;
- iCmdSlotAmount = (int)pCmd.slot_amount;
- iPack = pCmd.where;
+ {
+ var pCmdTask = GPDataTypeHelper.FromBytes(data);
+ idItem = pCmdTask.type;
+ iExpireDate = pCmdTask.expire_date;
+ iAmount = (int)pCmdTask.amount;
+ iCmdLastSlot = pCmdTask.index;
+ iCmdSlotAmount = (int)pCmdTask.slot_amount;
+ iPack = pCmdTask.where;
iMsg = (int)FixedMsg.FIXMSG_GAINITEM;
bDoOther = true;
-
-
- // Create new inventory item data
- var taskNewItem = EC_IvtrItem.CreateItem(idItem, iExpireDate, (int)iAmount);
-
- // Add item to inventory
- var task_ivt = GetInventory((byte)iPack);
- if (!task_ivt.MergeItem(idItem, iExpireDate, iAmount, out var iLastSlot, out var iSlotNum) ||
- iLastSlot != iCmdLastSlot || iSlotNum != iCmdSlotAmount)
- {
- return;
- }
-
- task_ivt.SetItem(iCmdLastSlot, taskNewItem);
-
- //Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
-
- // Trigger UI refresh if an EC_InventoryUI is present in scene
- var task_ui = GameObject.FindFirstObjectByType();
- if (task_ui != null)
- {
- task_ui.RefreshAll();
- }
-
break;
- case CommandID.PRODUCE_ONCE:
+ }
+ default:
+ return;
+ }
+
+#if UNITY_EDITOR
+ Debug.Log($"[Inventory] PICKUP_FLOW cmd={cmd} pack={iPack} slot={iCmdLastSlot} tid={idItem} amt={iAmount} slotAmt={iCmdSlotAmount}");
+#endif
+ EC_Inventory pInventory = GetPack(iPack);
+ if (pInventory == null)
+ return;
+
+ if (iCmdLastSlot >= pInventory.GetSize())
+ pInventory.Resize(iCmdLastSlot + 1);
+
+ bool placed = pInventory.PutItemInSlot(iCmdLastSlot, idItem, iExpireDate, iAmount, out int iLastSlot, out int iSlotNum);
+ if (!placed)
+ {
+ placed = pInventory.MergeItem(idItem, iExpireDate, iAmount, out iLastSlot, out iSlotNum);
+ if (!placed || iLastSlot != iCmdLastSlot || iSlotNum != iCmdSlotAmount)
{
- // Parse cmd_produce_once struct data
- cmd_produce_once produceCmd = GPDataTypeHelper.FromBytes(data);
+#if UNITY_EDITOR
+ Debug.LogWarning($"[Inventory] PICKUP_FLOW desync: placed={placed} lastSlot={iLastSlot} slotNum={iSlotNum} expectedSlot={iCmdLastSlot} expectedSlotAmt={iCmdSlotAmount}");
+#endif
+ return;
+ }
+ }
- int produceItemId = produceCmd.type;
- int produceExpireDate = 0;
- uint produceAmount = produceCmd.amount;
- byte producePack = produceCmd.where;
- byte produceSlot = produceCmd.index;
+ if (cmd == CommandID.HOST_OBTAIN_ITEM && iPack == Inventory_type.IVTRTYPE_PACK)
+ {
+ // C++: CECShoppingManager::Instance().OnObtainItem(iPack, idItem, iAmount);
+ }
- Debug.Log(
- $"[PRODUCE_ONCE] Received: itemId={produceItemId}, amount={produceAmount}, pack={producePack}, slot={produceSlot}");
+ EC_IvtrItem pItem = pInventory.GetItem(iCmdLastSlot, false);
+ if (pItem != null)
+ {
+ pItem.Package = (byte)iPack;
+ pItem.Slot = iCmdLastSlot;
+ int cid = pItem.GetClassID();
+ if (pItem.IsEquipment() ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKNMMATTER ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKITEM ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN_EXPPILL ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGBOOKCARD ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGINVITECARD ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_SKILLTOME ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_PETEGG)
+ {
+ UnityGameSession.c2s_CmdGetItemInfo((byte)iPack, (byte)iCmdLastSlot);
+ }
+ if (iMsg >= 0)
+ {
+ CECGameRun pGameRun = EC_Game.GetGameRun();
+ pGameRun?.AddFixedMessage(iMsg, iAmount, pItem.GetName());
+ }
+ }
- // Get inventory
- var produce_ivt = GetInventory(producePack);
- if (produce_ivt == null)
+ if (bDoOther)
+ {
+ // C++: m_pTaskInterface->DoAutoTeamForTask(idItem);
+ }
+
+ if (cmd == CommandID.PICKUP_ITEM)
+ {
+ var pickupScript = UnityEngine.Object.FindFirstObjectByType();
+ pickupScript?.OnPickupSuccess(idItem);
+ }
+
+ var ui = GameObject.FindFirstObjectByType();
+ ui?.RefreshAll();
+ UpdateEquipSkins();
+ }
+
+ /// Buy from NPC/booth: server sends PURCHASE_ITEM (cmd_purchase_item). C++ OnMsgHstPurchaseItems.
+ public void OnMsgHstPurchaseItems(ECMSG Msg)
+ {
+ var data = Msg.dwParam1 as byte[];
+ if (data == null || data.Length < 11)
+ return;
+
+ var header = GPDataTypeHelper.FromBytes(data);
+ int index = 11;
+ const int itemSize = 15; // item_id(4) + expire_date(4) + count(4) + inv_index(2) + booth_slot(1)
+ EC_Inventory pPack = GetPack(Inventory_type.IVTRTYPE_PACK);
+ if (pPack == null)
+ return;
+
+ var slotsNeedingDetail = new System.Collections.Generic.List();
+
+ for (int i = 0; i < header.item_count && index + itemSize <= data.Length; i++)
+ {
+ int item_id = BitConverter.ToInt32(data, index); index += 4;
+ int expire_date = BitConverter.ToInt32(data, index); index += 4;
+ int count = (int)BitConverter.ToUInt32(data, index); index += 4;
+ ushort inv_index = BitConverter.ToUInt16(data, index); index += 2;
+ index += 1; // booth_slot
+
+ if (inv_index >= pPack.GetSize())
+ pPack.Resize(inv_index + 1);
+
+ bool placed = pPack.PutItemInSlot(inv_index, item_id, expire_date, count, out int lastSlot, out int slotNum);
+ if (!placed)
+ {
+ placed = pPack.MergeItem(item_id, expire_date, count, out lastSlot, out slotNum);
+ if (!placed || lastSlot != inv_index)
+ continue;
+ }
+
+ var pItem = pPack.GetItem(inv_index, false);
+ if (pItem != null)
+ {
+ pItem.Package = (byte)Inventory_type.IVTRTYPE_PACK;
+ pItem.Slot = inv_index;
+ int cid = pItem.GetClassID();
+ if (pItem.IsEquipment() ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKNMMATTER ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKITEM ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN_EXPPILL ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGBOOKCARD ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGINVITECARD ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_SKILLTOME ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN ||
+ cid == (int)EC_IvtrItem.InventoryClassId.ICID_PETEGG)
{
- Debug.LogWarning($"[PRODUCE_ONCE] Invalid inventory package {producePack}");
- return;
- }
-
- // Check if the slot already has an item
- var existingItem = produce_ivt.GetItem(produceSlot, false);
-
- if (existingItem != null)
- {
- if (existingItem.m_tid == produceItemId)
- {
- existingItem.m_iCount = (int)produceAmount;
- Debug.Log(
- $"[PRODUCE_ONCE] Updated existing item count at slot {produceSlot} to {produceAmount}");
- }
- else
- {
- Debug.LogWarning(
- $"[PRODUCE_ONCE] Slot {produceSlot} already has different item (tid={existingItem.m_tid}), not overwriting with {produceItemId}");
- return;
- }
- }
- else
- {
- var produceNewItem = new EC_IvtrItem
- {
- Package = producePack,
- Slot = produceSlot,
- m_tid = produceItemId,
- m_expire_date = produceExpireDate,
- State = 0,
- m_iCount = (int)produceAmount,
- Crc = 0,
- Content = null
- };
-
- produce_ivt.SetItem(produceSlot, produceNewItem);
- Debug.Log($"[PRODUCE_ONCE] Created new item at slot {produceSlot} with count {produceAmount}");
- }
-
- // Trigger UI refresh
- var produce_ui = GameObject.FindFirstObjectByType();
- if (produce_ui != null)
- {
- produce_ui.RefreshAll();
- }
-
- UpdateEquipSkins();
-
- // Notify DlgProduce
- var dlgProduce = GameObject.FindFirstObjectByType();
- if (dlgProduce != null)
- {
- dlgProduce.OnProduceOnce(produceCmd);
+ slotsNeedingDetail.Add((byte)inv_index);
}
}
- break;
}
+
+ AddMoneyAmount(-(int)header.cost);
+
+ foreach (byte slot in slotsNeedingDetail)
+ UnityGameSession.c2s_CmdGetItemInfo((byte)Inventory_type.IVTRTYPE_PACK, slot);
+
+ var ui = GameObject.FindFirstObjectByType();
+ ui?.RefreshAll();
+ UpdateEquipSkins();
}
private void OnMsgHstUseItem(ECMSG Msg)
From 01ae1463f4a8e7bba3a665df1a02db8cdd4bac14 Mon Sep 17 00:00:00 2001
From: Tungdv
Date: Sat, 28 Feb 2026 18:34:39 +0700
Subject: [PATCH 3/3] fix: remove debug and "using" not use.
---
Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs | 1 -
.../Scripts/Managers/EC_HPWorkSpell.cs | 1 -
.../Scripts/Managers/EC_HPWorkTrace.cs | 1 -
.../PerfectWorld/Scripts/Managers/EC_Inventory.cs | 1 -
.../Scripts/Managers/EC_IvtrItem/EC_IvtrEquip.cs | 3 ---
.../Scripts/ModelFiles/CECTaoistRank.cs | 1 -
Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 9 ---------
Assets/PerfectWorld/Scripts/NPC/CECMonster.cs | 3 ---
.../Scripts/Objet/Shortcut/CECShortcutSet.cs | 1 -
Assets/PerfectWorld/Scripts/Skills/CECSCSkill.cs | 1 -
Assets/PerfectWorld/Scripts/Skills/ElementSkill.cs | 1 -
.../Scripts/Task/TaskTemplContainerSO.cs | 1 -
Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs | 1 -
.../Scripts/UI/GamePlay/AUIImagePicture.cs | 1 -
.../Scripts/UI/GamePlay/EC_GameUIMan.cs | 2 --
Assets/PerfectWorld/Scripts/UI/ShopDetailPanel.cs | 1 -
Assets/Scripts/CECHostPlayer.Inventory.cs | 7 -------
Assets/Scripts/CECHostPlayer.cs | 14 +++++++-------
18 files changed, 7 insertions(+), 43 deletions(-)
diff --git a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs
index da872c1e90..948d9bb4d0 100644
--- a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs
@@ -9,7 +9,6 @@ using System.Buffers.Binary;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BrewMonster.Network;
-using Unity.VisualScripting;
using UnityEngine;
public class CECNPCMan : IMsgHandler
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkSpell.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkSpell.cs
index 26b153b95d..7e3171f17a 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkSpell.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkSpell.cs
@@ -1,5 +1,4 @@
using CSNetwork.GPDataType;
-using Unity.VisualScripting;
using UnityEngine;
using static BrewMonster.Scripts.CECHPWorkSpell.Spell_magic_state;
namespace BrewMonster.Scripts
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs
index fdd5b8c8f9..7ca4f5100d 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs
@@ -6,7 +6,6 @@ using CSNetwork.GPDataType;
using System;
using System.Runtime.ConstrainedExecution;
using PerfectWorld.Scripts;
-using Unity.VisualScripting;
using UnityEngine;
///////////////////////////////////////////////////////////////////////////
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
index 4240c01bfb..5e020ab927 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using Unity.VisualScripting;
using UnityEngine;
namespace BrewMonster.Scripts.Managers
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrEquip.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrEquip.cs
index c90d41ef89..1ee7462d39 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrEquip.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrEquip.cs
@@ -14,9 +14,6 @@ using System.Text.RegularExpressions;
using System.Reflection;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts;
-using UnityEngine.AddressableAssets;
-using CSNetwork.Protocols;
-using Unity.VisualScripting;
namespace PerfectWorld.Scripts.Managers
{
diff --git a/Assets/PerfectWorld/Scripts/ModelFiles/CECTaoistRank.cs b/Assets/PerfectWorld/Scripts/ModelFiles/CECTaoistRank.cs
index 0ecaca5d44..ae799e4970 100644
--- a/Assets/PerfectWorld/Scripts/ModelFiles/CECTaoistRank.cs
+++ b/Assets/PerfectWorld/Scripts/ModelFiles/CECTaoistRank.cs
@@ -1,4 +1,3 @@
-using Unity.VisualScripting;
using UnityEngine;
namespace BrewMonster
diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs
index 2fc72a6358..e7111f217c 100644
--- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs
+++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs
@@ -1,26 +1,17 @@
-using Animancer;
-using BrewMonster;
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.PerfectWorld.Scripts.Vfx;
using BrewMonster.Scripts;
-using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts.Skills;
using CSNetwork.GPDataType;
using ModelRenderer.Scripts.GameData;
-using PerfectWorld.Scripts.Managers;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using System.Xml.Linq;
using TMPro;
-using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
-using UnityEngine.UIElements;
-using static BrewMonster.CECPlayer;
-using BrewMonster.Network;
using System.Runtime.InteropServices;
using PerfectWorld.Scripts.Managers.BrewMonster.Managers;
using CSNetwork;
diff --git a/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs b/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs
index fb1bbc6b59..af051e4245 100644
--- a/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs
+++ b/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs
@@ -4,9 +4,6 @@ using ModelRenderer.Scripts.GameData;
using System;
using System.Runtime.InteropServices;
using System.Text;
-using Unity.VisualScripting;
-using UnityEngine;
-using static CECNPC;
public class CECMonster : CECNPC
{
diff --git a/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs b/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs
index a213c693be..521fdd7c8d 100644
--- a/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs
+++ b/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs
@@ -22,7 +22,6 @@ using CSNetwork.GPDataType;
using CSNetwork.S2CCommand;
using System;
using System.Collections.Generic;
-using Unity.VisualScripting;
using UnityEngine;
using static BrewMonster.EC_Resource;
using static BrewMonster.IconResourceType;
diff --git a/Assets/PerfectWorld/Scripts/Skills/CECSCSkill.cs b/Assets/PerfectWorld/Scripts/Skills/CECSCSkill.cs
index 9714ea4f15..39d8402f47 100644
--- a/Assets/PerfectWorld/Scripts/Skills/CECSCSkill.cs
+++ b/Assets/PerfectWorld/Scripts/Skills/CECSCSkill.cs
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Unity.VisualScripting;
using static BrewMonster.SkillArrayWrapper;
namespace BrewMonster
diff --git a/Assets/PerfectWorld/Scripts/Skills/ElementSkill.cs b/Assets/PerfectWorld/Scripts/Skills/ElementSkill.cs
index 2f24ba1bc2..803301005d 100644
--- a/Assets/PerfectWorld/Scripts/Skills/ElementSkill.cs
+++ b/Assets/PerfectWorld/Scripts/Skills/ElementSkill.cs
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
-using Unity.VisualScripting;
namespace BrewMonster.Scripts.Skills
{
diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTemplContainerSO.cs b/Assets/PerfectWorld/Scripts/Task/TaskTemplContainerSO.cs
index ca680cf261..de231005e8 100644
--- a/Assets/PerfectWorld/Scripts/Task/TaskTemplContainerSO.cs
+++ b/Assets/PerfectWorld/Scripts/Task/TaskTemplContainerSO.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Serialization;
diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
index f0d1f7deac..15e287798d 100644
--- a/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
+++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs
@@ -17,7 +17,6 @@ using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
-using Unity.VisualScripting;
namespace BrewMonster.Scripts.Task.UI
{
diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs
index 7933bb173b..786d055af6 100644
--- a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs
+++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs
index 684d10e5d0..b1b532ee89 100644
--- a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs
+++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs
@@ -10,9 +10,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Unity.VisualScripting;
using UnityEngine;
-using static UnityEngine.Rendering.DebugUI;
namespace BrewMonster.UI
{
diff --git a/Assets/PerfectWorld/Scripts/UI/ShopDetailPanel.cs b/Assets/PerfectWorld/Scripts/UI/ShopDetailPanel.cs
index f351d1ba91..2ae252e81c 100644
--- a/Assets/PerfectWorld/Scripts/UI/ShopDetailPanel.cs
+++ b/Assets/PerfectWorld/Scripts/UI/ShopDetailPanel.cs
@@ -3,7 +3,6 @@ using NUnit.Framework;
using System;
using System.Collections.Generic;
using TMPro;
-using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
diff --git a/Assets/Scripts/CECHostPlayer.Inventory.cs b/Assets/Scripts/CECHostPlayer.Inventory.cs
index 86c64f28ad..c6105f2b43 100644
--- a/Assets/Scripts/CECHostPlayer.Inventory.cs
+++ b/Assets/Scripts/CECHostPlayer.Inventory.cs
@@ -22,13 +22,6 @@ namespace BrewMonster
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
int hostId = Convert.ToInt32(Msg.dwParam3);
-
- if (Application.isEditor || Debug.isDebugBuild)
- {
- if (cmd == CommandID.OWN_IVTR_DATA || cmd == CommandID.OWN_IVTR_DETAIL_DATA || cmd == CommandID.CHANGE_IVTR_SIZE)
- Debug.Log($"[INVNET] HST_IVTRINFO cmd={cmd} bytes={data?.Length ?? 0} hostId={hostId}");
- }
-
switch (cmd)
{
case CommandID.OWN_IVTR_DATA:
diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs
index a534d0d973..63405d1aee 100644
--- a/Assets/Scripts/CECHostPlayer.cs
+++ b/Assets/Scripts/CECHostPlayer.cs
@@ -43,9 +43,9 @@ namespace BrewMonster
private CECHPWorkMan m_pWorkMan; // Host work manager
private uint m_dwLIES; // Logic-influence extend states
private FACTION_FORTRESS_ENTER m_fortressEnter; // ½øÈë»ùµØÐÅÏ¢
- private PVPINFO m_pvp; // pvp information
- private bool m_bInSanctuary = false; // true, player is in sanctuary
- private int m_idFaction = 0; // ID of player's faction
+ //private PVPINFO m_pvp; // pvp information
+ //private bool m_bInSanctuary = false; // true, player is in sanctuary
+ //private int m_idFaction = 0; // ID of player's faction
public bool m_bPrepareFight = false; // true, prepare to fight
private int m_iJumpCount = 0;
private bool m_bJumpInWater = false;
@@ -1470,10 +1470,10 @@ namespace BrewMonster
}
// Get faction ID
- public int GetFactionID()
- {
- return m_idFaction;
- }
+ //public int GetFactionID()
+ //{
+ // return m_idFaction;
+ //}
public void SetPrayDistancePlus(float prayDistancePlus)
{
m_fPrayDistancePlus = prayDistancePlus;