diff --git a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs index 8afa6310..315af5d2 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs @@ -2,6 +2,7 @@ using Comfort.Common; using EFT; +using EFT.InventoryLogic; using Fika.Core.Coop.Players; using Fika.Core.Networking; using JetBrains.Annotations; diff --git a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs index 5377f2b3..6e37e59e 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs @@ -6,6 +6,7 @@ using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using JetBrains.Annotations; using System.IO; using UnityEngine; @@ -15,6 +16,7 @@ namespace Fika.Core.Coop.ClientClasses public sealed class CoopClientInventoryController(Player player, Profile profile, bool examined) : Player.PlayerOwnerInventoryController(player, profile, examined) { public override bool HasDiscardLimits => false; + ManualLogSource BepInLogger { get; set; } = BepInEx.Logging.Logger.CreateLogSource(nameof(CoopClientInventoryController)); @@ -39,8 +41,20 @@ public override void Execute(GClass2854 operation, [CanBeNull] Callback callback // Do not replicate picking up quest items, throws an error on the other clients if (operation is GClass2856 pickupOperation) { - if (pickupOperation.Item.Template.QuestItem) + Item lootedItem = pickupOperation.Item; + if (lootedItem.Template.QuestItem) { + if (CoopPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + { + if (!sharedQuestController.CheckForTemplateId(lootedItem.TemplateId)) + { + sharedQuestController.AddLootedTemplateId(lootedItem.TemplateId); + + // We use templateId because each client gets a unique itemId + QuestItemPacket packet = new(CoopPlayer.Profile.Info.MainProfileNickname, lootedItem.TemplateId); + CoopPlayer.PacketSender.SendQuestItemPacket(ref packet); + } + } base.Execute(operation, callback); return; } diff --git a/Fika.Core/Coop/ClientClasses/CoopSharedQuestController.cs b/Fika.Core/Coop/ClientClasses/CoopSharedQuestController.cs index f2e035d6..eedc47a8 100644 --- a/Fika.Core/Coop/ClientClasses/CoopSharedQuestController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopSharedQuestController.cs @@ -1,4 +1,5 @@ using EFT; +using EFT.InventoryLogic; using EFT.Quests; using Fika.Core.Coop.Players; using Fika.Core.Networking.Packets; @@ -12,6 +13,7 @@ public sealed class CoopSharedQuestController(Profile profile, InventoryControll private readonly CoopPlayer player = player; private readonly List lastFromNetwork = []; private readonly List acceptedTypes = ["Kill", "Hit", "InZone", "Location"]; + private readonly HashSet lootedTemplateIds = []; public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify = true) { @@ -21,7 +23,7 @@ public override void OnConditionValueChanged(IConditionCounter conditional, EQue lastFromNetwork.Remove(condition.id); return; } - SendQuestPacket(conditional, condition.id); + SendQuestPacket(conditional, condition); } public void AddNetworkId(string id) @@ -32,14 +34,30 @@ public void AddNetworkId(string id) } } - private void SendQuestPacket(IConditionCounter conditional, string conditionId) + public void AddLootedTemplateId(string templateId) + { + if (!lootedTemplateIds.Contains(templateId)) + { + lootedTemplateIds.Add(templateId); + } + } + + public bool CheckForTemplateId(string templateId) + { + return lootedTemplateIds.Contains(templateId); + } + + private void SendQuestPacket(IConditionCounter conditional, Condition condition) { if (conditional is GClass1258 quest) { - GClass3242 counter = quest.ConditionCountersManager.GetCounter(conditionId); + GClass3242 counter = quest.ConditionCountersManager.GetCounter(condition.id); if (counter != null) { QuestConditionPacket packet = new(player.Profile.Info.MainProfileNickname, counter.Id, counter.SourceId); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogInfo("SendQuestPacket: Sending quest progress"); +#endif player.PacketSender.SendQuestPacket(ref packet); } } @@ -55,10 +73,10 @@ internal void ReceiveQuestPacket(ref QuestConditionPacket packet) GClass3242 counter = quest.ConditionCountersManager.GetCounter(packet.Id); if (counter != null) { - if (!acceptedTypes.Contains(counter.Type)) + /*if (!acceptedTypes.Contains(counter.Type)) { return; - } + }*/ counter.Value++; NotificationManagerClass.DisplayMessageNotification($"Received shared quest progression from {packet.Nickname}", @@ -67,5 +85,28 @@ internal void ReceiveQuestPacket(ref QuestConditionPacket packet) } } } + + internal void ReceiveQuestItemPacket(ref QuestItemPacket packet) + { + if (!string.IsNullOrEmpty(packet.ItemId)) + { + Item item = player.FindItem(packet.ItemId, true); + if (item != null) + { + InventoryControllerClass playerInventory = player.GClass2777_0; + GStruct414 pickupResult = InteractionsHandlerClass.QuickFindAppropriatePlace(item, playerInventory, + playerInventory.Inventory.Equipment.ToEnumerable(), + InteractionsHandlerClass.EMoveItemOrder.PickUp, true); + + if (pickupResult.Succeeded && playerInventory.CanExecute(pickupResult.Value)) + { + AddLootedTemplateId(item.TemplateId); + playerInventory.RunNetworkTransaction(pickupResult.Value); + NotificationManagerClass.DisplayMessageNotification($"{packet.Nickname} picked up {item.Name.Localized()}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + } + } + } } } diff --git a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs index c9abf3c7..69589bb2 100644 --- a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs @@ -40,6 +40,11 @@ public void SendQuestPacket(ref QuestConditionPacket packet) } + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + + } + protected void FixedUpdate() { if (!Enabled) diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index d5ac1b07..fd7c6fc2 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -50,6 +50,12 @@ public void SendQuestPacket(ref QuestConditionPacket packet) Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); } + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + Writer.Reset(); + Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + protected void FixedUpdate() { if (player == null || Writer == null) diff --git a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs index b3cbdb9b..25a034f8 100644 --- a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs @@ -21,6 +21,7 @@ public interface IPacketSender public void Init(); public void SendQuestPacket(ref QuestConditionPacket packet); + public void SendQuestItemPacket(ref QuestItemPacket packet); public void DestroyThis(); } } diff --git a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs index 9390eda4..7c07aa00 100644 --- a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs @@ -50,6 +50,11 @@ public void SendQuestPacket(ref QuestConditionPacket packet) } + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + + } + protected void Update() { if (player == null || Writer == null) diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index 9f65a902..5f9f9b15 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -54,6 +54,12 @@ public void SendQuestPacket(ref QuestConditionPacket packet) Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); } + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + protected void FixedUpdate() { if (player == null || Writer == null || Server == null) diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index 95647e38..c4f373d4 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -47,7 +47,7 @@ public class CoopPlayer : LocalPlayer public Transform RaycastCameraTransform; public int NetId; public bool IsObservedAI = false; - public Dictionary> OperationCallbacks = []; + public Dictionary> OperationCallbacks = []; #endregion public static async Task Create(int playerId, Vector3 position, Quaternion rotation, @@ -1398,8 +1398,25 @@ public override void OnVaulting() }); } - public Item FindItem(string itemId) + public Item FindItem(string itemId, bool questItem = false) { + if (questItem) + { + //List itemPositions = Traverse.Create(Singleton.Instance).Field>("list_1").Value; + foreach (GInterface12 lootItem in Singleton.Instance.LootList) + { + if (lootItem is LootItem observedLootItem) + { + if (observedLootItem.Item.TemplateId == itemId) + { + return observedLootItem.Item; + } + } + } + FikaPlugin.Instance.FikaLogger.LogError($"CoopPlayer::FindItem: Could not find questItem with id '{itemId}' in the world at all."); + return null; + } + Item item = Inventory.Equipment.FindItem(itemId); if (item != null) { diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 2f0d3bc0..0985a9a9 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -93,6 +93,7 @@ public void Init() packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); _netClient = new NetManager(this) { @@ -122,11 +123,25 @@ public void Init() FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); } + private void OnQuestItemPacketReceived(QuestItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestItemPacket(ref packet); + } + } + } + private void OnQuestConditionPacketReceived(QuestConditionPacket packet) { - if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + if (MyPlayer.HealthController.IsAlive) { - sharedQuestController.ReceiveQuestPacket(ref packet); + if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestPacket(ref packet); + } } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 9b204127..fc663951 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -97,6 +97,7 @@ public async Task Init() packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); _netServer = new NetManager(this) { @@ -190,14 +191,31 @@ public async Task Init() FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); } + private void OnQuestItemPacketReceived(QuestItemPacket packet, NetPeer peer) + { + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestItemPacket(ref packet); + } + } + } + private void OnQuestConditionPacketReceived(QuestConditionPacket packet, NetPeer peer) { _dataWriter.Reset(); SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + if (MyPlayer.HealthController.IsAlive) { - sharedQuestController.ReceiveQuestPacket(ref packet); + if (MyPlayer.GClass3227_0 is CoopSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestPacket(ref packet); + } } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs index c1a9ded0..9bfc9e12 100644 --- a/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs @@ -7,14 +7,12 @@ public struct QuestConditionPacket(string nickname, string id, string sourceId) public string Nickname = nickname; public string Id = id; public string SourceId = sourceId; - public string ItemId; public void Deserialize(NetDataReader reader) { Nickname = reader.GetString(); Id = reader.GetString(); SourceId = reader.GetString(); - ItemId = reader.GetString(); } public void Serialize(NetDataWriter writer) @@ -22,7 +20,6 @@ public void Serialize(NetDataWriter writer) writer.Put(Nickname); writer.Put(Id); writer.Put(SourceId); - writer.Put(ItemId); } } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs new file mode 100644 index 00000000..cc6f618c --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets +{ + public struct QuestItemPacket(string nickname, string itemId) : INetSerializable + { + public string Nickname = nickname; + public string ItemId = itemId; + + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + ItemId = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(ItemId); + } + } +}