using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using PhoenixLib.MultiLanguage; using WingsAPI.Data.Families; using WingsEmu.Core; using WingsEmu.Game._i18n; using WingsEmu.Game.Extensions; using WingsEmu.Game.Families; using WingsEmu.Game.Families.Configuration; using WingsEmu.Game.Items; using WingsEmu.Game.Managers; using WingsEmu.Game.Managers.StaticData; using WingsEmu.Game.Networking; using WingsEmu.Packets.Enums.Chat; using WingsEmu.Packets.Enums.Families; namespace WingsAPI.Game.Extensions.Families { public static class FamilyPacketExtensions { #region Send Packets public static void SendFmpPacket(this IFamily family, ISessionManager sessionManager, IItemsManager itemsManager) { if (family == null) { return; } string packet = family.GenerateFmpPacket(itemsManager); foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); session?.SendPacket(packet); } } public static void SendFmpPacket(this IClientSession session, IItemsManager itemsManager) { IFamily family = session.PlayerEntity.Family; if (family == null) { return; } session.SendPacket(family.GenerateFmpPacket(itemsManager)); } public static string GenerateFmpPacket(this IFamily family, IItemsManager itemsManager) { var stringBuilder = new StringBuilder("fmp"); Dictionary toShow = new(); if (family?.Upgrades == null) { return stringBuilder.ToString(); } foreach ((int upgradeId, FamilyUpgrade upgrade) in family.Upgrades) { IGameItem item = itemsManager.GetItem(upgradeId); if (item == null) { continue; } // Get the highest upgradeLevel by FamilyUpgradeType FamilyUpgradeType type = Enum.Parse(item.Data[2].ToString()); byte upgradeLevel = (byte)item.Data[3]; if (!toShow.TryGetValue(type, out (byte level, FamilyUpgrade familyUpgrade) familyUpgrade)) { familyUpgrade = (upgradeLevel, upgrade); toShow[type] = familyUpgrade; continue; } if (upgradeLevel <= familyUpgrade.level) { continue; } familyUpgrade = (upgradeLevel, upgrade); toShow[type] = familyUpgrade; } foreach ((FamilyUpgradeType _, (byte _, FamilyUpgrade familyUpgrade)) in toShow) { stringBuilder.AppendFormat(" {0}|{1}", familyUpgrade.Id, (byte)familyUpgrade.State); } return stringBuilder.ToString(); } public static void SendFmiPacket(this IFamily family, ISessionManager sessionManager) { if (family == null) { return; } string packet = family.GenerateFmiPacket(); foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); session?.SendPacket(packet); } } public static void SendFmiPacket(this IClientSession session) { IFamily family = session.PlayerEntity.Family; if (family == null) { return; } session.SendPacket(family.GenerateFmiPacket()); } public static string GenerateFmiPacket(this IFamily family) { var stringBuilder = new StringBuilder("fmi"); // iterate over mission config & check level restrictions DateTime resetDate = DateTime.UtcNow.Date; foreach ((int missionId, FamilyMissionDto upgrade) in family.Mission) { // progressType => 1 = Completed // progressType => 2 = InProgress int progressType = upgrade.CompletionDate.HasValue && upgrade.CompletionDate > resetDate ? 1 : 2; string thirdArgument = progressType == 1 && upgrade.CompletionDate.HasValue ? upgrade.CompletionDate.Value.ToString("yyyyMMdd") : upgrade.Count.ToString(); stringBuilder.AppendFormat(" 0|{0}|{1}|{2}|{3}", missionId, progressType, thirdArgument, upgrade.CompletionCount); } foreach ((int achievementId, FamilyAchievementProgressDto upgrade) in family.AchievementProgress) { stringBuilder.AppendFormat(" 1|{0}|2|{1}|0", achievementId, upgrade.Count); } foreach ((int achievementId, FamilyAchievementCompletionDto upgrade) in family.Achievements) { stringBuilder.AppendFormat(" 1|{0}|1|{1:yyyyMMdd}|0", achievementId, upgrade.CompletionDate); } return stringBuilder.ToString(); } public static void SendFamilyLevelUpMessageToMembers(Family family, ISessionManager sessionManager, IGameLanguageService languageService, FamilyConfiguration familyConfiguration) { if (family == null) { return; } foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } session.RefreshFamilyInfo(family, familyConfiguration); session.BroadcastGidx(family, languageService); session.SendMsg(languageService.GetLanguageFormat(GameDialogKey.FAMILY_SHOUTMESSAGE_LEVEL_UP, session.UserLanguage, family.Level.ToString()), MsgMessageType.Middle); } } public static void SendFamilyNoticeMessage(Family family, ISessionManager sessionManager, FamilyConfiguration familyConfiguration) { if (family == null) { return; } string message = family.Message; if (string.IsNullOrEmpty(message)) { return; } foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); session?.SendInfo("--- Family Message ---\n" + message); session?.RefreshFamilyInfo(family, familyConfiguration); } } public static void SendFamilyInfoToMembers(Family family, ISessionManager sessionManager, FamilyConfiguration familyConfiguration) { if (family == null) { return; } foreach (FamilyMembership member in family.Members.ToArray()) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); session?.RefreshFamilyInfo(family, familyConfiguration); } } public static void SendFamilyMembersAuthorityToMembers(Family family, ISessionManager sessionManager, FamilyConfiguration familyConfiguration) { if (family == null) { return; } IReadOnlyList packets = null; foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } packets ??= new List { GenerateFamilyMembersPacket(sessionManager, family), GenerateFamilyMembersMessagesPacket(family) }; session.RefreshFamilyInfo(family, familyConfiguration); session.SendPackets(packets); } } public static void SendFamilyMembersInfoToMembers(IFamily family, ISessionManager sessionManager, FamilyConfiguration familyConfiguration) { if (family == null) { return; } IReadOnlyList packets = null; foreach (FamilyMembership member in family.Members.ToArray()) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } packets ??= new List { GenerateFamilyMembersPacket(sessionManager, family), GenerateFamilyMembersMessagesPacket(family), GenerateFamilyMembersExpPacket(family) }; session.RefreshFamilyInfo(family, familyConfiguration); session.SendPackets(packets); } } public static void SendOnlineStatusToMembers(this IFamily family, ISessionManager sessionManager, long characterId, bool connecting, IGameLanguageService gameLanguage) { string packet = null; FamilyMembership familyMember = family.Members.ToArray().FirstOrDefault(x => x.CharacterId == characterId); if (familyMember == null) { return; } foreach (FamilyMembership member in family.Members.ToArray()) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } packet ??= GenerateFamilyMemberConnectionStatus(characterId, connecting); session.SendPacket(packet); if (!connecting) { continue; } string authority = gameLanguage.GetLanguage(familyMember.Authority.GetMemberLanguageKey(), session.UserLanguage); session.SendInformationChatMessage(gameLanguage.GetLanguageFormat(GameDialogKey.FAMILY_CHATMESSAGE_CHARACTER_LOGGED_IN, session.UserLanguage, familyMember.Character.Name, authority)); } } public static void SendMembersExpToMembers(IFamily family, ISessionManager sessionManager) { if (family == null) { return; } string packet = null; foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } packet ??= GenerateFamilyMembersExpPacket(family); session.SendPacket(packet); } } public static void SendMembersDailyMessages(IFamily family, ISessionManager sessionManager) { if (family == null) { return; } string packet = null; foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); if (session == null) { continue; } packet ??= GenerateFamilyMembersMessagesPacket(family); session.SendPacket(packet); } } public static void SendFamilyLogsToMembers(IFamily family, ISessionManager sessionManager) { if (family == null) { return; } string packet = GenerateFamilyHistoryUpdate(); foreach (FamilyMembership member in family.Members) { IClientSession session = sessionManager.GetSessionByCharacterId(member.CharacterId); session?.SendPacket(packet); } } public static void SendFamilyLogsToMember(this IClientSession session, IFamily family) { session.SendPackets(GenerateGhisPackets(family)); } public static void SendResetFamilyInterface(this IClientSession session) { session.SendPacket(GenerateRestartFamilyInterface()); } public static void RefreshFamilyMembers(this IClientSession session, ISessionManager sessionManager, IFamily family) { if (family == null) { session.SendEmptyFamilyMembers(); return; } session.SendPacket(GenerateFamilyMembersPacket(sessionManager, family)); } public static void RefreshFamilyMembersMessages(this IClientSession session, IFamily family) { if (family == null) { session.SendEmptyFamilyMessages(); } session.SendPacket(GenerateFamilyMembersMessagesPacket(family)); } public static void RefreshFamilyMembersExp(this IClientSession session, IFamily family) { if (family == null) { session.SendEmptyFamilyExp(); return; } session.SendPacket(GenerateFamilyMembersExpPacket(family)); } public static void RefreshFamilyInfo(this IClientSession session, IFamily family, FamilyConfiguration familyConfiguration) { if (family == null) { session.SendEmptyFamilyInfo(); return; } session.SendPacket(session.GenerateFamilyInfoPacket(family, familyConfiguration)); } public static void BroadcastGidx(this IClientSession session, IFamily family, IGameLanguageService gameLanguageService) => session.CurrentMapInstance?.Broadcast(x => session.GenerateGidxPacket(gameLanguageService, family, x.UserLanguage)); public static void SendGidxPacket(this IClientSession session, IFamily family, IGameLanguageService gameLanguage) => session.SendPacket(session.GenerateGidxPacket(gameLanguage, family, session.UserLanguage)); public static void SendTargetGidxPacket(this IClientSession session, IClientSession target, IFamily family, IGameLanguageService gameLanguageService) => session.SendPacket(target.GenerateGidxPacket(gameLanguageService, family, session.UserLanguage)); public static void SendEmptyFamilyInfo(this IClientSession session) => session.SendPacket("ginfo"); public static void SendEmptyFamilyMembers(this IClientSession session) => session.SendPacket("gmbr 0"); public static void SendEmptyFamilyMessages(this IClientSession session) => session.SendPacket("gmsg"); public static void SendEmptyFamilyExp(this IClientSession session) => session.SendPacket("gexp"); #endregion #region Generate Packets public static string GenerateGidxPacket(this IClientSession session, IGameLanguageService gameLanguage, IFamily family, RegionLanguageType type) => family == null ? $"gidx 1 {session.PlayerEntity.Id} -1 - 0" : $"gidx 1 {session.PlayerEntity.Id} {family.Id}.-1 {family.Name}({gameLanguage.GetLanguage(session.PlayerEntity.GetFamilyAuthority().GetMemberLanguageKey(), type)}) {family.Level} 0|0|0"; public static IReadOnlyCollection GenerateGhisPackets(IFamily family) { var list = new List(); int index = 0; DateTime currentTime = DateTime.UtcNow; list.Add(GenerateGhisPacket(true, currentTime, family.Logs, ref index)); while (index < family.Logs.Count) { list.Add(GenerateGhisPacket(false, currentTime, family.Logs, ref index)); } return list; } public static string GenerateGhisPacket(bool initial, DateTime currentTime, IReadOnlyList logs, ref int index) { int initialIndexValue = index; var packet = new StringBuilder("ghis"); if (initial) { packet.Append(" 0"); } for (; index < logs.Count && index < initialIndexValue + 55; index++) { FamilyLogDto log = logs[index]; packet.Append(GenerateGhisSubPacket(log, currentTime)); } return packet.ToString(); } public static string GenerateGhisSubPacket(FamilyLogDto log, DateTime currentTime) { string packet = $" {(byte)log.FamilyLogType}|"; switch (log.FamilyLogType) { case FamilyLogType.DailyMessage: packet += $"{log.Actor}|{(log.Argument1 == null ? string.Empty : log.Argument1.Replace(' ', (char)0xB))}"; // memberName|message - Quarry|Hey break; case FamilyLogType.RaidWon: packet += $"{log.Actor}"; // Act4RaidType break; case FamilyLogType.RainbowBattle: packet += $"{log.Actor}|-"; // EnemyFamilyName - not used anymore because of the event rework break; case FamilyLogType.FamilyXP: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}"; // memberName|memberXp - Quarry|50000 break; case FamilyLogType.FamilyLevelUp: packet += $"{log.Actor}"; // FamilyLevel break; case FamilyLogType.LevelUp: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}"; // memberName|memberLevel - Quarry|60 break; case FamilyLogType.ItemUpgraded: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}|{log.Argument2 ?? string.Empty}"; // memberName|itemVnum|itemUpgrade - Quarry|1|10 break; case FamilyLogType.RightChanged: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}|{log.Argument2 ?? string.Empty}|{log.Argument3 ?? string.Empty}"; // memberName|FamilyAuthorityType|FamilyActionType|value - Quarry|2|1|1 break; case FamilyLogType.AuthorityChanged: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}|{log.Argument2 ?? string.Empty}"; // memberName|FamilyAuthorityType|targetMemberName - Blowa|2|Quarry break; case FamilyLogType.MemberLeave: packet += $"{log.Actor}"; // memberName break; case FamilyLogType.MemberJoin: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}"; // memberName|targetMemberName - Blowa|Quarry break; case FamilyLogType.FamilyMission: packet += $"{log.Actor}"; // FamilyMissionItemVnum break; case FamilyLogType.FamilyAchievement: packet += $"{log.Actor}"; // FamilyAchievementItemVnum break; case FamilyLogType.HeroLevelUp: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}"; // memberName|HeroLevel - Quarry|60 break; case FamilyLogType.MemberUsedItem: packet += $"{log.Actor}|{log.Argument1 ?? string.Empty}"; // memberName|ItemVnum - Quarry|5060 - used only for FXP Boosters break; } return $"{packet}|{Math.Floor((currentTime - log.Timestamp).TotalHours).ToString(CultureInfo.InvariantCulture)}"; } public static string GenerateFamilyMemberConnectionStatus(long characterId, bool connecting) => $"gcon {characterId.ToString()}|{(connecting ? 1 : 0).ToString()}"; public static string GenerateRestartFamilyInterface() => "fclear"; public static string GenerateFamilyMembersPacket(ISessionManager sessionManager, IFamily family) { var packet = new StringBuilder("gmbr 0"); DateTime actualTime = DateTime.UtcNow; foreach (FamilyMembership member in family.Members.OrderBy(x => x.JoinDate)) { bool isOnline = sessionManager.IsOnline(member.CharacterId); packet.Append($" {member.CharacterId.ToString()}|{member.JoinDate:yyMMddHH)}|{member.Character.Name}|{member.Character.Level.ToString()}|{((byte)member.Character.Class).ToString()}" + $"|{((byte)member.Authority).ToString()}|{((byte)member.Title).ToString()}|{(isOnline ? 1 : 0).ToString()}|{member.Character.HeroLevel.ToString()}" + $"|{(isOnline ? -1 : Math.Floor((actualTime - member.LastOnlineDate).TotalHours)).ToString(CultureInfo.InvariantCulture)}"); } return packet.ToString(); } public static string GenerateFamilyMembersMessagesPacket(IFamily family) { var packet = new StringBuilder("gmsg"); foreach (FamilyMembership member in family.Members) { if (string.IsNullOrEmpty(member.DailyMessage)) { continue; } packet.Append($" {member.CharacterId.ToString()}|{member.DailyMessage.Replace(" ", " ")}"); } return packet.ToString(); } public static string GenerateFamilyMembersExpPacket(IFamily family) { var packet = new StringBuilder("gexp"); foreach (FamilyMembership member in family.Members.ToArray()) { packet.Append($" {member.CharacterId.ToString()}|{member.Experience.ToString()}"); } return packet.ToString(); } public static string GenerateFamilyInfoPacket(this IClientSession session, IFamily family, FamilyConfiguration familyConfiguration) { if (family?.Head == null || session.PlayerEntity.Family == null) { return "ginfo"; } Range levelInfo = familyConfiguration.GetRangeByFamilyXp(family.Experience); bool hasLevelInfo = levelInfo != null; long refinedMinXp = hasLevelInfo ? family.Experience - levelInfo.Minimum : 0; long refinedMaxXp = hasLevelInfo ? levelInfo.Maximum - levelInfo.Minimum : 0; return "ginfo " + $"{family.Name} " + $"{family.Head.Character.Name} " + $"{((byte)family.HeadGender).ToString()} " + $"{family.Level.ToString()} " + $"{refinedMinXp.ToString()} " + $"{refinedMaxXp.ToString()} " + $"{family.Members.Count.ToString()} " + $"{family.GetMaximumMembershipCapacity().ToString()} " + $"{((byte)session.PlayerEntity.GetFamilyAuthority()).ToString()} " + $"{(family.AssistantCanInvite ? 1 : 0).ToString()} " + $"{(family.AssistantCanNotice ? 1 : 0).ToString()} " + $"{(family.AssistantCanShout ? 1 : 0).ToString()} " + $"{(family.AssistantCanGetHistory ? 1 : 0).ToString()} " + $"{((byte)family.AssistantWarehouseAuthorityType).ToString()} " + $"{(family.MemberCanGetHistory ? 1 : 0).ToString()} " + $"{((byte)family.MemberWarehouseAuthorityType).ToString()} " + $"{family.Message?.Replace(' ', '^') ?? string.Empty}"; } public static string GenerateFamilyHistoryUpdate() => "fhis_stc"; #endregion } }