Files
test/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
T
vuong dinh hoang b487e24d8a fix action
2026-05-20 15:08:09 +07:00

3109 lines
134 KiB
C#

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 BrewMonster.PerfectWorld.Scripts.UI;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Chat;
using BrewMonster.Scripts.ChatUI;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts.UI;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using UnityEngine;
using CECPlayer = BrewMonster.CECPlayer;
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<bool> _loginCallback;
private Action<List<RoleInfo>> _roleListCallback;
private List<RoleInfo> _accumulatedRoles;
private Action<RoleInfo> _selectRoleCallback;
private Action<RoleInfo> _createRoleCallback;
private RoleInfo _selectedRole;
public bool IsConnected => _networkManager?.IsConnected ?? false;
public bool IsConnectedInternet => Application.internetReachability != NetworkReachability.NotReachable;
// When true, suppress *gameplay traffic* (mostly gamedatasend C2S commands) during logout/scene transitions.
// We still allow account/role flow protocols like rolelist/selectrole so "Return to Select Role" can work.
private volatile bool _suppressGameplayTraffic = false;
public static SynchronizationContext Context;
private CECC2SCmdCache m_CmdCache; // C2S command cache
/// <summary>Raised when server sends PROTOCOL_PLAYERLOGOUT(69).</summary>
public event Action<playerlogout> PlayerLogoutReceived;
/// <summary>
/// Raised when server sends RPC_ADDFRIENDRQST(204) — another player wants to add you as friend.
/// Args: xid (RPC transaction id), srcroleid, askerName.
/// </summary>
public event Action<uint, int, string> FriendRequestReceived;
/// <summary>Raised when server sends PROTOCOL_ADDFRIEND_RE(203). Args: retcode (0=success), message.</summary>
public event Action<byte, string> AddFriendResultReceived;
/// <summary>Raised when server sends PROTOCOL_GETFRIENDS_RE(207).</summary>
public event Action<getfriends_re> FriendListReceived;
public event Action<delfriend_re> FriendDeletedReceived;
/// <summary>Raised when server sends PROTOCOL_FRIENDSTATUS(214). Args: roleid, status.</summary>
public event Action<int, byte> FriendStatusChanged;
/// <summary>Raised when server sends PROTOCOL_FRIENDEXTLIST.</summary>
public event Action<friendextlist> FriendExtListReceived;
/// <summary>Raised when the underlying network disconnects.</summary>
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 bool IsGameplayTrafficSuppressed => _suppressGameplayTraffic;
public void SetGameplayTrafficSuppressed(bool suppressed, bool clearCachedCmds = true)
{
_suppressGameplayTraffic = suppressed;
if (suppressed && clearCachedCmds)
{
try { m_CmdCache?.RemoveAllCmds(); } catch { /* best-effort */ }
}
}
public GameSession()
{
_networkManager = new NetworkManager();
m_CmdCache = new CECC2SCmdCache();
_networkManager.ProtocolReceived += OnProtocolReceived;
_networkManager.ErrorOccurred += OnErrorOccurred;
_networkManager.Disconnected += OnDisconnected;
TestLogoutLogic.Instance.WasClientSendLogoutMessage = false;
}
public void SetLogPath(string path)
{
LoggerFactory.SetFileLoggerImplementation(new FileLogger());
_logger = LoggerFactory.GetCustomLogger(path, nameof(GameSession) + GetHashCode(), LoggerType.File);
_networkManager.SetLogPath(path);
}
/// <summary>
/// Connects to the game server asynchronously.
/// </summary>
/// <param name="host">Server hostname or IP address.</param>
/// <param name="port">Server port.</param>
/// <returns>Task representing the asynchronous connect operation. Check IsConnected property or handle Disconnected event for status.</returns>
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();
}
/// <summary>
/// Initiates the login process asynchronously.
/// </summary>
/// <param name="username">Account username.</param>
/// <param name="password">Account password.</param>
/// <param name="callback">Action invoked with true on successful login (OnlineAnnounce received), false otherwise.</param>
public void LoginAsync(string username, string password, Action<bool> 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}'...");
}
/// <summary>
/// Initiates fetching the role list asynchronously. Requires successful login.
/// </summary>
/// <param name="callback">Action invoked with the complete list of roles, or null/empty list on failure.</param>
public void GetRoleListAsync(Action<List<RoleInfo>> 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<RoleInfo>();
_logger.Log(LogType.Info, "Requesting role list...");
RequestRoleListInternal();
}
public RoleInfo GetRoleInfo()
{
return _selectedRole;
}
public void SelectRoleAsync(RoleInfo role, Action<RoleInfo> callback)
{
_selectedRole = role;
_selectRoleCallback = callback;
SetCharacterID(role.roleid);
SendProtocol(new selectrole()
{
Roleid = role.roleid,
Flag = 0
});
}
public void CreateRoleAsync(RoleInfo roleInfo, Octets referId, Action<RoleInfo> 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);
}
/// <summary>
/// Helper method to create a new RoleInfo for character creation.
/// Matches C++ NewCharacterImpl behavior.
/// </summary>
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<GRoleInventory>();
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;
}
/// <summary>
/// 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
/// </summary>
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 RequestExchangeIvtrItem(byte index1, byte index2)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateExchangeIvtrItem(index1, index2);
SendProtocol(gamedatasendRequest);
}
public void RequestMoveIvtrItem(byte src, byte dest, uint count)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateMoveIvtrItem(src, dest, count);
SendProtocol(gamedatasendRequest);
}
public void RequestMultiExchangeItem(byte location, int pairCount, int[] indexPairs)
{
var data = C2SCommandFactory.CreateMultiExchangeItem(location, pairCount, indexPairs);
if (data == null)
return;
var gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = data;
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)
{
// During logout/role-select transitions, drop background gameplay commands (gamedatasend).
// Do NOT block rolelist/selectrole/login related protocols.
if (_suppressGameplayTraffic && protocol is gamedatasend)
{
_logger.Log(LogType.Warning,
$"Suppress outgoing: dropped {protocol?.GetType().Name ?? "null"} ({protocol?.GetPType().ToString() ?? "?"})");
return;
}
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:
OnPrtcSelectRoleRe((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:
OnPrtcChatMessage(protocol, false);
break;
case ProtocolType.PROTOCOL_PRIVATECHAT:
OnPrtcPrivateChat(protocol);
break;
case ProtocolType.PROTOCOL_WORLDCHAT:
OnPrtcWorldChat(protocol, false);
break;
case ProtocolType.PROTOCOL_PLAYERBASEINFO_RE:
OnPrtcPlayerBaseInfoRe(protocol);
break;
case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break;
case ProtocolType.PROTOCOL_SETUICONFIG_RE: OnPrtcSetConfigRe(protocol); break;
case ProtocolType.PROTOCOL_PLAYERLOGOUT:
// HandlePlayerLogout((playerlogout)protocol);
OnPrtcPlayerLogout((playerlogout)protocol);
break;
case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE:
{
// CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
// if( pAutoTeam !=null)
// pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
}
break;
case ProtocolType.RPC_ADDFRIENDRQST:
OnAddFriendRqst((addfriendrqst)protocol);
break;
case ProtocolType.PROTOCOL_ADDFRIEND_RE:
OnAddFriendRe((addfriend_re)protocol);
break;
case ProtocolType.PROTOCOL_GETFRIENDS_RE:
OnGetFriendsRe((getfriends_re)protocol);
break;
case ProtocolType.PROTOCOL_FRIENDEXTLIST:
OnFriendExtList((friendextlist)protocol);
break;
case ProtocolType.PROTOCOL_FRIENDSTATUS:
OnFriendStatus((friendstatus)protocol);
break;
case ProtocolType.PROTOCOL_DELFRIEND_RE:
OnDelFriendRe((delfriend_re)protocol);
break;
default:
_logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
break;
}
}
private void OnDelFriendRe(delfriend_re p)
{
PostToUnityContext(() =>
{
FriendDeletedReceived?.Invoke(p);
if (p != null && p.retcode == 0)
Friend_GetList();
});
}
private void HandlePlayerLogout(playerlogout protocol)
{
// old code of HUNGDK
// Original client receives this before EVENT_DISCONNECT.
// We just publish it to allow higher-level flow (UnityGameSession/UI) to react.
// PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
}
private void OnAddFriendRqst(addfriendrqst p)
{
string askerName = "";
if (p.Srcname != null && p.Srcname.Size > 0)
askerName = System.Text.Encoding.Unicode.GetString(p.Srcname.ToArray(), 0, p.Srcname.Size);
// Forward xid + srcroleid + name to Unity layer so it can answer via Friend_AddResponse(xid, agree)
PostToUnityContext(() => FriendRequestReceived?.Invoke(p.Xid, p.Srcroleid, askerName));
}
private void OnGetFriendsRe(getfriends_re p)
{
PostToUnityContext(() =>
{
try
{
var host = EC_Game.GetGameRun()?.GetHostPlayer();
host?.GetFriendMan()?.ResetFromServer(p.Groups, p.Friends, p.Status);
}
catch (Exception ex)
{
_logger.Log(LogType.Error, $"OnGetFriendsRe failed: {ex.Message}");
_logger.LogException(ex);
}
FriendListReceived?.Invoke(p);
});
}
private void OnFriendExtList(friendextlist p)
{
PostToUnityContext(() => FriendExtListReceived?.Invoke(p));
}
private void OnFriendStatus(friendstatus p)
{
PostToUnityContext(() =>
{
try
{
var host = EC_Game.GetGameRun()?.GetHostPlayer();
host?.GetFriendMan()?.ChangeFriendStatus(p.Roleid, p.Status);
}
catch (Exception ex)
{
_logger.Log(LogType.Error, $"OnFriendStatus failed: {ex.Message}");
_logger.LogException(ex);
}
FriendStatusChanged?.Invoke(p.Roleid, p.Status);
});
}
private void OnAddFriendRe(addfriend_re p)
{
string friendName = "";
if (p.info?.name != null && p.info.name.Size > 0)
friendName = System.Text.Encoding.Unicode.GetString(p.info.name.ToArray(), 0, p.info.name.Size);
string msg = p.retcode == 0
? $"Thêm bạn thành công: {friendName}"
: $"Không thể thêm bạn do: {GetAddFriendRetcodeMessage(p.retcode)}";
if (p.retcode == 0)
{
Friend_GetList();
}
PostToUnityContext(() => AddFriendResultReceived?.Invoke(p.retcode, msg));
}
/// <summary>FS_ERR server retcodes for addfriend_re.</summary>
private static string GetAddFriendRetcodeMessage(byte retcode)
{
switch (retcode)
{
//case 0: return "Success";
//case 1: return "Player is offline";
//case 2: return "Request was refused";
//case 3: return "Timeout";
//case 4: return "No remaining space";
//case 5: return "Not found";
//case 6: return "Invalid parameter";
//case 7: return "Duplicate entry";
//case 8: return "Friend list has not been retrieved yet";
case 0: return "thành công";
case 1: return "người chơi đang offline";
case 2: return "yêu cầu bị từ chối";
case 3: return "hết thời gian yêu cầu";
case 4: return "không còn chỗ trống";
case 5: return "không tìm thấy";
case 6: return "tham số không hợp lệ";
case 7: return "mục nhập trùng lặp";
case 8: return "danh sách bạn bè chưa được tải về";
default: return $"retcode={retcode}";
}
}
// void CECGameSession::OnPrtcPlayerLogout(GNET::Protocol* pProtocol)
// {
// using namespace GNET;
// PlayerLogout* p = (PlayerLogout*)pProtocol;
void OnPrtcPlayerLogout(playerlogout protocol)
{
// m_CmdCache.RemoveAllCmds();
m_CmdCache.RemoveAllCmds();
// int iFlag;
// switch (p.result)
// {
// case _PLAYER_LOGOUT_FULL: iFlag = 0; break;
// case _PLAYER_LOGOUT_HALF: iFlag = 1; break;
// default: iFlag = 2; break;
// }
int iFlag;
switch (protocol.Result)
{
case PendingActionConstants._PLAYER_LOGOUT_FULL: iFlag = 0; break;
case PendingActionConstants._PLAYER_LOGOUT_HALF: iFlag = 1; break;
default: iFlag = 2; break;
}
// g_pGame.GetGameRun().SetLogoutFlag(iFlag);
EC_Game.GetGameRun().SetLogoutFlag(iFlag);
// if (!IsConnected() && g_pGame.GetGameRun().GetLogoutFlag() == 1)
// {
// a_LogOutput(1, "CECGameSession::OnPrtcPlayerLogout, LogoutFlag=1 replaced by 2.");
// g_pGame.GetGameRun().SetLogoutFlag(2);
// }
// Keep half logout as role-select even if the server closes the socket.
// LoginScene will reconnect with saved credentials if needed.
}
/// <summary>Format parsed PLAYER_EQUIP_DETAIL with real game data: slot names and item names from element data.</summary>
private static string FormatPlayerEquipDetailReadable(PlayerEquipDetailData parsed)
{
var sb = new StringBuilder();
sb.Append($"RoleId={parsed.RoleId}");
if (parsed.Slots.Count == 0)
{
sb.Append(", no slots");
return sb.ToString();
}
sb.Append(" | ");
for (int i = 0; i < parsed.Slots.Count; i++)
{
var (slotIndex, tid) = parsed.Slots[i];
string slotName = GetEquipSlotName(slotIndex);
string itemName = tid <= 0 ? "(empty)" : (EC_IvtrItemUtils.Instance.ResolveItemName(tid) ?? $"(tid{tid})");
if (string.IsNullOrEmpty(itemName)) itemName = $"(tid{tid})";
if (i > 0) sb.Append(", ");
sb.Append($"{slotName}: {itemName}");
}
return sb.ToString();
}
public struct ServerErrorEvent
{
public int ErrorCode;
public ServerErrorEvent(int code) { ErrorCode = code; }
}
private static string GetEquipSlotName(byte slotIndex)
{
if (Enum.IsDefined(typeof(IndexOfIteminEquipmentInventory), slotIndex))
{
var name = Enum.GetName(typeof(IndexOfIteminEquipmentInventory), slotIndex);
if (!string.IsNullOrEmpty(name)) return name;
}
return $"Slot{slotIndex}";
}
private void HandleServerDataSend(gamedatasend protocol)
{
int lenghtHeader = Marshal.SizeOf<ushort>();
var pDataBuf = new byte[protocol.Data.ByteArray.Length - lenghtHeader];
var byteArrHeader = new byte[lenghtHeader];
long dwDataSize = protocol.Data.Size;
if (dwDataSize < Marshal.SizeOf<ushort>())
{
_logger.Error($"### GameDataSend: size invalid {dwDataSize}");
return;
}
dwDataSize -= Marshal.SizeOf<cmd_header>(); // 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<int>();
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<int>(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<int>(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.SPEND_MONEY:
// EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SPENDMONEY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
// break;
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.EQUIP_DATA:
case CommandID.EQUIP_DATA_CHANGED:
// BMLogger.Log($"### EQUIP_DATA: CMDID {pCmdHeader}");
// MSG_PM_PLAYEREQUIPDATA, MAN_PLAYER, -1, (DWORD)pDataBuf, pCmdHeader.cmd, dwDataSize);
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREQUIPDATA, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
break;
case CommandID.PLAYER_EQUIP_DETAIL:
// View other player profile/equip detail — parse and log with real game data (item names, slot names)
if (pDataBuf != null && pDataBuf.Length > 0)
{
try
{
var parsed = GPDataTypeHelper.ParsePlayerEquipDetail(pDataBuf);
string readable = FormatPlayerEquipDetailReadable(parsed);
Debug.Log($"[PLAYER_EQUIP_DETAIL] {readable}");
if (parsed.Slots.Count == 0 && pDataBuf.Length > 6)
Debug.Log($"[PLAYER_EQUIP_DETAIL] Raw(hex): {BitConverter.ToString(pDataBuf)}");
}
catch (Exception ex)
{
Debug.LogWarning($"[PLAYER_EQUIP_DETAIL] Parse failed: {ex.Message}. Raw length={pDataBuf.Length}, hex: {BitConverter.ToString(pDataBuf, 0, Math.Min(64, pDataBuf.Length))}...");
}
}
else
Debug.Log("[PLAYER_EQUIP_DETAIL] Server sent empty payload.");
break;
case CommandID.PLAYER_CASH:
{
if (pDataBuf != null)
{
try
{
var cashData = GPDataTypeHelper.FromBytes<player_cash>(pDataBuf);
int cashAmount = cashData.cash_amount;
PostToUnityContext(() => ShopUIManager.CacheCash(cashAmount));
}
catch (Exception)
{
// Ignore parse failures for optional cash payload.
}
}
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.ITEM_TO_MONEY:
// Host sells items to NPC (server converts items to money)
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ITEMTOMONEY, (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<int>(pDataBuf);
cmd_object_atk_result pCmdAtk = GPDataTypeHelper.FromBytes<cmd_object_atk_result>(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.ATTACK_ONCE:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATTACKONCE, 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_LEADER_INVITE:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TEAMINVITE, (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<cmd_error_msg>(pDataBuf);
#if UNITY_EDITOR
BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}");
Debug.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
if (pCmd.iMessage != 0)
{
EventBus.Publish(new ServerErrorEvent(pCmd.iMessage));
}
#endif
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<int>();
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<cmd_npc_greeting>(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<cmd_server_time>(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<cmd_object_cast_skill>(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.RECEIVE_EXP:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_RECEIVEEXP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.LEVEL_UP:
{
cmd_level_up pCmdLevelUp = GPDataTypeHelper.FromBytes<cmd_level_up>(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.HOST_SKILL_ATTACKED:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLATTACKED, 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<cmd_enchant_result>(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:
// BMLogger.LogError("EC_MsgDef.MSG_PM_PLAYEREXTPROP");
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<uint>(pDataBuf);
int offset2 = sizeof(uint);
pCmd5.idList = new int[pCmd5.uCount];
for (int i = 0; i < pCmd5.uCount; i++)
{
pCmd5.idList[i] = GPDataTypeHelper.FromBytes<int>(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<cmd_object_takeoff>((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<cmd_object_landing>((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<cmd_player_leave_world>(pDataBuf);
if (ISPLAYERID(pCmd.id))
{
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXIT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
}
break;
}
case CommandID.PLAYER_CHGSHAPE:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERCHGSHAPE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
break;
case CommandID.SET_MOVE_STAMP:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETMOVESTAMP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.UPDATE_EXT_STATE:
{
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])pDataBuf);
if (ISPLAYERID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXTSTATE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
else if (ISNPCID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCEXTSTATE, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
break;
}
case CommandID.ICON_STATE_NOTIFY:
{
// this is a bit hacky, but it works. because the cmd_icon_state_notify is not blittable (contains List<>); read leading id only for routing.
// cmd_icon_state_notify is not blittable (contains List<>); read leading id only for routing.
if (pDataBuf == null || pDataBuf.Length < sizeof(int))
break;
int iconStateEntityId = BitConverter.ToInt32(pDataBuf, 0);
if (ISPLAYERID(iconStateEntityId))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXTSTATE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
else if (ISNPCID(iconStateEntityId))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCEXTSTATE, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
break;
}
case CommandID.ENTER_SANCTUARY:
case CommandID.LEAVE_SANCTUARY:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SANCTUARY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.OBJECT_SKILL_ATTACK_RESULT:
{
cmd_object_skill_attack_result pCmd = GPDataTypeHelper.FromBytes <cmd_object_skill_attack_result>((byte[])pDataBuf);
if (ISPLAYERID(pCmd.attacker_id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERSKILLRESULT, MANAGER_INDEX.MAN_PLAYER, -1,pDataBuf, pCmdHeader);
else if (ISNPCID(pCmd.attacker_id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCSKILLRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
break;
}
case CommandID.HOST_NOTIFY_ROOT:
case CommandID.HOST_DISPEL_ROOT:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ROOTNOTIFY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.INVALID_OBJECT:
{
cmd_invalid_object pCmd = GPDataTypeHelper.FromBytes<cmd_invalid_object>((byte[])pDataBuf);
if (ISPLAYERID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_INVALIDOBJECT, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
else if (ISNPCID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_INVALIDOBJECT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
else if (ISMATTERID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_INVALIDOBJECT, MANAGER_INDEX.MAN_MATTER, 0, pDataBuf, pCmdHeader);
break;
}
case CommandID.OBJECT_SIT_DOWN:
case CommandID.OBJECT_STAND_UP:
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERSITDOWN, 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 OnPrtcSelectRoleRe(SelectRole_Re protocol)
{
_logger.Info($"Select role response {protocol.result}");
var callback = _selectRoleCallback;
PostToUnityContext(() => callback?.Invoke(_selectedRole));
// in C++: we call CECGameRun() via pLoginUIMan.LaunchLoading();
// now: quick hack to start game immediately after role selection - can refactor later if needed
EC_Game.GetGameRun().StartGame(0, Vector3.zero);
}
private void HandleCreateRoleResponse(createrole_re protocol)
{
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);
}
/// <summary>
/// Client logout request. Mirrors original client behavior:
/// - outType=1: back to select role
/// - outType=0: logout account
/// </summary>
public void c2s_SendCmdLogout(int outType, Action complete = null)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType);
SendProtocol(g, complete);
}
public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode,
byte byDir, ushort wStamp, int iTime)
{
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 const int SUPER_FAR_CRY_EMOTION_SET = 6;
/// <summary>
/// Convert string become byte (Encoding.Unicode trong .NET = UTF-16 LE)
/// </summary>
/// <param name="cChannel"> Data type: enum, Chanel chat </param>
/// <param name="szMsg"> Text send to server </param>
/// <param name="iPack"></param>
/// <param name="iSlot"></param>
public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
{
if (string.IsNullOrEmpty(szMsg))
return;
publicchat p = new publicchat();
p.Channel = cChannel;
p.Roleid = m_iCharID;
if (iPack == EC_Inventory.IVTRTYPE_CLIENT_GENERALCARD_PACK)
{
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
EC_Inventory clientPack =
pHost != null ? pHost.GetPack(EC_Inventory.IVTRTYPE_CLIENT_GENERALCARD_PACK) : null;
if (pHost != null && clientPack != null)
{
EC_IvtrItem pCard = clientPack.GetItem(iSlot);
if (pCard != null)
{
short cmd = (short)CHAT_S2C.EChatS2CCommand.CHAT_GENERALCARD_COLLECTION;
int cardId = pCard.GetTemplateID();
byte[] bytes = new byte[6];
BitConverter.GetBytes(cmd).CopyTo(bytes, 0);
BitConverter.GetBytes(cardId).CopyTo(bytes, 2);
p.Data.Replace(bytes);
}
}
}
else if (iPack >= 0 && iSlot >= 0)
{
byte[] bytes = new byte[5];
short cmd = (short)CHAT_S2C.EChatS2CCommand.CHAT_EQUIP_ITEM;
short index = (short)iSlot;
byte where = (byte)iPack;
BitConverter.GetBytes(cmd).CopyTo(bytes, 0);
bytes[2] = where;
BitConverter.GetBytes(index).CopyTo(bytes, 3);
p.Data.Replace(bytes);
}
byte[] unicodeBytes = Encoding.Unicode.GetBytes(szMsg);
p.Msg.Replace(unicodeBytes);
SendProtocol(p);
// [Port] C++: DlgChat::OnCommand_speak dòng 736:
// Server KHÔNG echo tin nhắn quay lại cho chính người gửi.
// => Phải gọi AddChatMessage ngay tại local để hiển thị lên UI Chat Panel.
string szMsgCopy = szMsg;
byte cChannelCopy = cChannel;
EnqueueChatUI(() => SendChatData_ApplyLocalEchoToChatPanel(cChannelCopy, szMsgCopy));
}
/// <summary>Echo local sau SendProtocol — chỉ chạy trên main thread qua EnqueueChatUI.</summary>
private static void SendChatData_ApplyLocalEchoToChatPanel(byte cChannel, string szMsgBody)
{
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
if (pHost == null)
return;
int nEmotionSet = 0;
// Giữ logic C++ port: SUPERFARCRY → bộ emotion 6; strMsg cắt 8 ký tự trong bản gốc không dùng cho Format (Format vẫn dùng szMsg đầy đủ).
// Keep C++ port: SUPERFARCRY → emotion set 6; original trimmed strMsg was unused for Format (Format still used full szMsg).
if (cChannel is (byte)ChatChannel.GP_CHAT_LOCAL
or (byte)ChatChannel.GP_CHAT_FARCRY
or (byte)ChatChannel.GP_CHAT_SUPERFARCRY
or (byte)ChatChannel.GP_CHAT_BATTLE
or (byte)ChatChannel.GP_CHAT_COUNTRY)
{
if (cChannel == (byte)ChatChannel.GP_CHAT_SUPERFARCRY)
nEmotionSet = SUPER_FAR_CRY_EMOTION_SET;
}
string pszMsgOrigion = szMsgBody;
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
string szName = pHost.GetName();
char[] szText = new char[80];
AUICommon.AUI_ConvertChatString(ref szName, ref szText, false);
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
string str;
try
{
str = string.Format(fmt, szName, szMsgBody);
}
catch
{
str = $"{szName}: {szMsgBody}";
}
EC_Game.GetGameRun().AddChatMessage(str, cChannel, pHost.GetCharacterID(),
null, 0, nEmotionSet, null, pszMsgOrigion);
}
public void SendPrivateChatData(string szDstName, string szMsg, byte byFlag = 0,
int idPlayer = 0, int iPack = -1, int iSlot = -1)
{
if (string.IsNullOrEmpty(szMsg))
return;
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
// In Unity version, pHost doesn't have AddRelatedPlayer.
// C++ uses it to cache player names for chat, but here it's handled differently.
var p = new privatechat();
p.Srcroleid = m_iCharID;
p.Dstroleid = idPlayer;
p.Channel = byFlag;
Octets data = new Octets();
if (iPack >= 0 && iSlot >= 0 && pHost != null)
{
EC_Inventory pPack = pHost.GetPack(iPack);
if (pPack != null)
{
EC_IvtrItem pItem = pPack.GetItem(iSlot);
if (pItem != null)
{
// TODO: Implement item serialization if needed
}
}
}
p.Data = data;
p.Src_name = new Octets(Encoding.Unicode.GetBytes(pHost != null ? pHost.GetName() : ""));
p.Dst_name = new Octets(Encoding.Unicode.GetBytes(szDstName));
p.Msg = new Octets(Encoding.Unicode.GetBytes(szMsg));
SendProtocol(p);
}
public void LoadConfigData()
{
getuiconfig p = new getuiconfig();
p.Roleid = m_iCharID;
SendProtocol(p);
}
/// <summary>
/// Save config data to server (sends setuiconfig with compressed config blob).
/// 保存配置数据到服务器(发送 setuiconfig 及压缩后的配置数据)。
/// </summary>
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;
}
/// <summary>
/// Wire 正文(表情等)→ TMP — 供仅走 EventBus 的系统消息使用;玩家消息由 CECGameUIMan.AddChatMessage 处理。
/// Wire body (emotions, etc.) → TMP — for system messages that only use EventBus; player messages use CECGameUIMan.AddChatMessage.
/// </summary>
private string ConvertWireBodyForChatPanel(string wireBody, int cEmotion)
{
if (string.IsNullOrEmpty(wireBody))
return wireBody;
var ui = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan() as CECGameUIMan;
return ui != null ? ui.ConvertWireChatBodyForDisplay(wireBody, cEmotion) : wireBody;
}
/// <summary>
/// Luồng mạng → SynchronizationContext (main) → hàng đợi ChatThreadDispatcher — cùng pipeline với ChatSystemlUI / MiniChatUI.
/// Network thread → SynchronizationContext (main) → ChatThreadDispatcher queue (same pipeline as ChatSystemlUI / MiniChatUI).
/// </summary>
private void EnqueueChatUI(Action action)
{
if (action == null)
return;
SynchronizationContext ctx = Context;
if (ctx == null)
{
_logger.Log(LogType.Error, "[GameSession] EnqueueChatUI: Context is null; chat UI not marshaled.");
return;
}
ctx.Post(_ => ChatThreadDispatcher.Instance.Post(action), null);
}
private void OnPrtcWorldChat_Apply(int roleId, byte channel, int emotion, string strMsg, string strSrcName)
{
CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
Debug.Log($"[Cuong] {strMsg}");
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
string formatted;
try
{
formatted = string.Format(fmt, strSrcName, strMsg);
}
catch
{
formatted = $"{strSrcName}: {strMsg}";
}
pGameUI.AddChatMessage(formatted, (ChatChannel)channel, roleId, strSrcName,
0, emotion, null, strMsg);
if (!CECGameUIMan.ChannelShowsChatBubbleAboveHead((ChatChannel)channel))
{
CECPlayer pSrcPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(roleId);
if (pSrcPlayer != null)
{
string bubbleTmp = pGameUI.ConvertWireBodyForHeadBubble(
strMsg, (ChatChannel)channel, roleId, emotion);
EventBus.PublishChannel(roleId, new EventChatMessageOnTopPlayer(roleId, bubbleTmp));
}
}
}
private void OnPrtcChatMessage_ApplyNpcBranch(int srcRoleid, int channel, int emotion, string szMsgBody, string strTempFull)
{
BMLogger.Log("[Cuong] ISNPCID " + strTempFull);
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
CECNPC pNPC = EC_Game.GetGameRun().GetWorld().GetNPCMan().GetNPC(srcRoleid);
if (pNPC == null)
return;
string template = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT2));
string message = string.Format(
template,
pNPC.GetName(),
szMsgBody);
EC_Game.GetGameRun().AddChatMessage(
message,
channel,
srcRoleid,
null,
0,
emotion);
string szMsgOut = szMsgBody;
CECUIHelper.RemoveNameFlagFromNPCChat(strTempFull, out szMsgOut);
pNPC.SetLastSaidWords(szMsgOut);
}
private void OnPrtcPrivateChat_Apply(
int srcRoleid,
byte whisperChannelFlag,
int emotion,
string strMsg,
string strSrcName)
{
CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
if (whisperChannelFlag == 0 || whisperChannelFlag == 1)
{
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_PRIVATECHAT1));
BMLogger.Log($"[Cuong] OnPrtcPrivateChat {fmt}");
string formatted;
try
{
formatted = string.Format(fmt, strSrcName, strMsg);
}
catch
{
formatted = $"{strSrcName} whispers to you: {strMsg}";
}
pGameUI.AddChatMessage(formatted, ChatChannel.GP_CHAT_WHISPER, srcRoleid, strSrcName,
whisperChannelFlag, emotion, null, strMsg);
}
else
{
pGameUI.AddChatMessage(strMsg, ChatChannel.GP_CHAT_WHISPER, srcRoleid, strSrcName,
whisperChannelFlag, emotion, null, strMsg);
}
CECPlayer pSrcPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(srcRoleid);
if (pSrcPlayer != null)
{
string bubbleTmp = pGameUI.ConvertWireBodyForHeadBubble(
strMsg, ChatChannel.GP_CHAT_WHISPER, srcRoleid, emotion);
EventBus.PublishChannel(srcRoleid, new EventChatMessageOnTopPlayer(srcRoleid, bubbleTmp));
}
}
private bool OnPrtcWorldChat(Protocol pProtocol, bool bCalledagain)
{
worldchat p = (worldchat)pProtocol;
// Tránh trùng với echo local trong SendChatData: server vẫn có thể broadcast
// worldchat về đúng người gửi; khi đó đã hiển thị ở SendChatData rồi.
if (p.Roleid == m_iCharID)
return true;
int roleId = p.Roleid;
byte channel = p.Channel;
int emotion = p.Emotion;
string strMsg = Encoding.Unicode.GetString(p.Msg.ToArray());
string strSrcName = Encoding.Unicode.GetString(p.Name.ToArray());
EnqueueChatUI(() => OnPrtcWorldChat_Apply(roleId, channel, emotion, strMsg, strSrcName));
return true;
}
/*private void OnTaskChatMessage(byte[] pBuf, int sz)
{
... logic ẩn ...
}*/
private bool OnBattleChatMessage(chatmessage p, List<int> pPendingFactions)
{
// Tương đương OnBattleChatMessage trong C++
// Hiện tại đơn giản hóa việc hiển thị, thực tế cần parse nội dung Battle cụ thể
Debug.Log($"[Battle Chat] RoleID: {p.Srcroleid}");
return true;
}
private bool OnFortressChatMessage(chatmessage p, List<int> pPendingFactions)
{
// Tương đương OnFortressChatMessage trong C++
Debug.Log($"[Fortress Chat] RoleID: {p.Srcroleid}");
return true;
}
private bool OnKingChatMessage(chatmessage p, bool bGetPlayerName = true)
{
// Tương đương OnKingChatMessage trong C++
Debug.Log($"[King Chat] RoleID: {p.Srcroleid}");
return true;
}
private void OnFactionPVPChatMessage(chatmessage p)
{
// Tương đương OnFactionPVPChatMessage trong C++
Debug.Log($"[FactionPVP Chat] RoleID: {p.Srcroleid}");
}
private bool OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
{
chatmessage p = (chatmessage)pProtocol;
//var channel = (ChatChannel)p.Channel;
if (Chat_GameSession.ShouldBlockByLevel(p))
{
return true;
}
EC_IvtrItem pItem = CHAT_S2C.CreateChatItem(p.Data);
string szMsg = null;
string strTemp = Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
string strMsgOrigion = strTemp;
// [Port] CECGameUIMan::FilterInvalidTags — Lọc các tag đặc biệt không hợp lệ
strTemp = AUICommon.FilterInvalidTags(strTemp, pItem == null);
if (!Chat_GameSession.PolicyResolver(pProtocol, p, ref strTemp, out szMsg))
{
return false;
}
if (p.Channel is (byte)ChatChannel.GP_CHAT_BROADCAST
or (byte)ChatChannel.GP_CHAT_SYSTEM || p.Srcroleid == 0)
{
if (p.Channel == (byte)ChatChannel.GP_CHAT_SYSTEM && p.Srcroleid > 0)
{
switch (p.Srcroleid)
{
case 1: case 2: case 3: case 4: case 6: case 7: // Battle Message
if (!bCalledagain)
{
List<int> pending = new List<int>();
if (!OnBattleChatMessage(p, pending))
{
// Handle pending faction info
return false;
}
}
break;
case 18: case 19: case 20: case 21: case 22: // Auction Message
{
string strAuction = strTemp;
byte chAuction = p.Channel;
int emoAuction = p.Emotion;
EnqueueChatUI(() =>
{
Debug.Log("[Cuong] Auction " + strAuction);
EventBus.Publish(new ChatMessageEvent(ConvertWireBodyForChatPanel(strAuction, emoAuction), chAuction));
});
break;
}
case 24: // Task Message
// OnTaskChatMessage(p.Data.RawBuffer, p.Data.Size);
break;
case 29: case 30: case 31: case 32: case 33: case 34:
case 35: case 36: case 37: case 38: case 39: case 40:
case 41: case 42: case 43: case 45: // Fortress Message
OnFortressChatMessage(p, null);
break;
case 46: case 47: case 48: case 49: // Country Battle
// OnCountryChatMessage(p);
break;
case 50: case 51: case 52: case 53: case 54:
case 55: case 56: case 57: case 58: case 59: // King Chat
OnKingChatMessage(p);
break;
case 60: case 61: case 62: case 63: case 64: // Faction PVP
OnFactionPVPChatMessage(p);
break;
}
}
else
{
string strSys = strTemp;
byte chSys = p.Channel;
int emoSys = p.Emotion;
EnqueueChatUI(() =>
{
EventBus.Publish(new ChatMessageEvent(ConvertWireBodyForChatPanel(strSys, emoSys), chSys));
});
}
}
else if (p.Channel == (byte)ChatChannel.GP_CHAT_INSTANCE && p.Srcroleid == 1)
{
// Chat_GameSession.AUICTranslate trans;
// EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().AddHeartBeatHint(trans.Translate(szMsg ));
}
else
{
if (ISPLAYERID(p.Srcroleid))
{
string szName = EC_Game.GetGameRun().GetPlayerName(p.Srcroleid, false);
if (string.IsNullOrEmpty(szName))
{
if (!bCalledagain)
{
Chat_GameSession.AddElemForPendingProtocols(pProtocol);
Chat_GameSession.AddChatPlayerID(p.Srcroleid);
}
return false;
}
string szNameCopy = szName;
string szMsgCopy = szMsg;
int srcRole = p.Srcroleid;
int chPl = p.Channel;
int emoPl = p.Emotion;
string strOrigCopy = strMsgOrigion;
EnqueueChatUI(() =>
{
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
char[] szText = new char[80];
string szNameMut = szNameCopy;
AUICommon.AUI_ConvertChatString(ref szNameMut, ref szText, false);
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
string str;
try
{
str = string.Format(fmt, szNameMut, szMsgCopy);
}
catch
{
str = $"{szNameMut}: {szMsgCopy}";
}
EC_Game.GetGameRun().AddChatMessage(str, chPl, srcRole,
null, 0, emoPl, null, strOrigCopy);
});
}
else if (ISNPCID(p.Srcroleid))
{
int srcNpc = p.Srcroleid;
int chNpc = p.Channel;
int emoNpc = p.Emotion;
string szMsgNpc = szMsg;
string strTempNpc = strTemp;
EnqueueChatUI(() => OnPrtcChatMessage_ApplyNpcBranch(srcNpc, chNpc, emoNpc, szMsgNpc, strTempNpc));
}
}
return true;
}
private void OnPrtcPrivateChat(Protocol pProtocol)
{
privatechat p = (privatechat)pProtocol;
// TODO: Blacklist check (PlayerIsBlack)
// if (PlayerIsBlack(p.Srcroleid)) return;
string strMsg = Encoding.Unicode.GetString(p.Msg.ToArray());
string strSrcName = Encoding.Unicode.GetString(p.Src_name.ToArray());
int srcRole = p.Srcroleid;
byte whisperCh = p.Channel;
int emotion = p.Emotion;
EnqueueChatUI(() => OnPrtcPrivateChat_Apply(srcRole, whisperCh, emotion, strMsg, strSrcName));
}
public struct ChatMessageEvent
{
public string context;
public byte channel;
public ChatMessageEvent(string context, byte channel = 0)
{
this.context = context;
this.channel = channel;
}
}
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 OnPrtcSetConfigRe(Protocol pProtocol)
{
SetUIConfig_Re p = (SetUIConfig_Re)pProtocol;
if (p.result != (int)ErrCode.ERR_SUCCESS)
BMLogger.LogError($"CECGameSession::OnPrtcSetConfigRe, link return error code of {p.result}");
if (CECGameRun.Instance != null)
{
TestLogoutLogic.Instance.WasClientSendLogoutMessage = true;
CECGameRun.Instance.GetPendingLogOut().TriggerAll();
CECGameRun.Instance.GetPendingLogOut().Clear();
}
}
private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
{
playerbaseinfo_re p = (playerbaseinfo_re)pProtocol;
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<int> 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<int> aIDs)
{
// using namespace C2S;
const int iNumLimit = 250;
int iCount = 0;
while (iCount < iNumID)
{
int iNumSend = iNumLimit;
if (iCount + iNumLimit > iNumID)
iNumSend = iNumID - iCount;
/*/
int iSize = sizeof (cmd_header) + sizeof (WORD) + iNumSend * sizeof (int);
BYTE* pBuf = (BYTE*)a_malloctemp(iSize);
if (!pBuf)
return;
((cmd_header*)pBuf).cmd = C2S::GET_OTHER_EQUIP;
cmd_get_other_equip* pCmd = (cmd_get_other_equip*)(pBuf + sizeof (cmd_header));
pCmd.size = (WORD)iNumSend;
for (int i=0; i < iNumSend; i++)
pCmd.idlist[i] = aIDs[iCount+i];
g_pGame.GetGameSession().SendGameData(pBuf, iSize);
a_freetemp(pBuf);
//*/
var idlist = new int[iNumSend];
for (int i=0; i < iNumSend; i++)
idlist[i] = aIDs[iCount+i];
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data = C2SCommandFactory.CreateGetOtherEquipCmd(iNumID, idlist);
SendProtocol(gamedatasend);
iCount += iNumSend;
}
}
/// <summary>Request other player profile/equip detail (C2S GET_OTHER_EQUIP_DETAIL). Server responds with PLAYER_EQUIP_DETAIL.</summary>
public void c2s_SendCmdGetOtherEquipDetail(int roleId)
{
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data = C2SCommandFactory.CreateGetOtherEquipDetailCmd(roleId);
SendProtocol(gamedatasend);
}
public void c2s_SendCmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
{
gamedatasend gamedatasend = new gamedatasend();
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<int> 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_SendCmdTeamAgreeInvite(int idLeader, int team_seq)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateTeamAgreeInviteCommand(idLeader, team_seq);
SendProtocol(g);
}
public void c2s_SendCmdTeamRejectInvite(int idLeader)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateTeamRejectInviteCommand(idLeader);
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);
}
/// <summary>Send PROTOCOL_ADDFRIEND(202). Port of CECGameSession::friend_Add(idPlayer, szName).</summary>
public void Friend_Add(int idPlayer, string name)
{
var p = new addfriend();
p.Srcroleid = m_iCharID;
p.Dstroleid = idPlayer;
if (!string.IsNullOrEmpty(name))
p.Dstname = new Octets(System.Text.Encoding.Unicode.GetBytes(name));
else
p.Dstname = new Octets();
p.Srclsid = (int)_localsid;
SendProtocol(p);
}
/// <summary>
/// Send RPC_ADDFRIENDRQST(204) response. Port of CECGameSession::friend_AddResponse(dwHandle, bAgree).
/// Answer to received friend request: xid from request, agree = true (0) or false (69).
/// Matches GNET Rpc XID: low bit = is_request; response must clear that bit.
/// </summary>
public void Friend_AddResponse(uint xid, bool agree)
{
const byte ERR_TRADE_AGREE = 0;
const byte ERR_TRADE_REFUSE = 69;
uint responseXid = xid & 0x7FFFFFFFu; // clear request bit, keep count
var p = new addfriendrqstres(responseXid, agree ? ERR_TRADE_AGREE : ERR_TRADE_REFUSE);
SendProtocol(p);
}
/// <summary>Send PROTOCOL_GETFRIENDS(206). Port of CECGameSession::friend_GetList().</summary>
public void Friend_GetList()
{
var p = new getfriends();
p.Roleid = m_iCharID;
p.Localsid = (int)_localsid;
SendProtocol(p);
}
/// <summary>Send PROTOCOL_DELFRIEND(212). Delete a friend by role id.</summary>
public void Friend_Del(int friendId)
{
var p = new delfriend();
p.Roleid = m_iCharID;
p.Friendid = friendId;
p.Localsid = (int)_localsid;
SendProtocol(p);
}
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_SendCmdSitDown()
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.SIT_DOWN);
SendProtocol(g);
}
public void c2s_SendCmdTeamAssistSel(int idTeamMember)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateTeamAssistSelCommand(idTeamMember);
SendProtocol(g);
}
public void c2s_SendCmdOpenBoothTest()
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.OPEN_BOOTH_TEST);
SendProtocol(g);
}
public void c2s_SendCmdBindPlayerRequest(int idTarget)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateBindPlayerRequestCommand(idTarget);
SendProtocol(g);
}
public void c2s_SendCmdBindPlayerInvite(int idTarget)
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateBindPlayerInviteCommand(idTarget);
SendProtocol(g);
}
/// <summary>Port of CECGameSession::trade_Start.</summary>
public bool trade_Start(int idTarget)
{
var p = new tradestart
{
Roleid = m_iCharID,
Partner_roleid = idTarget,
Localsid = (int)_localsid
};
SendProtocol(p);
return true;
}
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
}
}
}