using System.Collections.Generic; using System.Text; using BrewMonster.Network; using CSNetwork; using CSNetwork.Protocols.RPCData; namespace BrewMonster { public class CECFriendMan { // ========================= // Nested Types // ========================= public struct Friend { public int Id; public int Profession; public int GroupId; public byte Status; public int Level; public int AreaId; public string Name; public bool IsGameOnline() => IsGameOnline(Status); public bool IsGTOnline() => IsGTOnline(Status); // C++ gnetdef.h: GAME_ONLINE=0x01, GT_INVISIBLE=0x80 public static bool IsGameOnline(byte s) { return (s & 0x01) != 0; } // C++: except GAME_ONLINE and GT_INVISIBLE, any other status means GT online public static bool IsGTOnline(byte s) { const byte GAME_ONLINE = 0x01; const byte GT_INVISIBLE = 0x80; return (s & unchecked((byte)~GAME_ONLINE) & unchecked((byte)~GT_INVISIBLE)) != 0; } } public class FRIEND_EX { public bool NewFriendPlaceHolder; public int Uid; public int Rid; public int Level; public long LastLoginTime; public long UpdateTime; public int ReincarnationCount; public string Remarks = string.Empty; } public class SEND_INFO { public int Rid; public long SendMailTime; } public class GROUP { public string Name = string.Empty; public int GroupId; public uint Color; // nếu dùng Unity thì có thể đổi sang Color public List Friends = new(); } public class MESSAGE { public string SenderName = string.Empty; public int SenderId; public string MessageText = string.Empty; public byte Flag; } // ========================= // Fields // ========================= private List m_Groups = new(); private Dictionary m_FriendTable = new(); private List m_OfflineMsgs = new(); private List m_FriendEx = new(); private List m_SendInfo = new(); // ========================= // Operations // ========================= public bool CheckInit() => m_Groups.Count > 0; /// Reset from server getfriends_re. C++ format: status list is pairs (friendIndex, statusByte). public void ResetFromServer(List groups, List friends, List status) { m_Groups.Clear(); m_FriendTable.Clear(); m_OfflineMsgs.Clear(); m_FriendEx.Clear(); m_SendInfo.Clear(); if (groups != null) { for (int i = 0; i < groups.Count; i++) { var g = groups[i]; int gid = g?.gid ?? 0; string gname = DecodeOctets(g?.name); AddGroup(gid, string.IsNullOrEmpty(gname) ? $"Group{gid}" : gname); } } int friendCount = friends?.Count ?? 0; for (int i = 0; i < friendCount; i++) { var f = friends[i]; if (f == null) continue; int gid = f.gid; if (GetGroupByID(gid) == null) AddGroup(gid, $"Group{gid}"); string name = DecodeOctets(f.name); AddFriend(f.rid, f.cls, gid, 0, name); } // Apply status pairs: [friendIndex0, status0, friendIndex1, status1, ...] if (status != null && (status.Count % 2) == 0 && friends != null) { for (int i = 0; i < status.Count; i += 2) { int friendIndex = status[i]; byte st = status[i + 1]; if (friendIndex < 0 || friendIndex >= friends.Count) continue; var fi = friends[friendIndex]; if (fi == null) continue; ChangeFriendStatus(fi.rid, st); } } } private static string DecodeOctets(Octets o) { if (o == null || o.Size <= 0) return string.Empty; try { return Encoding.Unicode.GetString(o.ToArray(), 0, o.Size); } catch { return string.Empty; } } public Friend AddFriend(int id, int profession, int groupId, byte status, string name) { var friend = new Friend { Id = id, Profession = profession, GroupId = groupId, Status = status, Name = name }; m_FriendTable[id] = friend; var group = GetGroupByID(groupId); group?.Friends.Add(friend); if (friend.IsGameOnline()) { // Todo: Check again EC_Game.GetGameRun().AddPlayerName(id, name); } return friend; } public void RemoveFriend(int idFriend) { if (!m_FriendTable.TryGetValue(idFriend, out var friend)) return; m_FriendTable.Remove(idFriend); var group = GetGroupByID(friend.GroupId); group?.Friends.Remove(friend); } public void RemoveAllFriends() { m_FriendTable.Clear(); foreach (var group in m_Groups) group.Friends.Clear(); } public void ChangeFriendStatus(int idFriend, byte status) { if (!m_FriendTable.TryGetValue(idFriend, out var friend)) return; friend.Status = status; m_FriendTable[idFriend] = friend; var group = GetGroupByID(friend.GroupId); if (group == null) return; for (int i = 0; i < group.Friends.Count; i++) { if (group.Friends[i].Id != idFriend) continue; group.Friends[i] = friend; break; } } public void ChangeFriendGroup(int idFriend, int newGroupId) { if (!m_FriendTable.TryGetValue(idFriend, out var friend)) return; var oldGroup = GetGroupByID(friend.GroupId); oldGroup?.Friends.Remove(friend); friend.GroupId = newGroupId; var newGroup = GetGroupByID(newGroupId); newGroup?.Friends.Add(friend); } public Friend? GetFriendByID(int idFriend) { m_FriendTable.TryGetValue(idFriend, out var friend); return friend; } public Friend? GetFriendByName(string name) { foreach (var friend in m_FriendTable.Values) { if (friend.Name == name) return friend; } return null; } public void SetFriendLevel(int idFriend, int level) { if (m_FriendTable.TryGetValue(idFriend, out var friend)) friend.Level = level; } public void SetFriendArea(int idFriend, int areaId) { if (m_FriendTable.TryGetValue(idFriend, out var friend)) friend.AreaId = areaId; } // ========================= // Group Operations // ========================= public bool AddGroup(int idGroup, string name) { if (GetGroupByID(idGroup) != null) return false; m_Groups.Add(new GROUP { GroupId = idGroup, Name = name }); return true; } public void RemoveGroup(int idGroup) { m_Groups.RemoveAll(g => g.GroupId == idGroup); } public void ChangeGroupName(int idGroup, string name) { var group = GetGroupByID(idGroup); if (group != null) group.Name = name; } public void SetGroupColor(int idGroup, uint color) { var group = GetGroupByID(idGroup); if (group != null) group.Color = color; } public int GetGroupNum() => m_Groups.Count; public GROUP GetGroupByIndex(int index) { if (index < 0 || index >= m_Groups.Count) return null; return m_Groups[index]; } public GROUP GetGroupByID(int id) { return m_Groups.Find(g => g.GroupId == id); } // ========================= // Offline Messages // ========================= public int GetOfflineMsgNum() => m_OfflineMsgs.Count; public MESSAGE GetOfflineMsg(int index) { if (index < 0 || index >= m_OfflineMsgs.Count) return null; return m_OfflineMsgs[index]; } public void RemoveAllOfflineMsgs() { m_OfflineMsgs.Clear(); } } }