Add PROTOCOL_ADDFRIEND and enable add friend request

This commit is contained in:
HungDK
2026-03-12 20:58:29 +07:00
parent a41103c15d
commit 2015dbf73e
12 changed files with 253 additions and 5 deletions
@@ -509,7 +509,7 @@ MonoBehaviour:
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 0
m_Interactable: 1
m_TargetGraphic: {fileID: 3492245093881047436}
m_OnClick:
m_PersistentCalls:
@@ -51,6 +51,12 @@ namespace CSNetwork
/// <summary>Raised when server sends PROTOCOL_PLAYERLOGOUT(69).</summary>
public event Action<playerlogout> PlayerLogoutReceived;
/// <summary>Raised when server sends RPC_ADDFRIENDRQST(204) — another player wants to add you as friend. Args: srcroleid, askerName.</summary>
public event Action<int, string> FriendRequestReceived;
/// <summary>Raised when server sends PROTOCOL_ADDFRIEND_RE(203). Args: retcode (0=success), message.</summary>
public event Action<byte, string> AddFriendResultReceived;
/// <summary>Raised when the underlying network disconnects.</summary>
public event Action Disconnected;
@@ -587,6 +593,14 @@ namespace CSNetwork
}
break;
case ProtocolType.RPC_ADDFRIENDRQST:
OnAddFriendRqst((addfriendrqst)protocol);
break;
case ProtocolType.PROTOCOL_ADDFRIEND_RE:
OnAddFriendRe((addfriend_re)protocol);
break;
default:
_logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
break;
@@ -601,6 +615,43 @@ namespace CSNetwork
// PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
}
private void OnAddFriendRqst(addfriendrqst p)
{
string askerName = "";
if (p.Srcname != null && p.Srcname.Size > 0)
askerName = System.Text.Encoding.Unicode.GetString(p.Srcname.ToArray(), 0, p.Srcname.Size);
PostToUnityContext(() => FriendRequestReceived?.Invoke(p.Srcroleid, askerName));
}
private void OnAddFriendRe(addfriend_re p)
{
string friendName = "";
if (p.info?.name != null && p.info.name.Size > 0)
friendName = System.Text.Encoding.Unicode.GetString(p.info.name.ToArray(), 0, p.info.name.Size);
string msg = p.retcode == 0
? $"Add friend success: {friendName}"
: $"Add friend failed: {GetAddFriendRetcodeMessage(p.retcode)}";
PostToUnityContext(() => AddFriendResultReceived?.Invoke(p.retcode, msg));
}
/// <summary>FS_ERR server retcodes for addfriend_re.</summary>
private static string GetAddFriendRetcodeMessage(byte retcode)
{
switch (retcode)
{
case 0: return "Success";
case 1: return "Player is offline";
case 2: return "Request was refused";
case 3: return "Timeout";
case 4: return "No remaining space";
case 5: return "Not found";
case 6: return "Invalid parameter";
case 7: return "Duplicate entry";
case 8: return "Friend list has not been retrieved yet";
default: return $"retcode={retcode}";
}
}
// void CECGameSession::OnPrtcPlayerLogout(GNET::Protocol* pProtocol)
// {
// using namespace GNET;
@@ -2039,6 +2090,29 @@ namespace CSNetwork
SendProtocol(g);
}
/// <summary>Send PROTOCOL_ADDFRIEND(202). Port of CECGameSession::friend_Add(idPlayer, szName).</summary>
public void Friend_Add(int idPlayer, string name)
{
var p = new addfriend();
p.Srcroleid = m_iCharID;
p.Dstroleid = idPlayer;
if (!string.IsNullOrEmpty(name))
p.Dstname = new Octets(System.Text.Encoding.Unicode.GetBytes(name));
else
p.Dstname = new Octets();
p.Srclsid = (int)_localsid;
SendProtocol(p);
}
/// <summary>Send PROTOCOL_GETFRIENDS(206). Port of CECGameSession::friend_GetList().</summary>
public void Friend_GetList()
{
var p = new getfriends();
p.Roleid = m_iCharID;
p.Localsid = (int)_localsid;
SendProtocol(p);
}
public void c2s_SendCmdTeamKickMember(int idMember)
{
var g = new gamedatasend();
@@ -2047,7 +2121,7 @@ namespace CSNetwork
}
public void c2s_SendCmdTeamLeaveParty()
{
{
var g = new gamedatasend();
g.Data = C2SCommandFactory.CreateTeamLeavePartyCommand();
SendProtocol(g);
@@ -109,6 +109,23 @@ namespace CSNetwork
_position += 4;
}
/// <summary>Write int32 in little-endian (for GNET protocol compatibility with C++ client/server).</summary>
public void WriteInt32LE(int value)
{
var bytes = BitConverter.GetBytes(value);
_octets.Insert(_position, bytes);
_position += 4;
}
/// <summary>Read int32 in little-endian (for GNET protocol compatibility with C++ client/server).</summary>
public int ReadInt32LE()
{
if (_position + 4 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(4);
return BitConverter.ToInt32(bytes, 0);
}
public void Write(float value)
{
var bytes = BitConverter.GetBytes(value);
@@ -0,0 +1,43 @@
using CSNetwork.Protocols.RPCData;
namespace CSNetwork.Protocols
{
/// <summary>PROTOCOL_ADDFRIEND_RE(203). Port of GNET::AddFriend_Re (inl/addfriend_re).</summary>
public class addfriend_re : Protocol
{
public byte retcode { get; set; }
public GFriendInfo info { get; set; }
public uint srclsid { get; set; }
public addfriend_re() : base(ProtocolType.PROTOCOL_ADDFRIEND_RE)
{
info = new GFriendInfo();
}
public override Protocol Clone() => new addfriend_re
{
retcode = retcode,
info = info != null ? new GFriendInfo { rid = info.rid, cls = info.cls, gid = info.gid, name = new Octets(info.name?.ToArray() ?? System.Array.Empty<byte>()) } : new GFriendInfo(),
srclsid = srclsid
};
public override void Marshal(OctetsStream os)
{
os.Write(retcode);
if (info != null) info.Marshal(os); else new GFriendInfo().Marshal(os);
os.Write(srclsid);
}
public override void Unmarshal(OctetsStream os)
{
retcode = os.ReadByte();
info = new GFriendInfo();
info.Unmarshal(os);
srclsid = os.ReadUInt32();
}
public override int PriorPolicy() => 1;
public override bool SizePolicy(int size) => size <= 128;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 158e35cf46fa51a4cba4fa7485fdb534
@@ -0,0 +1,42 @@
using System;
namespace CSNetwork.Protocols
{
/// <summary>RPC_ADDFRIENDRQST(204). Port of GNET AddFriendRqstArg; server notifies target of friend request.</summary>
public class addfriendrqst : Protocol
{
public int Srcroleid { get; set; }
public Octets Srcname { get; set; }
public uint Dstlsid { get; set; }
public addfriendrqst() : base(ProtocolType.RPC_ADDFRIENDRQST)
{
Srcname = new Octets();
}
public override Protocol Clone() => new addfriendrqst
{
Srcroleid = Srcroleid,
Srcname = new Octets(Srcname.ToArray()),
Dstlsid = Dstlsid
};
public override void Marshal(OctetsStream os)
{
os.Write(Srcroleid);
os.Write(Srcname);
os.Write(Dstlsid);
}
public override void Unmarshal(OctetsStream os)
{
Srcroleid = os.ReadInt32();
Srcname = os.ReadOctets();
Dstlsid = os.ReadUInt32();
}
public override int PriorPolicy() => 1;
public override bool SizePolicy(int size) => size <= 256;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bf82fc1afd675674588f017b6f9e0621
@@ -0,0 +1,32 @@
namespace CSNetwork.Protocols.RPCData
{
/// <summary>Port of GNET::GFriendInfo (rpcdata/gfriendinfo).</summary>
public class GFriendInfo : IMarshallable
{
public int rid;
public byte cls;
public byte gid;
public Octets name;
public GFriendInfo()
{
name = new Octets();
}
public void Marshal(OctetsStream os)
{
os.Write(rid);
os.Write(cls);
os.Write(gid);
os.Write(name ?? new Octets());
}
public void Unmarshal(OctetsStream os)
{
rid = os.ReadInt32();
cls = os.ReadByte();
gid = os.ReadByte();
name = os.ReadOctets();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a0bf31ab9d8853a479215aa7e1fa5e94
@@ -173,7 +173,9 @@ namespace BrewMonster.Network
// Subscribe to unexpected disconnects
_gameSession.Disconnected += OnUnexpectedDisconnect;
_gameSession.FriendRequestReceived += OnFriendRequestReceived;
_gameSession.AddFriendResultReceived += OnAddFriendResultReceived;
_isInitialized = true;
DontDestroyOnLoad(gameObject);
@@ -184,6 +186,8 @@ namespace BrewMonster.Network
// Tell LoginScene what to show next.
LogoutFlowState.NextLoginEntry = entryTarget;
_gameSession.Disconnected -= OnUnexpectedDisconnect;
_gameSession.FriendRequestReceived -= OnFriendRequestReceived;
_gameSession.AddFriendResultReceived -= OnAddFriendResultReceived;
EC_ManMessageMono.Instance.CECNPCMan.Release();
if (clearSavedCreds)
@@ -619,6 +623,16 @@ namespace BrewMonster.Network
{
Instance._gameSession.c2s_SendCmdDuelReply(accept, idInviter);
}
/// <summary>Send PROTOCOL_ADDFRIEND(202). Port of CECGameSession::friend_Add.</summary>
public static void Friend_Add(int idPlayer, string name)
{
Instance._gameSession.Friend_Add(idPlayer, name ?? "");
}
/// <summary>Send PROTOCOL_GETFRIENDS(206). Port of CECGameSession::friend_GetList().</summary>
public static void Friend_GetList()
{
Instance._gameSession.Friend_GetList();
}
public static void c2s_CmdTeamKickMember(int idMember)
{
Instance._gameSession.c2s_SendCmdTeamKickMember(idMember);
@@ -694,6 +708,25 @@ namespace BrewMonster.Network
/// <summary>
/// Handles unexpected server disconnections. Shows a message box and returns to login.
/// </summary>
private void OnFriendRequestReceived(int srcroleid, string askerName)
{
string name = string.IsNullOrEmpty(askerName) ? ("Player " + srcroleid) : askerName;
CECUIManager.Instance?.ShowMessageBox(
title: "Friend Request",
message: $"{name} wants to add you as a friend.",
messageBoxType: MessageBoxType.BothYesNoButton,
onClickedYes: () => { /* TODO: accept and call friend_AddResponse */ },
onClickedNo: () => { /* TODO: refuse */ });
}
private void OnAddFriendResultReceived(byte retcode, string message)
{
CECUIManager.Instance?.ShowMessageBox(
title: retcode == 0 ? "Friend added" : "Add friend failed",
message: message,
messageBoxType: MessageBoxType.YesButton);
}
private void OnUnexpectedDisconnect()
{
// If this was an intentional disconnect (logout), skip UI
@@ -78,8 +78,8 @@ namespace BrewMonster.UI
void OnAddFriend(int characterId)
{
Debug.Log("OnAddFriend: " + characterId);
// TODO: c2s add friend when available
string name = EC_ManMessageMono.Instance?.GetECManPlayer?.GetElsePlayer(characterId)?.GetName() ?? "";
UnityGameSession.Friend_Add(characterId, name);
}
void OnDuel(int characterId)
@@ -370,6 +370,7 @@ namespace BrewMonster.UI
await Task.Delay(1000);
UnityGameSession.RequestCheckSecurityPassWd("");
await Task.Delay(1000);
UnityGameSession.Friend_GetList(); // C++ friend_GetList(); required before Add Friend
}
//private void OnInventoryReceived(List<InventoryItem> inventoryData)