1947 lines
86 KiB
C#
1947 lines
86 KiB
C#
using BrewMonster;
|
|
using BrewMonster.Common;
|
|
using BrewMonster.Network;
|
|
using BrewMonster.Scripts.Skills;
|
|
using BrewMonster.UI;
|
|
using CSNetwork.C2SCommand;
|
|
using CSNetwork.GPDataType;
|
|
using CSNetwork.Protocols;
|
|
using CSNetwork.Protocols.RPCData;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using CommandID = CSNetwork.GPDataType.CommandID;
|
|
|
|
namespace CSNetwork
|
|
{
|
|
public class GameSession : IDisposable
|
|
{
|
|
private static IPrefixedLogger
|
|
_logger = LoggerFactory.GetLogger(nameof(GameSession)); // Get class-specific logger
|
|
|
|
private NetworkManager _networkManager;
|
|
private string _host;
|
|
private int _port;
|
|
private string _username;
|
|
private string _password;
|
|
private int _currentUserId = -1; // To store the UserID after successful login
|
|
private uint _localsid = 0; // To store the LocalSID from onlineannounce
|
|
private int m_iCharID;
|
|
private int m_idLastSelTarget = 0; // ID of selected item last time
|
|
|
|
CECStringTab m_ErrorMsgs;
|
|
|
|
// State management for async operations and callbacks
|
|
private Action<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 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 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 GameSession()
|
|
{
|
|
_networkManager = new NetworkManager();
|
|
m_CmdCache = new CECC2SCmdCache();
|
|
_networkManager.ProtocolReceived += OnProtocolReceived;
|
|
_networkManager.ErrorOccurred += OnErrorOccurred;
|
|
_networkManager.Disconnected += OnDisconnected;
|
|
}
|
|
|
|
public void SetLogPath(string path)
|
|
{
|
|
LoggerFactory.SetFileLoggerImplementation(new FileLogger());
|
|
_logger = LoggerFactory.GetCustomLogger(path, nameof(GameSession) + GetHashCode(), LoggerType.File);
|
|
_networkManager.SetLogPath(path);
|
|
}
|
|
|
|
/// <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 RequestPickupItem(int idItem, int tid)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.CreatePickupItem(idItem, tid);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
public void c2s_SendCmdGetIvtrDetailData(byte byPackage, Action callback)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGetIvtrDetailData(byPackage);
|
|
SendProtocol(gamedatasendRequest, callback);
|
|
}
|
|
|
|
public void c2s_SendCmdQueryCashInfo()
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdQueryCashInfo();
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
public void c2s_SendCmdOpenFashionTrash(string password)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdOpenFashionTrash(password);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
public void c2s_SendCmdEquipItem(byte iIvtrIdx, byte iEquipIdx, Action callback)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdEquipItem(iIvtrIdx, iEquipIdx);
|
|
SendProtocol(gamedatasendRequest, callback);
|
|
}
|
|
|
|
public void c2s_SendCmdReviveVillage(int param = 0)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
public void c2s_SendCmdReviveItem(int param = 0)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
public void RequestReviveByPlayer(int param = 0)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.c2s_SendCmdReviveVillage(param);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
|
|
public void c2s_SendCmdMallShopping(uint count, CMD_MallShopping.goods[] goodsArray)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetMallShopping(count, goodsArray);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
public void c2s_SendCmdGatherMaterial(int idMatter, int iToolPack, int idToolIndex, int idTool, int idTask)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_SendCmdGatherMaterial(idMatter, iToolPack, idToolIndex, idTool, idTask);
|
|
SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
public void c2s_SendCmdUseItemWithTarget(byte byPackage, byte bySlot, int tid, byte byPVPMask)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateUseItemWithTarget(byPackage, bySlot, tid, byPVPMask);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void RequestOwnItemInfoAsync(
|
|
byte byPackage,
|
|
byte bySlot,
|
|
int type,
|
|
int expire_date,
|
|
int state,
|
|
uint count,
|
|
ushort crc,
|
|
ushort content_length,
|
|
byte[] content,
|
|
Action callback)
|
|
{
|
|
gamedatasend gamedatasendRequest = new gamedatasend();
|
|
gamedatasendRequest.Data = C2SCommandFactory.CreateOwnItemInfo(byPackage, bySlot, type, expire_date, state,
|
|
count, crc, content_length, content);
|
|
SendProtocol(gamedatasendRequest, callback);
|
|
}
|
|
|
|
// --- Protocol Sending ---
|
|
public void SendProtocol(Protocol protocol, Action complete = null)
|
|
{
|
|
if (IsConnected)
|
|
{
|
|
_logger.Log(LogType.Debug,
|
|
$"Sending protocol: {protocol.GetType().Name} (Detail: {protocol.ToString})");
|
|
BMLogger.Log($"[GameSession] Sending protocol: {protocol.GetType().Name} (Type: {protocol.GetPType()})");
|
|
_networkManager.Send(protocol);
|
|
complete?.Invoke();
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogType.Warning, $"Cannot send protocol ({protocol.GetType().Name}), not connected.");
|
|
BMLogger.LogError($"[GameSession] Cannot send protocol ({protocol.GetType().Name}), not connected.");
|
|
}
|
|
}
|
|
|
|
// --- Event Handlers (from NetworkManager) ---
|
|
|
|
private void OnProtocolReceived(Protocol protocol)
|
|
{
|
|
_logger.Log(LogType.Debug, $"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})");
|
|
if (protocol is null)
|
|
return;
|
|
|
|
|
|
// Route protocol to appropriate handler
|
|
switch (protocol.GetPType())
|
|
{
|
|
case ProtocolType.PROTOCOL_CHALLENGE:
|
|
HandleChallenge((challenge)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_KEYEXCHANGE:
|
|
HandleKeyExchange((KeyExchange)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_ONLINEANNOUNCE:
|
|
HandleOnlineAnnounce((onlineannounce)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_ROLELIST_RE:
|
|
HandleRoleListResponse((RoleListResponse)protocol);
|
|
break;
|
|
// Add cases for other protocols GameSession might need to handle
|
|
case ProtocolType.PROTOCOL_SELECTROLE_RE:
|
|
HandleSelectRoleResponse((SelectRole_Re)protocol);
|
|
//_networkManager.IgnoreBytes = 2;
|
|
break;
|
|
case ProtocolType.PROTOCOL_CREATEROLE_RE:
|
|
HandleCreateRoleResponse((createrole_re)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_ERRORINFO:
|
|
HandleErrorInfo((errorinfo)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_S2CGAMEDATASEND:
|
|
case ProtocolType.PROTOCOL_GAMEDATASEND:
|
|
HandleServerDataSend((gamedatasend)protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_CHATMESSAGE:
|
|
_logger.Log(LogType.Warning, $"HoangDev :ProtocolType.PROTOCOL_CHATMESSAGE {protocol.GetPType()}");
|
|
OnPrtcChatMessage(protocol, false);
|
|
break;
|
|
case ProtocolType.PROTOCOL_PLAYERBASEINFO_RE:
|
|
OnPrtcPlayerBaseInfoRe(protocol);
|
|
break;
|
|
case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break;
|
|
case ProtocolType.PROTOCOL_PLAYERLOGOUT:
|
|
HandlePlayerLogout((playerlogout)protocol);
|
|
break;
|
|
|
|
case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE:
|
|
{
|
|
// CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
|
|
// if( pAutoTeam !=null)
|
|
// pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
_logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void HandlePlayerLogout(playerlogout protocol)
|
|
{
|
|
// Original client receives this before EVENT_DISCONNECT.
|
|
// We just publish it to allow higher-level flow (UnityGameSession/UI) to react.
|
|
PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
|
|
}
|
|
|
|
private void HandleServerDataSend(gamedatasend protocol)
|
|
{
|
|
int lenghtHeader = Marshal.SizeOf<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
|
|
#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.CHANGE_IVTR_SIZE:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader, iHostID);
|
|
break;
|
|
case CommandID.EXG_IVTR_ITEM:
|
|
case CommandID.MOVE_IVTR_ITEM:
|
|
case CommandID.PLAYER_DROP_ITEM:
|
|
case CommandID.EXG_EQUIP_ITEM:
|
|
case CommandID.EQUIP_ITEM:
|
|
case CommandID.MOVE_EQUIP_ITEM:
|
|
case CommandID.UNFREEZE_IVTR_SLOT:
|
|
case CommandID.PLAYER_EQUIP_TRASHBOX_ITEM:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ITEMOPERATION, (int)MANAGER_INDEX.MAN_PLAYER, 0,
|
|
pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.PLAYER_CASH:
|
|
{
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_IVTRINFO, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader, iHostID);
|
|
break;
|
|
}
|
|
case CommandID.MATTER_INFO_LIST:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERINFO, (int)MANAGER_INDEX.MAN_MATTER, 0, pDataBuf,
|
|
pCmdHeader);
|
|
break;
|
|
case CommandID.MATTER_ENTER_WORLD:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_MM_MATTERENTWORLD, (int)MANAGER_INDEX.MAN_MATTER, 0,
|
|
pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.PICKUP_ITEM:
|
|
case CommandID.HOST_OBTAIN_ITEM:
|
|
case CommandID.PRODUCE_ONCE:
|
|
case CommandID.TASK_DELIVER_ITEM:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPITEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader);
|
|
break;
|
|
case CommandID.MATTER_PICKUP:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PICKUPMATTER, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.PICKUP_MONEY:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPMONEY, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader);
|
|
break;
|
|
case CommandID.HOST_CORRECT_POS:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CORRECTPOS, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader, iHostID);
|
|
break;
|
|
case CommandID.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.TEAM_JOIN_TEAM:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_JOINTEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.TEAM_LEAVE_PARTY:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.TEAM_NEW_MEMBER:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_NEWTEAMMEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.TEAM_MEMBER_DATA:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TEAMMEMBERDATA, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.TEAM_MEMBER_LEAVE:
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_LEAVETEAM, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
|
|
break;
|
|
|
|
case CommandID.ERROR_MESSAGE:
|
|
{
|
|
int errRaw = BitConverter.ToInt32(pDataBuf, 0);
|
|
// Note: _logger may be configured as a file logger via SetLogPath(), so also log to console for visibility.
|
|
_logger.Info($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
|
|
#if UNITY_EDITOR
|
|
BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
|
|
#endif
|
|
cmd_error_msg pCmd = GPDataTypeHelper.FromBytes<cmd_error_msg>(pDataBuf);
|
|
#if UNITY_EDITOR
|
|
BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}");
|
|
#endif
|
|
Debug.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
|
|
if (pCmd.iMessage != 0)
|
|
{
|
|
// string szMsg = m_ErrorMsgs.GetWideString(pCmd.iMessage);
|
|
// if (string.IsNullOrEmpty(szMsg))
|
|
// BMLogger.LogError("SERVER - unknown error !");
|
|
// else
|
|
// {
|
|
// BMLogger.LogError("SERVER - error: "+szMsg);
|
|
// }
|
|
// else if (pCmd.iMessage != 2)
|
|
// g_pGame.GetGameRun().AddChatMessage(szMsg, GP_CHAT_MISC);
|
|
}
|
|
|
|
if (pCmd.iMessage == 2)
|
|
{
|
|
// Attack target is too far
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
|
}
|
|
else if (pCmd.iMessage == 20)
|
|
{
|
|
// Failed to cast skill
|
|
//pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
|
|
}
|
|
else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
|
|
{
|
|
// deal failed
|
|
//pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
|
|
}
|
|
else if (pCmd.iMessage == 158)
|
|
{
|
|
// µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ
|
|
//c2s_CmdGetCashMoneyRate();
|
|
}
|
|
else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/)
|
|
{
|
|
/* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
|
|
if (pGameUI)
|
|
pGameUI.EndNPCService();*/
|
|
}
|
|
else if
|
|
(pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/
|
|
)
|
|
{
|
|
/* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
|
|
if (pGameUI)
|
|
pGameUI.EndNPCService();*/
|
|
}
|
|
else if (pCmd.iMessage == 175)
|
|
{
|
|
//c2s_CmdQueryParallelWorld();
|
|
}
|
|
else if (pCmd.iMessage == 6)
|
|
{
|
|
//AP_ActionEvent(AP_EVENT_CANNOTPICKUP);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CommandID.SELECT_TARGET:
|
|
case CommandID.UNSELECT:
|
|
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SELTARGET, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
|
|
pCmdHeader);
|
|
break;
|
|
case CommandID.NPC_DIED:
|
|
case CommandID.NPC_DIED2:
|
|
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDIED, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
|
|
break;
|
|
case CommandID.OBJECT_DISAPPEAR:
|
|
{
|
|
int lenghtDataType1 = Marshal.SizeOf<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.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.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:
|
|
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.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;
|
|
default:
|
|
#if UNITY_EDITOR
|
|
if (isDebug)
|
|
{
|
|
BMLogger.LogError($"### GameDataSend: Unhandled CMDID {pCmdHeader} (payloadBytes={pDataBuf?.Length ?? 0})");
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
private void HandleSelectRoleResponse(SelectRole_Re protocol)
|
|
{
|
|
_logger.Info($"Select role response {protocol.result}");
|
|
var callback = _selectRoleCallback;
|
|
PostToUnityContext(() => callback?.Invoke(_selectedRole));
|
|
}
|
|
|
|
private void HandleCreateRoleResponse(createrole_re protocol)
|
|
{
|
|
Debug.Log($"[GameSession] HandleCreateRoleResponse - result: {protocol.result}, roleid: {protocol.roleid}");
|
|
|
|
if (protocol.result != (int)ErrCode.ERR_SUCCESS)
|
|
{
|
|
string errorMsg = $"Create role failed with result code: {protocol.result} (ERR_SUCCESS = {(int)ErrCode.ERR_SUCCESS})";
|
|
_logger.Log(LogType.Error, errorMsg);
|
|
Debug.LogError($"[GameSession] {errorMsg}");
|
|
var callback = _createRoleCallback;
|
|
_createRoleCallback = null;
|
|
PostToUnityContext(() => callback?.Invoke(null));
|
|
return;
|
|
}
|
|
|
|
Debug.Log($"[GameSession] Create role successful! RoleID: {protocol.roleid}");
|
|
var successCallback = _createRoleCallback;
|
|
_createRoleCallback = null;
|
|
PostToUnityContext(() => successCallback?.Invoke(protocol.roleinfo));
|
|
}
|
|
|
|
private void HandleErrorInfo(errorinfo protocol)
|
|
{
|
|
Debug.LogError($"[GameSession] Server error - Errcode: {protocol.Errcode}");
|
|
|
|
// If we're waiting for create role response and get an error, fail the callback
|
|
if (_createRoleCallback != null)
|
|
{
|
|
Debug.LogError($"[GameSession] Create role failed due to server error: {protocol.Errcode}");
|
|
var callback = _createRoleCallback;
|
|
_createRoleCallback = null;
|
|
PostToUnityContext(() => callback?.Invoke(null));
|
|
}
|
|
}
|
|
|
|
private void OnErrorOccurred(string errorMessage)
|
|
{
|
|
_logger.Log(LogType.Error, $"Network Error: {errorMessage}");
|
|
FailLoginInProgress(errorMessage);
|
|
FailRoleListInProgress(errorMessage);
|
|
}
|
|
|
|
private void OnDisconnected()
|
|
{
|
|
_logger.Log(LogType.Info, "Disconnected from server.");
|
|
_currentUserId = -1;
|
|
FailLoginInProgress("Disconnected");
|
|
FailRoleListInProgress("Disconnected");
|
|
// Clear command cache
|
|
m_CmdCache.RemoveAllCmds();
|
|
PostToUnityContext(() => Disconnected?.Invoke());
|
|
}
|
|
|
|
private void HandleChallenge(challenge challenge)
|
|
{
|
|
if (_loginCallback == null || string.IsNullOrEmpty(_username))
|
|
{
|
|
_logger.Log(LogType.Warning, "Received Challenge but not expecting it or username not set.");
|
|
return;
|
|
}
|
|
|
|
_logger.Log(LogType.Info, "Handling Challenge...");
|
|
|
|
response response = new response();
|
|
byte[] usernameBytes = Encoding.ASCII.GetBytes(_username);
|
|
byte[] passwordBytes = Encoding.ASCII.GetBytes(_password);
|
|
response.identity.Replace(usernameBytes);
|
|
response.Setup(new Octets(usernameBytes), new Octets(passwordBytes), challenge.nonce);
|
|
|
|
uint clientId = 0xffffffff;
|
|
byte[] clientIdBytes = BitConverter.GetBytes(clientId);
|
|
response.cli_fingerprint.Replace(clientIdBytes);
|
|
response.use_token = 0;
|
|
|
|
_networkManager.SetNonce(response.response_data);
|
|
SendProtocol(response);
|
|
_logger.Log(LogType.Info, "Sent Response.");
|
|
}
|
|
|
|
private void HandleKeyExchange(KeyExchange keyExchange)
|
|
{
|
|
if (_loginCallback == null || string.IsNullOrEmpty(_username))
|
|
{
|
|
_logger.Log(LogType.Warning, "Received KeyExchange but not expecting it.");
|
|
return;
|
|
}
|
|
|
|
_logger.Log(LogType.Info, "Handling KeyExchange...");
|
|
keyExchange.Setup(_networkManager, _username);
|
|
keyExchange.Blkickuser = 1;
|
|
SendProtocol(keyExchange);
|
|
_logger.Log(LogType.Info, "Sent KeyExchange acknowledgment/response.");
|
|
}
|
|
|
|
private void HandleOnlineAnnounce(onlineannounce announce)
|
|
{
|
|
if (_loginCallback == null)
|
|
{
|
|
_logger.Log(LogType.Warning, "Received OnlineAnnounce but not expecting it.");
|
|
return;
|
|
}
|
|
|
|
_logger.Log(LogType.Info, $"Login successful! UserID: {announce.Userid}, LocalSID: {announce.Localsid}");
|
|
_currentUserId = announce.Userid;
|
|
_localsid = announce.Localsid;
|
|
|
|
var callback = _loginCallback;
|
|
_loginCallback = null;
|
|
PostToUnityContext(() => callback?.Invoke(true));
|
|
}
|
|
|
|
private void RequestRoleListInternal(int lastHandle = -1)
|
|
{
|
|
rolelist rolelistRequest = new rolelist();
|
|
rolelistRequest.Userid = _currentUserId;
|
|
rolelistRequest.Localsid = 0;
|
|
rolelistRequest.Handle = lastHandle;
|
|
|
|
SendProtocol(rolelistRequest);
|
|
|
|
|
|
//gamedatasend gamedatasendRequest = new gamedatasend();
|
|
//gamedatasendRequest.Data = C2SCommandFactory.CreatePlayerMove();
|
|
|
|
//SendProtocol(gamedatasendRequest);
|
|
}
|
|
|
|
private void HandleRoleListResponse(RoleListResponse response)
|
|
{
|
|
if (_roleListCallback == null || _accumulatedRoles == null)
|
|
{
|
|
_logger.Log(LogType.Warning, "Received RoleListResponse but not expecting it.");
|
|
return;
|
|
}
|
|
|
|
_logger.Log(LogType.Debug,
|
|
$"Received RoleListResponse. Handle: {response.handle}, Result: {response.result}, Count: {response.rolelist.Count}");
|
|
|
|
if (response.result == 0)
|
|
{
|
|
_accumulatedRoles.AddRange(response.rolelist);
|
|
|
|
foreach (var role in response.rolelist)
|
|
{
|
|
try
|
|
{
|
|
string roleName = Encoding.UTF8.GetString(role.name.ByteArray, 0, role.name.Length);
|
|
_logger.Log(LogType.Info, $" - Role ID: {role.roleid}, Name: {roleName}, Level: {role.level}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Log(LogType.Error, $" - Error decoding role name: {ex.Message}");
|
|
_logger.LogException(ex);
|
|
}
|
|
}
|
|
|
|
if (response.handle != -1)
|
|
{
|
|
_logger.Log(LogType.Debug, $"Requesting next batch of roles (handle: {response.handle})...");
|
|
RequestRoleListInternal(response.handle);
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogType.Info, $"Finished fetching roles. Total count: {_accumulatedRoles.Count}");
|
|
var callback = _roleListCallback;
|
|
var result = _accumulatedRoles;
|
|
_roleListCallback = null;
|
|
_accumulatedRoles = null;
|
|
PostToUnityContext(() => callback?.Invoke(result));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_logger.Log(LogType.Error, $"Role list retrieval failed. Result code: {response.result}");
|
|
FailRoleListInProgress($"Role list retrieval failed (Result: {response.result})");
|
|
}
|
|
}
|
|
|
|
// --- Helper methods for failure handling ---
|
|
private void FailLoginInProgress(string reason)
|
|
{
|
|
if (_loginCallback != null)
|
|
{
|
|
_logger.Log(LogType.Error, $"Login failed: {reason}");
|
|
var callback = _loginCallback;
|
|
_loginCallback = null;
|
|
PostToUnityContext(() => callback?.Invoke(false));
|
|
}
|
|
}
|
|
|
|
private void FailRoleListInProgress(string reason)
|
|
{
|
|
if (_roleListCallback != null)
|
|
{
|
|
_logger.Log(LogType.Error, $"Role list retrieval failed: {reason}");
|
|
var callback = _roleListCallback;
|
|
_roleListCallback = null;
|
|
_accumulatedRoles = null;
|
|
PostToUnityContext(() => callback?.Invoke(null));
|
|
}
|
|
}
|
|
|
|
// --- IDisposable Implementation ---
|
|
private bool disposedValue = false;
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!disposedValue)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (_networkManager != null)
|
|
{
|
|
_logger.Log(LogType.Info, "[DUCK] Disposing GameSession and disconnecting...");
|
|
_networkManager.ProtocolReceived -= OnProtocolReceived;
|
|
_networkManager.ErrorOccurred -= OnErrorOccurred;
|
|
_networkManager.Disconnected -= OnDisconnected;
|
|
_networkManager.Disconnect();
|
|
_networkManager.Dispose();
|
|
_networkManager = null;
|
|
}
|
|
|
|
_loginCallback = null;
|
|
_roleListCallback = null;
|
|
_accumulatedRoles = null;
|
|
}
|
|
|
|
disposedValue = true;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
// GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public bool ISPLAYERID(int id)
|
|
{
|
|
return id != 0 && (id & 0x80000000) == 0;
|
|
}
|
|
public bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0);
|
|
public bool ISMATTERID(int id) => ((id) & 0xC0000000) == 0xC0000000;
|
|
private byte[] GetBytes(byte[] bytes, int length, int index)
|
|
{
|
|
byte[] arrByteData = new byte[length];
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
arrByteData[i] = bytes[i + index];
|
|
}
|
|
|
|
return arrByteData;
|
|
}
|
|
|
|
public void c2s_CmdPlayerMove(in Vector3 vCurPos, in Vector3 vDest,
|
|
int iTime, float fSpeed, int iMoveMode, ushort wStamp)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data =
|
|
C2SCommandFactory.CreatePlayerMove(vCurPos, vDest, (ushort)iTime, fSpeed, (byte)iMoveMode, wStamp);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_SendCmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data =
|
|
C2SCommandFactory.CreatePlayerCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
|
|
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdCastInstantSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data =
|
|
C2SCommandFactory.CreatePlayerCastInstantSkill(idSkill, byPVPMask, iNumTarget, aTargets);
|
|
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_CmdCastPosSkill(int idSkill, Vector3 vDest, byte byPVPMask, int iNumTarget, int aTargets)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data =
|
|
C2SCommandFactory.CreatePlayerCastPosSkill(idSkill, vDest, byPVPMask, iNumTarget, aTargets);
|
|
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_SendCmdNotifyForceAttack(int iForceAttack, byte refuseBless)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNotifyForceAttack(iForceAttack, refuseBless);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_SendCmdContinueAction()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.CONTINUE_ACTION);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Client logout request. Mirrors original client behavior:
|
|
/// - outType=1: back to select role
|
|
/// - outType=0: logout account
|
|
/// </summary>
|
|
public void SendPlayerLogout(int outType, Action complete = null)
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType);
|
|
SendProtocol(g, complete);
|
|
}
|
|
public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode,
|
|
byte byDir, ushort wStamp, int iTime)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data =
|
|
C2SCommandFactory.CreatePlayerStop(vDest, fSpeed, (byte)iMoveMode, byDir, wStamp, (ushort)iTime);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_CmdSendEnterPKPrecinctint()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.ENTER_PK_PROTECTED);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
|
|
{
|
|
publicchat publicChat = new publicchat();
|
|
publicChat.Channel = cChannel;
|
|
publicChat.Roleid = m_iCharID;
|
|
|
|
byte[] unicodeBytes = Encoding.Unicode.GetBytes(szMsg);
|
|
publicChat.Msg.Replace(unicodeBytes);
|
|
_logger.Log(LogType.Warning, $"HoangDev : publicChat {publicChat}");
|
|
SendProtocol(publicChat);
|
|
}
|
|
public void LoadConfigData()
|
|
{
|
|
getuiconfig p = new getuiconfig();
|
|
p.Roleid = m_iCharID;
|
|
SendProtocol(p);
|
|
}
|
|
private void SetCharacterID(int iCharID)
|
|
{
|
|
m_iCharID = iCharID;
|
|
}
|
|
|
|
private void OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
|
|
{
|
|
chatmessage p = (chatmessage)pProtocol;
|
|
|
|
string strTemp = System.Text.Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
|
|
|
|
_logger.Log(LogType.Warning, $"HoangDev : OnPrtcChatMessage :{strTemp}");
|
|
EventBus.Publish(new ChatMessageEvent(strTemp));
|
|
}
|
|
|
|
public struct ChatMessageEvent
|
|
{
|
|
public string context;
|
|
|
|
public ChatMessageEvent(string context)
|
|
{
|
|
this.context = context;
|
|
}
|
|
}
|
|
public void OnPrtcGetConfigRe(Protocol pProtocol)
|
|
{
|
|
getuiconfig_re p = (getuiconfig_re)pProtocol;
|
|
if (p.Result != (int)ErrCode.ERR_SUCCESS)
|
|
BMLogger.LogError("CECGameSession::OnPrtcGetConfigRe, link return error code of " + p.Result);
|
|
else
|
|
{
|
|
if (!CECGameRun.Instance.LoadConfigsFromServer(p.UiConfig.RawBuffer, p.UiConfig.Size))
|
|
{
|
|
// if load failed then use current setting directly
|
|
//TODO : fix later
|
|
EC_Game.GetConfigs().ApplyUserSetting();
|
|
}
|
|
|
|
// Now, Get config data request is sent after all host initial data ready.
|
|
// so when we receive this reply, we can do some last work before game
|
|
// really starts. Maybe it's not the best place to do these work, but
|
|
// now we do it here.
|
|
// Enalbe game UI
|
|
CECGameUIMan pGameUI = (CECGameUIMan)EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
|
|
if (pGameUI != null)
|
|
{
|
|
pGameUI.EnableUI(true);
|
|
|
|
// Get referral name for adding friend or other display
|
|
//TODO: a Hung lam phan select role info di
|
|
/* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
|
|
if (info.referrer_role > 0)
|
|
GetPlayerBriefInfo(1, info.referrer_role, 2);*/
|
|
}
|
|
|
|
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
|
|
pHost.OnAllInitDataReady();
|
|
|
|
/* if (pHost.IsGM())
|
|
{
|
|
CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap");
|
|
pDlgCountryMap.GetConfig();
|
|
}
|
|
|
|
g_pGame.GetConfigs().ApplyOptimizeSetting();
|
|
|
|
if (g_pGame.GetConfigs().IsMiniClient())
|
|
CECMCDownload::GetInstance().SendGetDownloadOK();*/
|
|
}
|
|
}
|
|
private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
|
|
{
|
|
playerbaseinfo_re p = (playerbaseinfo_re)pProtocol;
|
|
BMLogger.Log($"OnPrtcPlayerBaseInfoRe: {p.Roleid} {p.Player.cls} {p.Player.gender}");
|
|
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERBASEINFO, MANAGER_INDEX.MAN_PLAYER, -1, p);
|
|
}
|
|
|
|
public void c2s_CmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(idTask, idStorage, idRefreshItem);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdGetAllData(bool byPack, bool byEquip, bool byTask)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
|
|
gamedatasend.Data = C2SCommandFactory.CreateGetAllDataCommand(byPack, byEquip, byTask);
|
|
_logger.Log(LogType.Warning, $"[Dat]- SendCmdGetAllData {byPack},{byEquip},{byTask}");
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevHello(int nid)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevHelloDataCommand(nid);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_CmdNormalAttack(byte byPVPMask)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNormalAttackDataCmd(byPVPMask);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdCancelAction()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.CANCEL_ACTION);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_CmdUnselect()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(CSNetwork.C2SCommand.CommandID.UNSELECT);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdSelectTarget(int idTarget)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateSelectTarget(idTarget);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevWaypoint()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevWaypointCmd(NPC_service_type.GP_NPCSEV_WAYPOINT, 0);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_SendCmdNPCSevMakeItem(int idSkill, int idItem, uint dwCount)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevMakeItemCmd(idSkill, idItem, dwCount);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void GetRoleBaseInfo(int iNumRole, List<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)
|
|
{
|
|
// int iNumLimit = 250;
|
|
// int iCount = 0;
|
|
|
|
// while (iCount < iNumID)
|
|
// {
|
|
// int iNumSend = iNumLimit;
|
|
// if (iCount + iNumLimit > iNumID)
|
|
// iNumSend = iNumID - iCount;
|
|
|
|
// if (iNumSend > 0)
|
|
// {
|
|
// }
|
|
// }
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevAcceptTask(int idTask, int idStorage, int idRefreshItem)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateCmdNPCSevAcceptTask(
|
|
idTask,
|
|
idStorage,
|
|
idRefreshItem);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevReturnTask(int idTask, int iChoice)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevReturnTaskCmd(
|
|
idTask,
|
|
iChoice);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevTaskMatter(int idTask)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevTaskMatterCmd(idTask);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevLearnSkill(int idSkill)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevLearnSkillCmd(idSkill);
|
|
BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill gamedatasend.Data : " + gamedatasend.Data.Size);
|
|
BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill idSkill : " + idSkill);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevBuy(int itemNum, C2SCommand.npc_trade_item[] items)
|
|
{
|
|
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
|
return;
|
|
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevBuyCmd(itemNum, items);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevSell(int itemNum, C2SCommand.npc_sell_item[] items)
|
|
{
|
|
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
|
return;
|
|
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevSellCmd(itemNum, items);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void GetRoleCustomizeData(int iNumRole, List<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_SendCmdTeamKickMember(int idMember)
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamKickMemberCommand(idMember);
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_SendCmdTeamLeaveParty()
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamLeavePartyCommand();
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_SendCmdTeamDismissParty()
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamDismissPartyCommand();
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_SendCmdTeamSetPickupFlag(short pickupFlag)
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamSetPickupFlagCommand(pickupFlag);
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_SendCmdTeamChangeLeader(int idNewLeader)
|
|
{
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamChangeLeaderCommand(idNewLeader);
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_SendCmdTeamMemberPos(int count, int[] memberIds)
|
|
{
|
|
if (memberIds == null || count <= 0 || count > memberIds.Length) return;
|
|
var g = new gamedatasend();
|
|
g.Data = C2SCommandFactory.CreateTeamMemberPosCommand(count, memberIds);
|
|
SendProtocol(g);
|
|
}
|
|
|
|
public void c2s_CmdGoto(float x, float y, float z)
|
|
{
|
|
c2s_SendCmdGoto(x, y, z);
|
|
}
|
|
|
|
// Send C2S::GOTO command data
|
|
void c2s_SendCmdGoto(float x, float y, float z)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateGoToCommed( x, y, z);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdUseItem(byte byPackage, byte bySlot, int tid, byte byCount)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateUseItemCmd(byPackage, bySlot, tid, byCount);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdGetExtProps()
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.GET_EXT_PROP);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdGivePresent(int roleid, int mail_id, int goods_id, int goods_index, int goods_slot)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateGivePresentCmd(roleid, mail_id, goods_id, goods_index, goods_slot);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdEnterSanctuary(int id)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateEnterSanctuaryCmd(id);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdEnterInstance(int iTransIdx, int idInst)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateEnterInstanceCmd(iTransIdx, idInst);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdActiveRushFly(bool bActive)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateActiveRushFlyCmd(bActive);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdPetCtrl(int idTarget, int cmd, byte[] pParamBuf, int iParamLen)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreatePetCtrlCmd(idTarget, cmd, pParamBuf, iParamLen);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdQueryFactionPVPInfo(int faction_id)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateQueryFactionPVPInfo(faction_id);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void SendCmdPetCtrl(int idTarget, int cmd, object pParamBuf, int iParamLen)
|
|
{
|
|
m_CmdCache.SendCmdPetCtrl(idTarget, cmd, (byte[])pParamBuf, iParamLen);
|
|
}
|
|
|
|
public void c2s_SendCmdPetSummon(int iPetIdx)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreatePetSummon(iPetIdx);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdNPCSevEmbed(ushort wStoneIdx, ushort wEquipIdx, int tidStone, int tidEquip)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevEmbedCmd(wStoneIdx, wEquipIdx, tidStone, tidEquip);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_SendCmdGetItemInfo(byte byPackage, byte bySlot)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateGetItemInfoCmd(byPackage, bySlot);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
|
|
public void c2s_SendCmdSetStatusPts(int vitality, int energy, int strength, int agility)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateSetStatusPtCmd(vitality, energy, strength, agility);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
public void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip)
|
|
{
|
|
gamedatasend gamedatasend = new gamedatasend();
|
|
gamedatasend.Data = C2SCommandFactory.CreateNPCSevClearEmbeddedChipCmd(iEquipIdx, tidEquip);
|
|
SendProtocol(gamedatasend);
|
|
}
|
|
}
|
|
} |