diff --git a/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlarePatch.cs b/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlarePatch.cs index 3c244c91..8bb229f2 100644 --- a/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlarePatch.cs +++ b/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlarePatch.cs @@ -25,7 +25,8 @@ private static void PatchPostfix(BulletClass flareCartridge) if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id)) { - gameWorld.gameObject.AddComponent().isFlareDrop = true; + FikaAirdropsManager airdropsManager = gameWorld.gameObject.AddComponent(); + airdropsManager.isFlareDrop = true; } } } diff --git a/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs b/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs index 3257c561..ad94d25e 100644 --- a/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs +++ b/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs @@ -24,10 +24,11 @@ public FikaItemFactoryUtil() public void BuildContainer(LootableContainer container, FikaAirdropConfigModel config, string dropType) { - var containerId = config.ContainerIds[dropType]; - if (itemFactory.ItemTemplates.TryGetValue(containerId, out var template)) + string containerId = config.ContainerIds[dropType]; + if (itemFactory.ItemTemplates.TryGetValue(containerId, out ItemTemplate template)) { Item item = itemFactory.CreateItem(containerId, template._id, null); + item.Id = Singleton.Instance.MainPlayer.GClass2761_0.NextId; LootItem.CreateLootContainer(container, item, "CRATE", Singleton.Instance); } else @@ -69,7 +70,7 @@ public async void AddLoot(LootableContainer container, FikaAirdropLootResultMode await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Local, resources, JobPriority.Immediate, null, PoolManager.DefaultCancellationToken); } - if (Singleton.Instantiated) + if (Singleton.Instance != null) { Singleton.Instance.ClientLootBuilt = true; } diff --git a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs b/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs index 94e82dc7..45bb41d5 100644 --- a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs +++ b/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatchOverride.cs @@ -21,7 +21,7 @@ protected override MethodBase GetTargetMethod() [PatchPostfix] private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, List ____weatherCanvasGroups, - UpdatableToggle ____randomTimeToggle, UpdatableToggle ____randomWeatherToggle) + UpdatableToggle ____randomTimeToggle, UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups) { // Always disable the Coop Mode checkbox ____coopModeBlocker.SetBlock(true, "Co-op is always enabled in Fika"); @@ -31,6 +31,11 @@ private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker weatherCanvasGroups = ____weatherCanvasGroups; } + foreach (CanvasGroup canvasGroup in ____waterAndFoodCanvasGroups) + { + canvasGroup.SetUnlockStatus(true, true); + } + instance = __instance; ____randomWeatherToggle.Bind(new Action(ToggleWeather)); diff --git a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs index ba641d61..32bc1dbd 100644 --- a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs @@ -44,8 +44,8 @@ public class FikaAirdropsManager : MonoBehaviour protected void Awake() { Logger = BepInEx.Logging.Logger.CreateLogSource("FikaAirdropsManager"); - Logger.LogInfo("Initializing..."); - if (Singleton.Instantiated) + Logger.LogInfo(isFlareDrop ? "Initializing from flare..." : "Initializing..."); + if (Singleton.Instance != null) { Logger.LogWarning("Another manager already exists, destroying old..."); if (airdropPlane != null) @@ -57,17 +57,13 @@ protected void Awake() Destroy(AirdropBox.gameObject); } Destroy(Singleton.Instance); - Singleton.Release(Singleton.Instance); } Singleton.Create(this); } protected void OnDestroy() { - if (Singleton.Instantiated) - { - Singleton.Release(Singleton.Instance); - } + Logger.LogWarning("Destroying AirdropsManager"); } protected async void Start() @@ -99,6 +95,15 @@ protected async void Start() if (!AirdropParameters.AirdropAvailable) { Logger.LogInfo("Airdrop is not available, destroying manager..."); + + GenericPacket packet = new() + { + NetId = 0, + PacketType = EPackageType.RemoveAirdropManager + }; + + Singleton.Instance.SendDataToAll(new(), ref packet, DeliveryMethod.ReliableOrdered); + Destroy(this); return; } @@ -150,7 +155,7 @@ public void SendParamsToClients() LookPoint = airdropPlane.newRotation }; NetDataWriter writer = new(); - Singleton.Instance.SendDataToAll(writer, ref airdropPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + Singleton.Instance.SendDataToAll(writer, ref airdropPacket, DeliveryMethod.ReliableOrdered); } protected async void FixedUpdate() @@ -261,7 +266,7 @@ private void StartBox() { AirdropParameters.BoxSpawned = true; Vector3 pointPos = AirdropParameters.RandomAirdropPoint; - Vector3 dropPos = new Vector3(pointPos.x, AirdropParameters.DropHeight, pointPos.z); + Vector3 dropPos = new(pointPos.x, AirdropParameters.DropHeight, pointPos.z); AirdropBox.gameObject.SetActive(true); AirdropBox.StartCoroutine(AirdropBox.DropCrate(dropPos)); } @@ -304,6 +309,7 @@ private void BuildLootContainer(FikaAirdropConfigModel config) public void ReceiveBuildLootContainer(AirdropLootPacket packet) { + Logger.LogInfo("Received loot container parameters"); rootItem = packet.RootItem; ContainerId = packet.ContainerId; } diff --git a/Fika.Core/Coop/BotClasses/BotMovementContext.cs b/Fika.Core/Coop/BotClasses/BotMovementContext.cs index f57b13d3..f320a4d6 100644 --- a/Fika.Core/Coop/BotClasses/BotMovementContext.cs +++ b/Fika.Core/Coop/BotClasses/BotMovementContext.cs @@ -11,7 +11,7 @@ public sealed class BotMovementContext : MovementContext public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) { - if (!Bot.isStarted) + if (!Bot.isStarted || Bot.AIData.BotOwner.BotState == EBotState.NonActive) { return; } diff --git a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs index 4fb28c44..3dd9336a 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs @@ -30,8 +30,7 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback packet.ItemControllerExecutePacket = new() { CallbackId = operation.Id, - OperationBytes = opBytes, - InventoryId = ID + OperationBytes = opBytes }; CoopBot.PacketSender?.InventoryPackets?.Enqueue(packet); diff --git a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs index 4df9c193..355164a4 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs @@ -3,10 +3,12 @@ using EFT; using EFT.InventoryLogic; using EFT.UI; +using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; using Fika.Core.Networking; using JetBrains.Annotations; using System.IO; +using UnityEngine; namespace Fika.Core.Coop.ClientClasses { @@ -30,34 +32,192 @@ public override void CallMalfunctionRepaired(Weapon weapon) public override void Execute(GClass2837 operation, [CanBeNull] Callback callback) { - base.Execute(operation, callback); - - // Do not replicate picking up quest items, throws an error on the other clients - if (operation is GClass2839 pickupOperation) + if (MatchmakerAcceptPatches.IsServer) { - if (pickupOperation.Item.Template.QuestItem) + HostInventoryOperationManager operationManager = new(this, operation, callback); + if (vmethod_0(operationManager.operation)) { + operationManager.operation.vmethod_0(operationManager.HandleResult); + + // Do not replicate picking up quest items, throws an error on the other clients + if (operation is GClass2839 pickupOperation) + { + if (pickupOperation.Item.Template.QuestItem) + { + return; + } + } + + // TODO: Check for glass increments + if (operation is GClass2870) + { + return; + } + + InventoryPacket packet = new() + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(operation, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operation.Id, + OperationBytes = opBytes + }; + + CoopPlayer.PacketSender?.InventoryPackets?.Enqueue(packet); + return; } + operationManager.operation.Dispose(); + operationManager.callback?.Fail($"Can't execute {operationManager.operation}", 1); + } + else if (MatchmakerAcceptPatches.IsClient) + { + // Do not replicate picking up quest items, throws an error on the other clients + if (operation is GClass2839 pickupOperation) + { + if (pickupOperation.Item.Template.QuestItem) + { + base.Execute(operation, callback); + return; + } + } + + InventoryPacket packet = new() + { + HasItemControllerExecutePacket = true + }; + + ClientInventoryOperationManager clientOperationManager = new() + { + operation = operation, + callback = callback, + inventoryController = this + }; + + clientOperationManager.callback ??= new Callback(ClientPlayer.Control0.Class1400.class1400_0.method_0); + uint operationNum = AddOperationCallback(operation, new Callback(clientOperationManager.HandleResult)); + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(operation, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationNum, + OperationBytes = opBytes + }; + + CoopPlayer.PacketSender?.InventoryPackets?.Enqueue(packet); + } + } + + private uint AddOperationCallback(GClass2837 operation, Callback callback) + { + ushort id = operation.Id; + CoopPlayer.OperationCallbacks.Add(id, callback); + return id; + } + + private class HostInventoryOperationManager(CoopClientInventoryController inventoryController, GClass2837 operation, Callback callback) + { + public readonly CoopClientInventoryController inventoryController = inventoryController; + public GClass2837 operation = operation; + public readonly Callback callback = callback; + + public void HandleResult(IResult result) + { + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"[{Time.frameCount}][{inventoryController.Name}] {inventoryController.ID} - Local operation failed: {operation.Id} - {operation}\r\nError: {result.Error}"); + } + callback?.Invoke(result); } + } - InventoryPacket packet = new() + private class ClientInventoryOperationManager + { + public EOperationStatus? serverOperationStatus; + public EOperationStatus? localOperationStatus; + public GClass2837 operation; + public Callback callback; + public CoopClientInventoryController inventoryController; + + public void HandleResult(Result result) { - HasItemControllerExecutePacket = true - }; - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(operation, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() + ClientInventoryCallbackManager callbackManager = new() + { + clientOperationManager = this, + result = result + }; + + if (callbackManager.result.Succeed) + { + EOperationStatus value = callbackManager.result.Value; + if (value == EOperationStatus.Started) + { + localOperationStatus = EOperationStatus.Started; + serverOperationStatus = EOperationStatus.Started; + operation.vmethod_0(new Callback(callbackManager.HandleResult), true); + return; + } + if (value == EOperationStatus.Finished) + { + serverOperationStatus = EOperationStatus.Finished; + if (localOperationStatus == serverOperationStatus) + { + operation.Dispose(); + callback.Succeed(); + return; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"{inventoryController.ID} - Client operation rejected by server: {operation.Id} - {operation}\r\nReason: {callbackManager.result.Error}"); + serverOperationStatus = EOperationStatus.Failed; + localOperationStatus = EOperationStatus.Failed; + operation.Dispose(); + callback.Invoke(callbackManager.result); + } + } + } + + private class ClientInventoryCallbackManager + { + public Result result; + public ClientInventoryOperationManager clientOperationManager; + + public void HandleResult(IResult executeResult) { - CallbackId = operation.Id, - OperationBytes = opBytes, - InventoryId = ID - }; + if (!executeResult.Succeed && (executeResult.Error is not "skipped skippable" or "skipped _completed")) + { + FikaPlugin.Instance.FikaLogger.LogError($"{clientOperationManager.inventoryController.ID} - Client operation critical failure: {clientOperationManager.inventoryController.ID} - {clientOperationManager.operation}\r\nError: {executeResult.Error}"); + } + + clientOperationManager.localOperationStatus = EOperationStatus.Finished; - CoopPlayer.PacketSender?.InventoryPackets?.Enqueue(packet); + if (clientOperationManager.localOperationStatus == clientOperationManager.serverOperationStatus) + { + clientOperationManager.operation.Dispose(); + clientOperationManager.callback.Invoke(result); + return; + } + + if (clientOperationManager.serverOperationStatus != null) + { + if (clientOperationManager.serverOperationStatus == EOperationStatus.Failed) + { + clientOperationManager.operation.Dispose(); + clientOperationManager.callback.Invoke(result); + } + } + } } } -} +} \ No newline at end of file diff --git a/Fika.Core/Coop/Components/CoopExfilManager.cs b/Fika.Core/Coop/Components/CoopExfilManager.cs index 6dacb84a..7e3ef0ee 100644 --- a/Fika.Core/Coop/Components/CoopExfilManager.cs +++ b/Fika.Core/Coop/Components/CoopExfilManager.cs @@ -21,7 +21,7 @@ internal class CoopExfilManager : MonoBehaviour private ExfiltrationPoint[] exfiltrationPoints; private CarExtraction carExfil = null; - void Awake() + protected void Awake() { game = gameObject.GetComponent(); playerHandlers = []; @@ -30,7 +30,7 @@ void Awake() carExfil = FindObjectOfType(); } - void Update() + protected void Update() { if (exfiltrationPoints == null) { diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index fd367537..b39a573c 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -1,5 +1,4 @@ -using Aki.Common.Http; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; using EFT; using EFT.Interactive; @@ -36,7 +35,7 @@ public class CoopHandler : MonoBehaviour public Dictionary Players { get; } = new(); public int HumanPlayers = 1; public List ExtractedPlayers { get; set; } = []; - ManualLogSource Logger { get; set; } + ManualLogSource Logger; public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; public List queuedProfileIds = []; @@ -105,7 +104,6 @@ protected void Awake() // ---------------------------------------------------- // Create a BepInEx Logger for CoopHandler Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHandler"); - Logger.LogDebug("CoopHandler::Awake"); } /// @@ -116,13 +114,8 @@ protected void Start() if (MatchmakerAcceptPatches.IsServer) { PingRoutine = StartCoroutine(PingServer()); - /*loopToken = new(); - loopThread = new Thread(PingServers); - loopThread.Start();*/ } - Logger.LogDebug("CoopHandler::Start"); - if (MatchmakerAcceptPatches.IsClient) { _ = Task.Run(ReadFromServerCharactersLoop); @@ -151,23 +144,6 @@ private IEnumerator PingServer() { yield return null; } - - // TODO: replace with - // await FikaRequestHandler.UpdatePing(); - } - } - - private void PingServers() - { - string serialized = new PingRequest().ToJson(); - - while (RunAsyncTasks) - { - //Task.Delay(30000, loopToken.Token).GetAwaiter().GetResult(); - RequestHandler.PutJson("/fika/update/ping", serialized); - - // TODO: replace with - // await FikaRequestHandler.UpdatePing(); } } @@ -176,9 +152,6 @@ protected void OnDestroy() Players.Clear(); RunAsyncTasks = false; - /*loopToken?.Cancel(); - loopToken?.Dispose(); - loopThread?.Join();*/ StopCoroutine(ProcessSpawnQueue()); if (PingRoutine != null) diff --git a/Fika.Core/Coop/Custom/FikaHealthBar.cs b/Fika.Core/Coop/Custom/FikaHealthBar.cs index 48efef20..26dccb86 100644 --- a/Fika.Core/Coop/Custom/FikaHealthBar.cs +++ b/Fika.Core/Coop/Custom/FikaHealthBar.cs @@ -195,6 +195,8 @@ private void CreateHealthBar() currentPlayer.HealthController.HealthChangedEvent += HealthController_HealthChangedEvent; currentPlayer.HealthController.BodyPartDestroyedEvent += HealthController_BodyPartDestroyedEvent; currentPlayer.HealthController.BodyPartRestoredEvent += HealthController_BodyPartRestoredEvent; + + UpdateHealth(); } private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, EFT.HealthSystem.ValueStruct arg2) @@ -217,32 +219,29 @@ private void HealthController_HealthChangedEvent(EBodyPart arg1, float arg2, Dam /// private void UpdateHealth() { - if (playerPlate.healthBarScreen.gameObject.activeSelf) + float currentHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Current; + float maxHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Maximum; + if (FikaPlugin.UseHealthNumber.Value) { - float currentHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Current; - float maxHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Maximum; - if (FikaPlugin.UseHealthNumber.Value) + if (!playerPlate.healthNumberBackgroundScreen.gameObject.activeSelf) { - if (!playerPlate.healthNumberBackgroundScreen.gameObject.activeSelf) - { - playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(true); - playerPlate.healthBarBackgroundScreen.gameObject.SetActive(false); - } - int healthNumberPercentage = (int)Math.Round((currentHealth / maxHealth) * 100); - playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); + playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(true); + playerPlate.healthBarBackgroundScreen.gameObject.SetActive(false); } - else + int healthNumberPercentage = (int)Math.Round((currentHealth / maxHealth) * 100); + playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); + } + else + { + if (!playerPlate.healthBarBackgroundScreen.gameObject.activeSelf) { - if (!playerPlate.healthBarBackgroundScreen.gameObject.active) - { - playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(false); - playerPlate.healthBarBackgroundScreen.gameObject.SetActive(true); - } - - float normalizedHealth = Mathf.Clamp01(currentHealth / maxHealth); - playerPlate.healthBarScreen.fillAmount = normalizedHealth; - UpdateHealthBarColor(normalizedHealth); + playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(false); + playerPlate.healthBarBackgroundScreen.gameObject.SetActive(true); } + + float normalizedHealth = Mathf.Clamp01(currentHealth / maxHealth); + playerPlate.healthBarScreen.fillAmount = normalizedHealth; + UpdateHealthBarColor(normalizedHealth); } } diff --git a/Fika.Core/Coop/Factories/PingFactory.cs b/Fika.Core/Coop/Factories/PingFactory.cs index b5fb37b6..51851704 100644 --- a/Fika.Core/Coop/Factories/PingFactory.cs +++ b/Fika.Core/Coop/Factories/PingFactory.cs @@ -1,4 +1,7 @@ -using Fika.Core.Bundles; +using Comfort.Common; +using EFT; +using Fika.Core.Bundles; +using Fika.Core.Coop.Players; using UnityEngine; using UnityEngine.UI; using Object = System.Object; @@ -41,25 +44,35 @@ public abstract class AbstractPing : MonoBehaviour protected Vector3 hitPoint; private float screenScale = 1f; private Color _pingColor = Color.white; + private CoopPlayer mainPlayer; static AbstractPing() { pingBundle = InternalBundleLoader.Instance.GetAssetBundle("ping"); } - private void Awake() + protected void Awake() { image = GetComponentInChildren(); image.color = Color.clear; + mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + if (mainPlayer == null) + { + Destroy(gameObject); + FikaPlugin.Instance.FikaLogger.LogError("Ping::Awake: Could not find MainPlayer!"); + } Destroy(gameObject, FikaPlugin.PingTime.Value); } - private void Update() + protected void Update() { - if (CameraClass.Instance.OpticCameraManager.Boolean_0 && CameraClass.Instance.OpticCameraManager.CurrentOpticSight != null) + if (mainPlayer.HealthController.IsAlive && mainPlayer.ProceduralWeaponAnimation.IsAiming) { - image.color = Color.clear; - return; + if (mainPlayer.ProceduralWeaponAnimation.CurrentScope.IsOptic && !FikaPlugin.ShowPingDuringOptics.Value) + { + image.color = Color.clear; + return; + } } Camera camera = CameraClass.Instance.Camera; diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index 24cba179..a5f8d6a2 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -225,7 +225,7 @@ protected void Update() float movementSpeed = fastMode ? 20f : 2f; if (superFastMode) { - movementSpeed *= 3; + movementSpeed *= 8; } if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) diff --git a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs index f1cd7c20..8c7d2d9e 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs @@ -235,7 +235,7 @@ private void ShowExtractMessage() string text = FikaPlugin.ExtractKey.Value.MainKey.ToString(); if (FikaPlugin.ExtractKey.Value.Modifiers.Count() > 0) { - string modifiers = string.Join("+", FikaPlugin.ExtractKey.Value.Modifiers); + string modifiers = string.Join(" + ", FikaPlugin.ExtractKey.Value.Modifiers); text = modifiers + " + " + text; } extractText = FikaUIUtils.CreateOverlayText($"Press '{text}' to extract"); diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index fa8dfbd2..22f1911a 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -56,6 +56,8 @@ internal sealed class CoopGame : BaseLocalGame, IBotGame, IFika private CoopExfilManager exfilManager; private GameObject fikaStartButton; + public RaidSettings RaidSettings { get; private set; } + //WildSpawnType for sptUsec and sptBear const int sptUsecValue = 47; const int sptBearValue = 48; @@ -91,7 +93,8 @@ public IWeatherCurve WeatherCurve internal static CoopGame Create(InputTree inputTree, Profile profile, GameDateTime backendDateTime, InsuranceCompanyClass insurance, MenuUI menuUI, CommonUI commonUI, PreloaderUI preloaderUI, GameUI gameUI, LocationSettingsClass.Location location, TimeAndWeatherSettings timeAndWeather, - WavesSettings wavesSettings, EDateTime dateTime, Callback callback, float fixedDeltaTime, EUpdateQueue updateQueue, ISession backEndSession, TimeSpan sessionTime) + WavesSettings wavesSettings, EDateTime dateTime, Callback callback, float fixedDeltaTime, EUpdateQueue updateQueue, + ISession backEndSession, TimeSpan sessionTime, RaidSettings raidSettings) { Logger = BepInEx.Logging.Logger.CreateLogSource("Coop Game Mode"); Logger.LogInfo("CoopGame::Create"); @@ -113,7 +116,7 @@ internal static CoopGame Create(InputTree inputTree, Profile profile, GameDateTi BossLocationSpawn[] bossSpawns = EFT.LocalGame.smethod_8(wavesSettings, location.BossLocationSpawn); coopGame.GClass579 = GClass579.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); - if (useCustomWeather) + if (useCustomWeather && MatchmakerAcceptPatches.IsServer) { Logger.LogInfo("Custom weather enabled, initializing curves"); coopGame.SetupCustomWeather(timeAndWeather); @@ -124,6 +127,8 @@ internal static CoopGame Create(InputTree inputTree, Profile profile, GameDateTi Singleton.Create(coopGame); FikaEventDispatcher.DispatchEvent(new FikaGameCreatedEvent(coopGame)); + coopGame.RaidSettings = raidSettings; + return coopGame; } @@ -443,13 +448,19 @@ private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler c #endif return false; } - Player bot = Bots[botKey]; - #if DEBUG Logger.LogWarning($"Removing {bot.Profile.Info.Settings.Role} at a distance of {Math.Sqrt(furthestDistance)}m from ITs nearest player."); #endif + DespawnBot(coopHandler, bot); +#if DEBUG + Logger.LogWarning($"Bot {bot.Profile.Info.Settings.Role} despawned successfully."); +#endif + return true; + } + private void DespawnBot(CoopHandler coopHandler, Player bot) + { IBotGame botGame = Singleton.Instance; BotOwner botOwner = bot.AIData.BotOwner; @@ -458,13 +469,9 @@ private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler c BotUnspawn(botOwner); botOwner?.Dispose(); - Bots.Remove(botKey); CoopPlayer coopPlayer = (CoopPlayer)bot; coopHandler.Players.Remove(coopPlayer.NetId); -#if DEBUG - Logger.LogWarning($"Bot {bot.Profile.Info.Settings.Role} despawned successfully."); -#endif - return true; + Bots.Remove(bot.ProfileId); } /// @@ -653,8 +660,8 @@ public override async Task vmethod_2(int playerId, Vector3 position LocalPlayer myPlayer = await CoopPlayer.Create(playerId, spawnPoint.Position, spawnPoint.Rotation, "Player", "Main_", EPointOfView.FirstPerson, profile, false, UpdateQueue, Player.EUpdateMode.Auto, Player.EUpdateMode.Auto, - GClass549.Config.CharacterController.ClientPlayerMode, () => Singleton.Instance.Control.Settings.MouseSensitivity, - () => Singleton.Instance.Control.Settings.MouseAimingSensitivity, new GClass1445(), MatchmakerAcceptPatches.IsServer ? 0 : 1000, questController); + GClass549.Config.CharacterController.ClientPlayerMode, getSensitivity, + getAimingSensitivity, new GClass1445(), MatchmakerAcceptPatches.IsServer ? 0 : 1000, questController); profile.SetSpawnedInSession(profile.Side == EPlayerSide.Savage); @@ -664,6 +671,15 @@ public override async Task vmethod_2(int playerId, Vector3 position await Task.Delay(5000); } + if (MatchmakerAcceptPatches.IsServer) + { + if (RaidSettings.MetabolismDisabled) + { + myPlayer.HealthController.DisableMetabolism(); + NotificationManagerClass.DisplayMessageNotification("Metabolism disabled", iconType: EFT.Communications.ENotificationIconType.Alert); + } + } + CoopPlayer coopPlayer = (CoopPlayer)myPlayer; coopHandler.Players.Add(coopPlayer.NetId, coopPlayer); @@ -993,6 +1009,10 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co Logger.LogError("CoopGame::vmethod_4: Halloween controller could not be instantiated!"); } } + if (GClass549.Config.FixedFrameRate > 0f) + { + FixedDeltaTime = 1f / GClass549.Config.FixedFrameRate; + } bool isWinter = BackEndSession.IsWinter; Class420 winterEventController = new(); @@ -1594,7 +1614,7 @@ public override void Dispose() } } - if (Singleton.Instantiated) + if (Singleton.Instance != null) { Destroy(Singleton.Instance); } diff --git a/Fika.Core/Coop/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs b/Fika.Core/Coop/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs index d7aa1dd7..e3880d80 100644 --- a/Fika.Core/Coop/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs +++ b/Fika.Core/Coop/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs @@ -103,16 +103,14 @@ public static async Task Postfix(Task __result, TarkovApplication __instance, Ti await Task.Delay(1000); + StartHandler startHandler = new(__instance, session.Profile, session.ProfileOfPet, ____raidSettings.SelectedLocation, timeHasComeScreenController); + CoopGame localGame = CoopGame.Create(____inputTree, profile, ____localGameDateTime, session.InsuranceCompany, MonoBehaviourSingleton.Instance, MonoBehaviourSingleton.Instance, MonoBehaviourSingleton.Instance, MonoBehaviourSingleton.Instance, ____raidSettings.SelectedLocation, timeAndWeather, - ____raidSettings.WavesSettings, ____raidSettings.SelectedDateTime, new Callback((r) => - { - typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_46)) - .Invoke(__instance, [session.Profile.Id, session.ProfileOfPet, ____raidSettings.SelectedLocation, r, timeHasComeScreenController]); - - }), ____fixedDeltaTime, EUpdateQueue.Update, session, TimeSpan.FromSeconds(60 * ____raidSettings.SelectedLocation.EscapeTimeLimit) + ____raidSettings.WavesSettings, ____raidSettings.SelectedDateTime, new Callback(startHandler.HandleStart), + ____fixedDeltaTime, EUpdateQueue.Update, session, TimeSpan.FromSeconds(60 * ____raidSettings.SelectedLocation.EscapeTimeLimit), ____raidSettings ); Singleton.Create(localGame); FikaEventDispatcher.DispatchEvent(new AbstractGameCreatedEvent(localGame)); @@ -126,20 +124,35 @@ public static async Task Postfix(Task __result, TarkovApplication __instance, Ti timeHasComeScreenController.ChangeStatus("Created Coop Game"); } - Task initTask = localGame.method_4(____raidSettings.BotSettings, ____backendUrl, null, new Callback((r) => + Task finishTask = localGame.method_4(____raidSettings.BotSettings, ____backendUrl, null, new Callback(startHandler.HandleLoadComplete)); + __result = Task.WhenAll(finishTask); + } + + private class StartHandler(TarkovApplication tarkovApplication, Profile pmcProfile, Profile scavProfile, LocationSettingsClass.Location location, MatchmakerTimeHasCome.GClass3163 timeHasComeScreenController) + { + private readonly TarkovApplication tarkovApplication = tarkovApplication; + private readonly Profile pmcProfile = pmcProfile; + private readonly Profile scavProfile = scavProfile; + private readonly LocationSettingsClass.Location location = location; + private readonly MatchmakerTimeHasCome.GClass3163 timeHasComeScreenController = timeHasComeScreenController; + + public void HandleStart(Result result) + { + tarkovApplication.method_46(pmcProfile.Id, scavProfile, location, result, timeHasComeScreenController); + } + + public void HandleLoadComplete(IResult error) { using (GClass21.StartWithToken("LoadingScreen.LoadComplete")) { UnityEngine.Object.DestroyImmediate(MonoBehaviourSingleton.Instance.gameObject); - MainMenuController mmc = (MainMenuController)typeof(TarkovApplication).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Where(x => x.FieldType == typeof(MainMenuController)).FirstOrDefault().GetValue(__instance); - mmc.Unsubscribe(); + MainMenuController mmc = (MainMenuController)typeof(TarkovApplication).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Where(x => x.FieldType == typeof(MainMenuController)).FirstOrDefault().GetValue(tarkovApplication); + mmc?.Unsubscribe(); GameWorld gameWorld = Singleton.Instance; gameWorld.OnGameStarted(); FikaEventDispatcher.DispatchEvent(new GameWorldStartedEvent(gameWorld)); } - })); - - __result = Task.WhenAll(initTask); + } } } } diff --git a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreenShowPatch.cs b/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreenShowPatch.cs index 87f3f316..2f316dfc 100644 --- a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreenShowPatch.cs +++ b/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreenShowPatch.cs @@ -16,7 +16,7 @@ public class MatchmakerAcceptScreenShowPatch : ModulePatch private static GameObject MatchmakerObject { get; set; } [PatchPrefix] - private static void Pre(ref ISession session, ref RaidSettings raidSettings, Profile ___profile_0, MatchMakerAcceptScreen __instance, + private static void PreFix(ref ISession session, ref RaidSettings raidSettings, Profile ___profile_0, MatchMakerAcceptScreen __instance, DefaultUIButton ____acceptButton, DefaultUIButton ____backButton, MatchMakerPlayerPreview ____playerModelView) { if (MatchmakerObject == null) @@ -29,14 +29,14 @@ private static void Pre(ref ISession session, ref RaidSettings raidSettings, Pro raidSettings.RaidMode = ERaidMode.Local; } - var newMatchMaker = MatchmakerObject.GetOrAddComponent(); + MatchMakerUIScript newMatchMaker = MatchmakerObject.GetOrAddComponent(); newMatchMaker.RaidSettings = raidSettings; newMatchMaker.AcceptButton = ____acceptButton; newMatchMaker.BackButton = ____backButton; } [PatchPostfix] - private static void Post(ref ISession session, Profile ___profile_0, MatchMakerAcceptScreen __instance) + private static void PostFix(ref ISession session, Profile ___profile_0, MatchMakerAcceptScreen __instance) { MatchmakerAcceptPatches.MatchMakerAcceptScreenInstance = __instance; MatchmakerAcceptPatches.Profile = ___profile_0; diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs index 775a184d..3c5495b8 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs @@ -213,6 +213,12 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass } else if (packet.ShotInfoPacket.ShotType == EShotType.RegularShot) { + if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); + return; + } + BulletClass ammo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); InitiateShot(Item, ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, packet.ShotInfoPacket.FireportPosition, packet.ShotInfoPacket.ChamberIndex, packet.ShotInfoPacket.Overheat); diff --git a/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs b/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs index 6d37cc49..a0c3852f 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs @@ -1,13 +1,6 @@ // © 2024 Lacyway All Rights Reserved -using Comfort.Common; -using Diz.LanguageExtensions; - - -// © 2024 Lacyway All Rights Reserved - using EFT; -using EFT.InventoryLogic; namespace Fika.Core.Coop.ObservedClasses { @@ -41,7 +34,7 @@ public override bool IsInventoryBlocked() return false; } - public override void InProcess(TraderControllerClass executor, Item item, ItemAddress to, bool succeed, GInterface338 operation, Callback callback) + /*public override void InProcess(TraderControllerClass executor, Item item, ItemAddress to, bool succeed, GInterface338 operation, Callback callback) { if (!succeed) { @@ -54,6 +47,6 @@ public override void InProcess(TraderControllerClass executor, Item item, ItemAd return; } callback.Succeed(); - } + }*/ } } diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index 2c69f0dd..3421c752 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -122,11 +122,11 @@ private void Update() Client?.SendData(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); } } - if (Input.GetKey(FikaPlugin.PingButton.Value.MainKey) - && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey) + if (FikaPlugin.UsePingSystem.Value && player.IsYourPlayer && player.HealthController.IsAlive - && FikaPlugin.UsePingSystem.Value) + && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) + && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) { player?.Ping(); } @@ -134,6 +134,15 @@ private void Update() private IEnumerator SyncWorld() { + while (Client.NetClient.FirstPeer == null) + { + yield return null; + } + + Writer?.Reset(); + SessionSettingsPacket settingsPacket = new(true); + Client?.SendData(Writer, ref settingsPacket, DeliveryMethod.ReliableOrdered); + CoopGame coopGame = (CoopGame)Singleton.Instance; if (coopGame == null) diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index 21258714..3efd2cbc 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -129,11 +129,11 @@ private void Update() Server?.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); } } - if (Input.GetKey(FikaPlugin.PingButton.Value.MainKey) - && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey) + if (FikaPlugin.UsePingSystem.Value && player.IsYourPlayer && player.HealthController.IsAlive - && FikaPlugin.UsePingSystem.Value) + && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) + && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) { player?.Ping(); } diff --git a/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs new file mode 100644 index 00000000..43043ca0 --- /dev/null +++ b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs @@ -0,0 +1,25 @@ +using Aki.Reflection.Patching; +using EFT; +using Fika.Core.Coop.GameMode; +using System.Reflection; + +namespace Fika.Core.Coop.Patches +{ + /// + /// Used to support mods that rely on the property, which normally casts to + /// + internal class AbstractGame_InRaid_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(AbstractGame).GetProperty(nameof(AbstractGame.InRaid)).GetGetMethod(); + } + + [PatchPrefix] + private static bool PreFix(AbstractGame __instance, bool __result) + { + __result = __instance is CoopGame; + return false; + } + } +} diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index b737843a..1d97be72 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -213,24 +213,24 @@ private IEnumerator WaitForPlayersToLoadBot() ActiveHealthController.SetDamageCoeff(0); Vector3 spawnPosition = Position; int connectedPeers = Singleton.Instance.NetServer.ConnectedPeersCount; - float randomY = UnityEngine.Random.RandomRange(-9500, -10000); + float randomY = UnityEngine.Random.RandomRange(-500, -1000); DateTime start = DateTime.Now; Teleport(new(0, randomY, 0)); while (loadedPlayers < connectedPeers && Math.Abs((start - DateTime.Now).TotalSeconds) < 30) { yield return new WaitForSeconds(1); - Teleport(new(0, randomY, 0)); } - // Wait a little bit longer just to make sure... - yield return new WaitForSeconds(2); - - Teleport(new Vector3(spawnPosition.x, spawnPosition.y + 0.5f, spawnPosition.z)); - yield return new WaitForSeconds(1); - ActiveHealthController.SetDamageCoeff(originalDamageCoeff); + Teleport(new Vector3(spawnPosition.x, spawnPosition.y + 1f, spawnPosition.z)); AIData.BotOwner.BotState = EBotState.PreActive; isStarted = true; + yield return new WaitUntil(() => { return MovementContext.IsGrounded; }); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogWarning($"{gameObject.name} is now grounded, started at Y: {randomY}, now at {Position.ToStringVerbose()}"); +#endif + yield return new WaitForFixedUpdate(); + ActiveHealthController.SetDamageCoeff(originalDamageCoeff); } public override void OnDead(EDamageType damageType) diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index 6a58cfe4..6d772ebf 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -17,8 +17,10 @@ using Fika.Core.Coop.ObservedClasses; using Fika.Core.Coop.PacketHandlers; using Fika.Core.Networking; +using Fika.Core.Networking.Packets.Player; using System; using System.Collections; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -44,6 +46,7 @@ public class CoopPlayer : LocalPlayer public Transform RaycastCameraTransform; public int NetId; public bool IsObservedAI = false; + public Dictionary> OperationCallbacks = []; public static async Task Create( int playerId, @@ -1049,12 +1052,37 @@ public virtual void DoObservedVault(VaultPacket vaultPacket) } + public void HandleCallbackFromServer(in OperationCallbackPacket operationCallbackPacket) + { + if (OperationCallbacks.TryGetValue(operationCallbackPacket.CallbackId, out Callback callback)) + { + if (operationCallbackPacket.OperationStatus != EOperationStatus.Started) + { + OperationCallbacks.Remove(operationCallbackPacket.CallbackId); + } + if (operationCallbackPacket.OperationStatus != EOperationStatus.Failed) + { + callback(new Result(operationCallbackPacket.OperationStatus)); + } + else + { + callback(new Result(EOperationStatus.Failed) + { + Error = operationCallbackPacket.Error + }); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"Could not find CallbackId {operationCallbackPacket.CallbackId}!"); + } + } + public virtual void HandleInventoryPacket(in InventoryPacket packet) { if (packet.HasItemControllerExecutePacket) { - var inventory = _inventoryController; - if (inventory != null) + if (_inventoryController != null) { using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); using BinaryReader binaryReader = new(memoryStream); @@ -1062,22 +1090,12 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) { GStruct411 result = ToInventoryOperation(binaryReader.ReadPolymorph()); - if (!result.Succeeded) - { - /* - This happens whenever another player grabs something off of an AI - that this client didn't spawn - */ - FikaPlugin.Instance.FikaLogger.LogError("HandleInventoryPacket: " + result.Error); - return; - } - InventoryOperationHandler opHandler = new() { - result = result + opResult = result }; - opHandler.result.Value.vmethod_0(new Callback(opHandler.RunOperation), false); + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. // Unknown what problems this might cause so far. @@ -1098,7 +1116,9 @@ that this client didn't spawn } } else + { FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } } } @@ -1118,21 +1138,46 @@ that this client didn't spawn } } else + { FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } } + + /*// Fix for folding not replicating + if (result.Value is GClass2858 foldOperation) + { + if (HandsController is CoopObservedFirearmController observedFirearmController) + { + if (observedFirearmController.Weapon != null && observedFirearmController.Weapon.Foldable != null) + { + observedFirearmController.InitiateOperation().Start(foldOperation, null); + } + } + }*/ } catch (Exception exception) { - FikaPlugin.Instance.FikaLogger.LogError(exception); + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + if (MatchmakerAcceptPatches.IsServer) + { + OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } } } else { FikaPlugin.Instance.FikaLogger.LogError("ItemControllerExecutePacket: inventory was null!"); + if (MatchmakerAcceptPatches.IsServer) + { + OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } } } - if (packet.HasSearchPacket) + // Currently unused + /*if (packet.HasSearchPacket) { if (!packet.SearchPacket.IsStop) { @@ -1150,7 +1195,7 @@ that this client didn't spawn _inventoryController.ExecuteStop(operation); } } - } + }*/ } public virtual void HandleWeaponPacket(in WeaponPacket packet) @@ -1232,7 +1277,11 @@ public virtual void HandleWeaponPacket(in WeaponPacket packet) { if (HandsController is ItemHandsController handsController) { - handsController.CompassStateHandler(packet.CompassState); + handsController.ApplyCompassPacket(new() + { + Toggle = true, + Status = packet.CompassState + }); } } @@ -1241,16 +1290,24 @@ public virtual void HandleWeaponPacket(in WeaponPacket packet) if (HandsController is CoopObservedKnifeController knifeController) { if (packet.KnifePacket.Examine) + { knifeController.ExamineWeapon(); + } if (packet.KnifePacket.Kick) + { knifeController.MakeKnifeKick(); + } if (packet.KnifePacket.AltKick) + { knifeController.MakeAlternativeKick(); + } if (packet.KnifePacket.BreakCombo) + { knifeController.BrakeCombo(); + } } else { @@ -1297,7 +1354,6 @@ public virtual void HandleDamagePacket(DamagePacket packet) } } - //ClientApplyDamageInfo(damageInfo, packet.ApplyDamageInfo.BodyPartType, packet.ApplyDamageInfo.ColliderType, packet.ApplyDamageInfo.Absorbed); ClientApplyShot(damageInfo, packet.DamageInfo.BodyPartType, packet.DamageInfo.ColliderType, packet.DamageInfo.ArmorPlateCollider); } @@ -1330,7 +1386,7 @@ public virtual void SetupDogTag() dogtagComponent.Level = Profile.Info.Experience > 0 ? Profile.Info.Level : 1; dogtagComponent.Time = DateTime.Now; dogtagComponent.Status = "Killed by "; - dogtagComponent.WeaponName = LastDamageInfo.Weapon.Name; + dogtagComponent.WeaponName = LastDamageInfo.Weapon != null ? LastDamageInfo.Weapon.Name : "Unknown"; dogtagComponent.GroupId = GroupId; } } @@ -1388,7 +1444,9 @@ public Item FindItem(string itemId) { Item item = Inventory.Equipment.FindItem(itemId); if (item != null) + { return item; + } GStruct416 itemResult = FindItemById(itemId); if (itemResult.Error != null) @@ -1413,11 +1471,14 @@ internal void HandleKeyEvent() private class InventoryOperationHandler() { - public GStruct411 result; - internal void RunOperation(IResult result) + public GStruct411 opResult; + + internal void HandleResult(IResult result) { - if (!result.Succeed) - Debug.Log($"Error in operation: {result.Error}"); + if (!result.Succeed || !string.IsNullOrEmpty(result.Error)) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error}"); + } } } diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index e69a00c7..ff78b41b 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -150,7 +150,7 @@ public static async Task CreateObservedPlayer( player.IsYourPlayer = false; - InventoryControllerClass inventoryController = new ObservedInventoryController(player, profile, false); + InventoryControllerClass inventoryController = new ObservedInventoryController(player, profile, true); PlayerHealthController tempController = new(profile.Health, player, inventoryController, profile.Skills, aiControl); byte[] healthBytes = tempController.SerializeState(); @@ -278,7 +278,7 @@ public override bool ShouldVocalizeDeath(EBodyPart bodyPart) public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) { - /*if (damageInfo.Player == null) + if (damageInfo.Player == null) { return; } @@ -288,39 +288,11 @@ public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartTy return; } - if (damageInfo.HittedBallisticCollider != null) - { - BodyPartCollider bodyPartCollider = (BodyPartCollider)damageInfo.HittedBallisticCollider; - colliderType = bodyPartCollider.BodyPartColliderType; - } - - PacketSender?.HealthPackets?.Enqueue(new() - { - ApplyDamageInfo = new() - { - Damage = damageInfo.Damage, - DamageType = damageInfo.DamageType, - BodyPartType = bodyPartType, - ColliderType = colliderType, - Absorbed = absorbed, - Direction = damageInfo.Direction, - Point = damageInfo.HitPoint, - PenetrationPower = damageInfo.PenetrationPower, - BlockedBy = damageInfo.BlockedBy, - DeflectedBy = damageInfo.DeflectedBy, - SourceId = damageInfo.SourceId, - ProfileId = damageInfo.Player.iPlayer.ProfileId - } - }); - LastAggressor = damageInfo.Player.iPlayer; LastDamagedBodyPart = bodyPartType; LastBodyPart = bodyPartType; LastDamageInfo = damageInfo; LastDamageType = damageInfo.DamageType; - - // Run this to get weapon skill - ManageAggressor(damageInfo, bodyPartType, colliderType);*/ } /*public override void ShotReactions(DamageInfo shot, EBodyPart bodyPart) @@ -991,6 +963,29 @@ public override void LateUpdate() } } + public override void LandingAdjustments(float d) + { + // Do nothing + } + + public new void CreateCompass() + { + bool compassInstantiated = Traverse.Create(this).Field("_compassInstantiated").GetValue(); + if (!compassInstantiated) + { + Transform transform = Singleton.Instance.CreateFromPool(new ResourceKey + { + path = "assets/content/weapons/additional_hands/item_compass.bundle" + }); + transform.SetParent(PlayerBones.Ribcage.Original, false); + transform.localRotation = Quaternion.identity; + transform.localPosition = Vector3.zero; + method_29(transform.gameObject); + compassInstantiated = true; + return; + } + } + private void SetupCulling() { followerCullingObject = gameObject.AddComponent(); diff --git a/Fika.Core/FikaPlugin.cs b/Fika.Core/FikaPlugin.cs index 3667d613..0719446b 100644 --- a/Fika.Core/FikaPlugin.cs +++ b/Fika.Core/FikaPlugin.cs @@ -27,6 +27,9 @@ using Fika.Core.UI.Models; using Fika.Core.UI.Patches; using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Text; using UnityEngine; @@ -47,26 +50,20 @@ public class FikaPlugin : BaseUnityPlugin /// Stores the Instance of this Plugin /// public static FikaPlugin Instance; - public static InternalBundleLoader BundleLoaderPlugin { get; private set; } - /// /// If any mod dependencies fail, show an error. This is a flag to say it has occurred. /// private bool ShownDependencyError { get; set; } - /// /// This is the Official EFT Version defined by BSG /// public static string EFTVersionMajor { get; internal set; } - public static string[] LoadedPlugins { get; private set; } - public ManualLogSource FikaLogger { get => Logger; } - public BotDifficulties BotDifficulties; - public string Locale { get; private set; } = "en"; + public string[] LocalIPs; public static Dictionary RespectedPlayersList = new() { @@ -126,6 +123,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry PingSize { get; set; } public static ConfigEntry PingTime { get; set; } public static ConfigEntry PlayPingAnimation { get; set; } + public static ConfigEntry ShowPingDuringOptics { get; set; } // Coop | Debug public static ConfigEntry FreeCamButton { get; set; } @@ -156,6 +154,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry NativeSockets { get; set; } public static ConfigEntry ForceIP { get; set; } public static ConfigEntry ForceBindIP { get; set; } + public static ConfigEntry ForceBindIP2 { get; set; } public static ConfigEntry AutoRefreshRate { get; set; } public static ConfigEntry UDPPort { get; set; } public static ConfigEntry UseUPnP { get; set; } @@ -196,6 +195,7 @@ protected void Awake() new Minefield_method_2_Patch().Enable(); new BotCacher().Enable(); new InventoryScrollPatch().Enable(); + new AbstractGame_InRaid_Patch().Enable(); #if GOLDMASTER new TOSPatch().Enable(); #endif @@ -291,17 +291,19 @@ private void SetupConfig() // Coop | Custom - UsePingSystem = Config.Bind("Coop | Custom", "Ping System", false, new ConfigDescription("Toggle Ping System. If enabled you can receive and send pings by pressing the ping key.", tags: new ConfigurationManagerAttributes() { Order = 6 })); + UsePingSystem = Config.Bind("Coop | Custom", "Ping System", false, new ConfigDescription("Toggle Ping System. If enabled you can receive and send pings by pressing the ping key.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 5 })); + PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 6 })); - PingColor = Config.Bind("Coop | Custom", "Ping Color", Color.white, new ConfigDescription("The color of your pings when displayed for other players.", tags: new ConfigurationManagerAttributes() { Order = 4 })); + PingColor = Config.Bind("Coop | Custom", "Ping Color", Color.white, new ConfigDescription("The color of your pings when displayed for other players.", tags: new ConfigurationManagerAttributes() { Order = 5 })); - PingSize = Config.Bind("Coop | Custom", "Ping Size", 1f, new ConfigDescription("The multiplier of the ping size.", new AcceptableValueRange(0.1f, 2f), new ConfigurationManagerAttributes() { Order = 3 })); + PingSize = Config.Bind("Coop | Custom", "Ping Size", 1f, new ConfigDescription("The multiplier of the ping size.", new AcceptableValueRange(0.1f, 2f), new ConfigurationManagerAttributes() { Order = 4 })); - PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), new ConfigurationManagerAttributes() { Order = 2 })); + PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), new ConfigurationManagerAttributes() { Order = 3 })); - PlayPingAnimation = Config.Bind("Coop | Custom", "Play Ping Animation", false, new ConfigDescription("Plays the pointing animation automatically when pinging. Can interfere with gameplay.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + PlayPingAnimation = Config.Bind("Coop | Custom", "Play Ping Animation", false, new ConfigDescription("Plays the pointing animation automatically when pinging. Can interfere with gameplay.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + + ShowPingDuringOptics = Config.Bind("Coop | Custom", "Show Ping During Optics", false, new ConfigDescription("If pings should be displayed while aiming down an optics scope.", tags: new ConfigurationManagerAttributes() { Order = 1 })); // Coop | Debug @@ -325,7 +327,7 @@ private void SetupConfig() DespawnFurthest = Config.Bind("Performance | Max Bots", "Despawn Furthest", false, new ConfigDescription("When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", tags: new ConfigurationManagerAttributes() { Order = 13 })); - DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 750f), new ConfigurationManagerAttributes() { Order = 12 })); + DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 3000f), new ConfigurationManagerAttributes() { Order = 12 })); MaxBotsFactory = Config.Bind("Performance | Max Bots", "Max Bots Factory", 0, new ConfigDescription("Max amount of bots that can be active at the same time on Factory. Useful if you have a weaker PC. Set to 0 to use vanilla limits.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 11 })); @@ -353,7 +355,7 @@ private void SetupConfig() ForceIP = Config.Bind("Network", "Force IP", "", new ConfigDescription("Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 6 })); - ForceBindIP = Config.Bind("Network", "Force Bind IP", "", new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN. Leave empty to disable.", tags: new ConfigurationManagerAttributes() { Order = 5 })); + ForceBindIP = Config.Bind("Network", "Force Bind IP", "", new ConfigDescription("Forces the server when hosting to use this local IP when starting the server. Useful if you are hosting on a VPN.", new AcceptableValueList(GetLocalAddresses()), new ConfigurationManagerAttributes() { Order = 5 })); AutoRefreshRate = Config.Bind("Network", "Auto Server Refresh Rate", 10f, new ConfigDescription("Every X seconds the client will ask the server for the list of matches while at the lobby screen.", new AcceptableValueRange(3f, 60f), new ConfigurationManagerAttributes() { Order = 4 })); @@ -370,6 +372,23 @@ private void SetupConfig() ArmpitDamageMultiplier = Config.Bind("Gameplay", "Armpit Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the armpits collider. 0.2 = 20%", new AcceptableValueRange(0.2f, 1f), new ConfigurationManagerAttributes() { Order = 1 })); } + private string[] GetLocalAddresses() + { + IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName()); + List ips = []; + ips.Add("Disabled"); + foreach (IPAddress ip in host.AddressList) + { + if (ip.AddressFamily == AddressFamily.InterNetwork) + { + ips.Add(ip.ToString()); + } + } + + LocalIPs = ips.Skip(1).ToArray(); + return [.. ips]; + } + private void DisableSPTPatches() { // Disable these as they interfere with Fika diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 203be287..4b67d96a 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -18,6 +18,7 @@ using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; using Fika.Core.Networking.Packets.GameWorld; +using Fika.Core.Networking.Packets.Player; using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; @@ -57,29 +58,30 @@ public NetManager NetClient protected void Start() { - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDeathPacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDeathPacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSessionSettingsPacketReceived); _netClient = new NetManager(this) { @@ -114,7 +116,27 @@ protected void Start() ClientReady = true; } - private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet, NetPeer peer) + private void OnSessionSettingsPacketReceived(SessionSettingsPacket packet) + { + if (!packet.IsRequest) + { + if (packet.MetabolismDisabled) + { + Singleton.Instance.MainPlayer.HealthController.DisableMetabolism(); + NotificationManagerClass.DisplayMessageNotification("Metabolism disabled", iconType: EFT.Communications.ENotificationIconType.Alert); + } + } + } + + private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) + { + player.HandleCallbackFromServer(in packet); + } + } + + private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) { Dictionary newPlayers = Players; if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) @@ -143,7 +165,7 @@ private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet, NetPeer peer) } } - private void OnAssignNetIdPacketReceived(AssignNetIdPacket packet, NetPeer peer) + private void OnAssignNetIdPacketReceived(AssignNetIdPacket packet) { FikaPlugin.Instance.FikaLogger.LogInfo($"OnAssignNetIdPacketReceived: Assigned NetId {packet.NetId} to my own client."); MyPlayer.NetId = packet.NetId; @@ -168,7 +190,7 @@ private void OnAssignNetIdPacketReceived(AssignNetIdPacket packet, NetPeer peer) Players[packet.NetId] = MyPlayer; } - private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) + private void OnSendCharacterPacketReceived(SendCharacterPacket packet) { if (packet.PlayerInfo.Profile.ProfileId != MyPlayer.ProfileId) { @@ -176,7 +198,7 @@ private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer p } } - private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) + private void OnBorderZonePacketReceived(BorderZonePacket packet) { if (Singleton.Instantiated) { @@ -202,7 +224,7 @@ private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) } } - private void OnMinePacketReceived(MinePacket packet, NetPeer peer) + private void OnMinePacketReceived(MinePacket packet) { if (Singleton.Instance.MineManager != null) { @@ -219,7 +241,7 @@ private void OnMinePacketReceived(MinePacket packet, NetPeer peer) } } - private void OnDeathPacketReceived(DeathPacket packet, NetPeer peer) + private void OnDeathPacketReceived(DeathPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -227,7 +249,7 @@ private void OnDeathPacketReceived(DeathPacket packet, NetPeer peer) } } - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) { @@ -235,12 +257,12 @@ private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer } } - private void OnBTRPacketReceived(BTRPacket packet, NetPeer peer) + private void OnBTRPacketReceived(BTRPacket packet) { CoopHandler.clientBTR?.btrPackets.Enqueue(packet); } - private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) + private void OnWeatherPacketReceived(WeatherPacket packet) { if (!packet.IsRequest) { @@ -255,7 +277,7 @@ private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) } } - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) { if (!packet.IsRequest) { @@ -342,103 +364,115 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer pee } } - private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) + private void OnGenericPacketReceived(GenericPacket packet) { - if (!Players.ContainsKey(packet.NetId)) + switch (packet.PacketType) { - return; - } - - if (packet.PacketType == EPackageType.ClientExtract) - { - if (CoopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - CoopHandler.Players.Remove(packet.NetId); - if (!CoopHandler.ExtractedPlayers.Contains(packet.NetId)) + case EPackageType.ClientExtract: { - CoopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = (CoopGame)CoopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); + if (CoopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + CoopHandler.Players.Remove(packet.NetId); + if (!CoopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + CoopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = (CoopGame)CoopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + NotificationManagerClass.DisplayMessageNotification($"Group member '{playerToApply.Profile.Nickname}' has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } - if (FikaPlugin.ShowNotifications.Value) + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + break; + case EPackageType.Ping: + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer pingPlayerToApply)) { - NotificationManagerClass.DisplayMessageNotification($"Group member '{playerToApply.Profile.Nickname}' has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + pingPlayerToApply.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname); } } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname); - } - } - else if (packet.PacketType == EPackageType.TrainSync) - { - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - DateTime depart = new(packet.DepartureTime); - Traverse.Create(locomotive).Field("_depart").SetValue(depart); - } - else - { - clientLogger.LogWarning("GenericPacketReceived: Could not find locomotive!"); - } - } - else if (packet.PacketType == EPackageType.ExfilCountdown) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) + break; + case EPackageType.TrainSync: { - CoopGame game = (CoopGame)Singleton.Instance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) { - exfilPoint.Status = EExfiltrationStatus.Countdown; + DateTime depart = new(packet.DepartureTime); + Traverse.Create(locomotive).Field("_depart").SetValue(depart); + } + else + { + clientLogger.LogWarning("GenericPacketReceived: Could not find locomotive!"); } } - } - } - else if (packet.PacketType == EPackageType.TraderServiceNotification) - { - CoopHandler.clientBTR?.DisplayNetworkNotification(packet.TraderServiceType); - } - else if (packet.PacketType == EPackageType.DisposeBot) - { - if (CoopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer playerToApply)) - { + break; + case EPackageType.ExfilCountdown: + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - if (CoopHandler.Players.Remove(packet.BotNetId)) + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = (CoopGame)Singleton.Instance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + break; + case EPackageType.TraderServiceNotification: { - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - clientLogger.LogInfo("Disposing bot: " + packet.BotNetId); + CoopHandler.clientBTR?.DisplayNetworkNotification(packet.TraderServiceType); } - else + break; + case EPackageType.DisposeBot: { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + if (CoopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) + { + + if (CoopHandler.Players.Remove(packet.BotNetId)) + { + botToDispose.Dispose(); + AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); + clientLogger.LogInfo("Disposing bot: " + packet.BotNetId); + } + else + { + clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + } + } + else + { + clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + } } - } - else - { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); - } + break; + case EPackageType.RemoveAirdropManager: + { + if (Singleton.Instance != null) + { + Destroy(Singleton.Instance); + } + } + break; } } - private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) + private void OnHealthSyncPacketReceived(HealthSyncPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -446,19 +480,21 @@ private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) } } - private void OnAirdropLootPacketReceived(AirdropLootPacket packet, NetPeer peer) + private void OnAirdropLootPacketReceived(AirdropLootPacket packet) { - if (!Singleton.Instantiated) + if (Singleton.Instance != null) + { + Singleton.Instance.ReceiveBuildLootContainer(packet); + } + else { clientLogger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); - return; } - Singleton.Instance.ReceiveBuildLootContainer(packet); } - private void OnAirdropPacketReceived(AirdropPacket packet, NetPeer peer) + private void OnAirdropPacketReceived(AirdropPacket packet) { - if (Singleton.Instantiated) + if (Singleton.Instance != null) { Singleton.Instance.AirdropParameters = new() { @@ -483,7 +519,7 @@ private void OnAirdropPacketReceived(AirdropPacket packet, NetPeer peer) } } - private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) + private void OnInformationPacketReceived(InformationPacket packet) { if (!packet.IsRequest) { @@ -500,7 +536,7 @@ private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) } } - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) { if (!packet.IsRequest) { @@ -530,7 +566,7 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe } } - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -538,7 +574,7 @@ private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer pee } } - private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) + private void OnInventoryPacketReceived(InventoryPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -546,7 +582,7 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) } } - private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) + private void OnDamagePacketReceived(DamagePacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -554,7 +590,7 @@ private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) } } - private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) + private void OnFirearmPacketReceived(WeaponPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -562,7 +598,7 @@ private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) } } - private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) + private void OnGameTimerPacketReceived(GameTimerPacket packet) { CoopHandler coopHandler = CoopHandler.GetCoopHandler(); if (coopHandler == null) @@ -570,31 +606,28 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) return; } - if (MatchmakerAcceptPatches.IsClient) - { - TimeSpan sessionTime = new(packet.Tick); + TimeSpan sessionTime = new(packet.Tick); - if (coopHandler.LocalGameInstance is CoopGame coopGame) + if (coopHandler.LocalGameInstance is CoopGame coopGame) + { + GameTimerClass gameTimer = coopGame.GameTimer; + if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) { - GameTimerClass gameTimer = coopGame.GameTimer; - if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) + if (gameTimer.PastTime.TotalSeconds < 3) { - if (gameTimer.PastTime.TotalSeconds < 3) - { - return; - } + return; + } - TimeSpan timeRemain = gameTimer.PastTime + sessionTime; + TimeSpan timeRemain = gameTimer.PastTime + sessionTime; - gameTimer.ChangeSessionTime(timeRemain); + gameTimer.ChangeSessionTime(timeRemain); - Traverse.Create(coopGame.GameUi.TimerPanel).Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value); - } + Traverse.Create(coopGame.GameUi.TimerPanel).Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value); } } } - private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) + private void OnPlayerStatePacketReceived(PlayerStatePacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { @@ -607,7 +640,7 @@ protected void Awake() CoopHandler = CoopHandler.CoopHandlerParent.GetComponent(); } - void Update() + protected void Update() { _netClient.PollEvents(); @@ -617,7 +650,7 @@ void Update() } } - void OnDestroy() + protected void OnDestroy() { _netClient?.Stop(); FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); @@ -657,7 +690,7 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead public void OnNetworkLatencyUpdate(NetPeer peer, int latency) { Ping = latency; - NetworkGameSession.RTT = Ping; + NetworkGameSession.RTT = peer.RoundTripTime; NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; } diff --git a/Fika.Core/Networking/FikaSerialization.cs b/Fika.Core/Networking/FikaSerialization.cs index 9b7a57e7..709c3f49 100644 --- a/Fika.Core/Networking/FikaSerialization.cs +++ b/Fika.Core/Networking/FikaSerialization.cs @@ -701,14 +701,12 @@ public struct ItemControllerExecutePacket { public uint CallbackId; public byte[] OperationBytes; - public string InventoryId; public static ItemControllerExecutePacket Deserialize(NetDataReader reader) { ItemControllerExecutePacket packet = new() { CallbackId = reader.GetUInt(), OperationBytes = reader.GetByteArray(), - InventoryId = reader.GetString() }; return packet; } @@ -716,7 +714,6 @@ public static void Serialize(NetDataWriter writer, ItemControllerExecutePacket p { writer.Put(packet.CallbackId); writer.PutByteArray(packet.OperationBytes); - writer.Put(packet.InventoryId); } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 38701df8..54c419cd 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -5,6 +5,7 @@ using EFT; using EFT.AssetsManager; using EFT.Interactive; +using EFT.InventoryLogic; using EFT.UI; using Fika.Core.Coop.Components; using Fika.Core.Coop.GameMode; @@ -15,11 +16,13 @@ using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; using Fika.Core.Networking.Packets.GameWorld; +using Fika.Core.Networking.Packets.Player; using LiteNetLib; using LiteNetLib.Utils; using Open.Nat; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -80,6 +83,7 @@ public async void Start() packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSessionSettingsPacketReceived); _netServer = new NetManager(this) { @@ -140,7 +144,7 @@ await Task.Run(async () => } } - if (FikaPlugin.ForceBindIP.Value != "") + if (FikaPlugin.ForceBindIP.Value != "Disabled") { _netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); } @@ -161,6 +165,23 @@ await Task.Run(async () => ServerReady = true; } + private void OnSessionSettingsPacketReceived(SessionSettingsPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + SessionSettingsPacket returnPacket = new(false) + { + MetabolismDisabled = coopGame.RaidSettings.MetabolismDisabled + }; + + SendDataToPeer(peer, new(), ref returnPacket, DeliveryMethod.ReliableUnordered); + } + } + } + public int PopNetId() { int netId = _currentNetId; @@ -486,11 +507,91 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply?.PacketReceiver?.InventoryPackets?.Enqueue(packet); - } + using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); + using BinaryReader binaryReader = new(memoryStream); + try + { + GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); - _dataWriter.Reset(); - SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + InventoryOperationHandler opHandler = new() + { + opResult = result, + operationId = packet.ItemControllerExecutePacket.CallbackId, + netId = playerToApply.NetId, + peer = peer, + server = this + }; + + OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); + SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); + + // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. + // Unknown what problems this might cause so far. + if (result.Value is GClass2861 unloadOperation) + { + if (unloadOperation.InternalOperation is GClass2872 internalSplitOperation) + { + Item item = internalSplitOperation.To.Item; + if (item != null) + { + if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) + { + item.Id = internalSplitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + + // TODO: Same as above. + if (result.Value is GClass2872 splitOperation) + { + Item item = splitOperation.To.Item; + if (item != null) + { + if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) + { + item.Id = splitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + + /*// Fix for folding not replicating + if (result.Value is GClass2858 foldOperation) + { + if (playerToApply.HandsController is CoopObservedFirearmController observedFirearmController) + { + if (observedFirearmController.Weapon != null && observedFirearmController.Weapon.Foldable != null) + { + observedFirearmController.InitiateOperation().Start(foldOperation, null); + } + } + }*/ + } + catch (Exception exception) + { + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } } private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) @@ -589,6 +690,7 @@ public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, public void OnPeerConnected(NetPeer peer) { NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); + serverLogger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); hasHadPeer = true; } @@ -658,5 +760,51 @@ public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelN { packetProcessor.ReadAllPackets(reader, peer); } + + private class InventoryOperationHandler + { + public GStruct411 opResult; + public uint operationId; + public int netId; + public NetPeer peer; + public FikaServer server; + + internal void HandleResult(IResult result) + { + NetDataWriter writer = new(); + OperationCallbackPacket operationCallbackPacket; + + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); + operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + return; + } + + InventoryPacket packet = new(netId) + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(opResult.Value, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationId, + OperationBytes = opBytes + }; + + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); + + operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + } + } } } diff --git a/Fika.Core/Networking/Http/FikaHttpHandler.cs b/Fika.Core/Networking/Http/FikaHttpHandler.cs index c86bdbd2..232b7db0 100644 --- a/Fika.Core/Networking/Http/FikaHttpHandler.cs +++ b/Fika.Core/Networking/Http/FikaHttpHandler.cs @@ -108,7 +108,7 @@ public static async Task RaidSpawnPoint(SpawnPointRequest da public static void RaidLeave(PlayerLeftRequest data) { - PutJson("/fika/update/playerspawn", data); + PutJson("/fika/raid/leave", data); } public static CreateMatch RaidJoin(MatchJoinRequest data) diff --git a/Fika.Core/Networking/Packets/Backend/SessionSettingsPacket.cs b/Fika.Core/Networking/Packets/Backend/SessionSettingsPacket.cs new file mode 100644 index 00000000..d71661c5 --- /dev/null +++ b/Fika.Core/Networking/Packets/Backend/SessionSettingsPacket.cs @@ -0,0 +1,28 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking +{ + public struct SessionSettingsPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public bool MetabolismDisabled; + + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + if (!IsRequest) + { + MetabolismDisabled = reader.GetBool(); + } + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + if (!IsRequest) + { + writer.Put(MetabolismDisabled); + } + } + } +} diff --git a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs index 91969e17..32091157 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs @@ -96,6 +96,7 @@ public enum EPackageType TrainSync, ExfilCountdown, TraderServiceNotification, - DisposeBot + DisposeBot, + RemoveAirdropManager } } diff --git a/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs b/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs new file mode 100644 index 00000000..527007fd --- /dev/null +++ b/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs @@ -0,0 +1,35 @@ +using EFT; +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets.Player +{ + public struct OperationCallbackPacket(int netId, uint callbackId, EOperationStatus operationStatus, string error = null) : INetSerializable + { + public int NetId = netId; + public uint CallbackId = callbackId; + public EOperationStatus OperationStatus = operationStatus; + public string Error = error; + + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + CallbackId = reader.GetUInt(); + OperationStatus = (EOperationStatus)reader.GetInt(); + if (OperationStatus == EOperationStatus.Failed) + { + Error = reader.GetString(); + } + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(CallbackId); + writer.Put((int)OperationStatus); + if (OperationStatus == EOperationStatus.Failed) + { + writer.Put(Error); + } + } + } +} diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 3e2205dd..67e837d9 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -11,6 +11,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Net; using TMPro; using UnityEngine; using UnityEngine.UI; @@ -104,6 +105,43 @@ private void CreateMatchMakerUI() fikaMatchMakerUi.StartButton.onClick.AddListener(() => { Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); + if (FikaPlugin.ForceIP.Value != "") + { + // We need to handle DNS entries as well + string ip = FikaPlugin.ForceIP.Value; + try + { + IPAddress[] dnsAddress = Dns.GetHostAddresses(FikaPlugin.ForceIP.Value); + if (dnsAddress.Length > 0) + { + ip = dnsAddress[0].ToString(); + } + } + catch + { + + } + + if (!IPAddress.TryParse(ip, out _)) + { + Singleton.Instance.ShowCriticalErrorScreen( + "ERROR FORCING IP", + $"'{ip}' is not a valid IP address to connect to! Check your 'Force IP' setting.", + ErrorScreen.EButtonType.OkButton, 10f, null, null); + return; + } + } + if (FikaPlugin.ForceBindIP.Value != "Disabled") + { + if (!IPAddress.TryParse(FikaPlugin.ForceBindIP.Value, out _)) + { + Singleton.Instance.ShowCriticalErrorScreen( + "ERROR BINDING", + $"'{FikaPlugin.ForceBindIP.Value}' is not a valid IP address to bind to! Check your 'Force Bind IP' setting.", + ErrorScreen.EButtonType.OkButton, 10f, null, null); + return; + } + } MatchmakerAcceptPatches.HostExpectedNumberOfPlayers = int.Parse(fikaMatchMakerUi.PlayerAmountText.text); MatchmakerAcceptPatches.CreateMatch(MatchmakerAcceptPatches.Profile.ProfileId, MatchmakerAcceptPatches.PMCName, RaidSettings); AcceptButton.OnClick.Invoke(); @@ -144,24 +182,29 @@ private void ManualRefresh() private IEnumerator JoinMatch(string profileId, string serverId, Button button) { - button.enabled = false; + if (button != null) + { + button.enabled = false; + } - NotificationManagerClass.DisplayMessageNotification("Connecting to server...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); + NotificationManagerClass.DisplayMessageNotification("Connecting to session...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); FikaPingingClient pingingClient = new(serverId); if (pingingClient.Init()) { int attempts = 0; - bool success = false; + bool success; + + FikaPlugin.Instance.FikaLogger.LogInfo("Attempting to connect to host session..."); do { attempts++; - if (pingingClient.PingEndPoint()) - { - pingingClient.NetClient.PollEvents(); - success = pingingClient.Received; - } + + pingingClient.PingEndPoint(); + pingingClient.NetClient.PollEvents(); + success = pingingClient.Received; + yield return new WaitForSeconds(0.1f); } while (!success && attempts < 50); @@ -172,7 +215,12 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) "Unable to connect to the server. Make sure that all ports are open and that all settings are configured correctly.", ErrorScreen.EButtonType.OkButton, 10f, null, null); - button.enabled = true; + FikaPlugin.Instance.FikaLogger.LogError("Unable to connect to the session!"); + + if (button != null) + { + button.enabled = true; + } yield break; } } diff --git a/Fika.Core/UI/OfflineDisplayProgressPatch.cs b/Fika.Core/UI/OfflineDisplayProgressPatch.cs deleted file mode 100644 index 53634bad..00000000 --- a/Fika.Core/UI/OfflineDisplayProgressPatch.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Aki.Reflection.Patching; -using EFT; -using System.Reflection; - -namespace Fika.Core.UI -{ - internal class OfflineDisplayProgressPatch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_47)); - - [PatchPrefix] - public static bool PatchPrefix(ref RaidSettings ____raidSettings) - { - ____raidSettings.RaidMode = ERaidMode.Local; - return true; - } - - [PatchPostfix] - public static void PatchPostfix(ref RaidSettings ____raidSettings) - { - ____raidSettings.RaidMode = ERaidMode.Local; - } - } -} diff --git a/Fika.Core/UI/OfflineSettingsScreenPatch.cs b/Fika.Core/UI/OfflineSettingsScreenPatch.cs deleted file mode 100644 index a79f0d88..00000000 --- a/Fika.Core/UI/OfflineSettingsScreenPatch.cs +++ /dev/null @@ -1,63 +0,0 @@ -/*using Aki.Reflection.Patching; -using EFT; -using EFT.UI; -using EFT.UI.Matchmaker; -using System.Linq; -using System.Reflection; -using UnityEngine; - -namespace Fika.Core.UI -{ - public class OfflineSettingsScreenPatch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(MatchmakerOfflineRaidScreen).GetMethods().Where(x => x.Name == "Show" && x.GetParameters()[0].Name == "profileInfo").FirstOrDefault(); - - [PatchPrefix] - public static bool Prefix(MatchmakerOfflineRaidScreen __instance, InfoClass profileInfo, RaidSettings raidSettings, - UpdatableToggle ____offlineModeToggle, DefaultUIButton ____changeSettingsButton, UiElementBlocker ____onlineBlocker, DefaultUIButton ____readyButton, DefaultUIButton ____nextButtonSpawner) - { - raidSettings.RaidMode = ERaidMode.Local; - RemoveBlockers(__instance, profileInfo, raidSettings, ____offlineModeToggle, ____changeSettingsButton, ____onlineBlocker, ____readyButton, ____nextButtonSpawner); - return true; - } - - [PatchPostfix] - public static void PatchPostfix( - MatchmakerOfflineRaidScreen __instance, InfoClass profileInfo, - RaidSettings raidSettings, UpdatableToggle ____offlineModeToggle, - DefaultUIButton ____changeSettingsButton, UiElementBlocker ____onlineBlocker, - DefaultUIButton ____readyButton, DefaultUIButton ____nextButtonSpawner) - { - var warningPanel = GameObject.Find("WarningPanelHorLayout"); - warningPanel?.SetActive(false); - var settingslayoutcon = GameObject.Find("NonLayoutContainer"); - settingslayoutcon?.SetActive(false); - var settingslist = GameObject.Find("RaidSettingsSummary"); - settingslist?.SetActive(false); - RemoveBlockers(__instance, profileInfo, raidSettings, ____offlineModeToggle, ____changeSettingsButton, ____onlineBlocker, ____readyButton, ____nextButtonSpawner); - - ____changeSettingsButton?.OnPointerClick(new UnityEngine.EventSystems.PointerEventData(null) { }); - } - - public static void RemoveBlockers(MatchmakerOfflineRaidScreen __instance, InfoClass profileInfo, - RaidSettings raidSettings, UpdatableToggle ____offlineModeToggle, - DefaultUIButton ____changeSettingsButton, UiElementBlocker ____onlineBlocker, - DefaultUIButton ____readyButton, DefaultUIButton ____nextButtonSpawner) - { - raidSettings.RaidMode = ERaidMode.Local; - raidSettings.BotSettings.BossType = EFT.Bots.EBossType.AsOnline; - raidSettings.WavesSettings.IsBosses = true; - raidSettings.WavesSettings.BotAmount = EFT.Bots.EBotAmount.Medium; - - ____onlineBlocker.RemoveBlock(); - ____onlineBlocker.enabled = false; - ____offlineModeToggle.isOn = true; - ____offlineModeToggle.enabled = false; - ____offlineModeToggle.interactable = false; - ____changeSettingsButton.Interactable = false; - ____changeSettingsButton.enabled = false; - ____readyButton.Interactable = false; - ____readyButton.enabled = false; - } - } -}*/ \ No newline at end of file diff --git a/Fika.Core/UI/DisableInsuranceReadyButtonPatch.cs b/Fika.Core/UI/Patches/DisableInsuranceReadyButtonPatch.cs similarity index 97% rename from Fika.Core/UI/DisableInsuranceReadyButtonPatch.cs rename to Fika.Core/UI/Patches/DisableInsuranceReadyButtonPatch.cs index 2009ea3a..77361692 100644 --- a/Fika.Core/UI/DisableInsuranceReadyButtonPatch.cs +++ b/Fika.Core/UI/Patches/DisableInsuranceReadyButtonPatch.cs @@ -5,7 +5,7 @@ using System.Reflection; using UnityEngine; -namespace Fika.Core.UI +namespace Fika.Core.UI.Patches { /// /// Created by: Lacyway diff --git a/Fika.Core/UI/DisableMatchSettingsReadyButton.cs b/Fika.Core/UI/Patches/DisableMatchSettingsReadyButton.cs similarity index 100% rename from Fika.Core/UI/DisableMatchSettingsReadyButton.cs rename to Fika.Core/UI/Patches/DisableMatchSettingsReadyButton.cs diff --git a/Fika.Core/UI/DisableReadyButtonPatch.cs b/Fika.Core/UI/Patches/DisableReadyButtonPatch.cs similarity index 100% rename from Fika.Core/UI/DisableReadyButtonPatch.cs rename to Fika.Core/UI/Patches/DisableReadyButtonPatch.cs diff --git a/References b/References index c79307a8..1064f858 160000 --- a/References +++ b/References @@ -1 +1 @@ -Subproject commit c79307a8011144ad54d2bc6afb1f7e46c607f71c +Subproject commit 1064f8589c9ce0b1d5c1ffe015e9c33f914f3f95