Files
test/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
T
2025-11-05 18:13:46 +07:00

964 lines
39 KiB
C#

using System.Text;
using System;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using CommandID = CSNetwork.GPDataType.CommandID;
using System.IO;
using System.Diagnostics;
using System.Numerics;
using CSNetwork.C2SCommand;
using CSNetwork.GPDataType;
using BrewMonster;
using BrewMonster.Managers;
using BrewMonster.Scripts.Player;
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 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 RoleInfo _selectedRole;
public bool IsConnected => _networkManager?.IsConnected ?? false;
#if UNITY_EDITOR
public bool isDebug;
public bool IsDebug
{
get => isDebug;
set => isDebug = value;
}
#endif
public GameSession()
{
_networkManager = new NetworkManager();
_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 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 RequestInventoryAsync(byte byPackage, Action callback)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetInventoryDetail(byPackage);
SendProtocol(gamedatasendRequest, callback);
}
public void RequestQueryPlayerCash()
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateQueryPlayerCash();
SendProtocol(gamedatasendRequest);
}
public void RequestCheckSecurityPassWd(string password)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateCheckSecurityPassWd(password);
SendProtocol(gamedatasendRequest);
}
public void RequestEquipItem(byte iIvtrIdx, byte iEquipIdx, Action callback)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateEquipItem(iIvtrIdx, iEquipIdx);
SendProtocol(gamedatasendRequest, callback);
}
public void RequestMallShopping(uint count, CMD_MallShopping.goods[] goodsArray)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetMallShopping(count, goodsArray);
SendProtocol(gamedatasendRequest);
}
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})");
_networkManager.Send(protocol);
complete?.Invoke();
}
else
{
_logger.Log(LogType.Warning, $"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_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;
default:
_logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
break;
}
}
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;
BMLogger.LogError($"### GameDataSend: CMDID {pCmdHeader}");
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:
// OnMsgPlayerInfo(-1, pDataBuf, pCmdHeader);
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERINFO, (int)MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf,
pCmdHeader, iHostID, _selectedRole);
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.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:
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.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_PLAYER, 0, pDataBuf,
pCmdHeader);
break;
case CommandID.HOST_ATTACKRESULT:
_logger.Info($"HOST_ATTACKRESULT: " + pCmdHeader);
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATKRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
pCmdHeader);
break;
case CommandID.HOST_ATTACKED:
_logger.Info($"HOST_ATTACKED: " + pCmdHeader);
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ATTACKED, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf,
pCmdHeader);
break;
case CommandID.ERROR_MESSAGE:
_logger.Info($"### GameDataSend: ERROR_MESSAGE: {BitConverter.ToInt32(pDataBuf, 0)}");
cmd_error_msg pCmd = GPDataTypeHelper.FromBytes<cmd_error_msg>(pDataBuf);
BMLogger.LogError("hOANGdEV : ERROR_MESSAGE pCmd.iMessage!=0 " + pCmd.iMessage);
if (pCmd.iMessage != 0)
{
string szMsg = m_ErrorMsgs.GetWideString(pCmd.iMessage);
if (string.IsNullOrEmpty(szMsg))
BMLogger.LogError("SERVER - unknown error !");
//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 idObjMove1 = BitConverter.ToInt32(arrByteData1);
if (ISPLAYERID(idObjMove1))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERDISAPPEAR, MANAGER_INDEX.MAN_PLAYER, -1,
pDataBuf, pCmdHeader);
else if (ISNPCID(idObjMove1))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCDISAPPEAR, MANAGER_INDEX.MAN_NPC, 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:
{
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;
}
}
private void HandleSelectRoleResponse(SelectRole_Re protocol)
{
_logger.Info($"Select role response {protocol.result}");
_selectRoleCallback?.Invoke(_selectedRole);
}
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");
}
// --- Protocol Handling Logic ---
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;
var callback = _loginCallback;
_loginCallback = null;
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;
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;
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;
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 & unchecked((int)0x80000000)) != 0)
&& ((id & 0x40000000) == 0);
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_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 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);
}
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 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_CmdCancelAction()
{
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_CmdSelectTarget(int idTarget)
{
// Set selection first before server returns, so as to reduce the player waiting time.
CECHostPlayer pHost = EC_ManMessageMono.Instance.GetECManPlayer.GetHostPlayer();
pHost.SetSelectedTarget(idTarget);
if (m_idLastSelTarget != idTarget)
{
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data = C2SCommandFactory.CreateSelectTarget(idTarget);
SendProtocol(gamedatasend);
m_idLastSelTarget = idTarget;
}
}
public void c2s_SendCmdNPCSevWaypoint()
{
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data = C2SCommandFactory.CreateNPCSevWaypointCmd(NPC_service_type.GP_NPCSEV_WAYPOINT, 0);
SendProtocol(gamedatasend);
}
}
}