diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index a647a612..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "References"] - path = References - url = https://github.com/project-fika/References.git diff --git a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropParametersModel.cs b/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropParametersModel.cs deleted file mode 100644 index dd3f8ba6..00000000 --- a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropParametersModel.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Aki.Custom.Airdrops.Models; -using Newtonsoft.Json; -using UnityEngine; - -namespace Fika.Core.AkiSupport.Airdrops.Models -{ - /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/Models - /// Paulov: Property instead of Fields so I can easily Json the Model - /// - public class FikaAirdropParametersModel - { - public FikaAirdropConfigModel Config { get; set; } - public bool AirdropAvailable { get; set; } - public bool PlaneSpawned { get; set; } - public bool BoxSpawned { get; set; } - public float DistanceTraveled { get; set; } - public float DistanceToTravel { get; set; } - public float DistanceToDrop { get; set; } - public float Timer { get; set; } - public int DropHeight { get; set; } - public int TimeToStart { get; set; } - public Vector3 StartPosition { get; set; } - public Vector3 SpawnPoint { get; set; } - public Vector3 LookPoint { get; set; } - - [JsonIgnore] - public Vector3 RandomAirdropPoint { get; set; } = Vector3.zero; - - public float RandomAirdropPointX { get { return RandomAirdropPoint.x; } set { RandomAirdropPoint = new Vector3(value, RandomAirdropPoint.y, RandomAirdropPoint.z); } } - public float RandomAirdropPointY { get { return RandomAirdropPoint.y; } set { RandomAirdropPoint = new Vector3(RandomAirdropPoint.x, value, RandomAirdropPoint.z); } } - public float RandomAirdropPointZ { get { return RandomAirdropPoint.z; } set { RandomAirdropPoint = new Vector3(RandomAirdropPoint.x, RandomAirdropPoint.y, value); } } - } -} \ No newline at end of file diff --git a/Fika.Core/AkiSupport/Overrides/MaxBotPatch_Override.cs b/Fika.Core/AkiSupport/Overrides/MaxBotPatch_Override.cs deleted file mode 100644 index f6e7edac..00000000 --- a/Fika.Core/AkiSupport/Overrides/MaxBotPatch_Override.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Aki.Common.Http; -using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using Fika.Core.Coop.Matchmaker; -using System.Reflection; - -namespace Fika.Core.AkiSupport.Overrides -{ - internal class MaxBotPatch_Override : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; - const string methodName = "SetSettings"; - var desiredType = PatchConstants.EftTypes.SingleCustom(x => x.GetMethod(methodName, flags) != null && IsTargetMethod(x.GetMethod(methodName, flags))); - var desiredMethod = desiredType.GetMethod(methodName, flags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; - } - - private static bool IsTargetMethod(MethodInfo mi) - { - var parameters = mi.GetParameters(); - return parameters.Length == 3 && parameters[0].Name == "maxCount" && parameters[1].Name == "botPresets" && parameters[2].Name == "botScatterings"; - } - - [PatchPrefix] - private static void PatchPreFix(ref int maxCount) - { - if (MatchmakerAcceptPatches.IsServer) - { - if (int.TryParse(RequestHandler.GetJson("/singleplayer/settings/bot/maxCap"), out int parsedMaxCount)) - { - Logger.LogWarning($"Set max bot cap to: {parsedMaxCount}"); - maxCount = parsedMaxCount; - } - else - { - Logger.LogWarning($"Unable to parse data from singleplayer/settings/bot/maxCap, using existing map max of {maxCount}"); - } - } - else - { - maxCount = 0; - } - } - } -} diff --git a/Fika.Core/Bundles/Files/newmatchmakerui.bundle b/Fika.Core/Bundles/Files/newmatchmakerui.bundle index b4ebb1cb..b904ac13 100644 Binary files a/Fika.Core/Bundles/Files/newmatchmakerui.bundle and b/Fika.Core/Bundles/Files/newmatchmakerui.bundle differ diff --git a/Fika.Core/Console/FikaCommands.cs b/Fika.Core/Console/FikaCommands.cs index 3da46551..59e6bfa5 100644 --- a/Fika.Core/Console/FikaCommands.cs +++ b/Fika.Core/Console/FikaCommands.cs @@ -4,13 +4,14 @@ using EFT.UI; using Fika.Core.Coop.Components; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; namespace Fika.Core.Console { public class FikaCommands { +#if DEBUG [ConsoleCommand("bring", "", null, "Teleports all AI to yourself as the host", [])] public static void Bring() { @@ -30,7 +31,7 @@ public static void Bring() if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { int count = 0; foreach (CoopPlayer player in coopHandler.Players.Values) @@ -56,7 +57,7 @@ public static void Bring() } [ConsoleCommand("god", "", null, "Set god mode on/off", [])] - public static void God(bool state) + public static void God([ConsoleArgument(false, "true or false to toggle god mode")] bool state) { CoopGame coopGame = (CoopGame)Singleton.Instance; @@ -90,6 +91,27 @@ public static void God(bool state) ConsoleScreen.LogWarning("Could not find CoopHandler."); } } +#endif + + [ConsoleCommand("debug", "", null, "Toggle debug window", [])] + public static void Debug(bool state) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + + if (coopGame == null) + { + ConsoleScreen.LogWarning("You are not in a game."); + return; + } + + if (coopGame.Status != GameStatus.Started) + { + ConsoleScreen.LogWarning("Game is not running."); + return; + } + + coopGame.ToggleDebug(state); + } [ConsoleCommand("clear", "", null, "Clears the console output", [])] public static void Clear() diff --git a/Fika.Core/AkiSupport/Airdrops/FikaAirdropBox.cs b/Fika.Core/Coop/Airdrops/FikaAirdropBox.cs similarity index 95% rename from Fika.Core/AkiSupport/Airdrops/FikaAirdropBox.cs rename to Fika.Core/Coop/Airdrops/FikaAirdropBox.cs index 3a2ee31c..1be4574e 100644 --- a/Fika.Core/AkiSupport/Airdrops/FikaAirdropBox.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropBox.cs @@ -2,18 +2,18 @@ using EFT.Airdrop; using EFT.Interactive; using EFT.SynchronizableObjects; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AI; -namespace Fika.Core.AkiSupport.Airdrops +namespace Fika.Core.Coop.Airdrops { /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/AirdropBox.cs + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/AirdropBox.cs /// public class FikaAirdropBox : MonoBehaviour { @@ -79,7 +79,7 @@ private static async Task LoadCrate() IEasyAssets easyAssets = Singleton.Instance.EasyAssets; await easyAssets.Retain(AIRDROP_SOUNDS_PATH, null, null).LoadingJob; - Dictionary soundsDictionary = new Dictionary(); + Dictionary soundsDictionary = new(); AirdropSurfaceSet[] sets = easyAssets.GetAsset(AIRDROP_SOUNDS_PATH).Sets; foreach (AirdropSurfaceSet set in sets) { @@ -151,7 +151,7 @@ private void OnBoxLand(out float clipLength) Volume = surfaceSet.LandingSoundBank.BaseVolume }); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { AddNavMeshObstacle(); } @@ -171,7 +171,7 @@ private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo) private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo, Vector3 origin) { - Ray ray = new Ray(origin, Vector3.down); + Ray ray = new(origin, Vector3.down); bool raycast = Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerMask); if (!raycast) return false; diff --git a/Fika.Core/AkiSupport/Airdrops/FikaAirdropPlane.cs b/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs similarity index 94% rename from Fika.Core/AkiSupport/Airdrops/FikaAirdropPlane.cs rename to Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs index 84a275f4..17d80385 100644 --- a/Fika.Core/AkiSupport/Airdrops/FikaAirdropPlane.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs @@ -5,11 +5,11 @@ using System.Threading.Tasks; using UnityEngine; -namespace Fika.Core.AkiSupport.Airdrops +namespace Fika.Core.Coop.Airdrops { /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/AirdropPlane.cs + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/AirdropPlane.cs /// public class FikaAirdropPlane : MonoBehaviour { @@ -82,7 +82,7 @@ private void SetPosition(int dropHeight, Vector3 airdropPoint) transform.position = new Vector3(pointOnCircle.x, dropHeight, pointOnCircle.y); newPosition = transform.position; - Vector3 lookPoint = new Vector3(airdropPoint.x, dropHeight, airdropPoint.z); + Vector3 lookPoint = new(airdropPoint.x, dropHeight, airdropPoint.z); transform.LookAt(lookPoint); newRotation = lookPoint; } @@ -154,7 +154,7 @@ private IEnumerator ChangeHeading() private float EasingSmoothSquared(float x) { - return x < 0.5 ? x * x * 2 : (1 - (1 - x) * (1 - x) * 2); + return x < 0.5 ? x * x * 2 : 1 - (1 - x) * (1 - x) * 2; } } } \ No newline at end of file diff --git a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs index 32bc1dbd..596b3fc7 100644 --- a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs @@ -1,15 +1,13 @@ -using Aki.Custom.Airdrops.Models; -using Aki.Custom.Airdrops.Utils; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; using EFT; using EFT.InventoryLogic; -using Fika.Core.AkiSupport.Airdrops; -using Fika.Core.AkiSupport.Airdrops.Models; -using Fika.Core.AkiSupport.Airdrops.Utils; +using Fika.Core.Coop.Airdrops; +using Fika.Core.Coop.Airdrops.Models; +using Fika.Core.Coop.Airdrops.Utils; using Fika.Core.Coop.Components; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using LiteNetLib; using LiteNetLib.Utils; @@ -17,11 +15,11 @@ using System.Collections; using UnityEngine; -namespace Aki.Custom.Airdrops +namespace Coop.Airdrops { /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/AirdropsManager.cs + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/AirdropsManager.cs /// Modified by Lacyway and nexus4880: Uses BSG code to serialize/deserialize data from host to clients /// public class FikaAirdropsManager : MonoBehaviour @@ -84,7 +82,7 @@ protected async void Start() } // If this is not the server, then this manager will have to wait for the packet to initialize stuff. - if (MatchmakerAcceptPatches.IsClient) + if (FikaBackendUtils.IsClient) { return; } @@ -110,17 +108,15 @@ protected async void Start() try { - airdropPlane = await FikaAirdropPlane.Init( - AirdropParameters.RandomAirdropPoint, - AirdropParameters.DropHeight, - AirdropParameters.Config.PlaneVolume, + airdropPlane = await FikaAirdropPlane.Init(AirdropParameters.RandomAirdropPoint, + AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, AirdropParameters.Config.PlaneSpeed); AirdropBox = await FikaAirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); factory = new FikaItemFactoryUtil(); } catch { - Logger.LogError("[AKI-AIRDROPS]: Unable to create plane or crate, airdrop won't occur"); + Logger.LogError("[SPT-AIRDROPS]: Unable to create plane or crate, airdrop won't occur"); Destroy(this); throw; } @@ -132,7 +128,7 @@ protected async void Start() public void SendParamsToClients() { - if (!MatchmakerAcceptPatches.IsServer) + if (!FikaBackendUtils.IsServer) { return; } @@ -166,13 +162,13 @@ protected async void FixedUpdate() } // If we are a client. Wait until the server has sent all the data. - if (MatchmakerAcceptPatches.IsClient && rootItem == null) + if (FikaBackendUtils.IsClient && rootItem == null) { return; } // If we have all the parameters sent from the Server. Lets build the plane, box, container and loot - if (MatchmakerAcceptPatches.IsClient && !ClientLootBuilt) + if (FikaBackendUtils.IsClient && !ClientLootBuilt) { ClientLootBuilt = true; @@ -212,7 +208,7 @@ protected async void FixedUpdate() return; } - if (MatchmakerAcceptPatches.IsServer || MatchmakerAcceptPatches.IsSinglePlayer) + if (FikaBackendUtils.IsServer || FikaBackendUtils.IsSinglePlayer) { AirdropParameters.Timer += 0.02f; @@ -273,7 +269,7 @@ private void StartBox() private void BuildLootContainer(FikaAirdropConfigModel config) { - if (MatchmakerAcceptPatches.IsClient) + if (FikaBackendUtils.IsClient) { return; } @@ -301,7 +297,7 @@ private void BuildLootContainer(FikaAirdropConfigModel config) } // Get the lootData. Send to clients. - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { StartCoroutine(SendLootToClients(isFlareDrop)); } diff --git a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropConfigModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs similarity index 90% rename from Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropConfigModel.cs rename to Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs index 44eb19b4..db7c677b 100644 --- a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropConfigModel.cs +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs @@ -1,11 +1,11 @@ using Newtonsoft.Json; using System.Collections.Generic; -namespace Aki.Custom.Airdrops.Models +namespace Fika.Core.Coop.Airdrops.Models { /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/Models + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/Models /// public class FikaAirdropConfigModel { diff --git a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropLootModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs similarity index 79% rename from Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropLootModel.cs rename to Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs index b089f877..fa7ae4f1 100644 --- a/Fika.Core/AkiSupport/Airdrops/Models/FikaAirdropLootModel.cs +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs @@ -1,11 +1,11 @@ using Newtonsoft.Json; using System.Collections.Generic; -namespace Aki.Custom.Airdrops.Models +namespace Fika.Core.Coop.Airdrops.Models { /// - /// Created by: SPT-Aki team - /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/Models + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/Models /// public class FikaAirdropLootResultModel { diff --git a/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs new file mode 100644 index 00000000..f5f444da --- /dev/null +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using UnityEngine; + +namespace Fika.Core.Coop.Airdrops.Models +{ + /// + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/Models + /// + public class FikaAirdropParametersModel + { + public FikaAirdropConfigModel Config; + public bool AirdropAvailable; + public bool PlaneSpawned; + public bool BoxSpawned; + public float DistanceTraveled; + public float DistanceToTravel; + public float DistanceToDrop; + public float Timer; + public int DropHeight; + public int TimeToStart; + public Vector3 StartPosition; + public Vector3 SpawnPoint; + public Vector3 LookPoint; + + [JsonIgnore] + public Vector3 RandomAirdropPoint = Vector3.zero; + } +} \ No newline at end of file diff --git a/Fika.Core/AkiSupport/Airdrops/Utils/FikaAirdropUtil.cs b/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs similarity index 93% rename from Fika.Core/AkiSupport/Airdrops/Utils/FikaAirdropUtil.cs rename to Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs index 2ddf970b..acef3c5b 100644 --- a/Fika.Core/AkiSupport/Airdrops/Utils/FikaAirdropUtil.cs +++ b/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs @@ -1,17 +1,19 @@ -using Aki.Common.Http; -using Aki.Custom.Airdrops.Models; -using EFT; +using EFT; using EFT.Airdrop; -using Fika.Core.AkiSupport.Airdrops.Models; +using Fika.Core.Coop.Airdrops.Models; using Newtonsoft.Json; +using SPT.Common.Http; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using Random = UnityEngine.Random; -namespace Fika.Core.AkiSupport.Airdrops.Utils +namespace Fika.Core.Coop.Airdrops.Utils { + /// + /// Originally developed by SPT + /// public static class FikaAirdropUtil { public static FikaAirdropConfigModel AirdropConfigModel { get; private set; } @@ -119,7 +121,7 @@ public static FikaAirdropParametersModel InitAirdropParams(GameWorld gameWorld, if (flareAirdropPoints.Count == 0 && isFlare) { - Debug.LogError($"[AKI-AIRDROPS]: Airdrop called in by flare, Unable to find an airdropPoint within 100m, defaulting to normal drop"); + Debug.LogError($"[SPT-AIRDROPS]: Airdrop called in by flare, Unable to find an airdropPoint within 100m, defaulting to normal drop"); flareAirdropPoints.Add(allAirdropPoints.OrderBy(_ => Guid.NewGuid()).FirstOrDefault()); } diff --git a/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs b/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs similarity index 87% rename from Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs rename to Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs index ad94d25e..b913db85 100644 --- a/Fika.Core/AkiSupport/Airdrops/Utils/FikaItemFactoryUtil.cs +++ b/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs @@ -1,16 +1,19 @@ -using Aki.Common.Http; -using Aki.Custom.Airdrops.Models; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; +using Coop.Airdrops; using EFT; using EFT.Interactive; using EFT.InventoryLogic; -using Fika.Core; +using Fika.Core.Coop.Airdrops.Models; using Newtonsoft.Json; +using SPT.Common.Http; using System.Linq; -namespace Aki.Custom.Airdrops.Utils +namespace Fika.Core.Coop.Airdrops.Utils { + /// + /// Originally developed by SPT + /// public class FikaItemFactoryUtil { private readonly ItemFactory itemFactory; @@ -28,12 +31,12 @@ public void BuildContainer(LootableContainer container, FikaAirdropConfigModel c if (itemFactory.ItemTemplates.TryGetValue(containerId, out ItemTemplate template)) { Item item = itemFactory.CreateItem(containerId, template._id, null); - item.Id = Singleton.Instance.MainPlayer.GClass2761_0.NextId; + item.Id = Singleton.Instance.MainPlayer.InventoryControllerClass.NextId; LootItem.CreateLootContainer(container, item, "CRATE", Singleton.Instance); } else { - logSource.LogError($"[AKI-AIRDROPS]: unable to find template: {containerId}"); + logSource.LogError($"[SPT-AIRDROPS]: unable to find template: {containerId}"); } } diff --git a/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs b/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs index 1f06af9d..d709a9d8 100644 --- a/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs +++ b/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs @@ -1,7 +1,4 @@ -using Aki.Custom.BTR; -using Aki.Custom.BTR.Utils; -using Aki.SinglePlayer.Utils.TraderServices; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; using EFT; using EFT.AssetsManager; @@ -13,6 +10,9 @@ using Fika.Core.Networking; using HarmonyLib; using LiteNetLib.Utils; +using SPT.Custom.BTR; +using SPT.Custom.BTR.Utils; +using SPT.SinglePlayer.Utils.TraderServices; using System; using System.Collections.Generic; using System.Linq; @@ -25,6 +25,9 @@ namespace Fika.Core.Coop.BTR { + /// + /// Based on + /// internal class FikaBTRManager_Client : MonoBehaviour { private GameWorld gameWorld; @@ -37,9 +40,6 @@ internal class FikaBTRManager_Client : MonoBehaviour private BTRDataPacket btrDataPacket = default; private bool btrBotShooterInitialized = false; - private Coroutine _coverFireTimerCoroutine; - - private Coroutine _shootingTargetCoroutine; private BTRTurretServer btrTurretServer; private bool isTurretInDefaultRotation; private BulletClass btrMachineGunAmmo; @@ -86,7 +86,7 @@ private void Awake() } catch { - btrLogger.LogError("[AKI-BTR] Unable to spawn BTR. Check logs."); + btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); Destroy(this); throw; } @@ -112,7 +112,7 @@ public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPac // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` private bool IsUpdateTaxiPriceMethod(MethodInfo method) { - return (method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination)); + return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); } private void Update() @@ -173,7 +173,7 @@ public void AttachBot(int netId) weaponPrefab.transform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); weaponTransform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); - string[] gunModsToDisable = Traverse.Create(turretView).Field("_gunModsToDisable").GetValue(); + string[] gunModsToDisable = Traverse.Create(turretView).Field("_gunModsToDisable").Value; if (gunModsToDisable != null) { foreach (Transform child in weaponTransform) @@ -234,7 +234,7 @@ private void InitBtr() { // Initial setup botEventHandler = Singleton.Instance; - var botsController = Singleton.Instance.BotsController; + BotsController botsController = Singleton.Instance.BotsController; btrBotService = botsController.BotTradersServices.BTRServices; btrController.method_3(); // spawns server-side BTR game object //botsController.BotSpawner.SpawnBotBTR(); // spawns the scav bot which controls the BTR's turret @@ -257,7 +257,7 @@ private void InitBtr() btrServerSide.Initialization(btrMapConfig);*/ btrController.method_14(); // creates and assigns the BTR a fake stash - DisableServerSideRenderers(); + DisableServerSideObjects(); /*btrServerSide.MoveEnable();*/ btrServerSide.IncomingToDestinationEvent += ToDestinationEvent; @@ -269,7 +269,7 @@ private void InitBtr() // Initialise turret variables btrTurretServer = btrServerSide.BTRTurret; - var btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); + Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); @@ -281,7 +281,7 @@ private void InitBtr() private void ConfigureSettingsFromServer() { - var serverConfig = BTRUtil.GetConfigFromServer(); + SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); btrServerSide.moveSpeed = serverConfig.MoveSpeed; btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; @@ -360,8 +360,8 @@ private BTRDataPacket UpdateDataPacket() btrDataPacket.rotation = btrServerSide.transform.rotation; if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) { - btrDataPacket.turretRotation = btrTurretServer.transform.rotation; - btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.rotation; + btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; + btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; } btrDataPacket.State = (byte)btrServerSide.BtrState; btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; @@ -383,15 +383,28 @@ private BTRDataPacket UpdateDataPacket() return btrDataPacket; } - private void DisableServerSideRenderers() + private void DisableServerSideObjects() { - var meshRenderers = btrServerSide.transform.GetComponentsInChildren(); - foreach (var renderer in meshRenderers) + MeshRenderer[] meshRenderers = btrServerSide.transform.GetComponentsInChildren(); + foreach (MeshRenderer renderer in meshRenderers) { renderer.enabled = false; } btrServerSide.turnCheckerObject.GetComponent().enabled = false; // Disables the red debug sphere + + // For some reason the client BTR collider is disabled but the server collider is enabled. + // Initially we assumed there was a reason for this so it was left as is. + // Turns out disabling the server collider in favour of the client collider fixes the "BTR doing a wheelie" bug, + // while preventing the player from walking through the BTR. + const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; + Collider serverExteriorCollider = btrServerSide.GetComponentsInChildren(true) + .First(x => x.gameObject.name == exteriorColliderName); + Collider clientExteriorCollider = btrClientSide.GetComponentsInChildren(true) + .First(x => x.gameObject.name == exteriorColliderName); + + serverExteriorCollider.gameObject.SetActive(false); + clientExteriorCollider.gameObject.SetActive(true); } public void ClientInteraction(Player player, PlayerInteractPacket packet) @@ -399,7 +412,7 @@ public void ClientInteraction(Player player, PlayerInteractPacket packet) BTRView btrView = gameWorld.BtrController.BtrView; if (btrView == null) { - btrLogger.LogError("[AKI-BTR] BTRInteractionPatch - btrView is null"); + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); return; } @@ -448,9 +461,6 @@ private void OnDestroy() return; } - StaticManager.KillCoroutine(ref _shootingTargetCoroutine); - StaticManager.KillCoroutine(ref _coverFireTimerCoroutine); - if (TraderServicesManager.Instance != null) { TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased; @@ -458,13 +468,13 @@ private void OnDestroy() if (btrClientSide != null) { - Debug.LogWarning("[AKI-BTR] BTRManager - Destroying btrClientSide"); + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); Destroy(btrClientSide.gameObject); } if (btrServerSide != null) { - Debug.LogWarning("[AKI-BTR] BTRManager - Destroying btrServerSide"); + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); Destroy(btrServerSide.gameObject); } } diff --git a/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs b/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs index b5bcbb52..b199b8f2 100644 --- a/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs +++ b/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs @@ -1,7 +1,4 @@ -using Aki.Custom.BTR; -using Aki.Custom.BTR.Utils; -using Aki.SinglePlayer.Utils.TraderServices; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; using EFT; using EFT.GlobalEvents; @@ -13,6 +10,9 @@ using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; +using SPT.Custom.BTR; +using SPT.Custom.BTR.Utils; +using SPT.SinglePlayer.Utils.TraderServices; using System; using System.Collections; using System.Collections.Generic; @@ -24,6 +24,9 @@ namespace Fika.Core.Coop.BTR { + /// + /// Based on + /// internal class FikaBTRManager_Host : MonoBehaviour { private GameWorld gameWorld; @@ -108,7 +111,7 @@ private void Awake() } catch { - btrLogger.LogError("[AKI-BTR] Unable to spawn BTR. Check logs."); + btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); Destroy(this); throw; } @@ -223,7 +226,7 @@ public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPac // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` private bool IsUpdateTaxiPriceMethod(MethodInfo method) { - return (method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination)); + return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); } private void Update() @@ -260,7 +263,7 @@ private void InitBtr() { // Initial setup botEventHandler = Singleton.Instance; - var botsController = Singleton.Instance.BotsController; + BotsController botsController = Singleton.Instance.BotsController; btrBotService = botsController.BotTradersServices.BTRServices; btrController.method_3(); // spawns server-side BTR game object botsController.BotSpawner.SpawnBotBTR(); // spawns the scav bot which controls the BTR's turret @@ -278,12 +281,12 @@ private void InitBtr() // Get config from server and initialise respective settings ConfigureSettingsFromServer(); - var btrMapConfig = btrController.MapPathsConfiguration; + MapPathConfig btrMapConfig = btrController.MapPathsConfiguration; btrServerSide.CurrentPathConfig = btrMapConfig.PathsConfiguration.pathsConfigurations.RandomElement(); btrServerSide.Initialization(btrMapConfig); btrController.method_14(); // creates and assigns the BTR a fake stash - DisableServerSideRenderers(); + DisableServerSideObjects(); gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState; @@ -297,7 +300,7 @@ private void InitBtr() // Initialise turret variables btrTurretServer = btrServerSide.BTRTurret; - var btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); + Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); @@ -311,7 +314,7 @@ private void InitBtr() private void ConfigureSettingsFromServer() { - var serverConfig = BTRUtil.GetConfigFromServer(); + SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); btrServerSide.moveSpeed = serverConfig.MoveSpeed; btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; @@ -327,7 +330,7 @@ private void InitBtrBotService() { btrBotShooter = btrController.BotShooterBtr; firearmController = btrBotShooter.GetComponent(); - var weaponPrefab = (WeaponPrefab)AccessTools.Field(firearmController.GetType(), "weaponPrefab_0").GetValue(firearmController); + WeaponPrefab weaponPrefab = (WeaponPrefab)AccessTools.Field(firearmController.GetType(), "weaponPrefab_0").GetValue(firearmController); weaponSoundPlayer = weaponPrefab.GetComponent(); btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list @@ -434,7 +437,7 @@ private void StartCoverFireTimer(float time) private IEnumerator CoverFireTimer(float time) { - yield return new WaitForSecondsRealtime(time); + yield return new WaitForSeconds(time); botEventHandler.StopTraderServiceBtrSupport(); } @@ -470,7 +473,7 @@ private void UpdateBTRSideDoorState(byte state) } catch { - btrLogger.LogError("[AKI-BTR] lastInteractedBtrSide is null when it shouldn't be. Check logs."); + btrLogger.LogError("[SPT-BTR] lastInteractedBtrSide is null when it shouldn't be. Check logs."); throw; } } @@ -481,8 +484,8 @@ private BTRDataPacket UpdateDataPacket() btrDataPacket.rotation = btrServerSide.transform.rotation; if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) { - btrDataPacket.turretRotation = btrTurretServer.transform.rotation; - btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.rotation; + btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; + btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; } btrDataPacket.State = (byte)btrServerSide.BtrState; btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; @@ -520,15 +523,28 @@ private BTRDataPacket UpdateDataPacket() return btrDataPacket; } - private void DisableServerSideRenderers() + private void DisableServerSideObjects() { - var meshRenderers = btrServerSide.transform.GetComponentsInChildren(); - foreach (var renderer in meshRenderers) + MeshRenderer[] meshRenderers = btrServerSide.transform.GetComponentsInChildren(); + foreach (MeshRenderer renderer in meshRenderers) { renderer.enabled = false; } btrServerSide.turnCheckerObject.GetComponent().enabled = false; // Disables the red debug sphere + + // For some reason the client BTR collider is disabled but the server collider is enabled. + // Initially we assumed there was a reason for this so it was left as is. + // Turns out disabling the server collider in favour of the client collider fixes the "BTR doing a wheelie" bug, + // while preventing the player from walking through the BTR. + const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; + Collider serverExteriorCollider = btrServerSide.GetComponentsInChildren(true) + .First(x => x.gameObject.name == exteriorColliderName); + Collider clientExteriorCollider = btrClientSide.GetComponentsInChildren(true) + .First(x => x.gameObject.name == exteriorColliderName); + + serverExteriorCollider.gameObject.SetActive(false); + clientExteriorCollider.gameObject.SetActive(true); } private void UpdateTarget() @@ -596,7 +612,7 @@ private IEnumerator ShootMachineGun() { isShooting = true; - yield return new WaitForSecondsRealtime(machineGunAimDelay); + yield return new WaitForSeconds(machineGunAimDelay); if (currentTarget?.Person == null || currentTarget?.IsVisible == false || !btrBotShooter.BotBtrData.CanShoot()) { isShooting = false; @@ -604,7 +620,7 @@ private IEnumerator ShootMachineGun() } Transform machineGunMuzzle = btrTurretServer.machineGunLaunchPoint; - var ballisticCalculator = gameWorld.SharedBallisticsCalculator; + ISharedBallisticsCalculator ballisticCalculator = gameWorld.SharedBallisticsCalculator; int burstMin = Mathf.FloorToInt(machineGunBurstCount.x); int burstMax = Mathf.FloorToInt(machineGunBurstCount.y); @@ -622,11 +638,11 @@ private IEnumerator ShootMachineGun() firearmController.method_54(weaponSoundPlayer, btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, false); burstCount--; shotQueue.Enqueue(new(machineGunMuzzle.position, aimDirection)); - yield return new WaitForSecondsRealtime(0.092308f); // 650 RPM + yield return new WaitForSeconds(0.092308f); // 650 RPM } float waitTime = Random.Range(machineGunRecoveryTime.x, machineGunRecoveryTime.y); - yield return new WaitForSecondsRealtime(waitTime); + yield return new WaitForSeconds(waitTime); isShooting = false; } @@ -649,7 +665,7 @@ public bool HostInteraction(Player player, PlayerInteractPacket packet) BTRView btrView = gameWorld.BtrController.BtrView; if (btrView == null) { - btrLogger.LogError("[AKI-BTR] BTRInteractionPatch - btrView is null"); + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); return false; } @@ -668,7 +684,7 @@ public void HostObservedInteraction(Player player, PlayerInteractPacket packet) BTRView btrView = gameWorld.BtrController.BtrView; if (btrView == null) { - btrLogger.LogError("[AKI-BTR] BTRInteractionPatch - btrView is null"); + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); return; } @@ -736,13 +752,13 @@ private void OnDestroy() if (btrClientSide != null) { - Debug.LogWarning("[AKI-BTR] BTRManager - Destroying btrClientSide"); + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); Destroy(btrClientSide.gameObject); } if (btrServerSide != null) { - Debug.LogWarning("[AKI-BTR] BTRManager - Destroying btrServerSide"); + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); Destroy(btrServerSide.gameObject); } } diff --git a/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs index 15c779f2..4f105cb4 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs @@ -6,7 +6,7 @@ namespace Fika.Core.Coop.ClientClasses { - public sealed class CoopBotHealthController(Profile.GClass1756 healthInfo, Player player, InventoryControllerClass inventoryController, SkillManager skillManager, bool aiHealth) + public sealed class CoopBotHealthController(Profile.ProfileHealthClass healthInfo, Player player, InventoryControllerClass inventoryController, SkillManager skillManager, bool aiHealth) : PlayerHealthController(healthInfo, player, inventoryController, skillManager, aiHealth) { private readonly CoopBot coopBot = (CoopBot)player; diff --git a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs index 03410663..8afa6310 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs @@ -14,7 +14,7 @@ public class CoopBotInventoryController(Player player, Profile profile, bool exa { private readonly CoopBot CoopBot = (CoopBot)player; - public override void Execute(GClass2837 operation, [CanBeNull] Callback callback) + public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) { base.Execute(operation, callback); @@ -25,7 +25,7 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(operation, false)); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); byte[] opBytes = memoryStream.ToArray(); packet.ItemControllerExecutePacket = new() { diff --git a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs index 1263336e..849dcce2 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs @@ -6,7 +6,7 @@ namespace Fika.Core.Coop.ClientClasses { - public sealed class CoopClientHealthController(Profile.GClass1756 healthInfo, Player player, InventoryControllerClass inventoryController, SkillManager skillManager, bool aiHealth) + public sealed class CoopClientHealthController(Profile.ProfileHealthClass healthInfo, Player player, InventoryControllerClass inventoryController, SkillManager skillManager, bool aiHealth) : PlayerHealthController(healthInfo, player, inventoryController, skillManager, aiHealth) { private readonly CoopPlayer coopPlayer = (CoopPlayer)player; diff --git a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs index ae5d9882..9409d432 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs @@ -3,9 +3,10 @@ using EFT; using EFT.InventoryLogic; using EFT.UI; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using JetBrains.Annotations; using System.IO; using UnityEngine; @@ -15,9 +16,7 @@ namespace Fika.Core.Coop.ClientClasses public sealed class CoopClientInventoryController(Player player, Profile profile, bool examined) : Player.PlayerOwnerInventoryController(player, profile, examined) { public override bool HasDiscardLimits => false; - ManualLogSource BepInLogger { get; set; } = BepInEx.Logging.Logger.CreateLogSource(nameof(CoopClientInventoryController)); - private readonly Player Player = player; private CoopPlayer CoopPlayer => (CoopPlayer)Player; @@ -30,27 +29,44 @@ public override void CallMalfunctionRepaired(Weapon weapon) } } - public override void Execute(GClass2837 operation, [CanBeNull] Callback callback) + public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) { - // Do not replicate picking up quest items, throws an error on the other clients - if (operation is GClass2839 pickupOperation) +#if DEBUG + ConsoleScreen.Log("InvOperation: " + operation.GetType().Name); +#endif + + // Do not replicate picking up quest items, throws an error on the other clients + if (operation is InventoryItemFromToClass moveOperation) { - if (pickupOperation.Item.Template.QuestItem) + Item lootedItem = moveOperation.Item; + if (lootedItem.Template.QuestItem) { + if (CoopPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + if (!sharedQuestController.CheckForTemplateId(lootedItem.TemplateId)) + { + sharedQuestController.AddLootedTemplateId(lootedItem.TemplateId); + + // We use templateId because each client gets a unique itemId + QuestItemPacket packet = new(CoopPlayer.Profile.Info.MainProfileNickname, lootedItem.TemplateId); + CoopPlayer.PacketSender.SendQuestItemPacket(ref packet); + } + } base.Execute(operation, callback); return; } } - if (MatchmakerAcceptPatches.IsServer) + // Do not replicate quest operations + // Check for GClass increments + if (operation is GClass2897 or GClass2898 or QuestHandoverOperationClass) { - // Do not replicate quest operations - if (operation is GClass2866 or GClass2879) - { - base.Execute(operation, callback); - return; - } + base.Execute(operation, callback); + return; + } + if (FikaBackendUtils.IsServer) + { HostInventoryOperationManager operationManager = new(this, operation, callback); if (vmethod_0(operationManager.operation)) { @@ -63,7 +79,7 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(operation, false)); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); byte[] opBytes = memoryStream.ToArray(); packet.ItemControllerExecutePacket = new() { @@ -78,15 +94,8 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback operationManager.operation.Dispose(); operationManager.callback?.Fail($"Can't execute {operationManager.operation}", 1); } - else if (MatchmakerAcceptPatches.IsClient) + else if (FikaBackendUtils.IsClient) { - // Do not replicate quest operations - if (operation is GClass2866 or GClass2879) - { - base.Execute(operation, callback); - return; - } - InventoryPacket packet = new() { HasItemControllerExecutePacket = true @@ -99,12 +108,12 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback inventoryController = this }; - clientOperationManager.callback ??= new Callback(ClientPlayer.Control0.Class1400.class1400_0.method_0); + clientOperationManager.callback ??= new Callback(ClientPlayer.Control0.Class1426.class1426_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)); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); byte[] opBytes = memoryStream.ToArray(); packet.ItemControllerExecutePacket = new() { @@ -116,17 +125,17 @@ public override void Execute(GClass2837 operation, [CanBeNull] Callback callback } } - private uint AddOperationCallback(GClass2837 operation, Callback callback) + private uint AddOperationCallback(GClass2854 operation, Callback callback) { ushort id = operation.Id; CoopPlayer.OperationCallbacks.Add(id, callback); return id; } - private class HostInventoryOperationManager(CoopClientInventoryController inventoryController, GClass2837 operation, Callback callback) + private class HostInventoryOperationManager(CoopClientInventoryController inventoryController, GClass2854 operation, Callback callback) { public readonly CoopClientInventoryController inventoryController = inventoryController; - public GClass2837 operation = operation; + public GClass2854 operation = operation; public readonly Callback callback = callback; public void HandleResult(IResult result) @@ -143,7 +152,7 @@ private class ClientInventoryOperationManager { public EOperationStatus? serverOperationStatus; public EOperationStatus? localOperationStatus; - public GClass2837 operation; + public GClass2854 operation; public Callback callback; public CoopClientInventoryController inventoryController; diff --git a/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs new file mode 100644 index 00000000..56380e36 --- /dev/null +++ b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs @@ -0,0 +1,177 @@ +using EFT; +using EFT.InventoryLogic; +using EFT.Quests; +using Fika.Core.Coop.Players; +using Fika.Core.Networking.Packets; +using System; +using System.Collections.Generic; + +namespace Fika.Core.Coop.ClientClasses +{ + public sealed class CoopClientSharedQuestController(Profile profile, InventoryControllerClass inventoryController, + IQuestActions session, CoopPlayer player, bool fromServer = true) : LocalQuestControllerClass(profile, inventoryController, session, fromServer) + { + private readonly CoopPlayer player = player; + private readonly List lastFromNetwork = []; + private readonly HashSet acceptedTypes = []; + private readonly HashSet lootedTemplateIds = []; + + public override void Init() + { + base.Init(); + foreach (FikaPlugin.EQuestSharingTypes shareType in (FikaPlugin.EQuestSharingTypes[])Enum.GetValues(typeof(FikaPlugin.EQuestSharingTypes))) + { + if (FikaPlugin.QuestTypesToShareAndReceive.Value.HasFlag(shareType)) + { + switch (shareType) + { + case FikaPlugin.EQuestSharingTypes.Kill: + acceptedTypes.Add("Elimination"); + acceptedTypes.Add(shareType.ToString()); + break; + case FikaPlugin.EQuestSharingTypes.Item: + acceptedTypes.Add("FindItem"); + break; + case FikaPlugin.EQuestSharingTypes.Location: + acceptedTypes.Add("Exploration"); + acceptedTypes.Add("Discover"); + acceptedTypes.Add("VisitPlace"); + acceptedTypes.Add(shareType.ToString()); + break; + case FikaPlugin.EQuestSharingTypes.PlaceBeacon: + acceptedTypes.Add(shareType.ToString()); + break; + } + } + } + } + + public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify = true) + { + base.OnConditionValueChanged(conditional, status, condition, notify); + if (lastFromNetwork.Contains(condition.id)) + { + lastFromNetwork.Remove(condition.id); + return; + } + SendQuestPacket(conditional, condition); + } + + public void AddNetworkId(string id) + { + if (!lastFromNetwork.Contains(id)) + { + lastFromNetwork.Add(id); + } + } + + public void AddLootedTemplateId(string templateId) + { + if (!lootedTemplateIds.Contains(templateId)) + { + lootedTemplateIds.Add(templateId); + } + } + + public bool CheckForTemplateId(string templateId) + { + return lootedTemplateIds.Contains(templateId); + } + + private void SendQuestPacket(IConditionCounter conditional, Condition condition) + { + if (conditional is QuestClass quest) + { + TaskConditionCounterClass counter = quest.ConditionCountersManager.GetCounter(condition.id); + if (counter != null) + { + if (!ValidateQuestType(counter)) + { + return; + } + + QuestConditionPacket packet = new(player.Profile.Info.MainProfileNickname, counter.Id, counter.SourceId); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogInfo("SendQuestPacket: Sending quest progress"); +#endif + player.PacketSender.SendQuestPacket(ref packet); + } + } + } + + internal void ReceiveQuestPacket(ref QuestConditionPacket packet) + { + AddNetworkId(packet.Id); + foreach (QuestClass quest in Quests) + { + if (quest.Id == packet.SourceId && quest.QuestStatus == EQuestStatus.Started) + { + TaskConditionCounterClass counter = quest.ConditionCountersManager.GetCounter(packet.Id); + if (counter != null && !quest.CompletedConditions.Contains(counter.Id)) + { + if (!ValidateQuestType(counter)) + { + return; + } + + counter.Value++; + NotificationManagerClass.DisplayMessageNotification($"Received shared quest progression from {packet.Nickname}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + } + } + } + + internal void ReceiveQuestItemPacket(ref QuestItemPacket packet) + { + if (!string.IsNullOrEmpty(packet.ItemId)) + { + Item item = player.FindItem(packet.ItemId, true); + if (item != null) + { + InventoryControllerClass playerInventory = player.InventoryControllerClass; + GStruct414 pickupResult = InteractionsHandlerClass.QuickFindAppropriatePlace(item, playerInventory, + playerInventory.Inventory.Equipment.ToEnumerable(), + InteractionsHandlerClass.EMoveItemOrder.PickUp, true); + + if (pickupResult.Succeeded && playerInventory.CanExecute(pickupResult.Value)) + { + AddLootedTemplateId(item.TemplateId); + playerInventory.RunNetworkTransaction(pickupResult.Value); + NotificationManagerClass.DisplayMessageNotification($"{packet.Nickname} picked up {item.Name.Localized()}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + } + } + } + + /// + /// Validates quest typing, some quests use CounterCreator which we also need to validate. + /// + /// The counter to validate + /// Returns true if the quest type is valid, returns false if not + internal bool ValidateQuestType(TaskConditionCounterClass counter) + { + if (acceptedTypes.Contains(counter.Type)) + { + return true; + } + + if (counter.Type == "CounterCreator") + { + ConditionCounterCreator CounterCreator = (ConditionCounterCreator)counter.Template; + +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogInfo($"CoopClientSharedQuestController:: ValidateQuestType: CounterCreator Type {CounterCreator.type}"); +#endif + + if (acceptedTypes.Contains(CounterCreator.type.ToString())) + { + return true; + } + } + + return false; + } + } +} diff --git a/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs b/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs index ee90a2ee..3aedb6c2 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs @@ -3,7 +3,7 @@ namespace Fika.Core.Coop.ClientClasses { - public sealed class CoopClientStatisticsManager(Profile profile) : GClass1790() + public sealed class CoopClientStatisticsManager(Profile profile) : LocationStatisticsCollectorAbstractClass() { public Profile Profile = profile; @@ -17,7 +17,7 @@ public override void ShowStatNotification(LocalizationKey localizationKey1, Loca { if (value > 0) { - NotificationManagerClass.DisplayNotification(new GClass2040(localizationKey1, localizationKey2, value)); + NotificationManagerClass.DisplayNotification(new StatNotificationClass(localizationKey1, localizationKey2, value)); } } } diff --git a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs index d9db7d8b..819418e0 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs @@ -47,7 +47,7 @@ public override Dictionary GetOperationFactoryDe return operationFactoryDelegates; } - public Player.GClass1583 Weapon1() + public Player.GClass1594 Weapon1() { if (Item.ReloadMode == Weapon.EReloadMode.InternalMagazine && Item.Chambers.Length == 0) { @@ -60,32 +60,32 @@ public Player.GClass1583 Weapon1() return new FirearmClass2(this); } - public Player.GClass1583 Weapon2() + public Player.GClass1594 Weapon2() { return new FirearmClass1(this); } - public Player.GClass1583 Weapon3() + public Player.GClass1594 Weapon3() { if (Item.IsFlareGun) { - return new GClass1605(this); + return new GClass1616(this); } if (Item.IsOneOff) { - return new GClass1607(this); + return new GClass1618(this); } if (Item.ReloadMode == Weapon.EReloadMode.OnlyBarrel) { - return new GClass1604(this); + return new GClass1615(this); } - if (Item is GClass2696) + if (Item is GClass2711) { - return new GClass1603(this); + return new GClass1614(this); } if (!Item.BoltAction) { - return new GClass1601(this); + return new GClass1612(this); } return new FirearmClass4(this); } @@ -176,7 +176,6 @@ public override void DryShot(int chamberIndex = 0, bool underbarrelShot = false) HasShotInfo = true, ShotInfoPacket = new() { - IsPrimaryActive = true, ShotType = EShotType.DryFire, AmmoAfterShot = underbarrelShot ? 0 : Item.GetCurrentMagazineCount(), ChamberIndex = chamberIndex, @@ -198,9 +197,9 @@ public override bool ExamineWeapon() return flag; } - public override void InitiateShot(GInterface322 weapon, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, Vector3 fireportPosition, int chamberIndex, float overheat) + public override void InitiateShot(IWeapon weapon, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, Vector3 fireportPosition, int chamberIndex, float overheat) { - EShotType shotType = new(); + EShotType shotType = default; switch (weapon.MalfState.State) { @@ -229,7 +228,6 @@ public override void InitiateShot(GInterface322 weapon, BulletClass ammo, Vector HasShotInfo = true, ShotInfoPacket = new() { - IsPrimaryActive = (weapon == Item), ShotType = shotType, AmmoAfterShot = weapon.GetCurrentMagazineCount(), ShotPosition = shotPosition, @@ -270,7 +268,7 @@ public override void QuickReloadMag(MagazineClass magazine, Callback callback) }); } - public override void ReloadBarrels(GClass2495 ammoPack, GClass2769 placeToPutContainedAmmoMagazine, Callback callback) + public override void ReloadBarrels(AmmoPackReloadingClass ammoPack, ItemAddressClass placeToPutContainedAmmoMagazine, Callback callback) { if (!CanStartReload() && ammoPack.AmmoCount < 1) { @@ -281,7 +279,7 @@ public override void ReloadBarrels(GClass2495 ammoPack, GClass2769 placeToPutCon CurrentOperation.ReloadBarrels(ammoPack, placeToPutContainedAmmoMagazine, callback, new Callback(handler.Process)); } - public override void ReloadCylinderMagazine(GClass2495 ammoPack, Callback callback, bool quickReload = false) + public override void ReloadCylinderMagazine(AmmoPackReloadingClass ammoPack, Callback callback, bool quickReload = false) { if (Blindfire) { @@ -301,46 +299,28 @@ public override void ReloadCylinderMagazine(GClass2495 ammoPack, Callback callba CurrentOperation.ReloadCylinderMagazine(ammoPack, callback, new Callback(handler.Process), handler.quickReload); } - public override void ReloadGrenadeLauncher(GClass2495 ammoPack, Callback callback) + public override void ReloadGrenadeLauncher(AmmoPackReloadingClass ammoPack, Callback callback) { if (!CanStartReload()) { return; } + CurrentOperation.ReloadGrenadeLauncher(ammoPack, callback); + string[] reloadingAmmoIds = ammoPack.GetReloadingAmmoIds(); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() { - HasReloadLauncherPacket = true, ReloadLauncher = new() { Reload = true, AmmoIds = reloadingAmmoIds } }); - - CurrentOperation.ReloadGrenadeLauncher(ammoPack, callback); - } - - public override void UnderbarrelSightingRangeUp() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - UnderbarrelSightingRangeUp = true - }); - base.UnderbarrelSightingRangeUp(); - } - - public override void UnderbarrelSightingRangeDown() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - UnderbarrelSightingRangeDown = true - }); - base.UnderbarrelSightingRangeDown(); } - public override void ReloadMag(MagazineClass magazine, GClass2769 gridItemAddress, Callback callback) + public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback callback) { if (!CanStartReload() || Blindfire) { @@ -351,7 +331,7 @@ public override void ReloadMag(MagazineClass magazine, GClass2769 gridItemAddres CurrentOperation.ReloadMag(magazine, gridItemAddress, callback, new Callback(handler.Process)); } - public override void ReloadWithAmmo(GClass2495 ammoPack, Callback callback) + public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback callback) { if (Item.GetCurrentMagazine() == null) { @@ -366,7 +346,7 @@ public override void ReloadWithAmmo(GClass2495 ammoPack, Callback callback) CurrentOperation.ReloadWithAmmo(ammoPack, callback, new Callback(handler.Process)); } - public override void SetLightsState(GStruct163[] lightsStates, bool force = false) + public override void SetLightsState(FirearmLightStateStruct[] lightsStates, bool force = false) { if (force || CurrentOperation.CanChangeLightState(lightsStates)) { @@ -383,7 +363,24 @@ public override void SetLightsState(GStruct163[] lightsStates, bool force = fals base.SetLightsState(lightsStates, force); } - public override void SetScopeMode(GStruct164[] scopeStates) + public override void SetScopeMode(FirearmScopeStateStruct[] scopeStates) + { + SendScopeStates(scopeStates); + base.SetScopeMode(scopeStates); + } + public override void OpticCalibrationSwitchUp(FirearmScopeStateStruct[] scopeStates) + { + SendScopeStates(scopeStates); + base.OpticCalibrationSwitchUp(scopeStates); + } + + public override void OpticCalibrationSwitchDown(FirearmScopeStateStruct[] scopeStates) + { + SendScopeStates(scopeStates); + base.OpticCalibrationSwitchDown(scopeStates); + } + + private void SendScopeStates(FirearmScopeStateStruct[] scopeStates) { SendScopeStates(scopeStates); base.SetScopeMode(scopeStates); @@ -413,7 +410,7 @@ private void SendScopeStates(GStruct164[] scopeStates) ScopeStatesPacket = new() { Amount = scopeStates.Length, - GStruct164 = scopeStates + FirearmScopeStateStruct = scopeStates } }); } @@ -446,7 +443,6 @@ public override void ShotMisfired(BulletClass ammo, Weapon.EMalfunctionState mal HasShotInfo = true, ShotInfoPacket = new() { - IsPrimaryActive = true, ShotType = shotType, AmmoAfterShot = Item.GetCurrentMagazineCount(), Overheat = overheat, @@ -571,7 +567,7 @@ private void SendBoltActionReloadPacket() }); } - private class FirearmClass1(Player.FirearmController controller) : GClass1589(controller) + private class FirearmClass1(Player.FirearmController controller) : GClass1600(controller) { public override void SetTriggerPressed(bool pressed) { @@ -593,7 +589,7 @@ public override void SwitchToIdle() private CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; } - private class FirearmClass2(Player.FirearmController controller) : GClass1590(controller) + private class FirearmClass2(Player.FirearmController controller) : GClass1600(controller) { public override void SetTriggerPressed(bool pressed) { @@ -614,7 +610,7 @@ public override void SwitchToIdle() private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; } - private class FirearmClass3(Player.FirearmController controller) : GClass1591(controller) + private class FirearmClass3(Player.FirearmController controller) : GClass1601(controller) { public override void SetTriggerPressed(bool pressed) { @@ -635,7 +631,7 @@ public override void SwitchToIdle() private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; } - private class FirearmClass4(Player.FirearmController controller) : GClass1602(controller) + private class FirearmClass4(Player.FirearmController controller) : GClass1612(controller) { public override void Start() { @@ -655,7 +651,7 @@ public override void SetInventoryOpened(bool opened) SendBoltActionReloadPacket(true); } - public override void ReloadMag(MagazineClass magazine, GClass2769 gridItemAddress, Callback finishCallback, Callback startCallback) + public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback finishCallback, Callback startCallback) { base.ReloadMag(magazine, gridItemAddress, finishCallback, startCallback); SendBoltActionReloadPacket(true); @@ -667,7 +663,7 @@ public override void QuickReloadMag(MagazineClass magazine, Callback finishCallb SendBoltActionReloadPacket(true); } - public override void ReloadWithAmmo(GClass2495 ammoPack, Callback finishCallback, Callback startCallback) + public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback finishCallback, Callback startCallback) { base.ReloadWithAmmo(ammoPack, finishCallback, startCallback); SendBoltActionReloadPacket(true); @@ -692,15 +688,15 @@ public override void Reset() private bool hasSent; } - private class ReloadMagHandler(CoopPlayer coopPlayer, GClass2769 gridItemAddress, MagazineClass magazine) + private class ReloadMagHandler(CoopPlayer coopPlayer, ItemAddressClass gridItemAddress, MagazineClass magazine) { private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly GClass2769 gridItemAddress = gridItemAddress; + private readonly ItemAddressClass gridItemAddress = gridItemAddress; private readonly MagazineClass magazine = magazine; public void Process(IResult error) { - GClass1528 gridItemAddressDescriptor = (gridItemAddress == null) ? null : GClass1632.FromGridItemAddress(gridItemAddress); + GridItemAddressDescriptorClass gridItemAddressDescriptor = (gridItemAddress == null) ? null : FromObjectAbstractClass.FromGridItemAddress(gridItemAddress); using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); @@ -762,15 +758,15 @@ public void Process(IResult error) } } - private class ReloadBarrelsHandler(CoopPlayer coopPlayer, GClass2769 placeToPutContainedAmmoMagazine, GClass2495 ammoPack) + private class ReloadBarrelsHandler(CoopPlayer coopPlayer, ItemAddressClass placeToPutContainedAmmoMagazine, AmmoPackReloadingClass ammoPack) { private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly GClass2769 placeToPutContainedAmmoMagazine = placeToPutContainedAmmoMagazine; - private readonly GClass2495 ammoPack = ammoPack; + private readonly ItemAddressClass placeToPutContainedAmmoMagazine = placeToPutContainedAmmoMagazine; + private readonly AmmoPackReloadingClass ammoPack = ammoPack; public void Process(IResult error) { - GClass1528 gridItemAddressDescriptor = (placeToPutContainedAmmoMagazine == null) ? null : GClass1632.FromGridItemAddress(placeToPutContainedAmmoMagazine); + GridItemAddressDescriptorClass gridItemAddressDescriptor = (placeToPutContainedAmmoMagazine == null) ? null : FromObjectAbstractClass.FromGridItemAddress(placeToPutContainedAmmoMagazine); string[] ammoIds = ammoPack.GetReloadingAmmoIds(); diff --git a/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs b/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs new file mode 100644 index 00000000..6b0d1792 --- /dev/null +++ b/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs @@ -0,0 +1,59 @@ +using Comfort.Common; +using EFT; +using Fika.Core.Coop.Players; +using UnityEngine; + +namespace Fika.Core.Coop.ClientClasses +{ + public class NoInertiaPhysical : PlayerPhysicalClass + { + private CoopPlayer coopPlayer; + + public override void Init(IPlayer player) + { + base.Init(player); + coopPlayer = (CoopPlayer)player; + } + + public override void OnWeightUpdated() + { + BackendConfigSettingsClass.InertiaSettings inertia = Singleton.Instance.Inertia; + float num = iobserverToPlayerBridge_0.Skills.StrengthBuffElite ? coopPlayer.InventoryControllerClass.Inventory.TotalWeightEliteSkill : coopPlayer.InventoryControllerClass.Inventory.TotalWeight; + Inertia = 0.0113f; + SprintAcceleration = 0.9887f; + PreSprintAcceleration = 2.9853f; + float num2 = Mathf.Lerp(inertia.MinMovementAccelerationRangeRight.x, inertia.MaxMovementAccelerationRangeRight.x, Inertia); + float num3 = Mathf.Lerp(inertia.MinMovementAccelerationRangeRight.y, inertia.MaxMovementAccelerationRangeRight.y, Inertia); + EFTHardSettings.Instance.MovementAccelerationRange.MoveKey(1, new Keyframe(num2, num3)); + Overweight = BaseOverweightLimits.InverseLerp(num); + WalkOverweight = WalkOverweightLimits.InverseLerp(num); + WalkSpeedLimit = 1f; + float_3 = SprintOverweightLimits.InverseLerp(num); + MoveSideInertia = 1.9887f; + MoveDiagonalInertia = 1.3955f; + if (coopPlayer.IsAI) + { + float_3 = 0f; + } + MaxPoseLevel = (Overweight >= 1f) ? 0.9f : 1f; + Consumptions[EConsumptionType.OverweightIdle].SetActive(this, Overweight >= 1f); + Consumptions[EConsumptionType.OverweightIdle].Delta.SetDirty(); + Consumptions[EConsumptionType.SitToStand].AllowsRestoration = Overweight >= 1f; + Consumptions[EConsumptionType.StandUp].AllowsRestoration = Overweight >= 1f; + Consumptions[EConsumptionType.Walk].Delta.SetDirty(); + Consumptions[EConsumptionType.Sprint].Delta.SetDirty(); + Consumptions[EConsumptionType.VaultLegs].Delta.SetDirty(); + Consumptions[EConsumptionType.VaultHands].Delta.SetDirty(); + Consumptions[EConsumptionType.ClimbLegs].Delta.SetDirty(); + Consumptions[EConsumptionType.ClimbHands].Delta.SetDirty(); + TransitionSpeed.SetDirty(); + PoseLevelDecreaseSpeed.SetDirty(); + PoseLevelIncreaseSpeed.SetDirty(); + FallDamageMultiplier = Mathf.Lerp(1f, StaminaParameters.FallDamageMultiplier, Overweight); + SoundRadius = StaminaParameters.SoundRadius.Evaluate(Overweight); + MinStepSound.SetDirty(); + method_3(); + method_7(num); + } + } +} diff --git a/Fika.Core/Coop/Components/CoopExfilManager.cs b/Fika.Core/Coop/Components/CoopExfilManager.cs index 66bfc26c..6abd8740 100644 --- a/Fika.Core/Coop/Components/CoopExfilManager.cs +++ b/Fika.Core/Coop/Components/CoopExfilManager.cs @@ -2,8 +2,8 @@ using EFT; using EFT.Interactive; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using LiteNetLib; using LiteNetLib.Utils; @@ -99,9 +99,9 @@ public void Run(ExfiltrationPoint[] exfilPoints) exfiltrationPoint.OnCancelExtraction += ExfiltrationPoint_OnCancelExtraction; exfiltrationPoint.OnStatusChanged += ExfiltrationPoint_OnStatusChanged; game.UpdateExfiltrationUi(exfiltrationPoint, false, true); - if (FikaPlugin.Instance.DynamicVExfils && exfiltrationPoint.Settings.PlayersCount > 0 && exfiltrationPoint.Settings.PlayersCount < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers) + if (FikaPlugin.Instance.DynamicVExfils && exfiltrationPoint.Settings.PlayersCount > 0 && exfiltrationPoint.Settings.PlayersCount < FikaBackendUtils.HostExpectedNumberOfPlayers) { - exfiltrationPoint.Settings.PlayersCount = MatchmakerAcceptPatches.HostExpectedNumberOfPlayers; + exfiltrationPoint.Settings.PlayersCount = FikaBackendUtils.HostExpectedNumberOfPlayers; } } @@ -204,11 +204,11 @@ private void ExfiltrationPoint_OnStatusChanged(ExfiltrationPoint point, EExfiltr NetDataWriter writer = mainPlayer.PacketSender.Writer; writer.Reset(); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { mainPlayer.PacketSender.Server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); } - else if (MatchmakerAcceptPatches.IsClient) + else if (FikaBackendUtils.IsClient) { mainPlayer.PacketSender.Client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); } diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index b881b263..8361ae79 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -5,11 +5,9 @@ using EFT.InventoryLogic; using Fika.Core.Coop.BTR; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; -using Fika.Core.Networking.Http; -using Fika.Core.Networking.Http.Models; using LiteNetLib; using LiteNetLib.Utils; using System; @@ -28,22 +26,16 @@ public class CoopHandler : MonoBehaviour { #region Fields/Properties public Dictionary ListOfInteractiveObjects { get; private set; } = []; + public CoopGame LocalGameInstance { get; internal set; } public string ServerId { get; set; } = null; - /// - /// ProfileId to Player instance - /// - public Dictionary Players { get; } = new(); + public Dictionary Players = []; public int HumanPlayers = 1; - public List ExtractedPlayers { get; set; } = []; + public List ExtractedPlayers = []; ManualLogSource Logger; public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - public List queuedProfileIds = []; private Queue spawnQueue = new(50); - //private Thread loopThread; - //private CancellationTokenSource loopToken; - public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool isAI, int netId) { public Profile Profile { get; set; } = profile; @@ -60,8 +52,6 @@ public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool i internal static GameObject CoopHandlerParent; - private Coroutine PingRoutine; - #endregion #region Public Voids @@ -93,7 +83,7 @@ public static string GetServerId() CoopHandler coopGC = GetCoopHandler(); if (coopGC == null) { - return MatchmakerAcceptPatches.GetGroupId(); + return FikaBackendUtils.GetGroupId(); } return coopGC.ServerId; @@ -117,12 +107,7 @@ protected void Awake() /// protected void Start() { - if (MatchmakerAcceptPatches.IsServer) - { - PingRoutine = StartCoroutine(PingServer()); - } - - if (MatchmakerAcceptPatches.IsClient) + if (FikaBackendUtils.IsClient) { _ = Task.Run(ReadFromServerCharactersLoop); } @@ -136,23 +121,6 @@ protected void Start() } } - private IEnumerator PingServer() - { - //string serialized = new PingRequest().ToJson(); - PingRequest pingRequest = new(); - - while (true) - { - yield return new WaitForSeconds(30); - //RequestHandler.PutJson("/fika/update/ping", serialized); - Task pingTask = FikaRequestHandler.UpdatePing(pingRequest); - while (!pingTask.IsCompleted) - { - yield return null; - } - } - } - protected void OnDestroy() { Players.Clear(); @@ -160,10 +128,6 @@ protected void OnDestroy() RunAsyncTasks = false; StopCoroutine(ProcessSpawnQueue()); - if (PingRoutine != null) - { - StopCoroutine(PingRoutine); - } } private bool requestQuitGame = false; @@ -233,9 +197,10 @@ void ProcessQuitting() if (FikaPlugin.ExtractKey.Value.IsDown() && quitState != EQuitState.NONE && !requestQuitGame) { requestQuitGame = true; + CoopGame coopGame = (CoopGame)Singleton.Instance; // If you are the server / host - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { // A host needs to wait for the team to extract or die! if ((Singleton.Instance.NetServer.ConnectedPeersCount > 0) && quitState != EQuitState.NONE) @@ -244,7 +209,9 @@ void ProcessQuitting() requestQuitGame = false; return; } - else if (Singleton.Instance.NetServer.ConnectedPeersCount == 0 && Singleton.Instance.timeSinceLastPeerDisconnected > DateTime.Now.AddSeconds(-5) && Singleton.Instance.hasHadPeer) + else if (Singleton.Instance.NetServer.ConnectedPeersCount == 0 + && Singleton.Instance.timeSinceLastPeerDisconnected > DateTime.Now.AddSeconds(-5) + && Singleton.Instance.hasHadPeer) { NotificationManagerClass.DisplayWarningNotification($"HOSTING: Please wait at least 5 seconds after the last peer disconnected before quitting."); requestQuitGame = false; @@ -252,16 +219,12 @@ void ProcessQuitting() } else { - Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, - Singleton.Instance.MyExitStatus, - MyPlayer.ActiveHealthController.IsAlive ? Singleton.Instance.MyExitLocation : null, 0); + coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); } } else { - Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, - Singleton.Instance.MyExitStatus, - MyPlayer.ActiveHealthController.IsAlive ? Singleton.Instance.MyExitLocation : null, 0); + coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); } return; } @@ -283,7 +246,7 @@ private async Task ReadFromServerCharactersLoop() { while (RunAsyncTasks) { - CoopGame coopGame = (CoopGame)Singleton.Instance; + CoopGame coopGame = LocalGameInstance; int waitTime = 2500; if (coopGame.Status == GameStatus.Started) { @@ -321,24 +284,31 @@ private void ReadFromServerCharacters() private async void SpawnPlayer(SpawnObject spawnObject) { - if (Singleton.Instance.RegisteredPlayers.Any(x => x.ProfileId == spawnObject.Profile.ProfileId)) + if (spawnObject.Profile == null) { + Logger.LogError("SpawnPlayer Profile is NULL!"); + queuedProfileIds.Remove(spawnObject.Profile.ProfileId); return; } - if (Singleton.Instance.AllAlivePlayersList.Any(x => x.ProfileId == spawnObject.Profile.ProfileId)) + foreach (IPlayer player in Singleton.Instance.RegisteredPlayers) { - return; + if (player.ProfileId == spawnObject.Profile.ProfileId) + { + return; + } } - int playerId = Players.Count + Singleton.Instance.RegisteredPlayers.Count + 1; - if (spawnObject.Profile == null) + foreach (IPlayer player in Singleton.Instance.AllAlivePlayersList) { - Logger.LogError("SpawnPlayer Profile is NULL!"); - queuedProfileIds.Remove(spawnObject.Profile.ProfileId); - return; + if (player.ProfileId == spawnObject.Profile.ProfileId) + { + return; + } } + int playerId = Players.Count + Singleton.Instance.RegisteredPlayers.Count + 1; + IEnumerable allPrefabPaths = spawnObject.Profile.GetAllPrefabPaths(); if (allPrefabPaths.Count() == 0) { @@ -347,9 +317,7 @@ private async void SpawnPlayer(SpawnObject spawnObject) } await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, - PoolManager.AssemblyType.Local, - allPrefabPaths.ToArray(), - JobPriority.General).ContinueWith(x => + PoolManager.AssemblyType.Local, allPrefabPaths.ToArray(), JobPriority.General).ContinueWith(x => { if (x.IsCompleted) { @@ -372,12 +340,11 @@ await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.Pool // TODO: Spawn them as corpses? } - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { if (LocalGameInstance != null) { - CoopGame coopGame = (CoopGame)LocalGameInstance; - BotsController botController = coopGame.BotsController; + BotsController botController = LocalGameInstance.BotsController; if (botController != null) { // Start Coroutine as botController might need a while to start sometimes... @@ -424,14 +391,20 @@ private IEnumerator ProcessSpawnQueue() public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAlive = true, bool isAI = false) { - if (Singleton.Instance.RegisteredPlayers.Any(x => x.ProfileId == profile.ProfileId)) + foreach (IPlayer player in Singleton.Instance.RegisteredPlayers) { - return; + if (player.ProfileId == profile.ProfileId) + { + return; + } } - if (Singleton.Instance.AllAlivePlayersList.Any(x => x.ProfileId == profile.ProfileId)) + foreach (IPlayer player in Singleton.Instance.AllAlivePlayersList) { - return; + if (player.ProfileId == profile.ProfileId) + { + return; + } } if (queuedProfileIds.Contains(profile.ProfileId)) @@ -455,13 +428,13 @@ public WorldInteractiveObject GetInteractiveObject(string objectId, out WorldInt private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position, int playerId, bool isAI, int netId) { - ObservedCoopPlayer otherPlayer = ObservedCoopPlayer.CreateObservedPlayer(playerId, position, Quaternion.identity, - "Player", isAI == true ? "Bot_" : $"Player_{profile.Nickname}_", EPointOfView.ThirdPerson, profile, isAI, - EUpdateQueue.Update, Player.EUpdateMode.Manual, Player.EUpdateMode.Auto, - GClass549.Config.CharacterController.ObservedPlayerMode, + ObservedCoopPlayer otherPlayer = ObservedCoopPlayer.CreateObservedPlayer(playerId, position, + Quaternion.identity, "Player", isAI == true ? "Bot_" : $"Player_{profile.Nickname}_", + EPointOfView.ThirdPerson, profile, isAI, EUpdateQueue.Update, Player.EUpdateMode.Manual, + Player.EUpdateMode.Auto, BackendConfigAbstractClass.Config.CharacterController.ObservedPlayerMode, () => Singleton.Instance.Control.Settings.MouseSensitivity, () => Singleton.Instance.Control.Settings.MouseAimingSensitivity, - GClass1446.Default).Result; + GClass1457.Default).Result; if (otherPlayer == null) { @@ -501,7 +474,7 @@ private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position if (playerCollider != null && otherCollider != null) { - GClass649.IgnoreCollision(playerCollider, otherCollider); + EFTPhysicsClass.IgnoreCollision(playerCollider, otherCollider); } } @@ -510,7 +483,9 @@ private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position if (profile.Info.Side is EPlayerSide.Bear or EPlayerSide.Usec) { Item backpack = profile.Inventory.Equipment.GetSlot(EquipmentSlot.Backpack).ContainedItem; - backpack?.GetAllItems().Where(i => i != backpack).ExecuteForEach(i => i.SpawnedInSession = true); + backpack?.GetAllItems() + .Where(i => i != backpack) + .ExecuteForEach(i => i.SpawnedInSession = true); // We still want DogTags to be 'FiR' Item item = otherPlayer.Inventory.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; @@ -536,14 +511,14 @@ private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position Logger.LogDebug($"CreateLocalPlayer::{profile.Info.Nickname}::Spawned."); - SetWeaponInHandsOfNewPlayer(otherPlayer, () => { }); + SetWeaponInHandsOfNewPlayer(otherPlayer); return otherPlayer; } private IEnumerator AddClientToBotEnemies(BotsController botController, LocalPlayer playerToAdd) { - CoopGame coopGame = (CoopGame)LocalGameInstance; + CoopGame coopGame = LocalGameInstance; Logger.LogInfo($"AddClientToBotEnemies: " + playerToAdd.Profile.Nickname); @@ -584,13 +559,13 @@ private IEnumerator AddClientToBotEnemies(BotsController botController, LocalPla /// /// Attempts to set up the New Player with the current weapon after spawning /// - /// The player to set the item on - public void SetWeaponInHandsOfNewPlayer(Player person, Action successCallback) + /// The player to set the item on + public void SetWeaponInHandsOfNewPlayer(Player player) { - EquipmentClass equipment = person.Profile.Inventory.Equipment; + EquipmentClass equipment = player.Profile.Inventory.Equipment; if (equipment == null) { - Logger.LogError($"SetWeaponInHandsOfNewPlayer: {person.Profile.ProfileId} has no Equipment!"); + Logger.LogError($"SetWeaponInHandsOfNewPlayer: {player.Profile.Nickname}, {player.Profile.ProfileId} has no Equipment!"); } Item item = null; @@ -616,38 +591,16 @@ public void SetWeaponInHandsOfNewPlayer(Player person, Action successCallback) if (item == null) { - Logger.LogError($"SetWeaponInHandsOfNewPlayer:Unable to find any weapon for {person.Profile.ProfileId}"); + Logger.LogError($"SetWeaponInHandsOfNewPlayer: Unable to find any weapon for {player.Profile.Nickname}, {player.Profile.ProfileId}"); } - person.SetItemInHands(item, (IResult) => + player.SetItemInHands(item, (IResult) => { if (IResult.Failed == true) { - Logger.LogError($"SetWeaponInHandsOfNewPlayer:Unable to set item {item} in hands for {person.Profile.ProfileId}"); - } - - if (IResult.Succeed == true) - { - successCallback?.Invoke(); - } - - if (person.TryGetItemInHands() != null) - { - successCallback?.Invoke(); + Logger.LogError($"SetWeaponInHandsOfNewPlayer: Unable to set item {item} in hands for {player.Profile.Nickname}, {player.Profile.ProfileId}"); } }); } - - public BaseLocalGame LocalGameInstance { get; internal set; } - } - - public enum ESpawnState - { - None = 0, - Loading = 1, - Spawning = 2, - Spawned = 3, - Ignore = 98, - Error = 99, } } diff --git a/Fika.Core/Coop/Components/CoopTimeManager.cs b/Fika.Core/Coop/Components/CoopTimeManager.cs new file mode 100644 index 00000000..1dfb9f95 --- /dev/null +++ b/Fika.Core/Coop/Components/CoopTimeManager.cs @@ -0,0 +1,33 @@ +using Comfort.Common; +using EFT; +using Fika.Core.Coop.GameMode; +using Fika.Core.Coop.Players; +using UnityEngine; + +namespace Fika.Core.Coop.Components +{ + internal class CoopTimeManager : MonoBehaviour + { + public CoopGame CoopGame; + public GameTimerClass GameTimer; + + public static CoopTimeManager Create(CoopGame game) + { + CoopTimeManager timeManager = game.gameObject.AddComponent(); + timeManager.CoopGame = game; + timeManager.GameTimer = game.GameTimer; + return timeManager; + } + + protected void Update() + { + if (CoopGame.Status == GameStatus.Started && GameTimer != null && GameTimer.SessionTime != null && GameTimer.PastTime >= GameTimer.SessionTime) + { + CoopGame.MyExitStatus = ExitStatus.MissingInAction; + CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + CoopGame.Extract(coopPlayer, null); + enabled = false; + } + } + } +} diff --git a/Fika.Core/Coop/Components/FikaPinger.cs b/Fika.Core/Coop/Components/FikaPinger.cs new file mode 100644 index 00000000..aa32d7e4 --- /dev/null +++ b/Fika.Core/Coop/Components/FikaPinger.cs @@ -0,0 +1,50 @@ +using Fika.Core.Networking.Http; +using Fika.Core.Networking.Http.Models; +using System.Collections; +using System.Threading.Tasks; +using UnityEngine; + +namespace Fika.Core.Coop.Components +{ + /// + /// Used to ping the backend every 30 seconds to keep the session alive + /// + public class FikaPinger : MonoBehaviour + { + private Coroutine pingRoutine; + + public void StartPingRoutine() + { + pingRoutine = StartCoroutine(PingServer()); + } + + public void StopPingRoutine() + { + if (pingRoutine != null) + { + StopCoroutine(pingRoutine); + pingRoutine = null; + } + } + + private IEnumerator PingServer() + { + PingRequest pingRequest = new(); + + while (true) + { + Task pingTask = FikaRequestHandler.UpdatePing(pingRequest); + while (!pingTask.IsCompleted) + { + yield return null; + } + yield return new WaitForSeconds(30); + } + } + + private void OnDestroy() + { + StopPingRoutine(); + } + } +} diff --git a/Fika.Core/Coop/Custom/FikaChat.cs b/Fika.Core/Coop/Custom/FikaChat.cs new file mode 100644 index 00000000..5fa4a36e --- /dev/null +++ b/Fika.Core/Coop/Custom/FikaChat.cs @@ -0,0 +1,138 @@ +using Comfort.Common; +using EFT.UI; +using Fika.Core.Coop.Utils; +using Fika.Core.Networking; +using Fika.Core.Networking.Packets.Communication; +using HarmonyLib; +using LiteNetLib.Utils; +using UnityEngine; + +namespace Fika.Core.Coop.Custom +{ + internal class FikaChat : MonoBehaviour + { + private Rect windowRect; + private string nickname; + private string chatText; + private string textField; + private bool show; + private bool isServer; + private NetDataWriter writer; + private UISoundsWrapper soundsWrapper; + + protected void Awake() + { + windowRect = new(20, Screen.height - 260, 600, 250); + nickname = FikaBackendUtils.PMCName; + chatText = string.Empty; + textField = string.Empty; + show = false; + isServer = FikaBackendUtils.IsServer; + writer = new(); + GUISounds guiSounds = Singleton.Instance; + soundsWrapper = Traverse.Create(guiSounds).Field("uisoundsWrapper_0").Value; + } + + protected void Update() + { + if (Input.GetKeyDown(FikaPlugin.ChatKey.Value.MainKey)) + { + ToggleVisibility(); + } + } + + protected void OnGUI() + { + if (!show) + { + return; + } + + GUI.skin.label.alignment = TextAnchor.LowerLeft; + GUI.skin.window.alignment = TextAnchor.UpperLeft; + GUI.Window(0, windowRect, DrawWindow, "Fika Chat"); + + if (Event.current.isKey) + { + if (Event.current.keyCode is KeyCode.Return or KeyCode.KeypadEnter && show) + { + SendMessage(); + GUI.UnfocusWindow(); + GUI.FocusControl(null); + } + } + } + + private void ToggleVisibility() + { + show = !show; + } + + private void SendMessage() + { + if (!show) + { + return; + } + + if (!string.IsNullOrEmpty(textField)) + { + string message = textField; + textField = string.Empty; + + if (message.Length > 100) + { + message = message.Substring(0, 100); + } + + TextMessagePacket packet = new(nickname, message); + writer.Reset(); + + if (isServer) + { + Singleton.Instance.SendDataToAll(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); + } + else + { + Singleton.Instance.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); + } + AudioClip outgoingClip = soundsWrapper.GetSocialNetworkClip(ESocialNetworkSoundType.OutgoingMessage); + Singleton.Instance.PlaySound(outgoingClip); + AddMessage(nickname + ": " + message); + } + } + + public void ReceiveMessage(string nickname, string message) + { + AudioClip incomingClip = soundsWrapper.GetSocialNetworkClip(ESocialNetworkSoundType.IncomingMessage); + Singleton.Instance.PlaySound(incomingClip); + AddMessage(nickname + ": " + message); + } + + private void AddMessage(string message) + { + chatText = string.Concat(chatText, message, "\n"); + if (chatText.Length > 1000) + { + chatText = chatText.Substring(500); + } + } + + private void DrawWindow(int windowId) + { + Rect rect = new(5, 15, 580, 200); + GUI.Label(rect, chatText); + rect.y += rect.height; + Rect textRect = new(rect.x, rect.y, rect.width - 55, 25); + textField = GUI.TextField(textRect, textField); + rect.x += 535; + Rect buttonRect = new(rect.x, rect.y, 50, 25); + if (GUI.Button(buttonRect, "SEND")) + { + SendMessage(); + GUI.UnfocusWindow(); + GUI.FocusControl(null); + } + } + } +} diff --git a/Fika.Core/Coop/Custom/FikaDebug.cs b/Fika.Core/Coop/Custom/FikaDebug.cs new file mode 100644 index 00000000..dc945d5c --- /dev/null +++ b/Fika.Core/Coop/Custom/FikaDebug.cs @@ -0,0 +1,163 @@ +using Comfort.Common; +using Fika.Core.Coop.Components; +using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; +using Fika.Core.Networking; +using System.Collections.Generic; +using UnityEngine; + +namespace Fika.Core.Coop.Custom +{ + internal class FikaDebug : MonoBehaviour + { + private CoopHandler coopHandler; + private Rect windowRect = new(20, 20, 200, 200); + private int frameCounter = 0; + + private int Ping + { + get + { + return Singleton.Instance.Ping; + } + } + + private int RTT + { + get + { + return Singleton.Instance.NetClient.FirstPeer.RoundTripTime; + } + } + + private bool isServer = false; + + private List alivePlayers; + private List aliveBots; + + protected void Awake() + { + coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler == null) + { + FikaPlugin.Instance.FikaLogger.LogError("FikaDebug: CoopHandlera was null!"); + Destroy(this); + } + + if (FikaBackendUtils.IsServer) + { + isServer = true; + } + + alivePlayers = []; + aliveBots = []; + + enabled = false; + } + + protected void Update() + { + frameCounter++; + if (frameCounter % 300 == 0) + { + frameCounter = 0; + + CheckAndAdd(); + } + } + + private void CheckAndAdd() + { + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (player.gameObject.name.StartsWith("Player_") || player.IsYourPlayer) + { + if (!alivePlayers.Contains(player) && player.HealthController.IsAlive) + { + AddPlayer(player); + } + } + else + { + if (!aliveBots.Contains(player) && player.HealthController.IsAlive) + { + AddBot(player); + } + } + } + } + + protected void OnEnable() + { + CheckAndAdd(); + } + + protected void OnDisable() + { + foreach (CoopPlayer player in alivePlayers) + { + player.OnPlayerDead -= PlayerDied; + } + alivePlayers.Clear(); + + foreach (CoopPlayer bot in aliveBots) + { + bot.OnPlayerDead -= BotDied; + } + aliveBots.Clear(); + } + + private void AddPlayer(CoopPlayer player) + { + player.OnPlayerDead += PlayerDied; + alivePlayers.Add(player); + } + + private void PlayerDied(EFT.Player player, EFT.IPlayer lastAggressor, DamageInfo damageInfo, EBodyPart part) + { + player.OnPlayerDead -= PlayerDied; + alivePlayers.Remove((CoopPlayer)player); + } + + private void AddBot(CoopPlayer bot) + { + bot.OnPlayerDead += BotDied; + aliveBots.Add(bot); + } + + private void BotDied(EFT.Player player, EFT.IPlayer lastAggressor, DamageInfo damageInfo, EBodyPart part) + { + player.OnPlayerDead -= BotDied; + aliveBots.Remove((CoopPlayer)player); + } + + protected void OnGUI() + { + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUI.skin.window.alignment = TextAnchor.UpperCenter; + + GUI.Window(0, windowRect, DrawWindow, "Fika Debug"); + } + + private void DrawWindow(int windowId) + { + GUILayout.BeginArea(new(5, 15, 150, 150)); + GUILayout.BeginVertical(); + + GUILayout.Label($"Alive Players: {alivePlayers.Count}"); + GUILayout.Label($"Alive Bots: {aliveBots.Count}"); + if (isServer) + { + GUILayout.Label($"Clients: {Singleton.Instance.NetServer.ConnectedPeersCount}"); + } + else + { + GUILayout.Label($"Ping: {Ping}"); + GUILayout.Label($"RTT: {RTT}"); + } + + GUILayout.EndVertical(); + GUILayout.EndArea(); + } + } +} diff --git a/Fika.Core/Coop/Custom/FikaDynamicAI.cs b/Fika.Core/Coop/Custom/FikaDynamicAI.cs index 82e4db45..a0b8eea4 100644 --- a/Fika.Core/Coop/Custom/FikaDynamicAI.cs +++ b/Fika.Core/Coop/Custom/FikaDynamicAI.cs @@ -6,7 +6,6 @@ using Fika.Core.Coop.Components; using Fika.Core.Coop.Players; using System.Collections.Generic; -using System.Linq; using UnityEngine; namespace Fika.Core.Coop.Custom @@ -39,9 +38,9 @@ protected void Awake() resetCounter = FikaPlugin.DynamicAIRate.Value switch { - FikaPlugin.DynamicAIRates.Low => 600, - FikaPlugin.DynamicAIRates.Medium => 300, - FikaPlugin.DynamicAIRates.High => 120, + FikaPlugin.EDynamicAIRates.Low => 600, + FikaPlugin.EDynamicAIRates.Medium => 300, + FikaPlugin.EDynamicAIRates.High => 120, _ => 300, }; @@ -159,6 +158,7 @@ private void CheckForPlayers(CoopBot bot) } int notInRange = 0; + float range = FikaPlugin.DynamicAIRange.Value; foreach (CoopPlayer humanPlayer in humanPlayers) { @@ -175,7 +175,6 @@ private void CheckForPlayers(CoopBot bot) } float distance = Vector3.SqrMagnitude(bot.Position - humanPlayer.Position); - float range = FikaPlugin.DynamicAIRange.Value; if (distance > range * range) { @@ -207,13 +206,13 @@ public void EnabledChange(bool value) } } - internal void RateChanged(FikaPlugin.DynamicAIRates value) + internal void RateChanged(FikaPlugin.EDynamicAIRates value) { resetCounter = value switch { - FikaPlugin.DynamicAIRates.Low => 600, - FikaPlugin.DynamicAIRates.Medium => 300, - FikaPlugin.DynamicAIRates.High => 120, + FikaPlugin.EDynamicAIRates.Low => 600, + FikaPlugin.EDynamicAIRates.Medium => 300, + FikaPlugin.EDynamicAIRates.High => 120, _ => 300, }; } diff --git a/Fika.Core/Coop/Custom/FikaHealthBar.cs b/Fika.Core/Coop/Custom/FikaHealthBar.cs index 177caabf..6612a4ed 100644 --- a/Fika.Core/Coop/Custom/FikaHealthBar.cs +++ b/Fika.Core/Coop/Custom/FikaHealthBar.cs @@ -23,8 +23,6 @@ public class FikaHealthBar : MonoBehaviour private CoopPlayer mainPlayer; private PlayerPlateUI playerPlate; private float screenScale = 1f; - private int frameCounter = 0; - private readonly int throttleInterval = 60; // throttle to 1 update per 60 frames protected void Awake() { @@ -37,29 +35,7 @@ protected void Update() { if (currentPlayer != null) { - bool throttleUpdate = IsThrottleUpdate(); - if (throttleUpdate) - { - // Handling the visibility of elements - if (!FikaPlugin.UseNamePlates.Value) - { - playerPlate.gameObject.SetActive(false); - return; - } - else if (playerPlate.gameObject.active == false) - { - playerPlate.gameObject.SetActive(true); - } - SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); - SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); - } - // Finally, update the screen space position - UpdateScreenSpacePosition(throttleUpdate); - // Destroy if this player is dead - if (!currentPlayer.HealthController.IsAlive) - { - Destroy(this); - } + UpdateScreenSpacePosition(); } else { @@ -67,7 +43,7 @@ protected void Update() } } - private void UpdateScreenSpacePosition(bool throttleUpdate) + private void UpdateScreenSpacePosition() { // ADS opacity handling float opacityMultiplier = 1f; @@ -100,7 +76,7 @@ private void UpdateScreenSpacePosition(bool throttleUpdate) float processedDistance = Mathf.Clamp(sqrDistance / 625, 0.6f, 1f); Vector3 position = new(currentPlayer.PlayerBones.Neck.position.x, currentPlayer.PlayerBones.Neck.position.y + (1f * processedDistance), currentPlayer.PlayerBones.Neck.position.z); - if (!WorldToScreen.GetScreenPoint(position, mainPlayer, out Vector3 screenPoint)) + if (!WorldToScreen.GetScreenPoint(position, mainPlayer, out Vector3 screenPoint, FikaPlugin.NamePlateUseOpticZoom.Value)) { UpdateColorTextMeshProUGUI(playerPlate.playerNameScreen, 0); UpdateColorImage(playerPlate.healthBarScreen, 0); @@ -138,12 +114,12 @@ private void UpdateScreenSpacePosition(bool throttleUpdate) float alpha = 1f; float halfMaxDistanceToShow = maxDistanceToShow / 2; - float lerpValue = Mathf.Clamp01((sqrDistance - halfMaxDistanceToShow) / (halfMaxDistanceToShow)); + float lerpValue = Mathf.Clamp01((sqrDistance - halfMaxDistanceToShow) / halfMaxDistanceToShow); alpha = Mathf.LerpUnclamped(alpha, 0, lerpValue); float namePlateScaleMult = Mathf.LerpUnclamped(1f, 0.5f, lerpValue); namePlateScaleMult = Mathf.Clamp(namePlateScaleMult * FikaPlugin.NamePlateScale.Value, FikaPlugin.MinimumNamePlateScale.Value * FikaPlugin.NamePlateScale.Value, FikaPlugin.NamePlateScale.Value); - playerPlate.ScalarObjectScreen.transform.localScale = (Vector3.one / processedDistance) * namePlateScaleMult; + playerPlate.ScalarObjectScreen.transform.localScale = Vector3.one / processedDistance * namePlateScaleMult; alpha *= opacityMultiplier; alpha *= distFromCenterMultiplier; @@ -192,13 +168,27 @@ private void CreateHealthBar() playerPlate.bearPlateScreen.gameObject.SetActive(false); } + SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); + SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); + playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); + + FikaPlugin.UsePlateFactionSide.SettingChanged += UsePlateFactionSide_SettingChanged; + FikaPlugin.HideHealthBar.SettingChanged += HideHealthBar_SettingChanged; + FikaPlugin.UseNamePlates.SettingChanged += UseNamePlates_SettingChanged; + currentPlayer.HealthController.HealthChangedEvent += HealthController_HealthChangedEvent; currentPlayer.HealthController.BodyPartDestroyedEvent += HealthController_BodyPartDestroyedEvent; currentPlayer.HealthController.BodyPartRestoredEvent += HealthController_BodyPartRestoredEvent; + currentPlayer.HealthController.DiedEvent += HealthController_DiedEvent; UpdateHealth(); } + private void HealthController_DiedEvent(EDamageType obj) + { + Destroy(this); + } + private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, EFT.HealthSystem.ValueStruct arg2) { UpdateHealth(); @@ -214,6 +204,20 @@ private void HealthController_HealthChangedEvent(EBodyPart arg1, float arg2, Dam UpdateHealth(); } + private void UsePlateFactionSide_SettingChanged(object sender, EventArgs e) + { + SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); + } + private void HideHealthBar_SettingChanged(object sender, EventArgs e) + { + SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); + } + + private void UseNamePlates_SettingChanged(object sender, EventArgs e) + { + playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); + } + /// /// Updates the health on the HealthBar, this is invoked from events on the healthcontroller /// @@ -228,7 +232,7 @@ private void UpdateHealth() playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(true); playerPlate.healthBarBackgroundScreen.gameObject.SetActive(false); } - int healthNumberPercentage = (int)Math.Round((currentHealth / maxHealth) * 100); + int healthNumberPercentage = (int)Math.Round(currentHealth / maxHealth * 100); playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); } else @@ -293,23 +297,17 @@ private void SetPlayerPlateFactionVisibility(bool visible) } } - private bool IsThrottleUpdate() + protected void OnDestroy() { - // For throttling updates to various elements - frameCounter++; - bool throttleUpdate = frameCounter >= throttleInterval; - if (throttleUpdate) - { - frameCounter = 0; - } - return throttleUpdate; - } + FikaPlugin.UsePlateFactionSide.SettingChanged -= UsePlateFactionSide_SettingChanged; + FikaPlugin.HideHealthBar.SettingChanged -= HideHealthBar_SettingChanged; + FikaPlugin.UseNamePlates.SettingChanged -= UseNamePlates_SettingChanged; - private void OnDestroy() - { currentPlayer.HealthController.HealthChangedEvent -= HealthController_HealthChangedEvent; currentPlayer.HealthController.BodyPartDestroyedEvent -= HealthController_BodyPartDestroyedEvent; currentPlayer.HealthController.BodyPartRestoredEvent -= HealthController_BodyPartRestoredEvent; + currentPlayer.HealthController.DiedEvent -= HealthController_DiedEvent; + playerPlate.gameObject.SetActive(false); Destroy(this); } diff --git a/Fika.Core/Coop/Factories/PingFactory.cs b/Fika.Core/Coop/Factories/PingFactory.cs index 3a47c5b5..63fee528 100644 --- a/Fika.Core/Coop/Factories/PingFactory.cs +++ b/Fika.Core/Coop/Factories/PingFactory.cs @@ -83,13 +83,13 @@ protected void Update() screenScale = outputWidth / inputWidth; } - if (WorldToScreen.GetScreenPoint(hitPoint, mainPlayer, out Vector3 screenPoint)) + if (WorldToScreen.GetScreenPoint(hitPoint, mainPlayer, out Vector3 screenPoint, FikaPlugin.PingUseOpticZoom.Value)) { float distanceToCenter = Vector3.Distance(screenPoint, new Vector3(Screen.width, Screen.height, 0) / 2); if (distanceToCenter < 200) { - image.color = new Color(_pingColor.r, _pingColor.g, _pingColor.b, Mathf.Max(0.05f, distanceToCenter / 200)); + image.color = new Color(_pingColor.r, _pingColor.g, _pingColor.b, Mathf.Max(FikaPlugin.PingMinimumOpacity.Value, distanceToCenter / 200)); } else { @@ -108,7 +108,16 @@ public virtual void Initialize(ref Vector3 point, Object userObject, Color pingC float distance = Mathf.Clamp(Vector3.Distance(CameraClass.Instance.Camera.transform.position, transform.position) / 100, 0.4f, 0.6f); float pingSize = FikaPlugin.PingSize.Value; - image.rectTransform.localScale = new Vector3(pingSize, pingSize, pingSize) * distance; + Vector3 scaledSize = new(pingSize, pingSize, pingSize); + if (FikaPlugin.PingScaleWithDistance.Value == true) + { + scaledSize *= distance; + } + else + { + scaledSize *= 0.5f; + } + image.rectTransform.localScale = scaledSize; } } diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index a47b058c..efdc63c9 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -1,7 +1,9 @@ -using Comfort.Common; +using BSG.CameraEffects; +using Comfort.Common; using EFT; using Fika.Core.Coop.Components; using Fika.Core.Coop.Players; +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -22,7 +24,12 @@ public class FreeCamera : MonoBehaviour public bool IsActive = false; private CoopPlayer CurrentPlayer; private bool isFollowing = false; + private bool leftMode = false; private bool disableInput = false; + private bool showOverlay; + private NightVision nightVision; + private ThermalVision thermalVision; + private FreeCameraController freeCameraController; private KeyCode forwardKey = KeyCode.W; private KeyCode backKey = KeyCode.S; @@ -45,6 +52,52 @@ protected void Start() relUpKey = KeyCode.E; relDownKey = KeyCode.A; } + + showOverlay = FikaPlugin.KeybindOverlay.Value; + FikaPlugin.KeybindOverlay.SettingChanged += KeybindOverlay_SettingChanged; + + nightVision = CameraClass.Instance.NightVision; + thermalVision = CameraClass.Instance.ThermalVision; + + freeCameraController = Singleton.Instance.gameObject.GetComponent(); + } + + private void KeybindOverlay_SettingChanged(object sender, EventArgs e) + { + showOverlay = FikaPlugin.KeybindOverlay.Value; + } + + protected void OnGUI() + { + if (IsActive && showOverlay) + { + string visionText = "Enable nightvision"; + + if (nightVision != null && nightVision.On) + { + visionText = "Enable thermals"; + } + + if (thermalVision != null && thermalVision.On) + { + visionText = "Disable thermals"; + } + + GUILayout.BeginArea(new Rect(5, 5, 800, 800)); + GUILayout.BeginVertical(); + + GUILayout.Label($"Left/Right Mouse Button: Jump between players"); + GUILayout.Label($"CTRL + Left/Right Mouse Button: Jump and spectate in 3rd person"); + GUILayout.Label($"Spacebar + Left/Right Mouse Button: Jump and spectate in head cam"); + GUILayout.Label($"T: Teleport to cam position"); + GUILayout.Label($"N: {visionText}"); + GUILayout.Label($"M: Disable culling"); + GUILayout.Label($"HOME: {(disableInput ? "Enable Input" : "Disable Input")}"); + GUILayout.Label($"Shift + Ctrl: Turbo Speed"); + + GUILayout.EndVertical(); + GUILayout.EndArea(); + } } protected void Update() @@ -54,11 +107,11 @@ protected void Update() return; } + // Toggle input if (Input.GetKeyDown(KeyCode.Home)) { disableInput = !disableInput; - string status = disableInput == true ? "disabled" : "enabled"; - NotificationManagerClass.DisplayMessageNotification($"Free cam input is now {status}."); + NotificationManagerClass.DisplayMessageNotification($"Free cam input is now {(disableInput ? "disabled" : "enabled")}."); } if (disableInput) @@ -79,14 +132,14 @@ protected void Update() if (players.Count > 0) { - bool shouldFollow = Input.GetKey(KeyCode.Space); + bool shouldHeadCam = Input.GetKey(KeyCode.Space); bool should3rdPerson = Input.GetKey(KeyCode.LeftControl); foreach (CoopPlayer player in players) { if (CurrentPlayer == null && players[0] != null) { CurrentPlayer = players[0]; - if (shouldFollow) + if (shouldHeadCam) { AttachToPlayer(); } @@ -106,7 +159,7 @@ protected void Update() if (players.Count - 1 >= nextPlayer) { CurrentPlayer = players[nextPlayer]; - if (shouldFollow) + if (shouldHeadCam) { AttachToPlayer(); } @@ -123,7 +176,7 @@ protected void Update() else { CurrentPlayer = players[0]; - if (shouldFollow) + if (shouldHeadCam) { AttachToPlayer(); } @@ -240,14 +293,41 @@ protected void Update() } } + // Toggle vision + if (Input.GetKeyDown(KeyCode.N)) + { + ToggleVision(); + } + + // Disable culling + if (Input.GetKeyDown(KeyCode.M)) + { + if (freeCameraController != null) + { + freeCameraController.DisableAllCullingObjects(); + } + } + if (isFollowing) { + if (CurrentPlayer != null) + { + if (CurrentPlayer.MovementContext.LeftStanceEnabled && !leftMode) + { + SetLeftShoulderMode(true); + } + else if (!CurrentPlayer.MovementContext.LeftStanceEnabled && leftMode) + { + SetLeftShoulderMode(false); + } + } return; } bool fastMode = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); bool superFastMode = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); float movementSpeed = fastMode ? 20f : 2f; + if (superFastMode) { movementSpeed *= 8; @@ -255,22 +335,22 @@ protected void Update() if (Input.GetKey(leftKey) || Input.GetKey(KeyCode.LeftArrow)) { - transform.position += (-transform.right * (movementSpeed * Time.deltaTime)); + transform.position += -transform.right * (movementSpeed * Time.deltaTime); } if (Input.GetKey(rightKey) || Input.GetKey(KeyCode.RightArrow)) { - transform.position += (transform.right * (movementSpeed * Time.deltaTime)); + transform.position += transform.right * (movementSpeed * Time.deltaTime); } if (Input.GetKey(forwardKey) || Input.GetKey(KeyCode.UpArrow)) { - transform.position += (transform.forward * (movementSpeed * Time.deltaTime)); + transform.position += transform.forward * (movementSpeed * Time.deltaTime); } if (Input.GetKey(backKey) || Input.GetKey(KeyCode.DownArrow)) { - transform.position += (-transform.forward * (movementSpeed * Time.deltaTime)); + transform.position += -transform.forward * (movementSpeed * Time.deltaTime); } // Teleportation @@ -293,22 +373,22 @@ protected void Update() { if (Input.GetKey(relUpKey)) { - transform.position += (transform.up * (movementSpeed * Time.deltaTime)); + transform.position += transform.up * (movementSpeed * Time.deltaTime); } if (Input.GetKey(relDownKey)) { - transform.position += (-transform.up * (movementSpeed * Time.deltaTime)); + transform.position += -transform.up * (movementSpeed * Time.deltaTime); } if (Input.GetKey(upKey) || Input.GetKey(KeyCode.PageUp)) { - transform.position += (Vector3.up * (movementSpeed * Time.deltaTime)); + transform.position += Vector3.up * (movementSpeed * Time.deltaTime); } if (Input.GetKey(downKey) || Input.GetKey(KeyCode.PageDown)) { - transform.position += (-Vector3.up * (movementSpeed * Time.deltaTime)); + transform.position += -Vector3.up * (movementSpeed * Time.deltaTime); } } @@ -317,6 +397,56 @@ protected void Update() transform.localEulerAngles = new Vector3(newRotationY, newRotationX, 0f); } + private void SetLeftShoulderMode(bool enabled) + { + if (enabled) + { + // Use different coordinates for headcam + if (transform.localPosition.z == -0.17f) + { + transform.localPosition = new(transform.localPosition.x, transform.localPosition.y, -transform.localPosition.z); + } + else + { + transform.localPosition = new(-transform.localPosition.x, transform.localPosition.y, transform.localPosition.z); + } + leftMode = true; + + return; + } + + // Use different coordinates for headcam + if (transform.localPosition.z == 0.17f) + { + transform.localPosition = new(transform.localPosition.x, transform.localPosition.y, -transform.localPosition.z); + } + else + { + transform.localPosition = new(-transform.localPosition.x, transform.localPosition.y, transform.localPosition.z); + } + leftMode = false; + } + + private void ToggleVision() + { + if (nightVision != null && thermalVision != null) + { + if (!nightVision.On && !thermalVision.On) + { + nightVision.On = true; + } + else if (nightVision.On && !thermalVision.On) + { + nightVision.On = false; + thermalVision.On = true; + } + else if (thermalVision.On) + { + thermalVision.On = false; + } + } + } + public void JumpToPlayer() { transform.position = new Vector3(CurrentPlayer.Transform.position.x - 2, CurrentPlayer.Transform.position.y + 2, CurrentPlayer.Transform.position.z); @@ -324,6 +454,7 @@ public void JumpToPlayer() if (isFollowing) { isFollowing = false; + leftMode = false; transform.parent = null; } } @@ -346,8 +477,51 @@ public void Attach3rdPerson() public void SetActive(bool status) { + if (!status) + { + if (nightVision != null && nightVision.On) + { + nightVision.method_1(false); + } + + if (thermalVision != null && thermalVision.On) + { + thermalVision.method_1(false); + } + } + + if (status) + { + Player player = Singleton.Instance.MainPlayer; + if (player != null && player.HealthController.IsAlive) + { + if (player.NightVisionObserver.Component != null && player.NightVisionObserver.Component.Togglable.On) + { + player.NightVisionObserver.Component.Togglable.ForceToggle(false); + } + + if (player.ThermalVisionObserver.Component != null && player.ThermalVisionObserver.Component.Togglable.On) + { + player.ThermalVisionObserver.Component.Togglable.ForceToggle(false); + } + } + else if (player != null && !player.HealthController.IsAlive) + { + if (nightVision != null && nightVision.On) + { + nightVision.method_1(false); + } + + if (thermalVision != null && thermalVision.On) + { + thermalVision.method_1(false); + } + } + } + IsActive = status; isFollowing = false; + leftMode = false; transform.parent = null; } diff --git a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs index 812b80bb..2624262e 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs @@ -27,7 +27,7 @@ public class FreeCameraController : MonoBehaviour //private GameObject _mainCamera; private FreeCamera _freeCamScript; - private BattleUIScreen _playerUi; + private EftBattleUIScreen _playerUi; private bool _uiHidden; private bool _effectsCleared = false; @@ -46,6 +46,7 @@ public class FreeCameraController : MonoBehaviour private bool deathFadeEnabled; private DisablerCullingObjectBase[] allCullingObjects; private List previouslyActiveBakeGroups; + private bool hasEnabledCulling = false; protected void Awake() { @@ -145,8 +146,8 @@ protected void Update() if (quitState == CoopHandler.EQuitState.YouHaveExtracted && !extracted) { - CoopGame coopGame = (CoopGame)coopHandler.LocalGameInstance; - if (coopGame.ExtractedPlayers.Contains((_player).NetId)) + CoopGame coopGame = coopHandler.LocalGameInstance; + if (coopGame.ExtractedPlayers.Contains(_player.NetId)) { extracted = true; ShowExtractMessage(); @@ -227,16 +228,16 @@ private void ClearEffects() Traverse effectsController = Traverse.Create(cameraClass.EffectsController); - BloodOnScreen bloodOnScreen = effectsController.Field("bloodOnScreen_0").GetValue(); + BloodOnScreen bloodOnScreen = effectsController.Field("bloodOnScreen_0").Value; if (bloodOnScreen != null) { Destroy(bloodOnScreen); } - List effectsManagerList = effectsController.Field("list_0").GetValue>(); + List effectsManagerList = effectsController.Field>("list_0").Value; if (effectsManagerList != null) { - foreach (EffectsController.Class566 effectsManager in effectsManagerList) + foreach (EffectsController.Class576 effectsManager in effectsManagerList) { while (effectsManager.ActiveEffects.Count > 0) { @@ -267,8 +268,14 @@ private void ClearEffects() Destroy(cameraClass.VisorEffect); cameraClass.VisorSwitcher.Deinit(); Destroy(cameraClass.VisorSwitcher); - Destroy(cameraClass.NightVision); - Destroy(cameraClass.ThermalVision); + if (cameraClass.NightVision.On) + { + cameraClass.NightVision.method_1(false); + } + if (cameraClass.ThermalVision.On) + { + cameraClass.ThermalVision.method_1(false); + } } private void ShowExtractMessage() @@ -326,7 +333,7 @@ private void ToggleUi() return; } - _playerUi = gameObject.GetComponent(); + _playerUi = gameObject.GetComponent(); if (_playerUi == null) { @@ -362,7 +369,29 @@ private void SetPlayerToFreecamMode(Player localPlayer) _gamePlayerOwner.enabled = false; _freeCamScript.SetActive(true); + } + /// + /// A helper method to reset the player view back to First Person + /// + /// + private void SetPlayerToFirstPersonMode(Player localPlayer) + { + // re-enable _gamePlayerOwner + _gamePlayerOwner.enabled = true; + _freeCamScript.SetActive(false); + + localPlayer.PointOfView = EPointOfView.FirstPerson; + CameraClass.Instance.SetOcclusionCullingEnabled(true); + + if (hasEnabledCulling) + { + EnableAllCullingObjects(); + } + } + + public void DisableAllCullingObjects() + { int count = 0; foreach (DisablerCullingObjectBase cullingObject in allCullingObjects) { @@ -373,7 +402,9 @@ private void SetPlayerToFreecamMode(Player localPlayer) count++; cullingObject.SetComponentsEnabled(true); } - FikaPlugin.Instance.FikaLogger.LogDebug($"Enabled {count} Culling Triggers."); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogWarning($"Enabled {count} Culling Triggers."); +#endif PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); if (perfectCullingAdaptiveGrid != null) @@ -398,21 +429,12 @@ private void SetPlayerToFreecamMode(Player localPlayer) } } } + + hasEnabledCulling = true; } - /// - /// A helper method to reset the player view back to First Person - /// - /// - private void SetPlayerToFirstPersonMode(Player localPlayer) + public void EnableAllCullingObjects() { - // re-enable _gamePlayerOwner - _gamePlayerOwner.enabled = true; - _freeCamScript.SetActive(false); - - localPlayer.PointOfView = EPointOfView.FirstPerson; - CameraClass.Instance.SetOcclusionCullingEnabled(true); - int count = 0; foreach (DisablerCullingObjectBase cullingObject in allCullingObjects) { @@ -423,7 +445,9 @@ private void SetPlayerToFirstPersonMode(Player localPlayer) count++; cullingObject.SetComponentsEnabled(false); } - FikaPlugin.Instance.FikaLogger.LogDebug($"Disabled {count} Culling Triggers."); +#if DEBUG + FikaPlugin.Instance.FikaLogger.LogWarning($"Disabled {count} Culling Triggers."); +#endif PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); if (perfectCullingAdaptiveGrid != null) @@ -450,6 +474,8 @@ private void SetPlayerToFirstPersonMode(Player localPlayer) } } } + + hasEnabledCulling = false; } /// diff --git a/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs b/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs index c130fe39..3dda5851 100644 --- a/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs +++ b/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs @@ -1,4 +1,4 @@ -using Aki.Reflection.Patching; +using SPT.Reflection.Patching; using System; using System.Reflection; using UnityEngine; diff --git a/Fika.Core/Coop/FreeCamera/FadeBlackScreen_Patch.cs b/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs similarity index 93% rename from Fika.Core/Coop/FreeCamera/FadeBlackScreen_Patch.cs rename to Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs index 9bd93c5d..d0655da6 100644 --- a/Fika.Core/Coop/FreeCamera/FadeBlackScreen_Patch.cs +++ b/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs @@ -1,11 +1,11 @@ -using Aki.Reflection.Patching; -using EFT.UI; +using EFT.UI; +using SPT.Reflection.Patching; using System; using System.Reflection; using UnityEngine; using UnityEngine.UI; -namespace Fika.Core.Coop.FreeCamera +namespace Fika.Core.Coop.FreeCamera.Patches { internal class FadeBlackScreen_Patch : ModulePatch { diff --git a/Fika.Core/Coop/GameMode/AFikaGame.cs b/Fika.Core/Coop/GameMode/AFikaGame.cs deleted file mode 100644 index b1acc974..00000000 --- a/Fika.Core/Coop/GameMode/AFikaGame.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Aki.Reflection.Utils; -using BepInEx.Logging; -using Comfort.Common; -using EFT; -using EFT.InputSystem; -using EFT.UI; -using EFT.Weather; -using JsonType; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Fika.Core.Coop.GameMode -{ - public abstract class AFikaGame : BaseLocalGame, IBotGame - { - public new bool InRaid { get { return true; } } - - public ISession BackEndSession { get { return PatchConstants.BackEndSession; } } - - BotsController IBotGame.BotsController - { - get - { - if (BotsController == null) - { - BotsController = (BotsController)GetType().GetFields().Where(x => x.FieldType == typeof(BotsController)).FirstOrDefault().GetValue(this); - } - return BotsController; - } - } - - private static BotsController BotsController; - - public BotsController PBotsController - { - get - { - if (BotsController == null) - { - BotsController = (BotsController)GetType().GetFields().Where(x => x.FieldType == typeof(BotsController)).FirstOrDefault().GetValue(this); - } - return BotsController; - } - } - - public IWeatherCurve WeatherCurve - { - get - { - return WeatherController.Instance.WeatherCurve; - } - } - - ManualLogSource Logger { get; set; } - - public static T 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) where T : AFikaGame - { - - var r = smethod_0(inputTree, profile, backendDateTime, insurance, menuUI, commonUI, preloaderUI, gameUI, location, timeAndWeather, wavesSettings, dateTime, - callback, fixedDeltaTime, updateQueue, backEndSession, new TimeSpan?(sessionTime)); - - r.Logger = BepInEx.Logging.Logger.CreateLogSource("Coop Game Mode"); - r.Logger.LogInfo("CoopGame::Create"); - - // Non Waves Scenario setup - r.nonWavesSpawnScenario_0 = (NonWavesSpawnScenario)typeof(NonWavesSpawnScenario).GetMethod(nameof(NonWavesSpawnScenario.smethod_0), BindingFlags.Static | BindingFlags.Public).Invoke - (null, [r, location, r.PBotsController]); - r.nonWavesSpawnScenario_0.ImplementWaveSettings(wavesSettings); - - // Waves Scenario setup - r.wavesSpawnScenario_0 = (WavesSpawnScenario)typeof(WavesSpawnScenario).GetMethod(nameof(WavesSpawnScenario.smethod_0), BindingFlags.Static | BindingFlags.Public).Invoke - (null, [r.gameObject, location.waves, new Action(r.PBotsController.ActivateBotsByWave), location]); - - var bosswavemanagerValue = typeof(GClass579).GetMethod(nameof(GClass579.smethod_0), BindingFlags.Static | BindingFlags.Public).Invoke - (null, [location.BossLocationSpawn, new Action(r.PBotsController.ActivateBotsByWave)]); - - r.GetType().GetFields(BindingFlags.NonPublic).Where(x => x.FieldType == typeof(GClass579)).FirstOrDefault().SetValue(r, bosswavemanagerValue); - - r.GClass579 = bosswavemanagerValue as GClass579; - - r.func_1 = (player) => GamePlayerOwner.Create(player, inputTree, insurance, backEndSession, commonUI, preloaderUI, gameUI, r.GameDateTime, location); - - return r; - } - - public Dictionary Bots { get; set; } = new Dictionary(); - - /// - /// Matchmaker countdown - /// - /// - public override void vmethod_1(float timeBeforeDeploy) - { - base.vmethod_1(timeBeforeDeploy); - } - - /// - /// Reconnection handling. - /// - public override void vmethod_3() - { - base.vmethod_3(); - } - - private GClass579 GClass579; - - private WavesSpawnScenario wavesSpawnScenario_0; - - private NonWavesSpawnScenario nonWavesSpawnScenario_0; - - private Func func_1; - - public new void method_6(string backendUrl, string locationId, int variantId) - { - Logger.LogInfo("CoopGame:method_6"); - return; - } - } -} diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index e8ed5558..07321a44 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -1,13 +1,7 @@ -using Aki.Common.Http; -using Aki.Custom.Airdrops; -using Aki.Reflection.Utils; -using Aki.SinglePlayer.Models.Progression; -using Aki.SinglePlayer.Utils.Healing; -using Aki.SinglePlayer.Utils.Insurance; -using Aki.SinglePlayer.Utils.Progression; -using BepInEx.Logging; +using BepInEx.Logging; using Comfort.Common; using CommonAssets.Scripts.Game; +using Coop.Airdrops; using EFT; using EFT.AssetsManager; using EFT.Bots; @@ -15,11 +9,11 @@ using EFT.Counters; using EFT.EnvironmentEffect; using EFT.Game.Spawning; -using EFT.InputSystem; using EFT.Interactive; using EFT.InventoryLogic; using EFT.UI; using EFT.UI.BattleTimer; +using EFT.UI.Matchmaker; using EFT.UI.Screens; using EFT.Weather; using Fika.Core.Coop.BTR; @@ -27,8 +21,8 @@ using Fika.Core.Coop.Components; using Fika.Core.Coop.Custom; using Fika.Core.Coop.FreeCamera; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Modding; using Fika.Core.Modding.Events; using Fika.Core.Networking; @@ -40,6 +34,12 @@ using JsonType; using LiteNetLib.Utils; using Newtonsoft.Json; +using SPT.Common.Http; +using SPT.Reflection.Utils; +using SPT.SinglePlayer.Models.Progression; +using SPT.SinglePlayer.Utils.Healing; +using SPT.SinglePlayer.Utils.Insurance; +using SPT.SinglePlayer.Utils.Progression; using System; using System.Collections; using System.Collections.Generic; @@ -51,35 +51,34 @@ namespace Fika.Core.Coop.GameMode { /// - /// A custom Game Type + /// Coop game used in Fika /// - internal sealed class CoopGame : BaseLocalGame, IBotGame, IFikaGame + public sealed class CoopGame : BaseLocalGame, IBotGame, IFikaGame { public string InfiltrationPoint; public bool HasAddedFenceRep = false; - public bool forceStart = false; public ExitStatus MyExitStatus { get; set; } = ExitStatus.Survived; public string MyExitLocation { get; set; } = null; public ISpawnSystem SpawnSystem; - public Dictionary Bots = []; - private CoopExfilManager exfilManager; - private GameObject fikaStartButton; + public List ExtractedPlayers { get; } = []; + private readonly Dictionary botQueue = []; private Coroutine extractRoutine; - private GClass2928 spawnPoints = null; + private SpawnPointManagerClass spawnPoints = null; private ISpawnPoint spawnPoint = null; - private GClass579 GClass579; + private BossSpawnWaveManagerClass BossSpawnWaveManagerClass; private WavesSpawnScenario wavesSpawnScenario_0; private NonWavesSpawnScenario nonWavesSpawnScenario_0; - private Func func_1; + private Func func_1; private bool hasSaved = false; + private CoopExfilManager exfilManager; + private CoopTimeManager timeManager; + private FikaDebug fikaDebug; + private bool isServer; public FikaDynamicAI DynamicAI { get; private set; } public RaidSettings RaidSettings { get; private set; } - //WildSpawnType for sptUsec and sptBear - const int sptUsecValue = 47; - const int sptBearValue = 48; public ISession BackEndSession { get => PatchConstants.BackEndSession; } BotsController IBotGame.BotsController { @@ -105,19 +104,42 @@ public IWeatherCurve WeatherCurve private static ManualLogSource Logger; - 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, + /// + /// Creates a CoopGame + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal static CoopGame Create(IInputTree inputTree, Profile profile, GameDateTime backendDateTime, + InsuranceCompanyClass insurance, MenuUI menuUI, GameUI gameUI, LocationSettingsClass.Location location, + TimeAndWeatherSettings timeAndWeather, 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"); + Logger = BepInEx.Logging.Logger.CreateLogSource("CoopGame"); bool useCustomWeather = timeAndWeather.IsRandomWeather; timeAndWeather.IsRandomWeather = false; - CoopGame coopGame = smethod_0(inputTree, profile, backendDateTime, insurance, menuUI, commonUI, - preloaderUI, gameUI, location, timeAndWeather, wavesSettings, dateTime, callback, fixedDeltaTime, updateQueue, backEndSession, new TimeSpan?(sessionTime)); + CoopGame coopGame = smethod_0(inputTree, profile, backendDateTime, insurance, menuUI, gameUI, + location, timeAndWeather, wavesSettings, dateTime, callback, fixedDeltaTime, updateQueue, backEndSession, + new TimeSpan?(sessionTime)); + + coopGame.isServer = FikaBackendUtils.IsServer; // Non Waves Scenario setup coopGame.nonWavesSpawnScenario_0 = NonWavesSpawnScenario.smethod_0(coopGame, location, coopGame.botsController_0); @@ -128,24 +150,69 @@ internal static CoopGame Create(InputTree inputTree, Profile profile, GameDateTi coopGame.wavesSpawnScenario_0 = WavesSpawnScenario.smethod_0(coopGame.gameObject, waves, new Action(coopGame.botsController_0.ActivateBotsByWave), location); BossLocationSpawn[] bossSpawns = LocalGame.smethod_8(wavesSettings, location.BossLocationSpawn); - coopGame.GClass579 = GClass579.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); + coopGame.BossSpawnWaveManagerClass = BossSpawnWaveManagerClass.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); - if (useCustomWeather && MatchmakerAcceptPatches.IsServer) + if (useCustomWeather && coopGame.isServer) { Logger.LogInfo("Custom weather enabled, initializing curves"); coopGame.SetupCustomWeather(timeAndWeather); } - coopGame.func_1 = (Player player) => GamePlayerOwner.Create(player, inputTree, insurance, backEndSession, commonUI, preloaderUI, gameUI, coopGame.GameDateTime, location); + SetupGamePlayerOwnerHandler setupGamePlayerOwnerHandler = new(inputTree, insurance, backEndSession, gameUI, coopGame, location); + coopGame.func_1 = new Func(setupGamePlayerOwnerHandler.HandleSetup); Singleton.Create(coopGame); FikaEventDispatcher.DispatchEvent(new FikaGameCreatedEvent(coopGame)); + EndByExitTrigerScenario endByExitTrigger = coopGame.GetComponent(); + EndByTimerScenario endByTimerScenario = coopGame.GetComponent(); + + if (endByExitTrigger != null) + { + Destroy(endByExitTrigger); + } + if (endByTimerScenario != null) + { + Destroy(endByTimerScenario); + } + + coopGame.timeManager = CoopTimeManager.Create(coopGame); + coopGame.RaidSettings = raidSettings; return coopGame; } + /// + /// Used to create a + /// + /// + /// + /// + /// + /// + /// + private class SetupGamePlayerOwnerHandler(IInputTree inputTree, InsuranceCompanyClass insurance, ISession backEndSession, GameUI gameUI, CoopGame game, LocationSettingsClass.Location location) + { + private readonly IInputTree inputTree = inputTree; + private readonly InsuranceCompanyClass insurance = insurance; + private readonly ISession backEndSession = backEndSession; + private readonly GameUI gameUI = gameUI; + private readonly CoopGame game = game; + private readonly LocationSettingsClass.Location location = location; + + public EftGamePlayerOwner HandleSetup(Player player) + { + EftGamePlayerOwner gamePlayerOwner = EftGamePlayerOwner.Create(player, inputTree, insurance, backEndSession, gameUI, game.GameDateTime, location); + gamePlayerOwner.OnLeave += game.vmethod_3; + return gamePlayerOwner; + } + } + + /// + /// Sets up a custom weather curve + /// + /// Struct with custom settings private void SetupCustomWeather(TimeAndWeatherSettings timeAndWeather) { if (WeatherController.Instance == null) @@ -153,7 +220,7 @@ private void SetupCustomWeather(TimeAndWeatherSettings timeAndWeather) return; } - DateTime dateTime = GClass1296.StartOfDay(); + DateTime dateTime = EFTDateTimeClass.StartOfDay(); DateTime dateTime2 = dateTime.AddDays(1); WeatherClass weather = WeatherClass.CreateDefault(); @@ -164,11 +231,25 @@ private void SetupCustomWeather(TimeAndWeatherSettings timeAndWeather) weather.ScaterringFogDensity = weather2.ScaterringFogDensity = timeAndWeather.FogType.ToValue(); weather.Time = dateTime.Ticks; weather2.Time = dateTime2.Ticks; - WeatherController.Instance.method_4([weather, weather2]); + WeatherController.Instance.method_0([weather, weather2]); } - public async Task CreateCoopHandler() + public override void SetMatchmakerStatus(string status, float? progress = null) { + if (CurrentScreenSingleton.Instance.CurrentScreenController is MatchmakerTimeHasCome.TimeHasComeScreenClass gclass) + { + gclass.ChangeStatus(status, progress); + } + } + + /// + /// Creates and initializes the + /// + /// + /// If no ServerId was found + public Task CreateCoopHandler() + { + Logger.LogInfo("Creating CoopHandler..."); CoopHandler coopHandler = CoopHandler.GetCoopHandler(); if (coopHandler != null) { @@ -190,39 +271,25 @@ public async Task CreateCoopHandler() coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); coopHandler.LocalGameInstance = this; - if (!string.IsNullOrEmpty(MatchmakerAcceptPatches.GetGroupId())) + if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) { - coopHandler.ServerId = MatchmakerAcceptPatches.GetGroupId(); + coopHandler.ServerId = FikaBackendUtils.GetGroupId(); } else { Destroy(coopHandler); Logger.LogError("No Server Id found, Deleting Coop Handler"); - throw new Exception("No Server Id found"); - } - - if (MatchmakerAcceptPatches.IsServer) - { - FikaServer server = gameObject.AddComponent(); - - while (!server.ServerReady) - { - await Task.Delay(100); - } - Logger.LogInfo("FikaServer has started!"); + throw new MissingReferenceException("No Server Id found"); } - else if (MatchmakerAcceptPatches.IsClient) - { - FikaClient client = gameObject.AddComponent(); - while (!client.ClientReady) - { - await Task.Delay(100); - } - Logger.LogInfo("FikaClient has started!"); - } + return Task.CompletedTask; } + /// + /// Returns all human players + /// + /// used to fetch players + /// private List GetPlayers(CoopHandler coopHandler) { List humanPlayers = []; @@ -238,6 +305,12 @@ private List GetPlayers(CoopHandler coopHandler) return humanPlayers; } + /// + /// Calculates the distance from all players + /// + /// The position + /// of all human s + /// private float GetDistanceFromPlayers(Vector3 position, List humanPlayers) { float distance = float.PositiveInfinity; @@ -254,13 +327,17 @@ private float GetDistanceFromPlayers(Vector3 position, List humanPla return distance; } - private string GetFurthestBot(Dictionary bots, CoopHandler coopHandler, out float furthestDistance) + /// + /// Grabs the bot furthest away from all players and returns its distance + /// + /// List of all human s + /// The furthest distance + /// + private string GetFurthestBot(List humanPlayers, out float furthestDistance) { string furthestBot = string.Empty; furthestDistance = 0f; - List humanPlayers = GetPlayers(coopHandler); - foreach (var botKeyValuePair in Bots) { if (IsInvalidBotForDespawning(botKeyValuePair)) @@ -280,6 +357,11 @@ private string GetFurthestBot(Dictionary bots, CoopHandler coopH return furthestBot; } + /// + /// Checks whether this bot is valid for despawning + /// + /// of profileId and player + /// private bool IsInvalidBotForDespawning(KeyValuePair kvp) { if (kvp.Value == null || kvp.Value == null || kvp.Value.Position == null) @@ -302,21 +384,27 @@ private bool IsInvalidBotForDespawning(KeyValuePair kvp) WildSpawnType role = kvp.Value.Profile.Info.Settings.Role; - if ((int)role != sptUsecValue && (int)role != sptBearValue && role != WildSpawnType.assault) + if (role is not WildSpawnType.pmcUSEC and not WildSpawnType.pmcBEAR and not WildSpawnType.assault) { - // We skip all the bots that are not sptUsec, sptBear or assault. That means we never remove bosses, bossfollowers, and raiders + // We skip all the bots that are not pmcUSEC, pmcBEAR or assault. That means we never remove bosses, bossfollowers, and raiders return true; } return false; } + /// + /// Used to spawn a bot for the host + /// + /// to spawn + /// The position to spawn on + /// private async Task CreateBot(Profile profile, Vector3 position) { #if DEBUG Logger.LogWarning($"Creating bot {profile.Info.Settings.Role} at {position}"); #endif - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { return null; } @@ -329,7 +417,7 @@ private async Task CreateBot(Profile profile, Vector3 position) WildSpawnType role = profile.Info.Settings.Role; bool isSpecial = false; - if ((int)role != sptUsecValue && (int)role != sptBearValue && role != WildSpawnType.assault) + if (role is not WildSpawnType.pmcUSEC and not WildSpawnType.pmcBEAR and not WildSpawnType.assault) { #if DEBUG Logger.LogWarning($"Bot {profile.Info.Settings.Role} is a special bot."); @@ -343,14 +431,14 @@ private async Task CreateBot(Profile profile, Vector3 position) if (FikaPlugin.DespawnFurthest.Value) { - despawned = TryDespawnFurthest(profile, position, coopHandler); + despawned = TryDespawnFurthestBot(profile, position, coopHandler); } // If it's not special and we didnt despawn something, we dont spawn a new bot. if (!isSpecial && !despawned) { #if DEBUG - Logger.LogWarning($"Stopping spawn of bot {profile.Nickname}, max count reached and enforced limits enabled. Current: {botsController_0.AliveAndLoadingBotsCount}, Max: {botsController_0.BotSpawner.MaxBots}"); + Logger.LogWarning($"Stopping spawn of bot {profile.Nickname}, max count reached and enforced limits enabled. Current: {Bots.Count}, Max: {botsController_0.BotSpawner.MaxBots}, Alive & Loading: {botsController_0.BotSpawner.AliveAndLoadingBotsCount}"); #endif return null; } @@ -375,7 +463,7 @@ private async Task CreateBot(Profile profile, Vector3 position) FikaServer server = Singleton.Instance; netId = server.PopNetId(); - SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket() { Profile = profile }, true, true, position, netId); + SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(profile), true, true, position, netId); Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); if (server.NetServer.ConnectedPeersCount > 0) @@ -385,8 +473,8 @@ private async Task CreateBot(Profile profile, Vector3 position) localPlayer = await CoopBot.CreateBot(num, position, Quaternion.identity, "Player", "Bot_", EPointOfView.ThirdPerson, profile, true, UpdateQueue, Player.EUpdateMode.Manual, - Player.EUpdateMode.Auto, GClass549.Config.CharacterController.BotPlayerMode, () => 1f, - () => 1f, GClass1446.Default); + Player.EUpdateMode.Auto, BackendConfigAbstractClass.Config.CharacterController.BotPlayerMode, () => 1f, + () => 1f, GClass1457.Default); localPlayer.Location = Location_0.Id; @@ -403,7 +491,7 @@ private async Task CreateBot(Profile profile, Vector3 position) Bots.Add(localPlayer.ProfileId, localPlayer); } - if (profile.Info.Side == EPlayerSide.Bear || profile.Info.Side == EPlayerSide.Usec) + if (profile.Info.Side is EPlayerSide.Bear or EPlayerSide.Usec) { Slot backpackSlot = profile.Inventory.Equipment.GetSlot(EquipmentSlot.Backpack); Item backpack = backpackSlot.ContainedItem; @@ -441,11 +529,19 @@ private async Task CreateBot(Profile profile, Vector3 position) CoopBot coopBot = (CoopBot)localPlayer; coopBot.NetId = netId; + if (FikaPlugin.DisableBotMetabolism.Value) + { + coopBot.HealthController.DisableMetabolism(); + } coopHandler.Players.Add(coopBot.NetId, coopBot); return localPlayer; } + /// + /// Increments the amount of players that have loaded a bot, used for + /// + /// public void IncreaseLoadedPlayers(int netId) { if (botQueue.ContainsKey(netId)) @@ -458,6 +554,11 @@ public void IncreaseLoadedPlayers(int netId) } } + /// + /// used to ensure that all players loads a bot before it spawns + /// + /// The NetId to spawn + /// private async Task WaitForPlayersToLoadBotProfile(int netId) { botQueue.Add(netId, 0); @@ -479,19 +580,28 @@ private async Task WaitForPlayersToLoadBotProfile(int netId) botQueue.Remove(netId); } - private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler coopHandler) + /// + /// Tries to despawn the furthest bot from all players + /// + /// + /// + /// + /// + private bool TryDespawnFurthestBot(Profile profile, Vector3 position, CoopHandler coopHandler) { - string botKey = GetFurthestBot(Bots, coopHandler, out float furthestDistance); + List humanPlayers = GetPlayers(coopHandler); + + string botKey = GetFurthestBot(humanPlayers, out float furthestDistance); if (botKey == string.Empty) { #if DEBUG - Logger.LogWarning("TryDespawnFurthest: botKey was empty"); + Logger.LogWarning("TryDespawnFurthest: botKey was empty"); #endif return false; } - if (furthestDistance > GetDistanceFromPlayers(position, GetPlayers(coopHandler))) + if (furthestDistance > GetDistanceFromPlayers(position, humanPlayers)) { #if DEBUG Logger.LogWarning($"We're not despawning anything. The furthest bot is closer than the one we wanted to spawn."); @@ -509,7 +619,7 @@ private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler c } 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."); + 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 @@ -518,6 +628,11 @@ private bool TryDespawnFurthest(Profile profile, Vector3 position, CoopHandler c return true; } + /// + /// Despawns a bot + /// + /// + /// The bot to despawn private void DespawnBot(CoopHandler coopHandler, Player bot) { IBotGame botGame = Singleton.Instance; @@ -537,61 +652,71 @@ private void DespawnBot(CoopHandler coopHandler, Player bot) } /// - /// We use instead + /// The countdown deploy screen /// - /// - public override void vmethod_1(float timeBeforeDeploy) + /// + public override IEnumerator vmethod_1() { - /// Do nothing + CoopPlayer coopPlayer = (CoopPlayer)PlayerOwner.Player; + coopPlayer.PacketSender.Init(); + + int timeBeforeDeployLocal = Singleton.Instance.TimeBeforeDeployLocal; + DateTime dateTime = EFTDateTimeClass.Now.AddSeconds(timeBeforeDeployLocal); + new MatchmakerFinalCountdown.FinalCountdownScreenClass(Profile_0, dateTime).ShowScreen(EScreenState.Root); + MonoBehaviourSingleton.Instance.FadeInVolumeBeforeRaid(timeBeforeDeployLocal); + Singleton.Instance.StopMenuBackgroundMusicWithDelay(timeBeforeDeployLocal); + GameUi.gameObject.SetActive(true); + GameUi.TimerPanel.ProfileId = ProfileId; + yield return new WaitForSeconds(timeBeforeDeployLocal); } /// - /// Matchmaker countdown + /// This task ensures that all players are joined and loaded before continuing /// - /// Time in seconds to count down - private async void DeployScreen(float timeBeforeDeploy) + /// + private IEnumerator WaitForOtherPlayers() { if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { - if (MatchmakerAcceptPatches.IsServer && MatchmakerAcceptPatches.HostExpectedNumberOfPlayers <= 1) + if (isServer && FikaBackendUtils.HostExpectedNumberOfPlayers <= 1) { - if (fikaStartButton != null) - { - Destroy(fikaStartButton); - } - if (DynamicAI != null) { DynamicAI.AddHumans(); } SetStatusModel status = new(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); - await FikaRequestHandler.UpdateSetStatus(status); + Task updateStatus = FikaRequestHandler.UpdateSetStatus(status); + + while (!updateStatus.IsCompleted) + { + yield return null; + } Singleton.Instance.ReadyClients++; - base.vmethod_1(timeBeforeDeploy); - return; + yield break; } NetDataWriter writer = new(); - forceStart = false; - MatchmakerAcceptPatches.GClass3163.ChangeStatus("Waiting for other players to finish loading..."); + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading..."); - if (fikaStartButton != null) - { - fikaStartButton.SetActive(true); - } + int expectedPlayers = FikaBackendUtils.HostExpectedNumberOfPlayers; - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { SetStatusModel status = new(coopHandler.MyPlayer.ProfileId, LobbyEntry.ELobbyStatus.IN_GAME); - await FikaRequestHandler.UpdateSetStatus(status); + Task updateStatus = FikaRequestHandler.UpdateSetStatus(status); + + while (!updateStatus.IsCompleted) + { + yield return null; + } do { - await Task.Delay(100); - } while (coopHandler.HumanPlayers < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart); + yield return null; + } while (coopHandler.HumanPlayers < expectedPlayers); FikaServer server = Singleton.Instance; server.ReadyClients++; @@ -605,8 +730,9 @@ private async void DeployScreen(float timeBeforeDeploy) do { - await Task.Delay(250); - } while (Singleton.Instance.ReadyClients < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart); + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", server.ReadyClients / expectedPlayers); + yield return new WaitForEndOfFrame(); + } while (server.ReadyClients < expectedPlayers); foreach (CoopPlayer player in coopHandler.Players.Values) { @@ -621,12 +747,12 @@ private async void DeployScreen(float timeBeforeDeploy) DynamicAI.AddHumans(); } } - else if (MatchmakerAcceptPatches.IsClient) + else { do { - await Task.Delay(100); - } while (coopHandler.HumanPlayers < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart); + yield return null; + } while (coopHandler.HumanPlayers < expectedPlayers); FikaClient client = Singleton.Instance; InformationPacket packet = new(true) @@ -638,23 +764,20 @@ private async void DeployScreen(float timeBeforeDeploy) do { - await Task.Delay(250); - } while (Singleton.Instance.ReadyClients < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart); + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", client.ReadyClients / expectedPlayers); + yield return new WaitForEndOfFrame(); + } while (client.ReadyClients < expectedPlayers); } - - if (fikaStartButton != null) - { - Destroy(fikaStartButton); - } - } - - base.vmethod_1(timeBeforeDeploy); } + /// + /// Sends or receives the for the game + /// + /// private async Task SendOrReceiveSpawnPoint() { - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { bool spawnTogether = RaidSettings.PlayersSpawnPlace == EPlayersSpawnPlace.SamePlace; UpdateSpawnPointRequest body = new(spawnTogether ? spawnPoint.Id : ""); @@ -669,7 +792,7 @@ private async Task SendOrReceiveSpawnPoint() } await FikaRequestHandler.UpdateSpawnPoint(body); } - else if (MatchmakerAcceptPatches.IsClient) + else { SpawnPointRequest body = new(); SpawnPointResponse response = await FikaRequestHandler.RaidSpawnPoint(body); @@ -679,7 +802,7 @@ private async Task SendOrReceiveSpawnPoint() { Logger.LogInfo($"Retrieved Spawn Point '{name}' from server"); - Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field("dictionary_0").GetValue>(); + Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field>("dictionary_0").Value; foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys) { if (spawnPointObject.Id == name) @@ -698,7 +821,7 @@ private async Task SendOrReceiveSpawnPoint() } /// - /// Creating the EFT.LocalPlayer + /// /// /// /// @@ -716,27 +839,29 @@ private async Task SendOrReceiveSpawnPoint() /// /// /// + /// /// + /// public override async Task vmethod_2(int playerId, Vector3 position, Quaternion rotation, string layerName, string prefix, EPointOfView pointOfView, Profile profile, bool aiControl, EUpdateQueue updateQueue, Player.EUpdateMode armsUpdateMode, Player.EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, Func getAimingSensitivity, IStatisticsManager statisticsManager, AbstractQuestControllerClass questController, AbstractAchievementControllerClass achievementsController) { - Logger.LogInfo("Creating CoopHandler"); await CreateCoopHandler(); - CoopHandler.GetCoopHandler().LocalGameInstance = this; - - LocalPlayer myPlayer = await CoopPlayer.Create(playerId, spawnPoint.Position, spawnPoint.Rotation, "Player", "Main_", EPointOfView.FirstPerson, profile, - false, UpdateQueue, armsUpdateMode, bodyUpdateMode, - GClass549.Config.CharacterController.ClientPlayerMode, getSensitivity, - getAimingSensitivity, new GClass1445(), MatchmakerAcceptPatches.IsServer ? 0 : 1000, statisticsManager); profile.SetSpawnedInSession(profile.Side == EPlayerSide.Savage); + LocalPlayer myPlayer = await CoopPlayer.Create(playerId, spawnPoint.Position, spawnPoint.Rotation, "Player", + "Main_", EPointOfView.FirstPerson, profile, false, UpdateQueue, armsUpdateMode, bodyUpdateMode, + BackendConfigAbstractClass.Config.CharacterController.ClientPlayerMode, getSensitivity, + getAimingSensitivity, new GClass1456(), isServer ? 0 : 1000, statisticsManager); + + await NetManagerUtils.InitNetManager(isServer); + if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { - Logger.LogDebug($"{nameof(vmethod_2)}:Unable to find {nameof(CoopHandler)}"); + Logger.LogError($"{nameof(vmethod_2)}:Unable to find {nameof(CoopHandler)}"); throw new MissingComponentException("CoopHandler was missing during CoopGame init"); } @@ -749,19 +874,20 @@ public override async Task vmethod_2(int playerId, Vector3 position CoopPlayer coopPlayer = (CoopPlayer)myPlayer; coopHandler.Players.Add(coopPlayer.NetId, coopPlayer); - PlayerSpawnRequest body = new(myPlayer.ProfileId, MatchmakerAcceptPatches.GetGroupId()); + PlayerSpawnRequest body = new(myPlayer.ProfileId, FikaBackendUtils.GetGroupId()); await FikaRequestHandler.UpdatePlayerSpawn(body); myPlayer.SpawnPoint = spawnPoint; GameObject customButton = null; - GameObject customButtonStart = null; + + await NetManagerUtils.SetupGameVariables(isServer, coopPlayer); // This creates a "custom" Back button so that we can back out if we get stuck if (MenuUI.Instantiated) { MenuUI menuUI = MenuUI.Instance; - DefaultUIButton backButton = Traverse.Create(menuUI.MatchmakerTimeHasCome).Field("_cancelButton").GetValue(); + DefaultUIButton backButton = Traverse.Create(menuUI.MatchmakerTimeHasCome).Field("_cancelButton").Value; customButton = Instantiate(backButton.gameObject, backButton.gameObject.transform.parent); customButton.gameObject.name = "FikaBackButton"; customButton.gameObject.transform.position = new(customButton.transform.position.x, customButton.transform.position.y - 20, customButton.transform.position.z); @@ -772,59 +898,21 @@ public override async Task vmethod_2(int playerId, Vector3 position UnityEngine.Events.UnityEvent newEvent = new(); newEvent.AddListener(() => { - Singleton.Instance.ShowCriticalErrorScreen("WARNING", message: "Backing out from this stage is currently experimental. It is recommended to ALT+F4 instead. Do you still want to continue?", + Singleton.Instance.ShowCriticalErrorScreen("WARNING", + message: "Backing out from this stage is currently experimental. It is recommended to ALT+F4 instead. Do you still want to continue?", ErrorScreen.EButtonType.OkButton, 15f, () => { - StopFromError(myPlayer.ProfileId, ExitStatus.Runner); + StopFromCancel(myPlayer.ProfileId, ExitStatus.Runner); PlayerLeftRequest playerLeftRequest = new(coopPlayer.ProfileId); FikaRequestHandler.RaidLeave(playerLeftRequest); }, null); }); Traverse.Create(backButtonComponent).Field("OnClick").SetValue(newEvent); - - if (MatchmakerAcceptPatches.IsServer) - { - DefaultUIButton startButton = Traverse.Create(menuUI.MatchmakerTimeHasCome).Field("_cancelButton").GetValue(); - customButtonStart = Instantiate(backButton.gameObject, backButton.gameObject.transform.parent); - customButtonStart.gameObject.name = "FikaStartButton"; - customButtonStart.gameObject.SetActive(true); - customButtonStart.gameObject.transform.position = new(customButton.transform.position.x, customButton.transform.position.y + 60, customButton.transform.position.z); - DefaultUIButton startButtonComponent = customButtonStart.GetComponent(); - startButtonComponent.SetHeaderText("Force Start", 32); - startButtonComponent.SetEnabledTooltip("EXPERIMENTAL: Force starts the game. Use at own risk!"); - UnityEngine.Events.UnityEvent newStartEvent = new(); - newStartEvent.AddListener(() => - { - forceStart = true; - - InformationPacket packet = new(false) - { - ForceStart = true - }; - - FikaPlugin.Instance.FikaLogger.LogWarning("Force start was used!"); - - NetDataWriter writer = new(); - writer.Reset(); - Singleton.Instance.SendDataToAll(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - - if (fikaStartButton != null) - { - fikaStartButton.SetActive(false); - } - }); - Traverse.Create(startButtonComponent).Field("OnClick").SetValue(newStartEvent); - if (customButton != null) - { - customButton.SetActive(true); - } - fikaStartButton = customButtonStart; - } } - SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket() { Profile = myPlayer.Profile }, myPlayer.HealthController.IsAlive, false, myPlayer.Transform.position, (myPlayer as CoopPlayer).NetId); + SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(myPlayer.Profile), myPlayer.HealthController.IsAlive, false, myPlayer.Transform.position, (myPlayer as CoopPlayer).NetId); - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { await SetStatus(myPlayer, LobbyEntry.ELobbyStatus.COMPLETE); } @@ -833,51 +921,26 @@ public override async Task vmethod_2(int playerId, Vector3 position Singleton.Instance.SendData(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); } - if (MatchmakerAcceptPatches.IsServer) - { - while (!Singleton.Instantiated && !Singleton.Instance.ServerReady) - { - await Task.Delay(100); - } - } - else if (MatchmakerAcceptPatches.IsClient) - { - while (!Singleton.Instantiated && !Singleton.Instance.ClientReady) - { - await Task.Delay(100); - } - } - await WaitForPlayers(); - Destroy(customButton); - if (fikaStartButton != null) - { - fikaStartButton.SetActive(false); - } + fikaDebug = gameObject.AddComponent(); - myPlayer.ActiveHealthController.DiedEvent += MainPlayerDied; + Destroy(customButton); return myPlayer; } - private void MainPlayerDied(EDamageType obj) - { - EndByTimerScenario endByTimerScenario = GetComponent(); - if (endByTimerScenario != null) - { - Destroy(endByTimerScenario); - } - if (GameUi.TimerPanel.enabled) - { - GameUi.TimerPanel.Close(); - } - } - - public async Task InitPlayer(BotControllerSettings botsSettings, string backendUrl, InventoryControllerClass inventoryController, Callback runCallback) + /// + /// Initializes the local player + /// + /// + /// + /// + /// + public async Task InitPlayer(BotControllerSettings botsSettings, string backendUrl, Callback runCallback) { Status = GameStatus.Running; - UnityEngine.Random.InitState((int)GClass1296.Now.Ticks); + UnityEngine.Random.InitState((int)EFTDateTimeClass.Now.Ticks); LocationSettingsClass.Location location; if (Location_0.IsHideout) @@ -886,11 +949,11 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU } else { - using (GClass21.StartWithToken("LoadLocation")) + using (CounterCreatorAbstractClass.StartWithToken("LoadLocation")) { int num = UnityEngine.Random.Range(1, 6); method_6(backendUrl, Location_0.Id, num); - location = await ginterface145_0.LoadLocationLoot(Location_0.Id, num); + location = await BackEndSession.LoadLocationLoot(Location_0.Id, num); } } @@ -904,16 +967,16 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU } else { - Logger.LogError("Can't find event prefab in resources. Path : Prefabs/HALLOWEEN_CONTROLLER"); + Logger.LogError("Can't find event prefab in resources. Path: 'Prefabs/HALLOWEEN_CONTROLLER'"); } } - GClass785 config = GClass549.Config; + ApplicationConfigClass config = BackendConfigAbstractClass.Config; if (config.FixedFrameRate > 0f) { FixedDeltaTime = 1f / config.FixedFrameRate; } - using (GClass21.StartWithToken("player create")) + using (CounterCreatorAbstractClass.StartWithToken("player create")) { Player player = await CreateLocalPlayer(); dictionary_0.Add(player.ProfileId, player); @@ -928,9 +991,16 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU await method_11(location, startHandler.FinishLoading); } - private class StartHandler(BaseLocalGame localGame, BotControllerSettings botSettings, ISpawnSystem spawnSystem, Callback runCallback) + /// + /// Handler used to start the game + /// + /// + /// + /// + /// + private class StartHandler(BaseLocalGame localGame, BotControllerSettings botSettings, ISpawnSystem spawnSystem, Callback runCallback) { - private readonly BaseLocalGame localGame = localGame; + private readonly BaseLocalGame localGame = localGame; private readonly BotControllerSettings botSettings = botSettings; private readonly ISpawnSystem spawnSystem = spawnSystem; private readonly Callback runCallback = runCallback; @@ -941,28 +1011,32 @@ public void FinishLoading() } } + /// + /// Creates the local player + /// + /// A private async Task CreateLocalPlayer() { int num = method_12(); Player.EUpdateMode eupdateMode = Player.EUpdateMode.Auto; - if (GClass549.Config.UseHandsFastAnimator) + if (BackendConfigAbstractClass.Config.UseHandsFastAnimator) { eupdateMode = Player.EUpdateMode.Manual; } - spawnPoints = GClass2928.CreateFromScene(new DateTime?(GClass1296.LocalDateTimeFromUnixTime(Location_0.UnixDateTime)), Location_0.SpawnPointParams); + spawnPoints = SpawnPointManagerClass.CreateFromScene(new DateTime?(EFTDateTimeClass.LocalDateTimeFromUnixTime(Location_0.UnixDateTime)), Location_0.SpawnPointParams); int spawnSafeDistance = (Location_0.SpawnSafeDistanceMeters > 0) ? Location_0.SpawnSafeDistanceMeters : 100; - GStruct380 settings = new(Location_0.MinDistToFreePoint, Location_0.MaxDistToFreePoint, Location_0.MaxBotPerZone, spawnSafeDistance); - SpawnSystem = GClass2929.CreateSpawnSystem(settings, () => Time.time, Singleton.Instance, zones: botsController_0, spawnPoints); + GStruct379 settings = new(Location_0.MinDistToFreePoint, Location_0.MaxDistToFreePoint, Location_0.MaxBotPerZone, spawnSafeDistance); + SpawnSystem = GClass2950.CreateSpawnSystem(settings, new Func(Class1384.class1384_0.method_0), Singleton.Instance, zones: botsController_0, spawnPoints); - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); await SendOrReceiveSpawnPoint(); } - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { await SendOrReceiveSpawnPoint(); if (spawnPoint == null) @@ -975,8 +1049,8 @@ private async Task CreateLocalPlayer() IStatisticsManager statisticsManager = new CoopClientStatisticsManager(Profile_0); LocalPlayer myPlayer = await vmethod_2(num, spawnPoint.Position, spawnPoint.Rotation, "Player", "Main_", EPointOfView.FirstPerson, Profile_0, false, - UpdateQueue, eupdateMode, Player.EUpdateMode.Auto, GClass549.Config.CharacterController.ClientPlayerMode, - new Func(Class1362.class1362_0.method_1), new Func(Class1362.class1362_0.method_2), + UpdateQueue, eupdateMode, Player.EUpdateMode.Auto, BackendConfigAbstractClass.Config.CharacterController.ClientPlayerMode, + new Func(Class1384.class1384_0.method_3), new Func(Class1384.class1384_0.method_4), statisticsManager, null, null); myPlayer.Location = Location_0.Id; @@ -985,53 +1059,47 @@ private async Task CreateLocalPlayer() return myPlayer; } + /// + /// used to wait for all other players to join the game + /// + /// private async Task WaitForPlayers() { Logger.LogInfo("Starting task to wait for other players."); - if (MatchmakerAcceptPatches.GClass3163 != null) + if (FikaBackendUtils.ScreenController != null) { - MatchmakerAcceptPatches.GClass3163.ChangeStatus($"Initializing Coop Game..."); + FikaBackendUtils.ScreenController.ChangeStatus($"Initializing Coop Game..."); } int numbersOfPlayersToWaitFor = 0; - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { - while (!Singleton.Instantiated && !Singleton.Instance.ServerReady) - { - await Task.Delay(100); - } - FikaServer server = Singleton.Instance; do { - numbersOfPlayersToWaitFor = MatchmakerAcceptPatches.HostExpectedNumberOfPlayers - (server.NetServer.ConnectedPeersCount + 1); - if (MatchmakerAcceptPatches.GClass3163 != null) + numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (server.NetServer.ConnectedPeersCount + 1); + if (FikaBackendUtils.ScreenController != null) { if (numbersOfPlayersToWaitFor > 0) { - MatchmakerAcceptPatches.GClass3163.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); + FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); } else { - MatchmakerAcceptPatches.GClass3163.ChangeStatus($"All players joined, starting game..."); + FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); } } else { Logger.LogError("WaitForPlayers::GClass3163 was null!"); } - await Task.Delay(1000); - } while (numbersOfPlayersToWaitFor > 0 && !forceStart); + await Task.Delay(100); + } while (numbersOfPlayersToWaitFor > 0); } - else if (MatchmakerAcceptPatches.IsClient) + else { - while (!Singleton.Instantiated && !Singleton.Instance.ClientReady) - { - await Task.Delay(100); - } - FikaClient client = Singleton.Instance; while (client.NetClient == null) @@ -1044,13 +1112,14 @@ private async Task WaitForPlayers() while (client.ServerConnection == null && connectionAttempts < 5) { // Server retries 10 times with a 500ms interval, we give it 5 seconds to try - MatchmakerAcceptPatches.GClass3163.ChangeStatus("Waiting for client to connect to server... If there is no notification it failed."); + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for client to connect to server... If there is no notification it failed."); connectionAttempts++; await Task.Delay(1000); if (client.ServerConnection == null && connectionAttempts == 5) { - Singleton.Instance.ShowErrorScreen("Network Error", "Unable to connect to the raid server. Make sure ports are forwarded and/or UPnP is enabled and supported."); + Singleton.Instance.ShowErrorScreen("Network Error", + "Unable to connect to the raid server. Make sure ports are forwarded and/or UPnP is enabled and supported."); } } @@ -1065,16 +1134,16 @@ private async Task WaitForPlayers() client.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); do { - numbersOfPlayersToWaitFor = MatchmakerAcceptPatches.HostExpectedNumberOfPlayers - (client.ConnectedClients + 1); - if (MatchmakerAcceptPatches.GClass3163 != null) + numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (client.ConnectedClients + 1); + if (FikaBackendUtils.ScreenController != null) { if (numbersOfPlayersToWaitFor > 0) { - MatchmakerAcceptPatches.GClass3163.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); + FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); } else { - MatchmakerAcceptPatches.GClass3163.ChangeStatus($"All players joined, starting game..."); + FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); } } else @@ -1085,10 +1154,16 @@ private async Task WaitForPlayers() writer.Reset(); client.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); await Task.Delay(1000); - } while (numbersOfPlayersToWaitFor > 0 && !forceStart); + } while (numbersOfPlayersToWaitFor > 0); } } + /// + /// Sets the status of the game on the backend + /// + /// + /// + /// private async Task SetStatus(LocalPlayer myPlayer, LobbyEntry.ELobbyStatus status) { SetStatusModel statusBody = new(myPlayer.ProfileId, status); @@ -1099,30 +1174,34 @@ private async Task SetStatus(LocalPlayer myPlayer, LobbyEntry.ELobbyStatus statu /// /// Bot System Starter -> Countdown /// - /// /// /// /// /// - public override IEnumerator vmethod_4(float startDelay, BotControllerSettings controllerSettings, ISpawnSystem spawnSystem, Callback runCallback) + public override IEnumerator vmethod_4(BotControllerSettings controllerSettings, ISpawnSystem spawnSystem, Callback runCallback) { - if (MatchmakerAcceptPatches.IsClient) +#if DEBUG + Logger.LogWarning("vmethod_4"); +#endif + + if (!isServer) { controllerSettings.BotAmount = EBotAmount.NoBots; } - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { - BotsPresets profileCreator = new(BackEndSession, wavesSpawnScenario_0.SpawnWaves, GClass579.BossSpawnWaves, nonWavesSpawnScenario_0.GClass1467_0, false); + BotsPresets profileCreator = new(BackEndSession, wavesSpawnScenario_0.SpawnWaves, + BossSpawnWaveManagerClass.BossSpawnWaves, nonWavesSpawnScenario_0.GClass1478_0, false); - GClass813 botCreator = new(this, profileCreator, CreateBot); + GClass814 botCreator = new(this, profileCreator, CreateBot); BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); bool enableWaves = controllerSettings.BotAmount == EBotAmount.Horde; botsController_0.Init(this, botCreator, botZones, spawnSystem, wavesSpawnScenario_0.BotLocationModifier, controllerSettings.IsEnabled, controllerSettings.IsScavWars, enableWaves, false, - GClass579.HaveSectants, Singleton.Instance, Location_0.OpenZones); + BossSpawnWaveManagerClass.HaveSectants, Singleton.Instance, Location_0.OpenZones); Logger.LogInfo($"Location: {Location_0.Name}"); @@ -1137,7 +1216,7 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co _ => 0, }; - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { numberOfBots = 0; } @@ -1166,16 +1245,16 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co if (limits > 0) { botsController_0.BotSpawner.SetMaxBots(limits); - } + } } DynamicAI = gameObject.AddComponent(); } - else if (MatchmakerAcceptPatches.IsClient) + else { BotsPresets profileCreator = new(BackEndSession, [], [], [], false); - GClass813 botCreator = new(this, profileCreator, CreateBot); + GClass814 botCreator = new(this, profileCreator, CreateBot); BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); // Setting this to an empty array stops the client from downloading bots @@ -1191,37 +1270,34 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co BackendConfigSettingsClass instance = Singleton.Instance; - bool isWinter = BackEndSession.IsWinter; - Class420 winterEventController = new(); - Singleton.Instance.Class420_0 = winterEventController; - winterEventController.Run(isWinter).HandleExceptions(); - - if (startDelay < 5) - { - startDelay = 5; - NotificationManagerClass.DisplayWarningNotification("You have set the deploy timer too low, resetting to 5!"); - } + LocalGame.Class1391 seasonTaskHandler = new(); + ESeason season = BackEndSession.Season; + Class394 seasonHandler = new(); + Singleton.Instance.GInterface26_0 = seasonHandler; + seasonTaskHandler.task = seasonHandler.Run(season); + yield return new WaitUntil(new Func(seasonTaskHandler.method_0)); - DeployScreen(startDelay); + yield return WaitForOtherPlayers(); - if (MatchmakerAcceptPatches.IsServer) + int expectedPlayers = FikaBackendUtils.HostExpectedNumberOfPlayers; + if (isServer) { - while (Singleton.Instance.ReadyClients < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart) + FikaServer server = Singleton.Instance; + while (server.ReadyClients < expectedPlayers) { yield return new WaitForEndOfFrame(); } } - else if (MatchmakerAcceptPatches.IsClient) + else { - while (Singleton.Instance.ReadyClients < MatchmakerAcceptPatches.HostExpectedNumberOfPlayers && !forceStart) + FikaClient client = Singleton.Instance; + while (client.ReadyClients < expectedPlayers) { yield return new WaitForEndOfFrame(); } } - yield return new WaitForSeconds(startDelay); - - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { if (Location_0.OldSpawn && wavesSpawnScenario_0.SpawnWaves != null && wavesSpawnScenario_0.SpawnWaves.Length != 0) { @@ -1241,7 +1317,7 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co } } - GClass579.Run(EBotsSpawnMode.Anyway); + BossSpawnWaveManagerClass.Run(EBotsSpawnMode.Anyway); FikaPlugin.DynamicAI.SettingChanged += DynamicAI_SettingChanged; FikaPlugin.DynamicAIRate.SettingChanged += DynamicAIRate_SettingChanged; @@ -1256,33 +1332,51 @@ public override IEnumerator vmethod_4(float startDelay, BotControllerSettings co { nonWavesSpawnScenario_0.Stop(); } - if (GClass579 != null) + if (BossSpawnWaveManagerClass != null) { - GClass579.Stop(); + BossSpawnWaveManagerClass.Stop(); } } - yield return new WaitForEndOfFrame(); - - CreateExfiltrationPointAndInitDeathHandler(); - // Add FreeCamController to GameWorld GameObject Singleton.Instance.gameObject.GetOrAddComponent(); Singleton.Instance.gameObject.GetOrAddComponent(); FikaAirdropsManager.ContainerCount = 0; SetupBorderzones(); + SetupRaidCode(); if (Singleton.Instance.MineManager != null) { Singleton.Instance.MineManager.OnExplosion += OnMineExplode; } - runCallback.Succeed(); + Singleton.Instance.TimeBeforeDeployLocal = Math.Max(Singleton.Instance.TimeBeforeDeployLocal, 5); + yield return base.vmethod_4(controllerSettings, spawnSystem, runCallback); yield break; } + private void SetupRaidCode() + { + string raidCode = FikaBackendUtils.GetRaidCode(); + if (!string.IsNullOrEmpty(raidCode)) + { + Traverse preloaderUiTraverse = Traverse.Create(MonoBehaviourSingleton.Instance); + // Raid code + preloaderUiTraverse.Field("string_3").SetValue($"{raidCode}"); + // Update version label + preloaderUiTraverse.Method("method_6").GetValue(); + + Logger.LogInfo($"MatchingType: {FikaBackendUtils.MatchingType}, Raid Code: {raidCode}"); + } + } + + /// + /// Triggers when the setting is changed + /// + /// + /// private void DynamicAIRate_SettingChanged(object sender, EventArgs e) { if (DynamicAI != null) @@ -1291,6 +1385,11 @@ private void DynamicAIRate_SettingChanged(object sender, EventArgs e) } } + /// + /// Triggers when the setting is changed + /// + /// + /// private void DynamicAI_SettingChanged(object sender, EventArgs e) { if (DynamicAI != null) @@ -1299,6 +1398,9 @@ private void DynamicAI_SettingChanged(object sender, EventArgs e) } } + /// + /// Sets up all the s on the map + /// private void SetupBorderzones() { GameWorld gameWorld = Singleton.Instance; @@ -1308,14 +1410,14 @@ private void SetupBorderzones() gameWorld.BorderZones[i].Id = i; } - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { foreach (BorderZone borderZone in gameWorld.BorderZones) { borderZone.PlayerShotEvent += OnBorderZoneShot; } } - else if (MatchmakerAcceptPatches.IsClient) + else { foreach (BorderZone borderZone in gameWorld.BorderZones) { @@ -1324,7 +1426,14 @@ private void SetupBorderzones() } } - private void OnBorderZoneShot(GInterface94 player, BorderZone zone, float arg3, bool arg4) + /// + /// Triggered when a triggers (only runs on host) + /// + /// + /// + /// + /// + private void OnBorderZoneShot(IPlayerOwner player, BorderZone zone, float arg3, bool arg4) { BorderZonePacket packet = new() { @@ -1334,6 +1443,10 @@ private void OnBorderZoneShot(GInterface94 player, BorderZone zone, float arg3, Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); } + /// + /// Triggers when a explodes + /// + /// private void OnMineExplode(MineDirectional directional) { if (!directional.gameObject.active) @@ -1345,29 +1458,36 @@ private void OnMineExplode(MineDirectional directional) { MinePositon = directional.transform.position }; - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { Singleton.Instance.SendData(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); } - else if (MatchmakerAcceptPatches.IsServer) + else { Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); } } - public void CreateExfiltrationPointAndInitDeathHandler() + /// + /// Sets up events and all s + /// + public override void vmethod_5() { - Logger.LogInfo("CreateExfiltrationPointAndInitDeathHandler"); - - GameTimer.Start(); - gparam_0.vmethod_0(); + GameTimer.Start(null, null); gparam_0.Player.HealthController.DiedEvent += HealthController_DiedEvent; + gparam_0.vmethod_0(); + + SkillClass[] skills = Profile_0.Skills.Skills; + for (int i = 0; i < skills.Length; i++) + { + skills[i].SetPointsEarnedInSession(0f, false); + } InfiltrationPoint = spawnPoint.Infiltration; Profile_0.Info.EntryPoint = InfiltrationPoint; - Logger.LogDebug("SpawnPoint: " + spawnPoint.Id + ", InfiltrationPoint: " + InfiltrationPoint); + Logger.LogInfo("SpawnPoint: " + spawnPoint.Id + ", InfiltrationPoint: " + InfiltrationPoint); - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { CarExtraction carExtraction = FindObjectOfType(); if (carExtraction != null) @@ -1376,10 +1496,10 @@ public void CreateExfiltrationPointAndInitDeathHandler() } } - ExfiltrationControllerClass.Instance.InitAllExfiltrationPoints(Location_0.exits, MatchmakerAcceptPatches.IsClient, ""); + ExfiltrationControllerClass.Instance.InitAllExfiltrationPoints(Location_0.exits, !isServer, ""); ExfiltrationPoint[] exfilPoints = ExfiltrationControllerClass.Instance.EligiblePoints(Profile_0); - GameUi.TimerPanel.SetTime(GClass1296.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), exfilPoints); + GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), exfilPoints); exfilManager = gameObject.AddComponent(); exfilManager.Run(exfilPoints); @@ -1397,11 +1517,11 @@ public void CreateExfiltrationPointAndInitDeathHandler() { if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { coopHandler.serverBTR = gameWorld.gameObject.AddComponent(); } - else if (MatchmakerAcceptPatches.IsClient) + else { coopHandler.clientBTR = gameWorld.gameObject.AddComponent(); } @@ -1410,34 +1530,41 @@ public void CreateExfiltrationPointAndInitDeathHandler() } catch (Exception) { - Logger.LogError("CreateExfiltrationPointAndInitDeathHandler: Exception thrown during BTR init, check logs."); + Logger.LogError("vmethod_5: Exception thrown during BTR init, check logs."); } } - dateTime_0 = GClass1296.Now; + dateTime_0 = EFTDateTimeClass.Now; Status = GameStatus.Started; ConsoleScreen.ApplyStartCommands(); } + /// + /// Updates a from the server + /// + /// + /// public void UpdateExfilPointFromServer(ExfiltrationPoint point, bool enable) { exfilManager.UpdateExfilPointFromServer(point, enable); } + /// + /// Resets all s from the server + /// + /// public void ResetExfilPointsFromServer(ExfiltrationPoint[] points) { - Dictionary currentExfils = Traverse.Create(GameUi.TimerPanel).Field("dictionary_0").GetValue>(); + Dictionary currentExfils = Traverse.Create(GameUi.TimerPanel).Field>("dictionary_0").Value; foreach (ExitTimerPanel exitTimerPanel in currentExfils.Values) { exitTimerPanel.Close(); } currentExfils.Clear(); - GameUi.TimerPanel.SetTime(GClass1296.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), points); + GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), points); } - public List ExtractedPlayers { get; } = []; - /// /// When the local player successfully extracts, enable freecam, notify other players about the extract /// @@ -1447,6 +1574,18 @@ public void Extract(CoopPlayer player, ExfiltrationPoint point) { PreloaderUI preloaderUI = Singleton.Instance; + + if (MyExitStatus == ExitStatus.MissingInAction) + { + NotificationManagerClass.DisplayMessageNotification("You have gone missing in action...", iconType: EFT.Communications.ENotificationIconType.Alert, textColor: Color.red); + } + + BackendConfigSettingsClass.GClass1361.GClass1367 matchEndConfig = Singleton.Instance.Experience.MatchEnd; + if (player.Profile.EftStats.SessionCounters.GetAllInt([CounterTag.Exp]) < matchEndConfig.SurvivedExpRequirement || PastTime < matchEndConfig.SurvivedTimeRequirement) + { + MyExitStatus = ExitStatus.Runner; + } + if (point != null) { point.Disable(); @@ -1476,11 +1615,11 @@ public void Extract(CoopPlayer player, ExfiltrationPoint point) try // This is to allow clients to extract if they lose connection { - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { Singleton.Instance.SendData(new NetDataWriter(), ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); } - else if (MatchmakerAcceptPatches.IsServer) + else { Singleton.Instance.SendDataToAll(new NetDataWriter(), ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); ClearHostAI(player); @@ -1493,7 +1632,7 @@ public void Extract(CoopPlayer player, ExfiltrationPoint point) CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - CoopPlayer coopPlayer = (CoopPlayer)player; + CoopPlayer coopPlayer = player; ExtractedPlayers.Add(coopPlayer.NetId); coopHandler.ExtractedPlayers.Add(coopPlayer.NetId); coopHandler.Players.Remove(coopPlayer.NetId); @@ -1508,28 +1647,25 @@ public void Extract(CoopPlayer player, ExfiltrationPoint point) extractRoutine = StartCoroutine(ExtractRoutine(player)); // Prevents players from looting after extracting - GClass3107.Instance.CloseAllScreensForced(); + CurrentScreenSingleton.Instance.CloseAllScreensForced(); // Detroys session timer - EndByTimerScenario endByTimerScenario = GetComponent(); - if (endByTimerScenario != null) + if (timeManager != null) { - Destroy(endByTimerScenario); + Destroy(timeManager); } if (GameUi.TimerPanel.enabled) { GameUi.TimerPanel.Close(); } - player.ActiveHealthController.DiedEvent -= MainPlayerDied; - if (FikaPlugin.AutoExtract.Value) { - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); } - else if (MatchmakerAcceptPatches.IsServer && Singleton.Instance.NetServer.ConnectedPeersCount == 0) + else if (Singleton.Instance.NetServer.ConnectedPeersCount == 0) { Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); } @@ -1566,7 +1702,7 @@ public void ClearHostAI(Player player) { if (botsController_0 != null) { - foreach (KeyValuePair kvp in botsController_0.Groups()) + foreach (KeyValuePair kvp in botsController_0.Groups()) { foreach (BotsGroup botsGroup in kvp.Value.GetGroups(true)) { @@ -1575,8 +1711,8 @@ public void ClearHostAI(Player player) } } - BotsClass bots = Traverse.Create(botsController_0.BotSpawner).Field("_bots").GetValue(); - HashSet allBots = Traverse.Create(bots).Field("hashSet_0").GetValue>(); + BotsClass bots = Traverse.Create(botsController_0.BotSpawner).Field("_bots").Value; + HashSet allBots = Traverse.Create(bots).Field>("hashSet_0").Value; foreach (BotOwner bot in allBots) { @@ -1586,8 +1722,21 @@ public void ClearHostAI(Player player) } } + /// + /// Triggers when the main player dies + /// + /// private void HealthController_DiedEvent(EDamageType obj) { + if (timeManager != null) + { + Destroy(timeManager); + } + if (GameUi.TimerPanel != null && GameUi.TimerPanel.enabled) + { + GameUi.TimerPanel.Close(); + } + gparam_0.Player.HealthController.DiedEvent -= method_15; gparam_0.Player.HealthController.DiedEvent -= HealthController_DiedEvent; @@ -1597,22 +1746,20 @@ private void HealthController_DiedEvent(EDamageType obj) if (FikaPlugin.Instance.ForceSaveOnDeath) { - StartCoroutine(SaveOnDeathRoutine()); + Task.Run(() => SavePlayer((CoopPlayer)gparam_0.Player, MyExitStatus, null, true)); } } - private IEnumerator SaveOnDeathRoutine() - { - Task saveTask = SavePlayer((CoopPlayer)gparam_0.Player, MyExitStatus, null, true); - while (!saveTask.IsCompleted) - { - yield return null; - } - } - + /// + /// Stops the local + /// + /// + /// + /// + /// public override void Stop(string profileId, ExitStatus exitStatus, string exitName, float delay = 0f) { - Logger.LogInfo("CoopGame::Stop"); + Logger.LogDebug("Stop"); CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; myPlayer.PacketSender.DestroyThis(); @@ -1621,10 +1768,11 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa { if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) { - GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, myPlayer.GClass2761_0, false, true); + GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, + myPlayer.InventoryControllerClass, false, true); if (result.Error != null) { - FikaPlugin.Instance.FikaLogger.LogWarning("CoopGame::Stop: Error removing dog tag!"); + Logger.LogError("Stop: Error removing dog tag!"); } } } @@ -1642,15 +1790,15 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa } } - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { botsController_0.Stop(); botsController_0.DestroyInfo(gparam_0.Player); } - if (GClass579 != null) + if (BossSpawnWaveManagerClass != null) { - GClass579.Stop(); + BossSpawnWaveManagerClass.Stop(); } if (nonWavesSpawnScenario_0 != null) { @@ -1668,7 +1816,7 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa } catch (Exception) { - FikaPlugin.Instance.FikaLogger.LogError("Unable to send RaidLeave request to server."); + Logger.LogError("Unable to send RaidLeave request to server."); } if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) @@ -1693,7 +1841,7 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa } else { - Logger.LogError("CoopGame::Stop: Could not find CoopHandler!"); + Logger.LogError("Stop: Could not find CoopHandler!"); } coopHandler.RunAsyncTasks = false; @@ -1706,23 +1854,12 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa ExitManager stopManager = new(this, exitStatus, exitName, delay, myPlayer); - EndByExitTrigerScenario endByExitTrigger = GetComponent(); - EndByTimerScenario endByTimerScenario = GetComponent(); GameUI gameUI = GameUI.Instance; - if (endByTimerScenario != null) - { - if (Status == GameStatus.Starting || Status == GameStatus.Started) - { - endByTimerScenario.GameStatus_0 = GameStatus.SoftStopping; - } - } - exfilManager.Stop(); Status = GameStatus.Stopping; GameTimer.TryStop(); - endByExitTrigger.Stop(); if (gameUI.TimerPanel.enabled) { gameUI.TimerPanel.Close(); @@ -1732,10 +1869,18 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa EnvironmentManager.Instance.Stop(); } MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.HandleExit)); - GClass549.Config.UseSpiritPlayer = false; + BackendConfigAbstractClass.Config.UseSpiritPlayer = false; } - private Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitName, bool fromDeath) + /// + /// Saves your own to the server + /// + /// + /// + /// + /// + /// + private async Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitName, bool fromDeath) { if (hasSaved) { @@ -1750,12 +1895,13 @@ private Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitNam player.CheckAndResetControllers(exitStatus, PastTime, Location_0.Id, exitName); } - //Method taken directly from AKI, can be found in the aki-singleplayer assembly as OfflineSaveProfilePatch + //Method taken directly from SPT, can be found in the aki-singleplayer assembly as OfflineSaveProfilePatch Type converterClass = typeof(AbstractGame).Assembly.GetTypes().First(t => t.GetField("Converters", BindingFlags.Static | BindingFlags.Public) != null); - JsonConverter[] Converters = Traverse.Create(converterClass).Field("Converters").Value; + JsonConverter[] converters = Traverse.Create(converterClass).Field("Converters").Value; + converters = [.. converters, new NotesJsonConverter()]; - SaveProfileRequest SaveRequest = new() + SaveProfileRequest saveRequest = new() { Exit = exitStatus.ToString().ToLowerInvariant(), Profile = player.Profile, @@ -1764,15 +1910,19 @@ private Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitNam IsPlayerScav = player.Side is EPlayerSide.Savage }; - RequestHandler.PutJson("/raid/profile/save", SaveRequest.ToJson(Converters.AddItem(new NotesJsonConverter()).ToArray())); - + await RequestHandler.PutJsonAsync("/raid/profile/save", saveRequest.ToJson(converters)); hasSaved = true; return Task.CompletedTask; } - private void StopFromError(string profileId, ExitStatus exitStatus) + /// + /// Stops the local when waiting for other players + /// + /// + /// + private void StopFromCancel(string profileId, ExitStatus exitStatus) { - Logger.LogInfo("CoopGame::StopFromError"); + Logger.LogWarning("Game init was cancelled!"); CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; myPlayer.PacketSender.DestroyThis(); @@ -1781,10 +1931,11 @@ private void StopFromError(string profileId, ExitStatus exitStatus) { if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) { - GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, myPlayer.GClass2761_0, false, true); + GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, + myPlayer.InventoryControllerClass, false, true); if (result.Error != null) { - FikaPlugin.Instance.FikaLogger.LogWarning("CoopGame::StopFromError: Error removing dog tag!"); + Logger.LogWarning("StopFromError: Error removing dog tag!"); } } } @@ -1811,7 +1962,7 @@ private void StopFromError(string profileId, ExitStatus exitStatus) } else { - Logger.LogError("CoopGame::Stop: Could not find CoopHandler!"); + Logger.LogError("Stop: Could not find CoopHandler!"); } coopHandler.RunAsyncTasks = false; @@ -1822,9 +1973,9 @@ private void StopFromError(string profileId, ExitStatus exitStatus) Destroy(CoopHandler.CoopHandlerParent); } - if (GClass579 != null) + if (BossSpawnWaveManagerClass != null) { - GClass579.Stop(); + BossSpawnWaveManagerClass.Stop(); } if (nonWavesSpawnScenario_0 != null) { @@ -1835,7 +1986,7 @@ private void StopFromError(string profileId, ExitStatus exitStatus) wavesSpawnScenario_0.Stop(); } - ErrorExitManager stopManager = new() + CancelExitManager stopManager = new() { baseLocalGame_0 = this, exitStatus = exitStatus, @@ -1843,18 +1994,8 @@ private void StopFromError(string profileId, ExitStatus exitStatus) delay = delay }; - EndByExitTrigerScenario endByExitTrigger = GetComponent(); - EndByTimerScenario endByTimerScenario = GetComponent(); GameUI gameUI = GameUI.Instance; - if (endByTimerScenario != null) - { - if (Status == GameStatus.Starting || Status == GameStatus.Started) - { - endByTimerScenario.GameStatus_0 = GameStatus.SoftStopping; - } - } - if (exfilManager != null) { exfilManager.Stop(); @@ -1865,10 +2006,6 @@ private void StopFromError(string profileId, ExitStatus exitStatus) { GameTimer.TryStop(); } - if (endByExitTrigger != null) - { - endByExitTrigger.Stop(); - } if (gameUI.TimerPanel.enabled) { gameUI.TimerPanel.Close(); @@ -1879,9 +2016,24 @@ private void StopFromError(string profileId, ExitStatus exitStatus) EnvironmentManager.Instance.Stop(); } MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.ExitOverride)); - GClass549.Config.UseSpiritPlayer = false; + BackendConfigAbstractClass.Config.UseSpiritPlayer = false; + } + + /// + /// Toggles the menu + /// + /// + public void ToggleDebug(bool enabled) + { + if (fikaDebug != null) + { + fikaDebug.enabled = enabled; + } } + /// + /// Cleans up after the stops + /// public override void CleanUp() { foreach (Player player in dictionary_0.Values) @@ -1900,11 +2052,16 @@ public override void CleanUp() } } dictionary_0.Clear(); + // Reset MatchingType to Single when the game ends. + FikaBackendUtils.MatchingType = EMatchmakerType.Single; } + /// + /// Disposes of the + /// public override void Dispose() { - Logger.LogDebug("CoopGame::Dispose()"); + Logger.LogDebug("Dispose()"); if (Singleton.Instance.MineManager != null) { @@ -1916,34 +2073,32 @@ public override void Dispose() StopCoroutine(extractRoutine); } - if (MatchmakerAcceptPatches.IsServer) + if (isServer) { CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; coopPlayer.PacketSender.DestroyThis(); - Singleton.Instance.NetServer.Stop(); - Singleton.TryRelease(Singleton.Instance); - FikaDynamicAI newDynamicAI = gameObject.GetComponent(); if (newDynamicAI != null) { Destroy(newDynamicAI); } + NetManagerUtils.StopPinger(); + FikaPlugin.DynamicAI.SettingChanged -= DynamicAI_SettingChanged; FikaPlugin.DynamicAIRate.SettingChanged -= DynamicAIRate_SettingChanged; } - else if (MatchmakerAcceptPatches.IsClient) + else { - Singleton.Instance.NetClient.Stop(); - Singleton.TryRelease(Singleton.Instance); - // Resetting this array to null forces the game to re-allocate it if the client hosts the next session typeof(BotsController).GetField("_allTypes", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(botsController_0, null); } - MatchmakerAcceptPatches.Nodes = null; - MatchmakerAcceptPatches.HostExpectedNumberOfPlayers = 1; + NetManagerUtils.DestroyNetManager(isServer); + + FikaBackendUtils.Nodes = null; + FikaBackendUtils.HostExpectedNumberOfPlayers = 1; if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { @@ -1964,6 +2119,14 @@ public override void Dispose() base.Dispose(); } + /// + /// Used to manage the stopping of the gracefully + /// + /// + /// + /// + /// + /// private class ExitManager(CoopGame localGame, ExitStatus exitStatus, string exitName, float delay, CoopPlayer localPlayer) { private readonly CoopGame localGame = localGame; @@ -1975,7 +2138,7 @@ private class ExitManager(CoopGame localGame, ExitStatus exitStatus, string exit public void HandleExit() { - GClass3107 screenManager = GClass3107.Instance; + CurrentScreenSingleton screenManager = CurrentScreenSingleton.Instance; if (screenManager.CheckCurrentScreen(EEftScreenType.Reconnect)) { screenManager.CloseAllScreensForced(); @@ -1983,31 +2146,34 @@ public void HandleExit() localGame.gparam_0.Player.OnGameSessionEnd(exitStatus, localGame.PastTime, localGame.Location_0.Id, exitName); localGame.CleanUp(); localGame.Status = GameStatus.Stopped; - TimeSpan timeSpan = GClass1296.Now - localGame.dateTime_0; - localGame.ginterface145_0.OfflineRaidEnded(exitStatus, exitName, timeSpan.TotalSeconds).HandleExceptions(); + TimeSpan timeSpan = EFTDateTimeClass.Now - localGame.dateTime_0; + localGame.BackEndSession.OfflineRaidEnded(exitStatus, exitName, timeSpan.TotalSeconds).HandleExceptions(); MonoBehaviourSingleton.Instance.FadeOutVolumeAfterRaid(); StaticManager staticManager = StaticManager.Instance; float num = delay; EndAction = new Action(FireCallback); - staticManager.WaitSeconds(num, EndAction); + staticManager.WaitSeconds(num, EndAction); } private void FireCallback() { - Callback endCallback = Traverse.Create(localGame).Field("callback_0").GetValue>(); + Callback endCallback = Traverse.Create(localGame).Field>("callback_0").Value; - localGame.SavePlayer(localPlayer, exitStatus, exitName, false); + Task.Run(() => localGame.SavePlayer(localPlayer, exitStatus, exitName, false)); - endCallback(new Result(exitStatus, GClass1296.Now - localGame.dateTime_0, new MetricsClass())); + endCallback(new Result(exitStatus, EFTDateTimeClass.Now - localGame.dateTime_0, new MetricsClass())); UIEventSystem.Instance.Enable(); } } - private class ErrorExitManager : Class1364 + /// + /// Used to manage the stopping of the gracefully when cancelling + /// + private class CancelExitManager : Class1386 { public void ExitOverride() { - GClass3107 instance = GClass3107.Instance; + CurrentScreenSingleton instance = CurrentScreenSingleton.Instance; if (instance != null && instance.CheckCurrentScreen(EEftScreenType.Reconnect)) { instance.CloseAllScreensForced(); @@ -2023,14 +2189,14 @@ public void ExitOverride() } MonoBehaviour instance2 = StaticManager.Instance; float num = delay; - action_0 = new Action(method_1); - instance2.WaitSeconds(num, action_0); + action_0 = new Action(method_1); + instance2.WaitSeconds(num, action_0); } } public new void method_6(string backendUrl, string locationId, int variantId) { - Logger.LogDebug("CoopGame::method_6"); + Logger.LogDebug("method_6"); return; } } diff --git a/Fika.Core/Coop/Matchmaker/EnvironmentUIRoot_Patch.cs b/Fika.Core/Coop/Matchmaker/EnvironmentUIRoot_Patch.cs deleted file mode 100644 index cedf7b6c..00000000 --- a/Fika.Core/Coop/Matchmaker/EnvironmentUIRoot_Patch.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Aki.Reflection.Patching; -using EFT.UI; -using System.Reflection; - -namespace Fika.Core.Coop.Matchmaker -{ - public class EnvironmentUIRoot_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(EnvironmentUIRoot).GetMethod("Init", BindingFlags.Instance | BindingFlags.Public); - } - - [PatchPostfix] - private static void PatchPostfix(EnvironmentUIRoot __instance) - { - MatchmakerAcceptPatches.EnvironmentUIRoot = __instance.gameObject; - } - } -} diff --git a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Awake_Patch.cs b/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Awake_Patch.cs deleted file mode 100644 index d01f2324..00000000 --- a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Awake_Patch.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using EFT.UI.Matchmaker; -using Newtonsoft.Json; -using System; -using System.Linq; -using System.Reflection; - -namespace Fika.Core.Coop.Matchmaker -{ - public class MatchmakerAcceptScreen_Awake_Patch : ModulePatch - { - [Serializable] - private class ServerStatus - { - [JsonProperty("ip")] - public string ip { get; set; } - - [JsonProperty("status")] - public string status { get; set; } - } - - //static BindingFlags privateFlags = BindingFlags.NonPublic | BindingFlags.Instance; - - public static Type GetThisType() => PatchConstants.EftTypes.Single(x => x == typeof(MatchMakerAcceptScreen)); - - protected override MethodBase GetTargetMethod() => typeof(MatchMakerAcceptScreen).GetMethod("Awake"); - - [PatchPrefix] - private static bool PatchPrefix(MatchMakerAcceptScreen __instance, PlayersRaidReadyPanel ____playersRaidReadyPanel, MatchMakerGroupPreview ____groupPreview) - { - MatchmakerAcceptPatches.MatchMakerAcceptScreenInstance = __instance; - MatchmakerAcceptPatches.PlayersRaidReadyPanel = ____playersRaidReadyPanel; - MatchmakerAcceptPatches.MatchMakerGroupPreview = ____groupPreview; - return true; - } - - } -} - - - - - - - - - diff --git a/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs b/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs index 951f304c..635b9b38 100644 --- a/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs +++ b/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs @@ -46,12 +46,12 @@ public void Init(Player player) Player = player; } - public void OnEnemyDamage(DamageInfo damage, EBodyPart bodyPart, EPlayerSide playerSide, string role, string groupId, float fullHealth, bool isHeavyDamage, float distance, int hour, List targetEquipment, HealthEffects enemyEffects, List zoneIds) + public void OnEnemyDamage(DamageInfo damage, EBodyPart bodyPart, string playerProfileId, EPlayerSide playerSide, WildSpawnType role, string groupId, float fullHealth, bool isHeavyDamage, float distance, int hour, List targetEquipment, HealthEffects enemyEffects, List zoneIds) { // Do nothing } - public void OnEnemyKill(DamageInfo damage, EDamageType lethalDamageType, EBodyPart bodyPart, EPlayerSide playerSide, WildSpawnType role, string playerAccountId, string playerProfileId, string playerName, string groupId, int level, int killExp, float distance, int hour, List targetEquipment, HealthEffects enemyEffects, List zoneIds, bool isFriendly) + public void OnEnemyKill(DamageInfo damage, EDamageType lethalDamageType, EBodyPart bodyPart, EPlayerSide playerSide, WildSpawnType role, string playerAccountId, string playerProfileId, string playerName, string groupId, int level, int killExp, float distance, int hour, List targetEquipment, HealthEffects enemyEffects, List zoneIds, bool isFriendly, bool isAI) { // Do nothing } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs index 11a1fdd0..9de24603 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs @@ -3,7 +3,6 @@ using Comfort.Common; using EFT; using EFT.InventoryLogic; -using EFT.UI; using Fika.Core.Coop.Players; using Fika.Core.Networking; using HarmonyLib; @@ -14,9 +13,6 @@ using UnityEngine; using static EFT.Player; -// HandsControllerClass::method_10(GClass2249) -// GClass2370::method_39(GClass2296) - namespace Fika.Core.Coop.ObservedClasses { public class CoopObservedFirearmController : FirearmController @@ -30,7 +26,7 @@ public class CoopObservedFirearmController : FirearmController private float aimMovementSpeed = 1f; private bool hasFired = false; private WeaponPrefab weaponPrefab; - private GClass1582 underBarrelManager; + private GClass1593 underBarrelManager; public override bool IsAiming { get => base.IsAiming; @@ -67,7 +63,7 @@ protected void Start() weaponPrefab = ControllerGameObject.GetComponent(); if (UnderbarrelWeapon != null) { - underBarrelManager = Traverse.Create(this).Field("gclass1582_0").GetValue(); + underBarrelManager = Traverse.Create(this).Field("gclass1593_0").Value; } } @@ -162,7 +158,7 @@ private IEnumerator BreakFiringLoop() } } - public override void SetScopeMode(GStruct164[] scopeStates) + public override void SetScopeMode(FirearmScopeStateStruct[] scopeStates) { _player.ProceduralWeaponAnimation.ObservedCalibration(); base.SetScopeMode(scopeStates); @@ -252,7 +248,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass } BulletClass ammo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); - InitiateShot(packet.ShotInfoPacket.UnderbarrelShot ? UnderbarrelWeapon : Item, ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, + InitiateShot(Item, ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, packet.ShotInfoPacket.FireportPosition, packet.ShotInfoPacket.ChamberIndex, packet.ShotInfoPacket.Overheat); if (Weapon.SelectedFireMode == Weapon.EFireMode.fullauto) @@ -260,7 +256,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass triggerPressed = true; } - float pitchMult = method_55(); + float pitchMult = method_57(); WeaponSoundPlayer.FireBullet(ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, pitchMult, Malfunction, false, IsBirstOf2Start); @@ -301,7 +297,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (Weapon.Chambers[i].ContainedItem is BulletClass bClass && !bClass.IsUsed) { bClass.IsUsed = true; - if (weaponPrefab != null && weaponPrefab.ObjectInHands is GClass1668 weaponEffectsManager) + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) { if (!bClass.AmmoTemplate.RemoveShellAfterFire) { @@ -322,8 +318,8 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass } else { - Weapon.Chambers[0].RemoveItem(); - if (weaponPrefab != null && weaponPrefab.ObjectInHands is GClass1668 weaponEffectsManager) + Weapon.Chambers[0].RemoveItem(false); + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) { HandleShellEvent(weaponEffectsManager, packet, ammo, magazine); } @@ -331,7 +327,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass } // Remember to check if classes increment - if (Weapon is GClass2696) + if (Weapon is GClass2711) { Weapon.CylinderHammerClosed = Weapon.FireMode.FireMode == Weapon.EFireMode.doubleaction; @@ -364,7 +360,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass { if (Item.HasChambers) { - magazine.Cartridges.PopTo(inventoryController, new GClass2767(Item.Chambers[0])); + magazine.Cartridges.PopTo(inventoryController, new GClass2783(Item.Chambers[0])); } else { @@ -379,7 +375,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (ammo.AmmoTemplate.IsLightAndSoundShot) { - method_56(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection); + method_58(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection); LightAndSoundShot(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, ammo.AmmoTemplate); } } @@ -422,7 +418,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (packet.ChangeSightMode) { - SetScopeMode(packet.ScopeStatesPacket.GStruct164); + SetScopeMode(packet.ScopeStatesPacket.FirearmScopeStateStruct); } if (packet.ToggleLauncher) @@ -458,7 +454,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket: There is no item {packet.ReloadMagPacket.MagId} in profile {coopPlayer.ProfileId}"); throw; } - GClass2769 gridItemAddress = null; + ItemAddressClass gridItemAddress = null; if (packet.ReloadMagPacket.LocationDescription != null) { using MemoryStream memoryStream = new(packet.ReloadMagPacket.LocationDescription); @@ -467,7 +463,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass { if (packet.ReloadMagPacket.LocationDescription.Length != 0) { - GClass1528 descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); + GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); gridItemAddress = inventoryController.ToGridItemAddress(descriptor); } } @@ -524,7 +520,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload) { List bullets = FindAmmoByIds(packet.ReloadWithAmmo.AmmoIds); - GClass2495 ammoPack = new(bullets); + AmmoPackReloadingClass ammoPack = new(bullets); if (!packet.HasCylinderMagPacket) { CurrentOperation.ReloadWithAmmo(ammoPack, null, null); @@ -550,7 +546,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload) { List bullets = FindAmmoByIds(packet.ReloadWithAmmo.AmmoIds); - GClass2495 ammoPack = new(bullets); + AmmoPackReloadingClass ammoPack = new(bullets); ReloadCylinderMagazine(ammoPack, null); } } @@ -559,9 +555,11 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass { CurrentOperation.SetTriggerPressed(true); } + + }*/ - if (packet.HasRollCylinder && Weapon is GClass2696 rollWeapon) + if (packet.HasRollCylinder && Weapon is GClass2711 rollWeapon) { RollCylinder(packet.RollToZeroCamora); } @@ -571,7 +569,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass if (packet.ReloadLauncher.Reload) { List ammo = FindAmmoByIds(packet.ReloadLauncher.AmmoIds); - GClass2495 ammoPack = new(ammo); + AmmoPackReloadingClass ammoPack = new(ammo); ReloadGrenadeLauncher(ammoPack, null); } } @@ -582,9 +580,9 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass { List ammo = FindAmmoByIds(packet.ReloadBarrels.AmmoIds); - GClass2495 ammoPack = new(ammo); + AmmoPackReloadingClass ammoPack = new(ammo); - GClass2769 gridItemAddress = null; + ItemAddressClass gridItemAddress = null; using MemoryStream memoryStream = new(packet.ReloadBarrels.LocationDescription); using BinaryReader binaryReader = new(memoryStream); @@ -592,7 +590,7 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass { if (packet.ReloadBarrels.LocationDescription.Length > 0) { - GClass1528 descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); + GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); gridItemAddress = inventoryController.ToGridItemAddress(descriptor); } } @@ -649,7 +647,7 @@ private IEnumerator ObservedBoltAction(FirearmsAnimator animator, FirearmControl yield return new WaitForSeconds(0.75f); - if (weaponPrefab != null && weaponPrefab.ObjectInHands is GClass1668 weaponEffectsManager) + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) { weaponEffectsManager.StartSpawnShell(coopPlayer.Velocity * 0.33f, 0); } @@ -658,14 +656,14 @@ private IEnumerator ObservedBoltAction(FirearmsAnimator animator, FirearmControl if (controller.Item.GetCurrentMagazine() != null && magazine is not CylinderMagazineClass) { - magazine.Cartridges.PopTo(inventoryController, new GClass2767(controller.Item.Chambers[0])); + magazine.Cartridges.PopTo(inventoryController, new GClass2783(controller.Item.Chambers[0])); } animator.SetBoltActionReload(false); animator.SetFire(false); } - private void HandleShellEvent(GClass1668 weaponEffectsManager, WeaponPacket packet, BulletClass ammo, MagazineClass magazine) + private void HandleShellEvent(WeaponManagerClass weaponEffectsManager, WeaponPacket packet, BulletClass ammo, MagazineClass magazine) { weaponEffectsManager.DestroyPatronInWeapon(packet.ShotInfoPacket.ChamberIndex); if (!ammo.AmmoTemplate.RemoveShellAfterFire) @@ -683,7 +681,7 @@ private void HandleShellEvent(GClass1668 weaponEffectsManager, WeaponPacket pack weaponEffectsManager.SetRoundIntoWeapon(ammo, 0); } - if (Weapon is GClass2696 || Weapon.ReloadMode == Weapon.EReloadMode.OnlyBarrel || Weapon.BoltAction) + if (Weapon is GClass2711 || Weapon.ReloadMode == Weapon.EReloadMode.OnlyBarrel || Weapon.BoltAction) { return; } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs index c71583e5..77d1e117 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs @@ -51,10 +51,10 @@ public static CoopObservedGrenadeController Create(CoopPlayer player, GrenadeCla }; }*/ - private void CreateGrenadeClass1() + /*private void CreateGrenadeClass1() { - } + }*/ public override bool CanChangeCompassState(bool newState) { diff --git a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs index c9bbc2f6..e90f9912 100644 --- a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs +++ b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs @@ -4,7 +4,7 @@ namespace Fika.Core.Coop.ObservedClasses.MovementStates { - internal class ObservedRunState : GClass1705 + internal class ObservedRunState : GClass1717 { public ObservedRunState(MovementContext movementContext) : base(movementContext) { diff --git a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs index 1b52dabf..b914a2b8 100644 --- a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs +++ b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs @@ -5,7 +5,7 @@ namespace Fika.Core.Coop.ObservedClasses.MovementStates { - internal class ObservedSprintState : GClass1705 + internal class ObservedSprintState : GClass1719 { public ObservedSprintState(MovementContext movementContext) : base(movementContext) { @@ -37,7 +37,7 @@ public override void ManualAnimatorMoveUpdate(float deltaTime) /*MovementContext.ProcessSpeedLimits(deltaTime);*/ MovementContext.MovementDirection = Vector2.Lerp(MovementContext.MovementDirection, Direction, deltaTime * EFTHardSettings.Instance.DIRECTION_LERP_SPEED); //MovementContext.ApplyRotation(Quaternion.AngleAxis(MovementContext.Yaw, Vector3.up)); - MovementContext.SetUpDiscreteDirection(GClass1657.ConvertToMovementDirection(Direction)); + MovementContext.SetUpDiscreteDirection(GClass1669.ConvertToMovementDirection(Direction)); Direction = Vector2.zero; MovementContext.SprintAcceleration(deltaTime); UpdateRotationAndPosition(deltaTime); diff --git a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs index a0379a58..27524938 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs @@ -5,7 +5,7 @@ namespace Fika.Core.Coop.ObservedClasses { - public sealed class ObservedHealthController(byte[] serializedState, InventoryControllerClass inventory, SkillManager skills) : GClass2417(serializedState, inventory, skills) + public sealed class ObservedHealthController(byte[] serializedState, InventoryControllerClass inventory, SkillManager skills) : NetworkHealthControllerAbstractClass(serializedState, inventory, skills) { public override bool ApplyItem(Item item, EBodyPart bodyPart, float? amount = null) { diff --git a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs index ef51e3eb..dda7cfd5 100644 --- a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs @@ -3,6 +3,7 @@ using Comfort.Common; using Fika.Core.Coop.Players; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections.Generic; @@ -20,6 +21,7 @@ public class BotPacketSender : MonoBehaviour, IPacketSender public NetDataWriter Writer { get; set; } = new(); public Queue FirearmPackets { get; set; } = new(50); public Queue DamagePackets { get; set; } = new(50); + public Queue ArmorDamagePackets { get; set; } = new(50); public Queue InventoryPackets { get; set; } = new(50); public Queue CommonPlayerPackets { get; set; } = new(50); public Queue HealthSyncPackets { get; set; } = new(50); @@ -29,6 +31,21 @@ protected void Awake() player = GetComponent(); } + public void Init() + { + + } + + public void SendQuestPacket(ref QuestConditionPacket packet) + { + + } + + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + + } + protected void FixedUpdate() { if (!Enabled) @@ -42,12 +59,14 @@ protected void FixedUpdate() return; } - PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, - player.LastDirection, player.CurrentManagedState.Name, player.MovementContext.SmoothedTilt, - player.MovementContext.Step, player.CurrentAnimatorStateIndex, player.MovementContext.SmoothedCharacterMovementSpeed, - player.IsInPronePose, player.PoseLevel, player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, - player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, player.MovementContext.IsGrounded, - player.hasGround, player.CurrentSurface, player.MovementContext.SurfaceNormal); + PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, + player.HeadRotation, player.LastDirection, player.CurrentManagedState.Name, + player.MovementContext.SmoothedTilt, player.MovementContext.Step, player.CurrentAnimatorStateIndex, + player.MovementContext.SmoothedCharacterMovementSpeed, player.IsInPronePose, player.PoseLevel, + player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, + player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, + player.MovementContext.IsGrounded, player.hasGround, player.CurrentSurface, + player.MovementContext.SurfaceNormal); Writer.Reset(); Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); @@ -69,16 +88,28 @@ protected void Update() Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); } } - int healthPackets = DamagePackets.Count; - if (healthPackets > 0) + int damagePackets = DamagePackets.Count; + if (damagePackets > 0) + { + for (int i = 0; i < damagePackets; i++) + { + DamagePacket damagePacket = DamagePackets.Dequeue(); + damagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + } + int armorDamagePackets = ArmorDamagePackets.Count; + if (armorDamagePackets > 0) { - for (int i = 0; i < healthPackets; i++) + for (int i = 0; i < armorDamagePackets; i++) { - DamagePacket healthPacket = DamagePackets.Dequeue(); - healthPacket.NetId = player.NetId; + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; Writer.Reset(); - Server.SendDataToAll(Writer, ref healthPacket, DeliveryMethod.ReliableOrdered); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); } } int inventoryPackets = InventoryPackets.Count; diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index 3475682a..0f65a5d0 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -6,6 +6,7 @@ using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Players; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections; @@ -25,6 +26,7 @@ public class ClientPacketSender : MonoBehaviour, IPacketSender public NetDataWriter Writer { get; set; } = new(); public Queue FirearmPackets { get; set; } = new(50); public Queue DamagePackets { get; set; } = new(50); + public Queue ArmorDamagePackets { get; set; } = new(50); public Queue InventoryPackets { get; set; } = new(50); public Queue CommonPlayerPackets { get; set; } = new(50); public Queue HealthSyncPackets { get; set; } = new(50); @@ -33,11 +35,28 @@ protected void Awake() { player = GetComponent(); Client = Singleton.Instance; + enabled = false; + } + public void Init() + { + enabled = true; StartCoroutine(SyncWorld()); StartCoroutine(SyncWeather()); } + public void SendQuestPacket(ref QuestConditionPacket packet) + { + Writer.Reset(); + Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + Writer.Reset(); + Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + protected void FixedUpdate() { if (player == null || Writer == null) @@ -45,12 +64,14 @@ protected void FixedUpdate() return; } - PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, - player.LastDirection, player.CurrentManagedState.Name, player.MovementContext.SmoothedTilt, - player.MovementContext.Step, player.CurrentAnimatorStateIndex, player.MovementContext.SmoothedCharacterMovementSpeed, - player.IsInPronePose, player.PoseLevel, player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, - player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, player.MovementContext.IsGrounded, - player.hasGround, player.CurrentSurface, player.MovementContext.SurfaceNormal); + PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, + player.HeadRotation, player.LastDirection, player.CurrentManagedState.Name, + player.MovementContext.SmoothedTilt, player.MovementContext.Step, player.CurrentAnimatorStateIndex, + player.MovementContext.SmoothedCharacterMovementSpeed, player.IsInPronePose, player.PoseLevel, + player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, + player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, + player.MovementContext.IsGrounded, player.hasGround, player.CurrentSurface, + player.MovementContext.SurfaceNormal); Writer.Reset(); Client.SendData(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); @@ -75,16 +96,28 @@ protected void Update() Client.SendData(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); } } - int healthPackets = DamagePackets.Count; - if (healthPackets > 0) + int damagePackets = DamagePackets.Count; + if (damagePackets > 0) + { + for (int i = 0; i < damagePackets; i++) + { + DamagePacket damagePacket = DamagePackets.Dequeue(); + damagePacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + } + int armorDamagePackets = ArmorDamagePackets.Count; + if (armorDamagePackets > 0) { - for (int i = 0; i < healthPackets; i++) + for (int i = 0; i < armorDamagePackets; i++) { - DamagePacket healthPacket = DamagePackets.Dequeue(); - healthPacket.NetId = player.NetId; + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; Writer.Reset(); - Client.SendData(Writer, ref healthPacket, DeliveryMethod.ReliableOrdered); + Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); } } int inventoryPackets = InventoryPackets.Count; diff --git a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs index bf832a3f..27ad5e43 100644 --- a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs @@ -1,6 +1,7 @@ // © 2024 Lacyway All Rights Reserved using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using LiteNetLib.Utils; using System.Collections.Generic; @@ -14,10 +15,14 @@ public interface IPacketSender public NetDataWriter Writer { get; set; } public Queue FirearmPackets { get; set; } public Queue DamagePackets { get; set; } + public Queue ArmorDamagePackets { get; set; } public Queue InventoryPackets { get; set; } public Queue CommonPlayerPackets { get; set; } public Queue HealthSyncPackets { get; set; } + public void Init(); + public void SendQuestPacket(ref QuestConditionPacket packet); + public void SendQuestItemPacket(ref QuestItemPacket packet); public void DestroyThis(); } } diff --git a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs index ed74228d..7cdd48b5 100644 --- a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs @@ -1,9 +1,10 @@ // © 2024 Lacyway All Rights Reserved using Comfort.Common; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using LiteNetLib; using LiteNetLib.Utils; using System.Collections.Generic; @@ -21,6 +22,7 @@ public class ObservedPacketSender : MonoBehaviour, IPacketSender public NetDataWriter Writer { get; set; } = new(); public Queue FirearmPackets { get; set; } = new(50); public Queue DamagePackets { get; set; } = new(50); + public Queue ArmorDamagePackets { get; set; } = new(50); public Queue InventoryPackets { get; set; } = new(50); public Queue CommonPlayerPackets { get; set; } = new(50); public Queue HealthSyncPackets { get; set; } = new(50); @@ -28,7 +30,7 @@ public class ObservedPacketSender : MonoBehaviour, IPacketSender protected void Awake() { player = GetComponent(); - isServer = MatchmakerAcceptPatches.IsServer; + isServer = FikaBackendUtils.IsServer; if (isServer) { Server = Singleton.Instance; @@ -39,6 +41,21 @@ protected void Awake() } } + public void Init() + { + + } + + public void SendQuestPacket(ref QuestConditionPacket packet) + { + + } + + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + + } + protected void Update() { if (player == null || Writer == null) @@ -50,26 +67,44 @@ protected void Update() { if (isServer) { - int healthPackets = DamagePackets.Count; - for (int i = 0; i < healthPackets; i++) + int damagePackets = DamagePackets.Count; + for (int i = 0; i < damagePackets; i++) + { + DamagePacket damagePacket = DamagePackets.Dequeue(); + damagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + int armorDamagePackets = ArmorDamagePackets.Count; + for (int i = 0; i < armorDamagePackets; i++) { - DamagePacket healthPacket = DamagePackets.Dequeue(); - healthPacket.NetId = player.NetId; + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; Writer.Reset(); - Server.SendDataToAll(Writer, ref healthPacket, DeliveryMethod.ReliableOrdered); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); } } else { - int healthPackets = DamagePackets.Count; - for (int i = 0; i < healthPackets; i++) + int damagePackets = DamagePackets.Count; + for (int i = 0; i < damagePackets; i++) + { + DamagePacket damagePacket = DamagePackets.Dequeue(); + damagePacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + int armorDamagePackets = ArmorDamagePackets.Count; + for (int i = 0; i < armorDamagePackets; i++) { - DamagePacket healthPacket = DamagePackets.Dequeue(); - healthPacket.NetId = player.NetId; + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; Writer.Reset(); - Client.SendData(Writer, ref healthPacket, DeliveryMethod.ReliableOrdered); + Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); } } } diff --git a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs index 18293c8e..c5bf0534 100644 --- a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs +++ b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs @@ -1,9 +1,8 @@ // © 2024 Lacyway All Rights Reserved using Comfort.Common; -using EFT.UI; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using System.Collections.Generic; using UnityEngine; @@ -20,6 +19,7 @@ public class PacketReceiver : MonoBehaviour public PlayerStatePacket NewState { get; set; } public Queue FirearmPackets { get; private set; } = new(50); public Queue DamagePackets { get; private set; } = new(50); + public Queue ArmorDamagePackets { get; private set; } = new(50); public Queue InventoryPackets { get; private set; } = new(50); public Queue CommonPlayerPackets { get; private set; } = new(50); public Queue HealthSyncPackets { get; private set; } = new(50); @@ -35,7 +35,7 @@ protected void Awake() protected void Start() { - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { Server = Singleton.Instance; } @@ -72,7 +72,7 @@ protected void Update() HealthSyncPacket packet = HealthSyncPackets.Dequeue(); if (packet.Packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Packet.Data.IsAlive.IsAlive) { - observedPlayer.SetAggressor(packet.KillerId, packet.KillerWeaponId); + observedPlayer.SetAggressor(packet.KillerId); observedPlayer.SetInventory(packet.Equipment); observedPlayer.RagdollPacket = packet.RagdollPacket; if (packet.TriggerZones.Length > 0) @@ -100,12 +100,22 @@ protected void Update() player.HandleWeaponPacket(FirearmPackets.Dequeue()); } } - int healthPackets = DamagePackets.Count; - if (healthPackets > 0) + int damagePackets = DamagePackets.Count; + if (damagePackets > 0) { - for (int i = 0; i < healthPackets; i++) + for (int i = 0; i < damagePackets; i++) { - player.HandleDamagePacket(DamagePackets.Dequeue()); + DamagePacket damagePacket = DamagePackets.Dequeue(); + player.HandleDamagePacket(ref damagePacket); + } + } + int armorDamagePackets = ArmorDamagePackets.Count; + if (armorDamagePackets > 0) + { + for (int i = 0; i < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + player.HandleArmorDamagePacket(ref armorDamagePacket); } } int inventoryPackets = InventoryPackets.Count; diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index 042467f3..d02c8a51 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -7,6 +7,7 @@ using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Players; using Fika.Core.Networking; +using Fika.Core.Networking.Packets; using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; @@ -28,6 +29,7 @@ public class ServerPacketSender : MonoBehaviour, IPacketSender public NetDataWriter Writer { get; set; } = new(); public Queue FirearmPackets { get; set; } = new(50); public Queue DamagePackets { get; set; } = new(50); + public Queue ArmorDamagePackets { get; set; } = new(50); public Queue InventoryPackets { get; set; } = new(50); public Queue CommonPlayerPackets { get; set; } = new(50); public Queue HealthSyncPackets { get; set; } = new(50); @@ -38,13 +40,27 @@ protected void Awake() { logger = BepInEx.Logging.Logger.CreateLogSource("ServerPacketSender"); player = GetComponent(); + enabled = false; } - protected void Start() + public void Init() { + enabled = true; StartCoroutine(SendTrainTime()); } + public void SendQuestPacket(ref QuestConditionPacket packet) + { + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + public void SendQuestItemPacket(ref QuestItemPacket packet) + { + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + protected void FixedUpdate() { if (player == null || Writer == null || Server == null) @@ -52,12 +68,14 @@ protected void FixedUpdate() return; } - PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, - player.LastDirection, player.CurrentManagedState.Name, player.MovementContext.SmoothedTilt, - player.MovementContext.Step, player.CurrentAnimatorStateIndex, player.MovementContext.SmoothedCharacterMovementSpeed, - player.IsInPronePose, player.PoseLevel, player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, - player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, player.MovementContext.IsGrounded, - player.hasGround, player.CurrentSurface, player.MovementContext.SurfaceNormal); + PlayerStatePacket playerStatePacket = new(player.NetId, player.Position, player.Rotation, + player.HeadRotation, player.LastDirection, player.CurrentManagedState.Name, + player.MovementContext.SmoothedTilt, player.MovementContext.Step, player.CurrentAnimatorStateIndex, + player.MovementContext.SmoothedCharacterMovementSpeed, player.IsInPronePose, player.PoseLevel, + player.MovementContext.IsSprintEnabled, player.Physical.SerializationStruct, + player.MovementContext.BlindFire, player.observedOverlap, player.leftStanceDisabled, + player.MovementContext.IsGrounded, player.hasGround, player.CurrentSurface, + player.MovementContext.SurfaceNormal); Writer.Reset(); Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); @@ -82,16 +100,28 @@ protected void Update() Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); } } - int healthPackets = DamagePackets.Count; - if (healthPackets > 0) + int damagePackets = DamagePackets.Count; + if (damagePackets > 0) + { + for (int i = 0; i < damagePackets; i++) + { + DamagePacket damagePacket = DamagePackets.Dequeue(); + damagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + } + int armorDamagePackets = ArmorDamagePackets.Count; + if (armorDamagePackets > 0) { - for (int i = 0; i < healthPackets; i++) + for (int i = 0; i < armorDamagePackets; i++) { - DamagePacket healthPacket = DamagePackets.Dequeue(); - healthPacket.NetId = player.NetId; + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; Writer.Reset(); - Server.SendDataToAll(Writer, ref healthPacket, DeliveryMethod.ReliableOrdered); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); } } int inventoryPackets = InventoryPackets.Count; @@ -169,7 +199,7 @@ private IEnumerator SendTrainTime() Locomotive locomotive = FindObjectOfType(); if (locomotive != null) { - long time = Traverse.Create(locomotive).Field("_depart").GetValue().Ticks; + long time = Traverse.Create(locomotive).Field("_depart").Value.Ticks; GenericPacket packet = new() { diff --git a/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs index 8a5f9a6e..354f9127 100644 --- a/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs +++ b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs @@ -1,6 +1,6 @@ -using Aki.Reflection.Patching; -using EFT; +using EFT; using Fika.Core.Coop.GameMode; +using SPT.Reflection.Patching; using System.Reflection; namespace Fika.Core.Coop.Patches diff --git a/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlare_Patch.cs b/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs similarity index 89% rename from Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlare_Patch.cs rename to Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs index 854e0f89..aeba1dcf 100644 --- a/Fika.Core/AkiSupport/Airdrops/Patches/FikaAirdropFlare_Patch.cs +++ b/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs @@ -1,11 +1,12 @@ -using Aki.Reflection.Patching; -using Comfort.Common; +using Comfort.Common; +using Coop.Airdrops; using EFT; using EFT.Airdrop; +using SPT.Reflection.Patching; using System.Linq; using System.Reflection; -namespace Aki.Custom.Airdrops.Patches +namespace Fika.Core.Coop.Patches.Airdrop { public class FikaAirdropFlare_Patch : ModulePatch { diff --git a/Fika.Core/Coop/Patches/BotCacher_Patch.cs b/Fika.Core/Coop/Patches/BotCacher_Patch.cs index 815db9b1..8e636099 100644 --- a/Fika.Core/Coop/Patches/BotCacher_Patch.cs +++ b/Fika.Core/Coop/Patches/BotCacher_Patch.cs @@ -1,6 +1,6 @@ -using Aki.Reflection.Patching; -using EFT; -using Fika.Core.Coop.Matchmaker; +using EFT; +using Fika.Core.Coop.Utils; +using SPT.Reflection.Patching; using System; using System.Reflection; @@ -10,11 +10,11 @@ public class BotCacher_Patch : ModulePatch { protected override MethodBase GetTargetMethod() { - return typeof(GClass532).GetMethod(nameof(GClass532.LoadInternal), BindingFlags.Static | BindingFlags.Public); + return typeof(GClass531).GetMethod(nameof(GClass531.LoadInternal), BindingFlags.Static | BindingFlags.Public); } [PatchPrefix] - private static bool PatchPrefix(out GClass531 core, ref bool __result) + private static bool PatchPrefix(out CoreBotSettingsClass core, ref bool __result) { if (FikaPlugin.Instance.BotDifficulties != null) { @@ -22,17 +22,17 @@ private static bool PatchPrefix(out GClass531 core, ref bool __result) } else { - string text = GClass532.LoadCoreByString(); + string text = GClass531.LoadCoreByString(); if (text == null) { core = null; __result = false; return false; } - core = GClass531.Create(text); + core = CoreBotSettingsClass.Create(text); } - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { foreach (object type in Enum.GetValues(typeof(WildSpawnType))) { @@ -42,19 +42,19 @@ private static bool PatchPrefix(out GClass531 core, ref bool __result) botSettingsComponents = FikaPlugin.Instance.BotDifficulties.GetComponent((BotDifficulty)difficulty, (WildSpawnType)type); if (botSettingsComponents != null) { - if (!GClass532.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) + if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) { - GClass532.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); + GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); } } else { - botSettingsComponents = GClass532.smethod_1(GClass532.CheckOnExclude((BotDifficulty)difficulty, (WildSpawnType)type), (WildSpawnType)type, false); + botSettingsComponents = GClass531.smethod_1(GClass531.CheckOnExclude((BotDifficulty)difficulty, (WildSpawnType)type), (WildSpawnType)type, false); if (botSettingsComponents != null) { - if (!GClass532.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) + if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) { - GClass532.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); + GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); } } else diff --git a/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs index 135572ac..05c583d8 100644 --- a/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs @@ -1,6 +1,6 @@ -using Aki.Reflection.Patching; -using EFT; -using Fika.Core.Coop.Matchmaker; +using EFT; +using Fika.Core.Coop.Utils; +using SPT.Reflection.Patching; using System.Reflection; namespace Fika.Core.Coop.Patches.LocalGame @@ -12,7 +12,7 @@ internal class NonWaveSpawnScenario_Patch : ModulePatch [PatchPrefix] public static bool PatchPrefix(NonWavesSpawnScenario __instance) { - var result = MatchmakerAcceptPatches.IsServer; + bool result = FikaBackendUtils.IsServer; typeof(NonWavesSpawnScenario).GetProperty(nameof(NonWavesSpawnScenario.Enabled)).SetValue(__instance, result); return result; } diff --git a/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs index cd340717..4756e10d 100644 --- a/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs @@ -1,15 +1,15 @@ -using Aki.Reflection.Patching; -using Comfort.Common; +using Comfort.Common; using EFT; using EFT.InputSystem; using EFT.UI; using EFT.UI.Matchmaker; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; using Fika.Core.Modding; using Fika.Core.Modding.Events; using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; +using SPT.Reflection.Patching; using System; using System.Linq; using System.Reflection; @@ -23,7 +23,7 @@ namespace Fika.Core.Coop.Patches.LocalGame /// internal class TarkovApplication_LocalGameCreator_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_43)); + protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_47)); static ISession CurrentSession { get; set; } @@ -32,8 +32,10 @@ public static bool Prefix(TarkovApplication __instance) { Logger.LogDebug("TarkovApplication_LocalGameCreator_Patch:Prefix"); - if (MatchmakerAcceptPatches.IsSinglePlayer) + if (FikaBackendUtils.IsSinglePlayer) + { return true; + } ISession session = __instance.GetClientBackEndSession(); if (session == null) @@ -48,10 +50,10 @@ public static bool Prefix(TarkovApplication __instance) } [PatchPostfix] - public static async Task Postfix(Task __result, TarkovApplication __instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.GClass3163 timeHasComeScreenController, + public static async Task Postfix(Task __result, TarkovApplication __instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController, RaidSettings ____raidSettings, InputTree ____inputTree, GameDateTime ____localGameDateTime, float ____fixedDeltaTime, string ____backendUrl) { - if (MatchmakerAcceptPatches.IsSinglePlayer) + if (FikaBackendUtils.IsSinglePlayer) { return; } @@ -73,15 +75,23 @@ public static async Task Postfix(Task __result, TarkovApplication __instance, Ti throw new ArgumentNullException("timeHasComeScreenController"); } + bool isServer = FikaBackendUtils.IsServer; + LocationSettingsClass.Location location = ____raidSettings.SelectedLocation; - MatchmakerAcceptPatches.GClass3163 = timeHasComeScreenController; + FikaBackendUtils.ScreenController = timeHasComeScreenController; if (Singleton.Instantiated) { Singleton.Instance.Deactivate(); } + NetManagerUtils.CreateNetManager(FikaBackendUtils.IsServer); + if (isServer) + { + NetManagerUtils.StartPinger(); + } + ISession session = CurrentSession; Profile profile = session.GetProfileBySide(____raidSettings.Side); @@ -94,9 +104,9 @@ public static async Task Postfix(Task __result, TarkovApplication __instance, Ti await session.SendRaidSettings(____raidSettings); - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { - timeHasComeScreenController.ChangeStatus("Joining Coop Game"); + timeHasComeScreenController.ChangeStatus("Joining coop game..."); RaidSettingsRequest data = new(); RaidSettingsResponse raidSettingsResponse = await FikaRequestHandler.GetRaidSettings(data); @@ -106,51 +116,49 @@ public static async Task Postfix(Task __result, TarkovApplication __instance, Ti } else { - timeHasComeScreenController.ChangeStatus("Creating Coop Game"); + timeHasComeScreenController.ChangeStatus("Creating coop game..."); } 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(startHandler.HandleStart), - ____fixedDeltaTime, EUpdateQueue.Update, session, TimeSpan.FromSeconds(60 * ____raidSettings.SelectedLocation.EscapeTimeLimit), ____raidSettings - ); + TimeSpan raidLimits = __instance.method_48(____raidSettings.SelectedLocation.EscapeTimeLimit); + + CoopGame coopGame = CoopGame.Create(____inputTree, profile, ____localGameDateTime, session.InsuranceCompany, MonoBehaviourSingleton.Instance, MonoBehaviourSingleton.Instance, + ____raidSettings.SelectedLocation, timeAndWeather, ____raidSettings.WavesSettings, ____raidSettings.SelectedDateTime, new Callback(startHandler.HandleStop), + ____fixedDeltaTime, EUpdateQueue.Update, session, raidLimits, ____raidSettings); - Singleton.Create(localGame); - FikaEventDispatcher.DispatchEvent(new AbstractGameCreatedEvent(localGame)); + Singleton.Create(coopGame); + FikaEventDispatcher.DispatchEvent(new AbstractGameCreatedEvent(coopGame)); - if (MatchmakerAcceptPatches.IsClient) + if (!isServer) { - timeHasComeScreenController.ChangeStatus("Joined Coop Game"); + coopGame.SetMatchmakerStatus("Coop game joined"); } else { - timeHasComeScreenController.ChangeStatus("Created Coop Game"); + coopGame.SetMatchmakerStatus("Coop game created"); } - Task finishTask = localGame.InitPlayer(____raidSettings.BotSettings, ____backendUrl, null, new Callback(startHandler.HandleLoadComplete)); + Task finishTask = coopGame.InitPlayer(____raidSettings.BotSettings, ____backendUrl, new Callback(startHandler.HandleLoadComplete)); __result = Task.WhenAll(finishTask); } - private class StartHandler(TarkovApplication tarkovApplication, Profile pmcProfile, Profile scavProfile, LocationSettingsClass.Location location, MatchmakerTimeHasCome.GClass3163 timeHasComeScreenController) + private class StartHandler(TarkovApplication tarkovApplication, Profile pmcProfile, Profile scavProfile, LocationSettingsClass.Location location, MatchmakerTimeHasCome.TimeHasComeScreenClass 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; + private readonly MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController = timeHasComeScreenController; - public void HandleStart(Result result) + public void HandleStop(Result result) { - tarkovApplication.method_46(pmcProfile.Id, scavProfile, location, result, timeHasComeScreenController); + tarkovApplication.method_50(pmcProfile.Id, scavProfile, location, result, timeHasComeScreenController); } public void HandleLoadComplete(IResult error) { - using (GClass21.StartWithToken("LoadingScreen.LoadComplete")) + using (CounterCreatorAbstractClass.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(tarkovApplication); diff --git a/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs index 32e27772..849d14a9 100644 --- a/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs @@ -1,6 +1,6 @@ -using Aki.Reflection.Patching; -using EFT; -using Fika.Core.Coop.Matchmaker; +using EFT; +using Fika.Core.Coop.Utils; +using SPT.Reflection.Patching; using System.Reflection; namespace Fika.Core.Coop.Patches.LocalGame @@ -12,7 +12,7 @@ internal class WaveSpawnScenario_Patch : ModulePatch [PatchPrefix] public static bool PatchPrefix(WavesSpawnScenario __instance) { - var result = MatchmakerAcceptPatches.IsServer; + bool result = FikaBackendUtils.IsServer; typeof(WavesSpawnScenario).GetProperty(nameof(WavesSpawnScenario.Enabled)).SetValue(__instance, result); return result; } diff --git a/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs b/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs index 386fcdaf..57cac2ed 100644 --- a/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs +++ b/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs @@ -1,9 +1,9 @@ -using Aki.Reflection.Patching; -using Comfort.Common; +using Comfort.Common; using EFT; using EFT.Interactive; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; +using SPT.Reflection.Patching; using System; using System.Collections.Generic; using System.Linq; @@ -29,7 +29,7 @@ public static bool Prefix(IPlayer player, bool first, List ___TargetedP { if (player is ObservedCoopPlayer) { - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { Vector3 position = player.Position; foreach (IPlayer player2 in ___TargetedPlayers.Concat(___NotTargetedPlayers).ToList()) @@ -65,10 +65,10 @@ private static void DoReplicatedMineDamage(IPlayer player, float distance, bool IEnumerable enumerable = isCollateral ? player.PlayerBones.BodyPartColliders.Where(new Func(minefield.method_4)) : player.PlayerBones.BodyPartColliders.Where(new Func(minefield.method_5)); - enumerable = enumerable.DistinctBy(new Func(Minefield.Class2286.class2286_0.method_0)).ToArray(); + enumerable = enumerable.DistinctBy(new Func(Minefield.Class2316.class2316_0.method_0)).ToArray(); enumerable = enumerable.Randomize(); - int num3 = ((isCollateral || first) ? UnityEngine.Random.Range(2, enumerable.Count()) : int.MaxValue); + int num3 = (isCollateral || first) ? UnityEngine.Random.Range(2, enumerable.Count()) : int.MaxValue; float num4 = (isCollateral || first) ? firstExplosionDamage : secondExplosionDamage; int num5 = 0; @@ -76,17 +76,14 @@ private static void DoReplicatedMineDamage(IPlayer player, float distance, bool { coopPlayer.PacketSender.DamagePackets.Enqueue(new() { - DamageInfo = new() - { - DamageType = EDamageType.Landmine, - Damage = num4 * num2, - ArmorDamage = 0.5f, - PenetrationPower = 30f, - Direction = Vector3.zero, - HitNormal = Vector3.zero, - ColliderType = bodyPartCollider.BodyPartColliderType, - BodyPartType = bodyPartCollider.BodyPartType - } + DamageType = EDamageType.Landmine, + Damage = num4 * num2, + ArmorDamage = 0.5f, + PenetrationPower = 30f, + Direction = Vector3.zero, + HitNormal = Vector3.zero, + ColliderType = bodyPartCollider.BodyPartColliderType, + BodyPartType = bodyPartCollider.BodyPartType }); if (++num5 >= num3) { diff --git a/Fika.Core/AkiSupport/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs similarity index 94% rename from Fika.Core/AkiSupport/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs rename to Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs index e8e229b8..03953b6b 100644 --- a/Fika.Core/AkiSupport/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs @@ -1,11 +1,11 @@ -using Aki.Reflection.Patching; -using EFT; +using EFT; using Fika.Core.Coop.Players; using HarmonyLib; +using SPT.Reflection.Patching; using System.Collections.Generic; using System.Reflection; -namespace Fika.Core.AkiSupport.Overrides +namespace Fika.Core.Coop.Patches.Overrides { internal class AddEnemyToAllGroupsInBotZonePatch_Override : ModulePatch { @@ -43,7 +43,7 @@ private static bool PatchPrefix(BotsController __instance, IPlayer aggressor, IP } BotZone botZone = groupOwner.AIData.BotOwner.BotsGroup.BotZone; - foreach (KeyValuePair item in __instance.Groups()) + foreach (KeyValuePair item in __instance.Groups()) { if (item.Key != botZone) { diff --git a/Fika.Core/AkiSupport/Overrides/BotDifficultyPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs similarity index 79% rename from Fika.Core/AkiSupport/Overrides/BotDifficultyPatch_Override.cs rename to Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs index d75a8300..a7a5d0ab 100644 --- a/Fika.Core/AkiSupport/Overrides/BotDifficultyPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs @@ -1,22 +1,22 @@ -using Aki.Common.Http; -using Aki.Reflection.Patching; -using EFT; -using Fika.Core.Coop.Matchmaker; +using EFT; +using Fika.Core.Coop.Utils; +using SPT.Common.Http; +using SPT.Reflection.Patching; using System.Reflection; -namespace Fika.Core.AkiSupport.Overrides +namespace Fika.Core.Coop.Patches.Overrides { public class BotDifficultyPatch_Override : ModulePatch { protected override MethodBase GetTargetMethod() { - return typeof(GClass532).GetMethod(nameof(GClass532.LoadDifficultyStringInternal)); + return typeof(GClass531).GetMethod(nameof(GClass531.LoadDifficultyStringInternal)); } [PatchPrefix] private static bool PatchPrefix(ref string __result, BotDifficulty botDifficulty, WildSpawnType role) { - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { __result = RequestHandler.GetJson($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}"); bool resultIsNullEmpty = string.IsNullOrWhiteSpace(__result); diff --git a/Fika.Core/AkiSupport/Overrides/BotTemplateLimitPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs similarity index 80% rename from Fika.Core/AkiSupport/Overrides/BotTemplateLimitPatch_Override.cs rename to Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs index c6e98227..f19493c3 100644 --- a/Fika.Core/AkiSupport/Overrides/BotTemplateLimitPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs @@ -1,12 +1,12 @@ -using Aki.Common.Http; -using Aki.Reflection.Patching; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; using HarmonyLib; +using SPT.Common.Http; +using SPT.Reflection.Patching; using System; using System.Collections.Generic; using System.Reflection; -namespace Fika.Core.AkiSupport.Overrides +namespace Fika.Core.Coop.Patches.Overrides { internal class BotTemplateLimitPatch_Override : ModulePatch { @@ -30,12 +30,12 @@ So we change Limit of each group. delayed?.Clear(); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { foreach (WaveInfo wave in __result) { - var json = RequestHandler.GetJson($"/singleplayer/settings/bot/limit/{wave.Role}"); - wave.Limit = (string.IsNullOrWhiteSpace(json)) + string json = RequestHandler.GetJson($"/singleplayer/settings/bot/limit/{wave.Role}"); + wave.Limit = string.IsNullOrWhiteSpace(json) ? 30 : Convert.ToInt32(json); } diff --git a/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs new file mode 100644 index 00000000..6dd33996 --- /dev/null +++ b/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs @@ -0,0 +1,61 @@ +using Comfort.Common; +using EFT; +using Fika.Core.Coop.Utils; +using SPT.Common.Http; +using SPT.Reflection.Patching; +using SPT.Reflection.Utils; +using System.Reflection; + +namespace Fika.Core.Coop.Patches.Overrides +{ + /// + /// Override of SPT patch to reduce data requests to server.
+ /// Source + ///
+ /// + internal class MaxBotPatch_Override : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; + const string methodName = "SetSettings"; + System.Type desiredType = PatchConstants.EftTypes.SingleCustom(x => x.GetMethod(methodName, flags) != null && IsTargetMethod(x.GetMethod(methodName, flags))); + MethodInfo desiredMethod = desiredType.GetMethod(methodName, flags); + + Logger.LogDebug($"{GetType().Name} Type: {desiredType?.Name}"); + Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); + + return desiredMethod; + } + + private static bool IsTargetMethod(MethodInfo mi) + { + ParameterInfo[] parameters = mi.GetParameters(); + return parameters.Length == 3 && parameters[0].Name == "maxCount" && parameters[1].Name == "botPresets" && parameters[2].Name == "botScatterings"; + } + + [PatchPrefix] + private static void PatchPreFix(ref int maxCount) + { + if (FikaBackendUtils.IsServer) + { + GameWorld gameWorld = Singleton.Instance; + string location = gameWorld.MainPlayer.Location; + + if (int.TryParse(RequestHandler.GetJson($"/singleplayer/settings/bot/maxCap/{location ?? "default"}"), out int parsedMaxCount)) + { + Logger.LogWarning($"Set max bot cap to: {parsedMaxCount}"); + maxCount = parsedMaxCount; + } + else + { + Logger.LogWarning($"Unable to parse data from singleplayer/settings/bot/maxCap, using existing map max of {maxCount}"); + } + } + else + { + maxCount = 0; + } + } + } +} diff --git a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs similarity index 92% rename from Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatch_Override.cs rename to Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs index 8b76e5e7..cc624031 100644 --- a/Fika.Core/AkiSupport/Overrides/OfflineRaidSettingsMenuPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs @@ -1,13 +1,13 @@ -using Aki.Reflection.Patching; -using EFT.UI; +using EFT.UI; using EFT.UI.Matchmaker; using HarmonyLib; +using SPT.Reflection.Patching; using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; -namespace Fika.Core.AkiSupport.Overrides +namespace Fika.Core.Coop.Patches.Overrides { public class OfflineRaidSettingsMenuPatch_Override : ModulePatch { @@ -42,7 +42,7 @@ private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker } // Remove redundant settings and add our own "Random" to make the setting clear, while also renaming index 0 to "Together" - List labelList = Traverse.Create(____playersSpawnPlaceDropdown).Field("list_0").GetValue>(); + List labelList = Traverse.Create(____playersSpawnPlaceDropdown).Field>("list_0").Value; labelList.Clear(); labelList.Add(new() { diff --git a/Fika.Core/AkiSupport/Scav/ScavProfileLoad_Override.cs b/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs similarity index 74% rename from Fika.Core/AkiSupport/Scav/ScavProfileLoad_Override.cs rename to Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs index ae7caec2..69dc3d48 100644 --- a/Fika.Core/AkiSupport/Scav/ScavProfileLoad_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs @@ -1,12 +1,12 @@ -using Aki.Reflection.Patching; -using EFT; +using EFT; +using SPT.Reflection.Patching; using System.Reflection; -namespace Fika.Core.AkiSupport.Scav +namespace Fika.Core.SPTSupport.Scav { internal class ScavProfileLoad_Override : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_46)); + protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_50)); [PatchPrefix] private static void PatchPrefix(ref string profileId, Profile savageProfile, RaidSettings ____raidSettings) diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index c8648b32..ae914c58 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -33,18 +33,15 @@ public class CoopBot : CoopPlayer /// The amount of players that have loaded this bot /// public int loadedPlayers = 0; - //private FikaDynamicAI dynamicAi; private bool firstEnabled; public static async Task CreateBot(int playerId, Vector3 position, Quaternion rotation, string layerName, string prefix, EPointOfView pointOfView, Profile profile, bool aiControl, EUpdateQueue updateQueue, EUpdateMode armsUpdateMode, EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, - Func getAimingSensitivity, GInterface99 filter) + Func getAimingSensitivity, IViewFilter filter) { - CoopBot player = null; - - player = Create(GClass1388.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, + CoopBot player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, aiControl); player.IsYourPlayer = false; @@ -61,11 +58,14 @@ await player.Init(rotation, layerName, pointOfView, profile, inventoryController player._handsController = EmptyHandsController.smethod_5(player); player._handsController.Spawn(1f, delegate { }); + player.AIData = new AIData(null, player) { IsAI = true }; + player.AggressorFound = false; + player._animators[0].enabled = true; player._armsUpdateQueue = EUpdateQueue.Update; @@ -77,7 +77,7 @@ public override void OnVaulting() // Do nothing } - public override void OnSkillLevelChanged(GClass1766 skill) + public override void OnSkillLevelChanged(GClass1778 skill) { // Do nothing } @@ -93,15 +93,7 @@ public override void CreateMovementContext() MovementContext = BotMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); } - /*public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) - { - if (damageInfo.Player != null && damageInfo.Player.iPlayer is ObservedCoopPlayer) - return; - - base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); - }*/ - - public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct390 shotId) + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) { if (damageInfo.Player != null && damageInfo.Player.iPlayer is ObservedCoopPlayer) { @@ -120,11 +112,11 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy bool flag = !string.IsNullOrEmpty(damageInfo.DeflectedBy); float damage = damageInfo.Damage; List list = ProceedDamageThroughArmor(ref damageInfo, colliderType, armorPlateCollider, true); - MaterialType materialType = (flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material)); - GClass1676 hitInfo = new() + MaterialType materialType = flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material); + ShotInfoClass hitInfo = new() { PoV = PointOfView, - Penetrated = (string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy)), + Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), Material = materialType }; float num = damage - damageInfo.Damage; @@ -135,15 +127,13 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); ShotReactions(damageInfo, bodyPartType); ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); - return hitInfo; - } - protected override void Start() - { - if (FikaPlugin.DisableBotMetabolism.Value) + if (list != null) { - HealthController.DisableMetabolism(); + QueueArmorDamagePackets([.. list]); } + + return hitInfo; } public override void BtrInteraction() @@ -214,18 +204,18 @@ public override void OnDead(EDamageType damageType) // This one is already handled by SPT, so we do not add directly to profile until they move it to client side // They also do a flat value of 0.02 rather than 0.01 for 1 scav kill or 0.03 for >1 LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.02, [CounterTag.FenceStanding, EFenceStandingSource.ScavHelp]); - //LastAggressor.Profile.FenceInfo.AddStanding(0.01, EFenceStandingSource.ScavHelp); + LastAggressor.Profile.FenceInfo.AddStanding(0.01, EFenceStandingSource.ScavHelp); } else if (Side == EPlayerSide.Savage) { - //LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); + LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); LastAggressor.Profile.FenceInfo.AddStanding(0.03, EFenceStandingSource.TraitorKill); } } } } - CoopGame coopGame = (CoopGame)Singleton.Instance; + CoopGame coopGame = (CoopGame)Singleton.Instance; if (coopGame.Bots.ContainsKey(ProfileId)) { coopGame.Bots.Remove(ProfileId); @@ -244,6 +234,11 @@ private IEnumerator DestroyNetworkedComponents() } } + public override void ShowHelloNotification(string sender) + { + // Do nothing + } + public override void UpdateTick() { base.UpdateTick(); @@ -294,7 +289,7 @@ protected void OnDisable() public override void OnDestroy() { #if DEBUG - FikaPlugin.Instance.FikaLogger.LogInfo("Destroying " + ProfileId); + FikaPlugin.Instance.FikaLogger.LogInfo("Destroying " + ProfileId); #endif if (Singleton.Instantiated) { @@ -308,6 +303,11 @@ public override void OnDestroy() BotNetId = NetId }; server.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + + if (!coopGame.Bots.Remove(ProfileId)) + { + FikaPlugin.Instance.FikaLogger.LogWarning("Unable to remove " + ProfileId + " from CoopGame.Bots when Destroying"); + } } } if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index f5b623a1..6febebad 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -2,6 +2,7 @@ using Comfort.Common; using EFT; +using EFT.Ballistics; using EFT.Communications; using EFT.GlobalEvents; using EFT.HealthSystem; @@ -13,9 +14,9 @@ using Fika.Core.Coop.Components; using Fika.Core.Coop.Factories; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.ObservedClasses; using Fika.Core.Coop.PacketHandlers; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using Fika.Core.Networking.Packets.Player; using System; @@ -30,10 +31,11 @@ namespace Fika.Core.Coop.Players { /// - /// CoopPlayer is the LocalPlayer there can only be one CoopPlayer in every game and that is yourself. + /// is the , there can only be one in every game and that is always yourself. /// public class CoopPlayer : LocalPlayer { + #region Fields and Properties public PacketReceiver PacketReceiver; public IPacketSender PacketSender; private DateTime lastPingTime; @@ -47,17 +49,16 @@ public class CoopPlayer : LocalPlayer public int NetId; public bool IsObservedAI = false; public Dictionary> OperationCallbacks = []; + #endregion public static async Task Create(int playerId, Vector3 position, Quaternion rotation, string layerName, string prefix, EPointOfView pointOfView, Profile profile, bool aiControl, EUpdateQueue updateQueue, EUpdateMode armsUpdateMode, EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, - Func getAimingSensitivity, GInterface99 filter, int netId, IStatisticsManager statisticsManager) + Func getAimingSensitivity, IViewFilter filter, int netId, IStatisticsManager statisticsManager) { - CoopPlayer player = null; - - player = Create(GClass1388.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, - bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, false); + CoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, + bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, false); player.IsYourPlayer = true; player.NetId = netId; @@ -66,7 +67,15 @@ public static async Task Create(int playerId, Vector3 position, Qua ISession session = Singleton>.Instance.GetClientBackEndSession(); - GClass3206 questController = new(profile, inventoryController, session, true); + LocalQuestControllerClass questController; + if (FikaPlugin.Instance.SharedQuestProgression) + { + questController = new CoopClientSharedQuestController(profile, inventoryController, session, player); + } + else + { + questController = new LocalQuestControllerClass(profile, inventoryController, session, true); + } questController.Init(); questController.Run(); @@ -74,11 +83,11 @@ public static async Task Create(int playerId, Vector3 position, Qua achievementsController.Init(); achievementsController.Run(); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { player.PacketSender = player.gameObject.AddComponent(); } - else if (MatchmakerAcceptPatches.IsClient) + else if (FikaBackendUtils.IsClient) { player.PacketSender = player.gameObject.AddComponent(); } @@ -92,21 +101,31 @@ await player.Init(rotation, layerName, pointOfView, profile, inventoryController foreach (MagazineClass magazineClass in player.Inventory.GetPlayerItems(EPlayerItems.NonQuestItems).OfType()) { - player.GClass2761_0.StrictCheckMagazine(magazineClass, true, player.Profile.MagDrillsMastering, false, false); + player.InventoryControllerClass.StrictCheckMagazine(magazineClass, true, player.Profile.MagDrillsMastering, false, false); } player._handsController = EmptyHandsController.smethod_5(player); - player._handsController.Spawn(1f, new Action(Class1500.class1500_0.method_0)); + player._handsController.Spawn(1f, new Action(Class1526.class1526_0.method_0)); + player.AIData = new AIData(null, player); + player.AggressorFound = false; + player._animators[0].enabled = true; - player.Profile.Info.MainProfileNickname = MatchmakerAcceptPatches.PMCName; + player.Profile.Info.MainProfileNickname = FikaBackendUtils.PMCName; + + player.SetupMainPlayer(); return player; } - public override void OnSkillLevelChanged(GClass1766 skill) + public override BasePhysicalClass CreatePhysical() + { + return FikaPlugin.Instance.UseInertia ? new PlayerPhysicalClass() : new NoInertiaPhysical(); + } + + public override void OnSkillLevelChanged(GClass1778 skill) { NotificationManagerClass.DisplayMessageNotification(string.Format("SkillLevelUpMessage".Localized(null), skill.Id.ToString().Localized(null), @@ -129,7 +148,7 @@ public override void OnWeaponMastered(MasterSkillClass masterSkill) public override void BtrInteraction(BTRSide btr, byte placeId, EInteractionType interaction) { base.BtrInteraction(btr, placeId, interaction); - if (MatchmakerAcceptPatches.IsClient) + if (FikaBackendUtils.IsClient) { BTRInteractionPacket packet = new(NetId) { @@ -140,7 +159,7 @@ public override void BtrInteraction(BTRSide btr, byte placeId, EInteractionType PacketSender.Writer.Reset(); PacketSender.Client.SendData(PacketSender.Writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); } - else if (MatchmakerAcceptPatches.IsServer) + else if (FikaBackendUtils.IsServer) { if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { @@ -214,22 +233,56 @@ public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartTy base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); } - public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct390 shotId) + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) { if (damageInfo.DamageType is EDamageType.Sniper or EDamageType.Landmine) { - return base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); + return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); } - if (damageInfo.Player != null && damageInfo.Player.iPlayer is CoopBot) + if (damageInfo.Player?.iPlayer is CoopBot) { - return base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); + return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); } return null; } - public override void Proceed(bool withNetwork, Callback callback, bool scheduled = true) + private ShotInfoClass SimulatedApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) + { + ActiveHealthController activeHealthController = ActiveHealthController; + if (activeHealthController != null && !activeHealthController.IsAlive) + { + return null; + } + bool flag = !string.IsNullOrEmpty(damageInfo.DeflectedBy); + float damage = damageInfo.Damage; + List list = ProceedDamageThroughArmor(ref damageInfo, colliderType, armorPlateCollider, true); + MaterialType materialType = flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material); + ShotInfoClass gclass = new() + { + PoV = PointOfView, + Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), + Material = materialType + }; + ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); + ShotReactions(damageInfo, bodyPartType); + float num = damage - damageInfo.Damage; + if (num > 0) + { + damageInfo.DidArmorDamage = num; + } + ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, gclass.Material); + + if (list != null) + { + QueueArmorDamagePackets([.. list]); + } + + return gclass; + } + + public override void Proceed(bool withNetwork, Callback callback, bool scheduled = true) { base.Proceed(withNetwork, callback, scheduled); PacketSender.CommonPlayerPackets.Enqueue(new() @@ -243,7 +296,7 @@ public override void Proceed(bool withNetwork, Callback callback, }); } - public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) + public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) { FoodControllerHandler handler = new(this, foodDrink, amount, EBodyPart.Head, animationVariant); @@ -253,7 +306,7 @@ public override void Proceed(FoodClass foodDrink, float amount, Callback callback, bool scheduled = true) + public override void Proceed(Item item, Callback callback, bool scheduled = true) { QuickUseItemControllerHandler handler = new(this, item); @@ -273,7 +326,7 @@ public override void Proceed(KnifeComponent knife, Callback ca handler.process.method_0(new(handler.HandleResult), callback, scheduled); } - public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) + public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) { QuickKnifeControllerHandler handler = new(this, knife); @@ -283,7 +336,7 @@ public override void Proceed(KnifeComponent knife, Callback callb handler.process.method_0(new(handler.HandleResult), callback, scheduled); } - public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) + public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) { MedsControllerHandler handler = new(this, meds, bodyPart, animationVariant); @@ -293,7 +346,7 @@ public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, bool scheduled = true) + public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) { QuickGrenadeControllerHandler handler = new(this, throwWeap); @@ -328,7 +381,7 @@ public override void Proceed(Weapon weapon, Callback ca handler.process.method_0(new(handler.HandleResult), callback, scheduled); } - public override void Proceed(Item item, Callback callback, bool scheduled = true) + public override void Proceed(Item item, Callback callback, bool scheduled = true) { // what is this base.Proceed(item, callback, scheduled); @@ -382,12 +435,12 @@ public void ClientApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, public void ClientApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider) { - _ = base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, GStruct390.EMPTY_SHOT_ID); + _ = base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, GStruct389.EMPTY_SHOT_ID); } public override void SendHeadlightsPacket(bool isSilent) { - GStruct163[] lightStates = _helmetLightControllers.Select(new Func(ClientPlayer.Class1430.class1430_0.method_0)).ToArray(); + FirearmLightStateStruct[] lightStates = _helmetLightControllers.Select(new Func(ClientPlayer.Class1456.class1456_0.method_0)).ToArray(); if (PacketSender != null) { @@ -422,7 +475,7 @@ public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBan } } - public override void OperateStationaryWeapon(StationaryWeapon stationaryWeapon, GStruct169.EStationaryCommand command) + public override void OperateStationaryWeapon(StationaryWeapon stationaryWeapon, GStruct170.EStationaryCommand command) { base.OperateStationaryWeapon(stationaryWeapon, command); PacketSender.CommonPlayerPackets.Enqueue(new() @@ -463,7 +516,7 @@ public override void vmethod_0(WorldInteractiveObject interactiveObject, Interac InteractiveId = interactiveObject.Id, InteractionType = interactionResult.InteractionType, InteractionStage = EInteractionStage.Start, - ItemId = (interactionResult is GClass2964 keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty + ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty } }; PacketSender.CommonPlayerPackets.Enqueue(packet); @@ -488,7 +541,7 @@ public override void vmethod_1(WorldInteractiveObject door, InteractionResult in InteractiveId = door.Id, InteractionType = interactionResult.InteractionType, InteractionStage = EInteractionStage.Execute, - ItemId = (interactionResult is GClass2964 keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty + ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty } }; PacketSender.CommonPlayerPackets.Enqueue(packet); @@ -525,7 +578,6 @@ public HealthSyncPacket SetupDeathPacket(GStruct346 packet) { Packet = packet, KillerId = !string.IsNullOrEmpty(KillerId) ? KillerId : null, - KillerWeaponId = LastDamageInfo.Weapon?.Id, RagdollPacket = new() { BodyPartColliderType = LastDamageInfo.BodyPartColliderType, @@ -797,11 +849,11 @@ public void Ping() }; PacketSender.Writer.Reset(); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { PacketSender.Server.SendDataToAll(PacketSender.Writer, ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); } - else if (MatchmakerAcceptPatches.IsClient) + else if (FikaBackendUtils.IsClient) { PacketSender.Client.SendData(PacketSender.Writer, ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); } @@ -842,36 +894,55 @@ public override void TryInteractionCallback(LootableContainer container) _openAction = null; } - protected virtual void Start() + public void SetupMainPlayer() { + // Set own group id Profile.Info.GroupId = "Fika"; + // Setup own dog tag if (Side != EPlayerSide.Savage) { + FikaPlugin.Instance.FikaLogger.LogInfo("Setting up DogTag"); if (Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) { - GStruct414 result = InteractionsHandlerClass.Remove(Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, _inventoryController, false, true); + GStruct414 result = InteractionsHandlerClass.Remove(Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, _inventoryController, false, true); if (result.Error != null) { - FikaPlugin.Instance.FikaLogger.LogWarning("CoopPlayer::Start: Error removing dog tag!"); + FikaPlugin.Instance.FikaLogger.LogWarning("CoopPlayer::SetupMainPlayer: Error removing dog tag!"); } } - string templateId = Side == EPlayerSide.Usec ? "59f32c3b86f77472a31742f0" : "59f32bb586f774757e1e8442"; + string templateId = GetDogTagTemplateId(); - Item item = Singleton.Instance.CreateItem(MongoID.Generate(), templateId, null); + if (!string.IsNullOrEmpty(templateId)) + { + Item item = Singleton.Instance.CreateItem(MongoID.Generate(), templateId, null); - Equipment.GetSlot(EquipmentSlot.Dogtag).Add(item, false); + Slot dogtagSlot = Equipment.GetSlot(EquipmentSlot.Dogtag); + ItemFilter[] filters = dogtagSlot.Filters; // We need to temporarily remove and then re-add these as BSG did not include the new dog tags in their ItemFilter[] + dogtagSlot.Filters = null; + GStruct416 addResult = dogtagSlot.Add(item, false); + dogtagSlot.Filters = filters; - DogtagComponent dogtagComponent = item.GetItemComponent(); - if (dogtagComponent != null) - { - dogtagComponent.ProfileId = ProfileId; - dogtagComponent.GroupId = Profile.Info.GroupId; + if (addResult.Error != null) + { + FikaPlugin.Instance.FikaLogger.LogError("CoopPlayer::SetupMainPlayer: Error adding dog tag to slot: " + addResult.Error); + } + + DogtagComponent dogtagComponent = item.GetItemComponent(); + if (dogtagComponent != null) + { + dogtagComponent.ProfileId = ProfileId; + dogtagComponent.GroupId = Profile.Info.GroupId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning("Unable to find DogTagComponent"); + } } else { - FikaPlugin.Instance.FikaLogger.LogWarning("Unable to find DogTagComponent"); + FikaPlugin.Instance.FikaLogger.LogError("Could not get templateId for DogTag!"); } } @@ -879,14 +950,48 @@ protected virtual void Start() CoopGame coopGame = (CoopGame)Singleton.Instance; if (coopGame.Location_0.Name.ToLower() == "laboratory") { - Item accessCardItem = Profile.Inventory.AllRealPlayerItems.FirstOrDefault(x => x.TemplateId == "5c94bbff86f7747ee735c08f"); - if (accessCardItem != null) + foreach (Item item in Profile.Inventory.AllRealPlayerItems) { - InteractionsHandlerClass.Remove(accessCardItem, _inventoryController, false, true); + if (item.TemplateId == "5c94bbff86f7747ee735c08f") + { + InteractionsHandlerClass.Remove(item, _inventoryController, false, true); + } } } } + private string GetDogTagTemplateId() + { + //return Profile.Side == EPlayerSide.Usec ? "6662ea05f6259762c56f3189" : "59f32bb586f774757e1e8442"; + + if (Side is EPlayerSide.Usec) + { + switch (Profile.Info.MemberCategory) + { + case EMemberCategory.Default: + return "59f32c3b86f77472a31742f0"; + case EMemberCategory.UniqueId: + return "6662e9f37fa79a6d83730fa0"; + case EMemberCategory.Unheard: + return "6662ea05f6259762c56f3189"; + } + } + else if (Side is EPlayerSide.Bear) + { + switch (Profile.Info.MemberCategory) + { + case EMemberCategory.Default: + return "59f32bb586f774757e1e8442"; + case EMemberCategory.UniqueId: + return "6662e9aca7e0b43baa3d5f74"; + case EMemberCategory.Unheard: + return "6662e9cda7e0b43baa3d5f76"; + } + } + + return string.Empty; + } + public virtual void HandleCommonPacket(in CommonPlayerPacket packet) { if (packet.Phrase != EPhraseTrigger.PhraseNone) @@ -931,9 +1036,13 @@ public virtual void HandleCommonPacket(in CommonPlayerPacket packet) void Interact() => lootableContainer.Invoke(methodName, 0); if (packet.ContainerInteractionPacket.InteractionType == EInteractionType.Unlock) + { Interact(); + } else + { lootableContainer.StartBehaviourTimer(EFTHardSettings.Instance.DelayToOpenContainer, Interact); + } } } } @@ -982,7 +1091,7 @@ public virtual void HandleCommonPacket(in CommonPlayerPacket packet) if (packet.HasStationaryPacket) { StationaryWeapon stationaryWeapon = (packet.StationaryPacket.Command == StationaryPacket.EStationaryCommand.Occupy) ? Singleton.Instance.FindStationaryWeapon(packet.StationaryPacket.Id) : null; - base.OperateStationaryWeapon(stationaryWeapon, (GStruct169.EStationaryCommand)packet.StationaryPacket.Command); + base.OperateStationaryWeapon(stationaryWeapon, (GStruct170.EStationaryCommand)packet.StationaryPacket.Command); } if (packet.Pickup) @@ -1037,7 +1146,7 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) using BinaryReader binaryReader = new(memoryStream); try { - GStruct411 result = ToInventoryOperation(binaryReader.ReadPolymorph()); + GStruct411 result = ToInventoryOperation(binaryReader.ReadPolymorph()); InventoryOperationHandler opHandler = new(result); @@ -1045,9 +1154,9 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) // 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 (result.Value is UnloadOperationClass unloadOperation) { - if (unloadOperation.InternalOperation is GClass2872 internalSplitOperation) + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) { Item item = internalSplitOperation.To.Item; if (item != null) @@ -1069,7 +1178,7 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) } // TODO: Same as above. - if (result.Value is GClass2872 splitOperation) + if (result.Value is SplitOperationClass splitOperation) { Item item = splitOperation.To.Item; if (item != null) @@ -1088,23 +1197,11 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) 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($"ItemControllerExecutePacket::Exception thrown: {exception}"); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); @@ -1114,7 +1211,7 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) else { FikaPlugin.Instance.FikaLogger.LogError("ItemControllerExecutePacket: inventory was null!"); - if (MatchmakerAcceptPatches.IsServer) + if (FikaBackendUtils.IsServer) { OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); @@ -1262,26 +1359,48 @@ public virtual void HandleWeaponPacket(in WeaponPacket packet) } } - public virtual void HandleDamagePacket(DamagePacket packet) + public void QueueArmorDamagePackets(ArmorComponent[] armorComponents) + { + int amount = armorComponents.Length; + if (amount > 0) + { + string[] ids = new string[amount]; + float[] durabilities = new float[amount]; + + for (int i = 0; i < amount; i++) + { + ids[i] = armorComponents[i].Item.Id; + durabilities[i] = armorComponents[i].Repairable.Durability; + } + + PacketSender.ArmorDamagePackets.Enqueue(new() + { + ItemIds = ids, + Durabilities = durabilities, + }); + } + } + + public virtual void HandleDamagePacket(ref DamagePacket packet) { DamageInfo damageInfo = new() { - Damage = packet.DamageInfo.Damage, - DamageType = packet.DamageInfo.DamageType, - BodyPartColliderType = packet.DamageInfo.ColliderType, - HitPoint = packet.DamageInfo.Point, - HitNormal = packet.DamageInfo.HitNormal, - Direction = packet.DamageInfo.Direction, - PenetrationPower = packet.DamageInfo.PenetrationPower, - BlockedBy = packet.DamageInfo.BlockedBy, - DeflectedBy = packet.DamageInfo.DeflectedBy, - SourceId = packet.DamageInfo.SourceId, - ArmorDamage = packet.DamageInfo.ArmorDamage + Damage = packet.Damage, + DamageType = packet.DamageType, + BodyPartColliderType = packet.ColliderType, + HitPoint = packet.Point, + HitNormal = packet.HitNormal, + Direction = packet.Direction, + PenetrationPower = packet.PenetrationPower, + BlockedBy = packet.BlockedBy, + DeflectedBy = packet.DeflectedBy, + SourceId = packet.SourceId, + ArmorDamage = packet.ArmorDamage }; - if (!string.IsNullOrEmpty(packet.DamageInfo.ProfileId)) + if (!string.IsNullOrEmpty(packet.ProfileId)) { - GInterface94 player = Singleton.Instance.GetAlivePlayerBridgeByProfileID(packet.DamageInfo.ProfileId); + IPlayerOwner player = Singleton.Instance.GetAlivePlayerBridgeByProfileID(packet.ProfileId); if (player != null) { @@ -1295,13 +1414,49 @@ public virtual void HandleDamagePacket(DamagePacket packet) } } - if (Singleton.Instance.GetAlivePlayerByProfileID(packet.DamageInfo.ProfileId).HandsController.Item is Weapon weapon) + // TODO: Fix this and consistently get the correct data... + if (Singleton.Instance.GetAlivePlayerByProfileID(packet.ProfileId).HandsController.Item is Weapon weapon) { damageInfo.Weapon = weapon; } } - ClientApplyShot(damageInfo, packet.DamageInfo.BodyPartType, packet.DamageInfo.ColliderType, packet.DamageInfo.ArmorPlateCollider); + ShotReactions(damageInfo, packet.BodyPartType); + ReceiveDamage(damageInfo.Damage, packet.BodyPartType, damageInfo.DamageType, packet.Absorbed, packet.Material); + ClientApplyDamageInfo(damageInfo, packet.BodyPartType, packet.ColliderType, packet.Absorbed); + //ClientApplyShot(damageInfo, packet.BodyPartType, packet.ColliderType, packet.ArmorPlateCollider); + } + + public void HandleArmorDamagePacket(ref ArmorDamagePacket packet) + { + for (int i = 0; i < packet.ItemIds.Length; i++) + { + _preAllocatedArmorComponents.Clear(); + Inventory.GetPutOnArmorsNonAlloc(_preAllocatedArmorComponents); + foreach (ArmorComponent armorComponent in _preAllocatedArmorComponents) + { + if (armorComponent.Item.Id == packet.ItemIds[i]) + { + armorComponent.Repairable.Durability = packet.Durabilities[i]; + armorComponent.Buff.TryDisableComponent(armorComponent.Repairable.Durability); + armorComponent.Item.RaiseRefreshEvent(false, false); + return; + } + } + GStruct416 gstruct = Singleton.Instance.FindItemById(packet.ItemIds[i]); + if (gstruct.Failed) + { + FikaPlugin.Instance.FikaLogger.LogError("HandleArmorDamagePacket: " + gstruct.Error); + return; + } + ArmorComponent itemComponent = gstruct.Value.GetItemComponent(); + if (itemComponent != null) + { + itemComponent.Repairable.Durability = packet.Durabilities[i]; + itemComponent.Buff.TryDisableComponent(itemComponent.Repairable.Durability); + itemComponent.Item.RaiseRefreshEvent(false, false); + } + } } public virtual void SetupDogTag() @@ -1391,8 +1546,25 @@ public override void OnVaulting() }); } - public Item FindItem(string itemId) + public Item FindItem(string itemId, bool questItem = false) { + if (questItem) + { + //List itemPositions = Traverse.Create(Singleton.Instance).Field>("list_1").Value; + foreach (IKillableLootItem lootItem in Singleton.Instance.LootList) + { + if (lootItem is LootItem observedLootItem) + { + if (observedLootItem.Item.TemplateId == itemId && observedLootItem.isActiveAndEnabled) + { + return observedLootItem.Item; + } + } + } + FikaPlugin.Instance.FikaLogger.LogInfo($"CoopPlayer::FindItem: Could not find questItem with id '{itemId}' in the current session, either the quest is not active or something else occured."); + return null; + } + Item item = Inventory.Equipment.FindItem(itemId); if (item != null) { @@ -1411,7 +1583,7 @@ public Item FindItem(string itemId) private class KeyHandler(CoopPlayer player) { private readonly CoopPlayer player = player; - public GStruct416 unlockResult; + public GStruct416 unlockResult; internal void HandleKeyEvent() { @@ -1502,7 +1674,7 @@ private class QuickUseItemControllerHandler(CoopPlayer coopPlayer, Item item) { private readonly CoopPlayer coopPlayer = coopPlayer; private readonly Item item = item; - public Process process; + public Process process; public Action confirmCallback; internal QuickUseItemController ReturnController() @@ -1539,7 +1711,7 @@ private class MedsControllerHandler(CoopPlayer coopPlayer, MedsClass meds, EBody private readonly MedsClass meds = meds; private readonly EBodyPart bodyPart = bodyPart; private readonly int animationVariant = animationVariant; - public Process process; + public Process process; public Action confirmCallback; internal MedsController ReturnController() @@ -1579,7 +1751,7 @@ private class FoodControllerHandler(CoopPlayer coopPlayer, FoodClass foodDrink, private readonly float amount = amount; private readonly EBodyPart bodyPart = bodyPart; private readonly int animationVariant = animationVariant; - public Process process; + public Process process; public Action confirmCallback; internal MedsController ReturnController() @@ -1652,7 +1824,7 @@ private class QuickKnifeControllerHandler(CoopPlayer coopPlayer, KnifeComponent { private readonly CoopPlayer coopPlayer = coopPlayer; public readonly KnifeComponent knife = knife; - public Process process; + public Process process; public Action confirmCallback; internal QuickKnifeKickController ReturnController() @@ -1722,7 +1894,7 @@ private class QuickGrenadeControllerHandler(CoopPlayer coopPlayer, GrenadeClass { private readonly CoopPlayer coopPlayer = coopPlayer; private readonly GrenadeClass throwWeap = throwWeap; - public Process process; + public Process process; public Action confirmCallback; internal CoopClientQuickGrenadeController ReturnController() diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index 0df67055..b5e87133 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -11,9 +11,9 @@ using Fika.Core.Coop.Custom; using Fika.Core.Coop.Factories; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.ObservedClasses; using Fika.Core.Coop.PacketHandlers; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using HarmonyLib; using System; @@ -38,11 +38,11 @@ public class ObservedCoopPlayer : CoopPlayer private float observedFixedTime = 0f; private FikaHealthBar healthBar = null; private Coroutine waitForStartRoutine; - public GClass2417 NetworkHealthController + public NetworkHealthControllerAbstractClass NetworkHealthController { - get => HealthController as GClass2417; + get => HealthController as NetworkHealthControllerAbstractClass; } - private readonly GClass2156 ObservedVaultingParameters = new(); + private readonly ObservedVaultingParametersClass ObservedVaultingParameters = new(); public override bool CanBeSnapped => false; public override EPointOfView PointOfView { get => EPointOfView.ThirdPerson; } public override AbstractHandsController HandsController @@ -120,11 +120,9 @@ public static async Task CreateObservedPlayer(int playerId, string layerName, string prefix, EPointOfView pointOfView, Profile profile, bool aiControl, EUpdateQueue updateQueue, EUpdateMode armsUpdateMode, EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, - Func getAimingSensitivity, GInterface99 filter) + Func getAimingSensitivity, IViewFilter filter) { - ObservedCoopPlayer player = null; - - player = Create(GClass1388.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, + ObservedCoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, aiControl); @@ -144,17 +142,20 @@ await player.Init(rotation, layerName, pointOfView, profile, inventoryController player._handsController = EmptyHandsController.smethod_5(player); player._handsController.Spawn(1f, delegate { }); + player.AIData = new AIData(null, player); + player.AggressorFound = false; + player._animators[0].enabled = true; player._armsUpdateQueue = EUpdateQueue.Update; return player; } - public override GClass681 CreatePhysical() + public override BasePhysicalClass CreatePhysical() { - return new GClass681(); + return new BasePhysicalClass(); } public override bool CheckSurface() @@ -165,12 +166,12 @@ public override bool CheckSurface() public override void PlayGroundedSound(float fallHeight, float jumpHeight) { - (bool hit, BaseBallistic.ESurfaceSound surfaceSound) values = method_53(); - method_54(values.hit, values.surfaceSound); + (bool hit, BaseBallistic.ESurfaceSound surfaceSound) values = method_57(); + method_58(values.hit, values.surfaceSound); base.PlayGroundedSound(fallHeight, jumpHeight); } - public override void OnSkillLevelChanged(GClass1766 skill) + public override void OnSkillLevelChanged(GClass1778 skill) { //base.OnSkillLevelChanged(skill); } @@ -240,12 +241,10 @@ public override void ManageAggressor(DamageInfo damageInfo, EBodyPart bodyPart, } bool flag = damageInfo.DidBodyDamage / HealthController.GetBodyPartHealth(bodyPart, false).Maximum >= 0.6f && HealthController.FindExistingEffect(bodyPart) != null; - player.StatisticsManager.OnEnemyDamage(damageInfo, bodyPart, - Profile.Info.Side, Profile.Info.Settings.Role.ToString(), - Profile.Info.GroupId, HealthController.GetBodyPartHealth(EBodyPart.Common, false).Maximum, - flag, Vector3.Distance(player.Transform.position, Transform.position), - CurrentHour, Inventory.EquippedInSlotsTemplateIds, - HealthController.BodyPartEffects, TriggerZones); + player.StatisticsManager.OnEnemyDamage(damageInfo, bodyPart, ProfileId, Side, Profile.Info.Settings.Role, + GroupId, HealthController.GetBodyPartHealth(EBodyPart.Common, false).Maximum, flag, + Vector3.Distance(player.Transform.position, Transform.position), CurrentHour, + Inventory.EquippedInSlotsTemplateIds, HealthController.BodyPartEffects, TriggerZones); } public override void UpdateArmsCondition() @@ -260,26 +259,23 @@ public override bool ShouldVocalizeDeath(EBodyPart bodyPart) public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) { - if (damageInfo.DamageType == EDamageType.Landmine && MatchmakerAcceptPatches.IsServer) + if (damageInfo.DamageType == EDamageType.Landmine && FikaBackendUtils.IsServer) { PacketSender.DamagePackets.Enqueue(new() { - DamageInfo = new() - { - Damage = damageInfo.Damage, - DamageType = damageInfo.DamageType, - BodyPartType = bodyPartType, - ColliderType = colliderType, - Absorbed = 0f, - Direction = damageInfo.Direction, - Point = damageInfo.HitPoint, - HitNormal = damageInfo.HitNormal, - PenetrationPower = damageInfo.PenetrationPower, - BlockedBy = damageInfo.BlockedBy, - DeflectedBy = damageInfo.DeflectedBy, - SourceId = damageInfo.SourceId, - ArmorDamage = damageInfo.ArmorDamage - } + Damage = damageInfo.Damage, + DamageType = damageInfo.DamageType, + BodyPartType = bodyPartType, + ColliderType = colliderType, + Absorbed = 0f, + Direction = damageInfo.Direction, + Point = damageInfo.HitPoint, + HitNormal = damageInfo.HitNormal, + PenetrationPower = damageInfo.PenetrationPower, + BlockedBy = damageInfo.BlockedBy, + DeflectedBy = damageInfo.DeflectedBy, + SourceId = damageInfo.SourceId, + ArmorDamage = damageInfo.ArmorDamage }); return; @@ -302,57 +298,28 @@ public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartTy LastDamageType = damageInfo.DamageType; } - /*public override void ShotReactions(DamageInfo shot, EBodyPart bodyPart) + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) { - Vector3 normalized = shot.Direction.normalized; - if (PointOfView == EPointOfView.ThirdPerson) - { - turnOffFbbikAt = Time.time + 0.6f; - _fbbik.solver.Quick = false; - BodyPartCollider bodyPartCollider; - if ((bodyPartCollider = shot.HittedBallisticCollider as BodyPartCollider) != null) - { - HitReaction.Hit(bodyPartCollider.BodyPartColliderType, bodyPartCollider.BodyPartType, normalized, shot.HitPoint, false); - } - } - if (shot.Weapon is KnifeClass knifeClass) - { - KnifeComponent itemComponent = knifeClass.GetItemComponent(); - Vector3 normalized2 = (shot.Player.iPlayer.Transform.position - Transform.position).normalized; - Vector3 vector = Vector3.Cross(normalized2, Vector3.up); - float y = normalized.y; - float num = Vector3.Dot(vector, normalized); - float num2 = 1f - Mathf.Abs(Vector3.Dot(normalized2, normalized)); - num2 = ((bodyPart == EBodyPart.Head) ? num2 : Mathf.Sqrt(num2)); - Rotation += new Vector2(-num, -y).normalized * itemComponent.Template.AppliedTrunkRotation.Random(false) * num2; - ProceduralWeaponAnimation.ForceReact.AddForce(new Vector3(-y, num, 0f).normalized, num2, 1f, itemComponent.Template.AppliedHeadRotation.Random(false)); - } - }*/ + ShotReactions(damageInfo, bodyPartType); - public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct390 shotId) - { - if (damageInfo.DamageType == EDamageType.Sniper && MatchmakerAcceptPatches.IsServer) + if (damageInfo.DamageType == EDamageType.Sniper && FikaBackendUtils.IsServer) { - ShotReactions(damageInfo, bodyPartType); PacketSender.DamagePackets.Enqueue(new() { - DamageInfo = new() - { - Damage = damageInfo.Damage, - DamageType = damageInfo.DamageType, - BodyPartType = bodyPartType, - ColliderType = colliderType, - ArmorPlateCollider = armorPlateCollider, - Absorbed = 0f, - Direction = damageInfo.Direction, - Point = damageInfo.HitPoint, - HitNormal = damageInfo.HitNormal, - PenetrationPower = damageInfo.PenetrationPower, - BlockedBy = damageInfo.BlockedBy, - DeflectedBy = damageInfo.DeflectedBy, - SourceId = damageInfo.SourceId, - ArmorDamage = damageInfo.ArmorDamage - } + Damage = damageInfo.Damage, + DamageType = damageInfo.DamageType, + BodyPartType = bodyPartType, + ColliderType = colliderType, + ArmorPlateCollider = armorPlateCollider, + Absorbed = 0f, + Direction = damageInfo.Direction, + Point = damageInfo.HitPoint, + HitNormal = damageInfo.HitNormal, + PenetrationPower = damageInfo.PenetrationPower, + BlockedBy = damageInfo.BlockedBy, + DeflectedBy = damageInfo.DeflectedBy, + SourceId = damageInfo.SourceId, + ArmorDamage = damageInfo.ArmorDamage }); return null; @@ -369,7 +336,7 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy // There should never be other instances than CoopPlayer or its derived types CoopPlayer player = (CoopPlayer)damageInfo.Player.iPlayer; - if (player.IsYourPlayer) + if (player.IsYourPlayer || player.IsAI) { if (HealthController != null && !HealthController.IsAlive) { @@ -379,11 +346,11 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy bool flag = !string.IsNullOrEmpty(damageInfo.DeflectedBy); float damage = damageInfo.Damage; List list = ProceedDamageThroughArmor(ref damageInfo, colliderType, armorPlateCollider, true); - MaterialType materialType = (flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material)); - GClass1676 hitInfo = new() + MaterialType materialType = flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material); + ShotInfoClass hitInfo = new() { PoV = PointOfView, - Penetrated = (string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy)), + Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), Material = materialType }; float num = damage - damageInfo.Damage; @@ -392,98 +359,33 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy damageInfo.DidArmorDamage = num; } damageInfo.DidBodyDamage = damageInfo.Damage; - //ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); - ShotReactions(damageInfo, bodyPartType); ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); - if (damageInfo.HittedBallisticCollider != null) - { - BodyPartCollider bodyPartCollider = (BodyPartCollider)damageInfo.HittedBallisticCollider; - colliderType = bodyPartCollider.BodyPartColliderType; - } - PacketSender.DamagePackets.Enqueue(new() { - DamageInfo = new() - { - Damage = damage, - DamageType = damageInfo.DamageType, - BodyPartType = bodyPartType, - ColliderType = colliderType, - ArmorPlateCollider = armorPlateCollider, - Absorbed = 0f, - Direction = damageInfo.Direction, - Point = damageInfo.HitPoint, - HitNormal = damageInfo.HitNormal, - PenetrationPower = damageInfo.PenetrationPower, - BlockedBy = damageInfo.BlockedBy, - DeflectedBy = damageInfo.DeflectedBy, - SourceId = damageInfo.SourceId, - ArmorDamage = damageInfo.ArmorDamage, - ProfileId = damageInfo.Player.iPlayer.ProfileId - } - }); - - // Run this to get weapon skill - ManageAggressor(damageInfo, bodyPartType, colliderType); - - return hitInfo; - } - else if (player.IsAI) - { - if (HealthController != null && !HealthController.IsAlive) - { - return null; - } - - bool flag = !string.IsNullOrEmpty(damageInfo.DeflectedBy); - float damage = damageInfo.Damage; - List list = ProceedDamageThroughArmor(ref damageInfo, colliderType, armorPlateCollider, true); - MaterialType materialType = (flag ? MaterialType.HelmetRicochet : ((list == null || list.Count < 1) ? MaterialType.Body : list[0].Material)); - GClass1676 hitInfo = new() - { - PoV = PointOfView, - Penetrated = (string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy)), + Damage = damageInfo.Damage, + DamageType = damageInfo.DamageType, + BodyPartType = bodyPartType, + ColliderType = colliderType, + ArmorPlateCollider = armorPlateCollider, + Absorbed = 0f, + Direction = damageInfo.Direction, + Point = damageInfo.HitPoint, + HitNormal = damageInfo.HitNormal, + PenetrationPower = damageInfo.PenetrationPower, + BlockedBy = damageInfo.BlockedBy, + DeflectedBy = damageInfo.DeflectedBy, + SourceId = damageInfo.SourceId, + ArmorDamage = damageInfo.ArmorDamage, + ProfileId = damageInfo.Player.iPlayer.ProfileId, Material = materialType - }; - float num = damage - damageInfo.Damage; - if (num > 0) - { - damageInfo.DidArmorDamage = num; - } - damageInfo.DidBodyDamage = damageInfo.Damage; - //ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); - ShotReactions(damageInfo, bodyPartType); - ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); + }); - if (damageInfo.HittedBallisticCollider != null) + if (list != null) { - BodyPartCollider bodyPartCollider = (BodyPartCollider)damageInfo.HittedBallisticCollider; - colliderType = bodyPartCollider.BodyPartColliderType; + QueueArmorDamagePackets([.. list]); } - PacketSender.DamagePackets.Enqueue(new() - { - DamageInfo = new() - { - Damage = damage, - DamageType = damageInfo.DamageType, - BodyPartType = bodyPartType, - ColliderType = colliderType, - ArmorPlateCollider = armorPlateCollider, - Absorbed = 0f, - Direction = damageInfo.Direction, - Point = damageInfo.HitPoint, - HitNormal = damageInfo.HitNormal, - PenetrationPower = damageInfo.PenetrationPower, - BlockedBy = damageInfo.BlockedBy, - DeflectedBy = damageInfo.DeflectedBy, - SourceId = damageInfo.SourceId, - ArmorDamage = damageInfo.ArmorDamage, - ProfileId = damageInfo.Player.iPlayer.ProfileId - } - }); - // Run this to get weapon skill ManageAggressor(damageInfo, bodyPartType, colliderType); @@ -491,18 +393,15 @@ public override GClass1676 ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartTy } return null; } - else - { - ShotReactions(damageInfo, bodyPartType); - return null; - } + + return null; } public override void SetControllerInsteadRemovedOne(Item removingItem, Callback callback) { RemoveHandsControllerHandler handler = new(this, callback); _removeFromHandsCallback = callback; - Proceed(false, new Callback(handler.Handle), false); + Proceed(false, new Callback(handler.Handle), false); } public override Corpse CreateCorpse() @@ -528,7 +427,7 @@ public override void CreateMovementContext() public override void OnHealthEffectAdded(IEffect effect) { // Remember to check if classes increment - if (effect is GInterface245 && FractureSound != null && Singleton.Instantiated) + if (effect is GInterface260 && FractureSound != null && Singleton.Instantiated) { Singleton.Instance.PlayAtPoint(Position, FractureSound, CameraClass.Instance.Distance(Position), BetterAudio.AudioSourceGroupType.Impacts, 15, 0.7f, EOcclusionTest.Fast, null, false); @@ -556,11 +455,11 @@ public override void Proceed(GrenadeClass throwWeap, Callback(this, func, throwWeap, false).method_0(null, callback, scheduled); } - public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) + public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) { HandsControllerFactory factory = new(this, throwWeap); Func func = new(factory.CreateObservedQuickGrenadeController); - new Process(this, func, throwWeap, false).method_0(null, callback, scheduled); + new Process(this, func, throwWeap, false).method_0(null, callback, scheduled); } public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) @@ -571,7 +470,7 @@ public override void Proceed(Weapon weapon, Callback ca .method_0(null, callback, scheduled); } - public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) + public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) { HandsControllerFactory factory = new(this) { @@ -580,11 +479,11 @@ public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback func = new(factory.CreateObservedMedsController); - new Process(this, func, meds, false) + new Process(this, func, meds, false) .method_0(null, callback, scheduled); } - public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) + public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) { HandsControllerFactory factory = new(this) { @@ -593,7 +492,7 @@ public override void Proceed(FoodClass foodDrink, float amount, Callback func = new(factory.CreateObservedMedsController); - new Process(this, func, foodDrink, false) + new Process(this, func, foodDrink, false) .method_0(null, callback, scheduled); } #endregion @@ -633,12 +532,12 @@ public override void DropCurrentController(Action callback, bool fastDrop, Item public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBank bank, PhraseSpeakerClass speaker) { - method_32(clip); + method_34(clip); } public PlayerStatePacket Interpolate(in PlayerStatePacket newState, in PlayerStatePacket lastState) { - method_54(newState.HasGround, newState.SurfaceSound); + method_58(newState.HasGround, newState.SurfaceSound); Rotation = new Vector2(Mathf.LerpAngle(MovementContext.Rotation.x, newState.Rotation.x, interpolationRatio), Mathf.Lerp(MovementContext.Rotation.y, newState.Rotation.y, interpolationRatio)); @@ -711,8 +610,8 @@ public PlayerStatePacket Interpolate(in PlayerStatePacket newState, in PlayerSta Move(Vector2.Lerp(newState.MovementDirection, lastState.MovementDirection, interpolationRatio)); } - Vector3 a = Vector3.Lerp(MovementContext.TransformPosition, newState.Position, interpolationRatio); - CharacterController.Move(a - MovementContext.TransformPosition, interpolationRatio); + Vector3 newPosition = Vector3.Lerp(MovementContext.TransformPosition, newState.Position, interpolationRatio); + CharacterController.Move(newPosition - MovementContext.TransformPosition, interpolationRatio); if (!Mathf.Approximately(MovementContext.Tilt, newState.Tilt)) { @@ -784,11 +683,11 @@ public override void OnDead(EDamageType damageType) // This one is already handled by SPT, so we do not add directly to profile until they move it to client side // They also do a flat value of 0.02 rather than 0.01 for 1 scav kill or 0.03 for >1 LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.02, [CounterTag.FenceStanding, EFenceStandingSource.ScavHelp]); - //LastAggressor.Profile.FenceInfo.AddStanding(0.01, EFenceStandingSource.ScavHelp); + LastAggressor.Profile.FenceInfo.AddStanding(0.01, EFenceStandingSource.ScavHelp); } else if (Side == EPlayerSide.Savage) { - //LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); + LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); LastAggressor.Profile.FenceInfo.AddStanding(0.03, EFenceStandingSource.TraitorKill); } } @@ -796,14 +695,23 @@ public override void OnDead(EDamageType damageType) } Singleton.Instance.ProtagonistHearingChanged -= SetSoundRollOff; - if (FikaPlugin.CullPlayers.Value) + /*if (FikaPlugin.CullPlayers.Value) { UnregisterCulling(); - } + }*/ base.OnDead(damageType); } + public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) + { + // Only handle if it was ourselves as otherwise it's irrelevant + if (LastAggressor.IsYourPlayer) + { + base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); + } + } + public override void SetupDogTag() { // Do nothing @@ -813,7 +721,7 @@ public override void SetInventory(EquipmentClass equipmentClass) { Inventory.Equipment = equipmentClass; - BindableState itemInHands = (BindableState)Traverse.Create(this).Field("_itemInHands").GetValue(); + BindableState itemInHands = Traverse.Create(this).Field>("_itemInHands").Value; if (HandsController != null && HandsController.Item != null) { Item item = FindItem(HandsController.Item.Id); @@ -828,8 +736,8 @@ public override void SetInventory(EquipmentClass equipmentClass) { Transform slotBone = PlayerBody.GetSlotBone(equipmentSlot); Transform alternativeHolsterBone = PlayerBody.GetAlternativeHolsterBone(equipmentSlot); - PlayerBody.GClass1860 gclass = new(PlayerBody, Inventory.Equipment.GetSlot(equipmentSlot), slotBone, equipmentSlot, Inventory.Equipment.GetSlot(EquipmentSlot.Backpack), alternativeHolsterBone); - PlayerBody.GClass1860 gclass2 = PlayerBody.SlotViews.AddOrReplace(equipmentSlot, gclass); + PlayerBody.GClass1875 gclass = new(PlayerBody, Inventory.Equipment.GetSlot(equipmentSlot), slotBone, equipmentSlot, Inventory.Equipment.GetSlot(EquipmentSlot.Backpack), alternativeHolsterBone); + PlayerBody.GClass1875 gclass2 = PlayerBody.SlotViews.AddOrReplace(equipmentSlot, gclass); if (gclass2 != null) { gclass2.Dispose(); @@ -911,13 +819,12 @@ public void InitObservedPlayer() PacketSender.Writer.Reset(); PacketSender.Client.SendData(PacketSender.Writer, ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - IVaultingComponent vaultingComponent = playerTraverse.Field("_vaultingComponent").GetValue(); + IVaultingComponent vaultingComponent = playerTraverse.Field("_vaultingComponent").Value; if (vaultingComponent != null) { UpdateEvent -= vaultingComponent.DoVaultingTick; } - playerTraverse.Field("_vaultingComponent").SetValue(null); playerTraverse.Field("_vaultingComponentDebug").SetValue(null); playerTraverse.Field("_vaultingParameters").SetValue(null); @@ -926,10 +833,10 @@ public void InitObservedPlayer() playerTraverse.Field("_sprintVaultAudioController").SetValue(null); playerTraverse.Field("_climbAudioController").SetValue(null); - if (FikaPlugin.CullPlayers.Value) + /*if (FikaPlugin.CullPlayers.Value) { SetupCulling(); - } + }*/ } PacketReceiver = gameObject.AddComponent(); @@ -938,11 +845,9 @@ public void InitObservedPlayer() { Profile.Info.GroupId = "Fika"; - var asd = Side; - CoopGame coopGame = (CoopGame)Singleton.Instance; - IVaultingComponent vaultingComponent = playerTraverse.Field("_vaultingComponent").GetValue(); + IVaultingComponent vaultingComponent = playerTraverse.Field("_vaultingComponent").Value; if (vaultingComponent != null) { UpdateEvent -= vaultingComponent.DoVaultingTick; @@ -962,7 +867,7 @@ public void InitObservedPlayer() waitForStartRoutine = StartCoroutine(CreateHealthBar()); - RaycastCameraTransform = playerTraverse.Field("_playerLookRaycastTransform").GetValue(); + RaycastCameraTransform = playerTraverse.Field("_playerLookRaycastTransform").Value; } } @@ -985,11 +890,6 @@ private IEnumerator CreateHealthBar() yield break; } - protected override void Start() - { - // Do nothing - } - public override void LateUpdate() { DistanceDirty = true; @@ -1029,7 +929,7 @@ public override void LandingAdjustments(float d) public new void CreateCompass() { - bool compassInstantiated = Traverse.Create(this).Field("_compassInstantiated").GetValue(); + bool compassInstantiated = Traverse.Create(this).Field("_compassInstantiated").Value; if (!compassInstantiated) { Transform transform = Singleton.Instance.CreateFromPool(new ResourceKey @@ -1051,7 +951,7 @@ private void SetupCulling() followerCullingObject.enabled = true; followerCullingObject.CullByDistanceOnly = false; followerCullingObject.Init(new Func(GetPlayerBones)); - followerCullingObject.SetParams(EFTHardSettings.Instance.CULLING_PLAYER_SPHERE_RADIUS, EFTHardSettings.Instance.CULLING_PLAYER_SPHERE_SHIFT, FikaPlugin.CullingRange.Value); + //followerCullingObject.SetParams(EFTHardSettings.Instance.CULLING_PLAYER_SPHERE_RADIUS, EFTHardSettings.Instance.CULLING_PLAYER_SPHERE_SHIFT, FikaPlugin.CullingRange.Value); //followerCullingObject.OnVisibilityChanged += OnObservedVisibilityChanged; if (_triggerColliderSearcher != null) @@ -1217,10 +1117,10 @@ public override void OnDestroy() public override void Dispose() { - if (FikaPlugin.CullPlayers.Value) + /*if (FikaPlugin.CullPlayers.Value) { UnregisterCulling(); - } + }*/ base.Dispose(); } @@ -1232,6 +1132,11 @@ public override void SendHandsInteractionStateChanged(bool value, int animationI } } + public override void HandleDamagePacket(ref DamagePacket packet) + { + // Do nothing + } + public void HandleProceedPacket(ProceedPacket packet) { switch (packet.ProceedType) @@ -1241,6 +1146,7 @@ public void HandleProceedPacket(ProceedPacket packet) CreateEmptyHandsController(); break; } + case EProceedType.FoodClass: case EProceedType.MedsClass: { CreateMedsController(packet.ItemId, packet.BodyPart, packet.Amount, packet.AnimationVariant); @@ -1285,7 +1191,7 @@ public void HandleProceedPacket(ProceedPacket packet) #region handControllers private void CreateHandsController(Func controllerFactory, Item item) { - CreateHandsControllerHandler handler = new((item != null) ? method_67(item) : null); + CreateHandsControllerHandler handler = new((item != null) ? method_71(item) : null); handler.setInHandsOperation?.Confirm(true); @@ -1426,13 +1332,13 @@ private void CreateQuickUseItemController(string itemId) } } - public void SetAggressor(string killerId, string weaponId) + public void SetAggressor(string killerId) { Player killer = Singleton.Instance.GetEverExistedPlayerByID(killerId); if (killer != null) { LastAggressor = killer; - if (killer.IsYourPlayer) + /*if (killer.IsYourPlayer) { Item weapon = FindItem(weaponId); if (weapon != null) @@ -1442,7 +1348,7 @@ public void SetAggressor(string killerId, string weaponId) Weapon = weapon }; } - } + }*/ } } @@ -1451,7 +1357,7 @@ private class RemoveHandsControllerHandler(ObservedCoopPlayer coopPlayer, Callba private readonly ObservedCoopPlayer coopPlayer = coopPlayer; private readonly Callback callback = callback; - public void Handle(Result result) + public void Handle(Result result) { if (coopPlayer._removeFromHandsCallback == callback) { @@ -1461,13 +1367,13 @@ public void Handle(Result result) } } - private class CreateHandsControllerHandler(Class1059 setInHandsOperation) + private class CreateHandsControllerHandler(Class1082 setInHandsOperation) { - public readonly Class1059 setInHandsOperation = setInHandsOperation; + public readonly Class1082 setInHandsOperation = setInHandsOperation; internal void DisposeHandler() { - Class1059 handler = setInHandsOperation; + Class1082 handler = setInHandsOperation; if (handler == null) return; handler.Dispose(); diff --git a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptPatches.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs similarity index 52% rename from Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptPatches.cs rename to Fika.Core/Coop/Utils/FikaBackendUtils.cs index 5aac7c1c..61cb69b8 100644 --- a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptPatches.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -3,11 +3,9 @@ using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; using System; -using System.Linq; using System.Reflection; -using UnityEngine; -namespace Fika.Core.Coop.Matchmaker +namespace Fika.Core.Coop.Utils { public enum EMatchmakerType { @@ -16,43 +14,27 @@ public enum EMatchmakerType GroupLeader = 2 } - public static class MatchmakerAcceptPatches + public static class FikaBackendUtils { - #region Fields/Properties - public static MatchMakerAcceptScreen MatchMakerAcceptScreenInstance { get; set; } - public static Profile Profile { get; set; } - public static string PMCName { get; set; } - public static EMatchmakerType MatchingType { get; set; } = EMatchmakerType.Single; + public static MatchMakerAcceptScreen MatchMakerAcceptScreenInstance; + public static Profile Profile; + public static string PMCName; + public static EMatchmakerType MatchingType = EMatchmakerType.Single; public static bool IsServer => MatchingType == EMatchmakerType.GroupLeader; public static bool IsClient => MatchingType == EMatchmakerType.GroupPlayer; public static bool IsSinglePlayer => MatchingType == EMatchmakerType.Single; - public static PlayersRaidReadyPanel PlayersRaidReadyPanel { get; set; } - public static MatchMakerGroupPreview MatchMakerGroupPreview { get; set; } - public static int HostExpectedNumberOfPlayers { get; set; } = 1; - public static WeatherClass[] Nodes { get; set; } = null; + public static PlayersRaidReadyPanel PlayersRaidReadyPanel; + public static MatchMakerGroupPreview MatchMakerGroupPreview; + public static int HostExpectedNumberOfPlayers = 1; + public static WeatherClass[] Nodes = null; + public static string RemoteIp; + public static int RemotePort; + public static int LocalPort = 0; + public static bool IsHostNatPunch = false; private static string groupId; - private static long timestamp; - #endregion + private static string raidCode; - #region Static Fields - - public static object MatchmakerScreenController - { - get - { - object screenController = typeof(MatchMakerAcceptScreen).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(x => x.Name == "ScreenController") - .FirstOrDefault().GetValue(MatchMakerAcceptScreenInstance); - if (screenController != null) - { - return screenController; - } - return null; - } - } - - public static GameObject EnvironmentUIRoot { get; internal set; } - public static MatchmakerTimeHasCome.GClass3163 GClass3163 { get; internal set; } - #endregion + public static MatchmakerTimeHasCome.TimeHasComeScreenClass ScreenController; public static string GetGroupId() { @@ -64,14 +46,14 @@ public static void SetGroupId(string newId) groupId = newId; } - public static long GetTimestamp() + public static void SetRaidCode(string newCode) { - return timestamp; + raidCode = newCode; } - public static void SetTimestamp(long ts) + public static string GetRaidCode() { - timestamp = ts; + return raidCode; } public static bool JoinMatch(string profileId, string serverId, out CreateMatch result, out string errorMessage) @@ -84,35 +66,54 @@ public static bool JoinMatch(string profileId, string serverId, out CreateMatch return false; } - var body = new MatchJoinRequest(serverId, profileId); + MatchJoinRequest body = new(serverId, profileId); result = FikaRequestHandler.RaidJoin(body); if (result.GameVersion != FikaPlugin.EFTVersionMajor) { - errorMessage = $"You are attempting to use a different version of EFT {FikaPlugin.EFTVersionMajor} than what the server is running {result.GameVersion}"; + errorMessage = $"You are attempting to use a different version of EFT than what the server is running.\nClient: {FikaPlugin.EFTVersionMajor}\nServer: {result.GameVersion}"; return false; } - var detectedFikaVersion = Assembly.GetExecutingAssembly().GetName().Version; + Version detectedFikaVersion = Assembly.GetExecutingAssembly().GetName().Version; if (result.FikaVersion != detectedFikaVersion) { - errorMessage = $"You are attempting to use a different version of Fika {detectedFikaVersion} than what the server is running {result.FikaVersion}"; + errorMessage = $"You are attempting to use a different version of Fika than what the server is running.\nClient: {detectedFikaVersion}\nServer: {result.FikaVersion}"; return false; } + SetRaidCode(result.RaidCode); + return true; } public static void CreateMatch(string profileId, string hostUsername, RaidSettings raidSettings) { long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); - var body = new CreateMatch(profileId, hostUsername, timestamp, raidSettings, HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime); + string raidCode = GenerateRaidCode(6); + CreateMatch body = new(raidCode, profileId, hostUsername, timestamp, raidSettings, + HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime); FikaRequestHandler.RaidCreate(body); SetGroupId(profileId); - SetTimestamp(timestamp); MatchingType = EMatchmakerType.GroupLeader; + + SetRaidCode(raidCode); + } + + public static string GenerateRaidCode(int length) + { + Random random = new(); + char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray(); + string raidCode = ""; + for (int i = 0; i < length; i++) + { + int charIndex = random.Next(chars.Length); + raidCode += chars[charIndex]; + } + + return raidCode; } } } diff --git a/Fika.Core/Coop/Utils/NetManagerUtils.cs b/Fika.Core/Coop/Utils/NetManagerUtils.cs new file mode 100644 index 00000000..43f4b745 --- /dev/null +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -0,0 +1,153 @@ +using BepInEx.Logging; +using Comfort.Common; +using Fika.Core.Coop.Components; +using Fika.Core.Coop.Players; +using Fika.Core.Networking; +using System.Threading.Tasks; +using UnityEngine; + +namespace Fika.Core.Coop.Utils +{ + public static class NetManagerUtils + { + private static ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("NetManagerUtils"); + public static GameObject FikaGameObject; + + public static void CreateFikaGameObject() + { + FikaGameObject = new GameObject("FikaGameObject"); + Object.DontDestroyOnLoad(FikaGameObject); + logger.LogInfo("FikaGameObject has been created!"); + } + + public static void CreateNetManager(bool isServer) + { + if (FikaGameObject == null) + { + CreateFikaGameObject(); + } + + if (isServer) + { + FikaServer server = FikaGameObject.AddComponent(); + Singleton.Create(server); + logger.LogInfo("FikaServer has started!"); + } + else + { + FikaClient client = FikaGameObject.AddComponent(); + Singleton.Create(client); + logger.LogInfo("FikaClient has started!"); + } + } + + public static void CreatePingingClient() + { + if (FikaGameObject == null) + { + CreateFikaGameObject(); + } + + FikaPingingClient pingingClient = FikaGameObject.AddComponent(); + Singleton.Create(pingingClient); + logger.LogInfo("FikaPingingClient has started!"); + } + + public static void DestroyNetManager(bool isServer) + { + if (FikaGameObject != null) + { + if (isServer) + { + Singleton.Instance.NetServer.Stop(); + Singleton.TryRelease(Singleton.Instance); + logger.LogInfo("Destroyed FikaServer"); + } + else + { + Singleton.Instance.NetClient.Stop(); + Singleton.TryRelease(Singleton.Instance); + logger.LogInfo("Destroyed FikaClient"); + } + } + } + + public static void DestroyPingingClient() + { + if (FikaGameObject != null) + { + Singleton.Instance.StopKeepAliveRoutine(); + Singleton.Instance.NetClient.Stop(); + Singleton.TryRelease(Singleton.Instance); + logger.LogInfo("Destroyed FikaPingingClient"); + } + } + + public static Task InitNetManager(bool isServer) + { + if (FikaGameObject != null) + { + if (isServer) + { + FikaServer server = Singleton.Instance; + if (!server.Started) + { + return server.Init(); + } + return Task.CompletedTask; + } + else + { + FikaClient client = Singleton.Instance; + if (!client.Started) + { + client.Init(); + } + return Task.CompletedTask; + } + } + + logger.LogError("InitNetManager: FikaGameObject was null!"); + return Task.CompletedTask; + } + + public static Task SetupGameVariables(bool isServer, CoopPlayer coopPlayer) + { + if (isServer) + { + Singleton.Instance.SetupGameVariables(coopPlayer); + } + else + { + Singleton.Instance.SetupGameVariables(coopPlayer); + } + + return Task.CompletedTask; + } + + public static void StartPinger() + { + if (FikaGameObject != null) + { + FikaPinger fikaPinger = FikaGameObject.AddComponent(); + fikaPinger.StartPingRoutine(); + } + } + + public static void StopPinger() + { + if (FikaGameObject != null) + { + FikaPinger fikaPinger = FikaGameObject.GetComponent(); + if (fikaPinger != null) + { + Object.Destroy(fikaPinger); + } + else + { + logger.LogError("StopPinger: Could not find FikaPinger!"); + } + } + } + } +} \ No newline at end of file diff --git a/Fika.Core/Coop/World/WeatherNode_Patch.cs b/Fika.Core/Coop/World/WeatherNode_Patch.cs index 9a67d11c..91dde424 100644 --- a/Fika.Core/Coop/World/WeatherNode_Patch.cs +++ b/Fika.Core/Coop/World/WeatherNode_Patch.cs @@ -1,25 +1,25 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Reflection.Patching; using EFT.Weather; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; +using SPT.Reflection.Patching; using System.Reflection; namespace Fika.Core.Coop.World { internal class WeatherNode_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(WeatherController).GetMethod(nameof(WeatherController.method_4)); + protected override MethodBase GetTargetMethod() => typeof(WeatherController).GetMethod(nameof(WeatherController.method_0)); [PatchPostfix] public static void Postfix(WeatherController __instance, WeatherClass[] nodes) { - if (MatchmakerAcceptPatches.IsClient) + if (FikaBackendUtils.IsClient) { return; } - MatchmakerAcceptPatches.Nodes = nodes; + FikaBackendUtils.Nodes = nodes; } } } diff --git a/Fika.Core/EssentialPatches/FikaVersionLabel_Patch.cs b/Fika.Core/EssentialPatches/FikaVersionLabel_Patch.cs deleted file mode 100644 index 5d7f1d23..00000000 --- a/Fika.Core/EssentialPatches/FikaVersionLabel_Patch.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Aki.Common.Http; -using Aki.Common.Utils; -using Aki.Custom.Models; -using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using EFT.UI; -using HarmonyLib; -using System.Linq; -using System.Reflection; - -namespace Fika.Core.EssentialPatches -{ - /// - /// Originally developed by SPT-Aki - /// - public class FikaVersionLabel_Patch : ModulePatch - { - private static string _versionLabel; - - protected override MethodBase GetTargetMethod() => PatchConstants.EftTypes.Single(x => x.GetField("Taxonomy", BindingFlags.Public | BindingFlags.Instance) != null).GetMethod("Create", BindingFlags.Public | BindingFlags.Static); - - [PatchPostfix] - internal static void PatchPostfix(string major, object __result) - { - FikaPlugin.EFTVersionMajor = major; - - if (string.IsNullOrEmpty(_versionLabel)) - { - string json = RequestHandler.GetJson("/singleplayer/settings/version"); - _versionLabel = Json.Deserialize(json).Version; - Logger.LogInfo($"Server version: {_versionLabel}"); - } - - string fikaVersion = Assembly.GetAssembly(typeof(FikaVersionLabel_Patch)).GetName().Version.ToString(); - - Traverse preloaderUiTraverse = Traverse.Create(MonoBehaviourSingleton.Instance); - - preloaderUiTraverse.Field("_alphaVersionLabel").Property("LocalizationKey").SetValue("{0}"); - preloaderUiTraverse.Field("string_2").SetValue($"Fika {fikaVersion} |"); - Traverse.Create(__result).Field("Major").SetValue($"FIKA BETA {fikaVersion} | {_versionLabel}"); - } - } -} \ No newline at end of file diff --git a/Fika.Core/Fika.Core.csproj b/Fika.Core/Fika.Core.csproj index 7d7b9d12..9ef82a59 100644 --- a/Fika.Core/Fika.Core.csproj +++ b/Fika.Core/Fika.Core.csproj @@ -39,89 +39,35 @@ - - ..\References\aki-custom.dll - False - - - ..\References\aki-singleplayer.dll - False - - - ..\References\Aki.Common.dll - False - - - ..\References\Aki.Reflection.dll - False - - - ..\References\AnimationSystem.Types.dll - False - - - ..\References\hollowed.dll - False - - - ..\References\bsg.componentace.compression.libs.zlib.dll - False - - - ..\References\bsg.console.core.dll - - - ..\References\Comfort.dll - False - - - ..\References\Comfort.Unity.dll - False - - - ..\References\CommonExtensions.dll - False - - - ..\References\DissonanceVoip.dll - False - - - ..\References\ItemComponent.Types.dll - false - - - ..\References\Newtonsoft.Json.dll - False - - - ..\References\Sirenix.Serialization.dll - False - - - ..\References\Unity.Postprocessing.Runtime.dll - False - - - ..\References\Unity.TextMeshPro.dll - False - - - ..\References\UnityEngine.dll - False - - - ..\References\UnityEngine.CoreModule.dll - - - ..\References\UnityEngine.UI.dll - False + + + + + + + + + + + + + + + + + + + + + + + ..\References\websocket-sharp.dll - + - + diff --git a/Fika.Core/FikaPlugin.cs b/Fika.Core/FikaPlugin.cs index d351c479..d2aca16b 100644 --- a/Fika.Core/FikaPlugin.cs +++ b/Fika.Core/FikaPlugin.cs @@ -1,32 +1,34 @@ -using Aki.Custom.Airdrops.Patches; -using Aki.Custom.BTR.Patches; -using Aki.Custom.Patches; -using Aki.SinglePlayer.Patches.MainMenu; -using Aki.SinglePlayer.Patches.Progression; -using Aki.SinglePlayer.Patches.Quests; -using Aki.SinglePlayer.Patches.RaidFix; -using BepInEx; -using BepInEx.Bootstrap; +using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using EFT.UI; -using Fika.Core.AkiSupport.Airdrops.Utils; -using Fika.Core.AkiSupport.Overrides; -using Fika.Core.AkiSupport.Scav; using Fika.Core.Bundles; using Fika.Core.Console; +using Fika.Core.Coop.Airdrops.Utils; using Fika.Core.Coop.FreeCamera.Patches; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Patches; +using Fika.Core.Coop.Patches.Airdrop; using Fika.Core.Coop.Patches.LocalGame; +using Fika.Core.Coop.Patches.Overrides; using Fika.Core.Coop.World; using Fika.Core.EssentialPatches; using Fika.Core.Models; using Fika.Core.Networking.Http; +using Fika.Core.SPTSupport.Scav; using Fika.Core.UI; using Fika.Core.UI.Models; using Fika.Core.UI.Patches; +using Fika.Core.UI.Patches.MatchmakerAcceptScreen; using Fika.Core.Utils; +using SPT.Common.Http; +using SPT.Custom.Airdrops.Patches; +using SPT.Custom.BTR.Patches; +using SPT.Custom.Patches; +using SPT.SinglePlayer.Patches.MainMenu; +using SPT.SinglePlayer.Patches.Progression; +using SPT.SinglePlayer.Patches.Quests; +using SPT.SinglePlayer.Patches.RaidFix; +using SPT.SinglePlayer.Patches.ScavMode; using System; using System.Collections; using System.Collections.Generic; @@ -34,7 +36,6 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reflection; -using System.Text; using UnityEngine; namespace Fika.Core @@ -46,24 +47,14 @@ namespace Fika.Core /// [BepInPlugin("com.fika.core", "Fika.Core", "0.9.8906")] [BepInProcess("EscapeFromTarkov.exe")] - [BepInDependency("com.spt-aki.custom", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches - [BepInDependency("com.spt-aki.singleplayer", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-singleplayer, that way we can disable its patches - [BepInDependency("com.spt-aki.core", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches - [BepInDependency("com.spt-aki.debugging", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches + [BepInDependency("com.SPT.custom", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches + [BepInDependency("com.SPT.singleplayer", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-singleplayer, that way we can disable its patches + [BepInDependency("com.SPT.core", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches + [BepInDependency("com.SPT.debugging", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after aki-custom, that way we can disable its patches 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 ManualLogSource FikaLogger { get => Logger; } public BotDifficulties BotDifficulties; @@ -101,6 +92,9 @@ public class FikaPlugin : BaseUnityPlugin // Hidden public static ConfigEntry AcceptedTOS { get; set; } + //Advanced + public static ConfigEntry OfficialVersion { get; set; } + // Coop public static ConfigEntry ShowNotifications { get; set; } public static ConfigEntry AutoExtract { get; set; } @@ -108,6 +102,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry FasterInventoryScroll { get; set; } public static ConfigEntry FasterInventoryScrollSpeed { get; set; } public static ConfigEntry ExtractKey { get; set; } + public static ConfigEntry ChatKey { get; set; } // Coop | Name Plates public static ConfigEntry UseNamePlates { get; set; } @@ -115,6 +110,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry UseHealthNumber { get; set; } public static ConfigEntry UsePlateFactionSide { get; set; } public static ConfigEntry HideNamePlateInOptic { get; set; } + public static ConfigEntry NamePlateUseOpticZoom { get; set; } public static ConfigEntry DecreaseOpacityNotLookingAt { get; set; } public static ConfigEntry NamePlateScale { get; set; } public static ConfigEntry OpacityInADS { get; set; } @@ -122,6 +118,9 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry MinimumOpacity { get; set; } public static ConfigEntry MinimumNamePlateScale { get; set; } + // Coop | Quest Sharing + public static ConfigEntry QuestTypesToShareAndReceive { get; set; } + // Coop | Custom public static ConfigEntry UsePingSystem { get; set; } public static ConfigEntry PingButton { get; set; } @@ -130,17 +129,21 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry PingTime { get; set; } public static ConfigEntry PlayPingAnimation { get; set; } public static ConfigEntry ShowPingDuringOptics { get; set; } + public static ConfigEntry PingUseOpticZoom { get; set; } + public static ConfigEntry PingScaleWithDistance { get; set; } + public static ConfigEntry PingMinimumOpacity { get; set; } // Coop | Debug public static ConfigEntry FreeCamButton { get; set; } public static ConfigEntry AZERTYMode { get; set; } + public static ConfigEntry KeybindOverlay { get; set; } // Performance public static ConfigEntry DynamicAI { get; set; } public static ConfigEntry DynamicAIRange { get; set; } - public static ConfigEntry DynamicAIRate { get; set; } - public static ConfigEntry CullPlayers { get; set; } - public static ConfigEntry CullingRange { get; set; } + public static ConfigEntry DynamicAIRate { get; set; } + //public static ConfigEntry CullPlayers { get; set; } + //public static ConfigEntry CullingRange { get; set; } // Performance | Bot Limits public static ConfigEntry EnforcedSpawnLimits { get; set; } @@ -165,6 +168,7 @@ public class FikaPlugin : BaseUnityPlugin public static ConfigEntry AutoRefreshRate { get; set; } public static ConfigEntry UDPPort { get; set; } public static ConfigEntry UseUPnP { get; set; } + public static ConfigEntry UseNatPunching { get; set; } public static ConfigEntry ConnectionTimeout { get; set; } // Gameplay @@ -180,14 +184,25 @@ public class FikaPlugin : BaseUnityPlugin public bool DynamicVExfils; public bool AllowFreeCam; public bool AllowItemSending; + public string[] BlacklistedItems; public bool ForceSaveOnDeath; + public bool UseInertia; + public bool SharedQuestProgression; + #endregion + + #region natpunch config + public bool NatPunchServerEnable; + public string NatPunchServerIP; + public int NatPunchServerPort; + public int NatPunchServerNatIntroduceAmount; #endregion protected void Awake() { Instance = this; - LogDependencyErrors(); + GetClientConfig(); + GetNatPunchServerConfig(); SetupConfig(); new FikaVersionLabel_Patch().Enable(); @@ -199,16 +214,18 @@ protected void Awake() new NonWaveSpawnScenario_Patch().Enable(); new WaveSpawnScenario_Patch().Enable(); new WeatherNode_Patch().Enable(); - new EnvironmentUIRoot_Patch().Enable(); new MatchmakerAcceptScreen_Awake_Patch().Enable(); new MatchmakerAcceptScreen_Show_Patch().Enable(); new Minefield_method_2_Patch().Enable(); new BotCacher_Patch().Enable(); new InventoryScroll_Patch().Enable(); new AbstractGame_InRaid_Patch().Enable(); + new DisconnectButton_Patch().Enable(); + new ChangeGameModeButton_Patch().Enable(); #if GOLDMASTER new TOS_Patch().Enable(); #endif + OfficialVersion.SettingChanged += OfficialVersion_SettingChanged; DisableSPTPatches(); EnableOverridePatches(); @@ -221,8 +238,7 @@ protected void Awake() BundleLoaderPlugin.Create(); FikaAirdropUtil.GetConfigFromServer(); - GetClientConfig(); - BotSettingsRepoClass.Init(); + BotSettingsRepoAbstractClass.Init(); if (AllowItemSending) { @@ -235,11 +251,6 @@ protected void Awake() StartCoroutine(RunModHandler()); } - private void Config_SettingChanged(object sender, SettingChangedEventArgs e) - { - throw new NotImplementedException(); - } - /// /// Coroutine to ensure all mods are loaded by waiting 5 seconds /// @@ -247,7 +258,7 @@ private void Config_SettingChanged(object sender, SettingChangedEventArgs e) private IEnumerator RunModHandler() { yield return new WaitForSeconds(5); - ModHandler.Run(); + ModHandler.VerifyMods(); } private void GetClientConfig() @@ -259,9 +270,24 @@ private void GetClientConfig() DynamicVExfils = clientConfig.DynamicVExfils; AllowFreeCam = clientConfig.AllowFreeCam; AllowItemSending = clientConfig.AllowItemSending; + BlacklistedItems = clientConfig.BlacklistedItems; ForceSaveOnDeath = clientConfig.ForceSaveOnDeath; + UseInertia = clientConfig.UseInertia; + SharedQuestProgression = clientConfig.SharedQuestProgression; + + clientConfig.LogValues(); + } + + private void GetNatPunchServerConfig() + { + NatPunchServerConfigModel natPunchServerConfig = FikaRequestHandler.GetNatPunchServerConfig(); + + NatPunchServerEnable = natPunchServerConfig.Enable; + NatPunchServerIP = RequestHandler.Host.Replace("http://", "").Split(':')[0]; + NatPunchServerPort = natPunchServerConfig.Port; + NatPunchServerNatIntroduceAmount = natPunchServerConfig.NatIntroduceAmount; - clientConfig.ToString(); + natPunchServerConfig.LogValues(); } private void SetupConfig() @@ -270,6 +296,9 @@ private void SetupConfig() AcceptedTOS = Config.Bind("Hidden", "Accepted TOS", false, new ConfigDescription("Has accepted TOS", tags: new ConfigurationManagerAttributes() { Browsable = false })); + // Advanced + OfficialVersion = Config.Bind("Advanced", "Official Version", false, new ConfigDescription("Show official version instead of Fika version.", tags: new ConfigurationManagerAttributes() { IsAdvanced = true })); + // Coop ShowNotifications = Instance.Config.Bind("Coop", "Show Feed", true, new ConfigDescription("Enable custom notifications when a player dies, extracts, kills a boss, etc.", tags: new ConfigurationManagerAttributes() { Order = 6 })); @@ -284,17 +313,21 @@ private void SetupConfig() ExtractKey = Config.Bind("Coop", "Extract Key", new KeyboardShortcut(KeyCode.F8), new ConfigDescription("The key used to extract from the raid.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + ChatKey = Config.Bind("Coop", "Chat Key", new KeyboardShortcut(KeyCode.Backspace), new ConfigDescription("The key used to open the chat window.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + // Coop | Name Plates - UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 10 })); + UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 11 })); + + HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 10 })); - HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 9 })); + UseHealthNumber = Config.Bind("Coop | Name Plates", "Show HP% instead of bar", false, new ConfigDescription("Shows health in % amount instead of using the bar.", tags: new ConfigurationManagerAttributes() { Order = 9 })); - UseHealthNumber = Config.Bind("Coop | Name Plates", "Show HP% instead of bar", false, new ConfigDescription("Shows health in % amount instead of using the bar.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + UsePlateFactionSide = Config.Bind("Coop | Name Plates", "Show Player Faction Icon", true, new ConfigDescription("Shows the player faction icon next to the HP bar.", tags: new ConfigurationManagerAttributes() { Order = 8 })); - UsePlateFactionSide = Config.Bind("Coop | Name Plates", "Show Player Faction Icon", true, new ConfigDescription("Shows the player faction icon next to the HP bar.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + HideNamePlateInOptic = Config.Bind("Coop | Name Plates", "Hide Name Plate in Optic", true, new ConfigDescription("Hides the name plate when viewing through PiP scopes.", tags: new ConfigurationManagerAttributes() { Order = 7 })); - HideNamePlateInOptic = Config.Bind("Coop | Name Plates", "Hide Name Plate in Optic", true, new ConfigDescription("Hides the name plate when viewing through PiP scopes since it's kinda janky.", tags: new ConfigurationManagerAttributes() { Order = 6 })); + NamePlateUseOpticZoom = Config.Bind("Coop | Name Plates", "Name Plates Use Optic Zoom", true, new ConfigDescription("If name plate location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 6, IsAdvanced = true })); DecreaseOpacityNotLookingAt = Config.Bind("Coop | Name Plates", "Decrease Opacity In Peripheral", true, new ConfigDescription("Decreases the opacity of the name plates when not looking at a player.", tags: new ConfigurationManagerAttributes() { Order = 5 })); @@ -308,21 +341,31 @@ private void SetupConfig() MinimumNamePlateScale = Config.Bind("Coop | Name Plates", "Minimum Name Plate Scale", 0.01f, new ConfigDescription("The minimum scale of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 0 })); + // Coop | Quest Sharing + + QuestTypesToShareAndReceive = Config.Bind("Coop | Quest Sharing", "Quest Types", EQuestSharingTypes.All, new ConfigDescription("Which quest types to receive and send.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + // 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 = 7 })); + 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 = 9 })); + + PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + + 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 = 7 })); - PingButton = Config.Bind("Coop | Custom", "Ping Button", new KeyboardShortcut(KeyCode.U), new ConfigDescription("Button used to send pings.", tags: new ConfigurationManagerAttributes() { Order = 6 })); + PingSize = Config.Bind("Coop | Custom", "Ping Size", 1f, new ConfigDescription("The multiplier of the ping size.", new AcceptableValueRange(0.1f, 2f), 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 = 5 })); + PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), 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 = 4 })); + 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 = 4 })); - PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), new ConfigurationManagerAttributes() { Order = 3 })); + 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 = 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 = 2 })); + PingUseOpticZoom = Config.Bind("Coop | Custom", "Ping Use Optic Zoom", true, new ConfigDescription("If ping location should be displayed using the PiP optic camera.", tags: new ConfigurationManagerAttributes() { Order = 2, IsAdvanced = true })); - 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 })); + PingScaleWithDistance = Config.Bind("Coop | Custom", "Ping Scale With Distance", true, new ConfigDescription("If ping size should scale with distance from player.", tags: new ConfigurationManagerAttributes() { Order = 1, IsAdvanced = true })); + + PingMinimumOpacity = Config.Bind("Coop | Custom", "Ping Minimum Opacity", 0.05f, new ConfigDescription("The minimum opacity of pings when looking straight at them.", new AcceptableValueRange(0f, 0.5f), new ConfigurationManagerAttributes() { Order = 0, IsAdvanced = true })); // Coop | Debug @@ -330,17 +373,19 @@ private void SetupConfig() AZERTYMode = Config.Bind("Coop | Debug", "AZERTY Mode", false, "If free camera should use AZERTY keys for input."); + KeybindOverlay = Config.Bind("Coop | Debug", "Keybind Overlay", true, "If an overlay with all free cam keybinds should show."); + // Performance DynamicAI = Config.Bind("Performance", "Dynamic AI", false, new ConfigDescription("Use the dynamic AI system, disabling AI when they are outside of any player's range.", tags: new ConfigurationManagerAttributes() { Order = 5 })); DynamicAIRange = Config.Bind("Performance", "Dynamic AI Range", 100f, new ConfigDescription("The range at which AI will be disabled dynamically.", new AcceptableValueRange(150f, 1000f), new ConfigurationManagerAttributes() { Order = 4 })); - DynamicAIRate = Config.Bind("Performance", "Dynamic AI Rate", DynamicAIRates.Medium, new ConfigDescription("How often DynamicAI should scan for the range from all players.", tags: new ConfigurationManagerAttributes() { Order = 3 })); + DynamicAIRate = Config.Bind("Performance", "Dynamic AI Rate", EDynamicAIRates.Medium, new ConfigDescription("How often DynamicAI should scan for the range from all players.", tags: new ConfigurationManagerAttributes() { Order = 3 })); - CullPlayers = Config.Bind("Performance", "Culling System", true, new ConfigDescription("Whether to use the culling system or not. When players are outside of the culling range, their animations will be simplified. This can dramatically improve performance in certain scenarios.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + //CullPlayers = Config.Bind("Performance", "Culling System", true, new ConfigDescription("Whether to use the culling system or not. When players are outside of the culling range, their animations will be simplified. This can dramatically improve performance in certain scenarios.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - CullingRange = Config.Bind("Performance", "Culling Range", 30f, new ConfigDescription("The range at which players should be culled.", new AcceptableValueRange(30f, 150f), new ConfigurationManagerAttributes() { Order = 1 })); + //CullingRange = Config.Bind("Performance", "Culling Range", 30f, new ConfigDescription("The range at which players should be culled.", new AcceptableValueRange(30f, 150f), new ConfigurationManagerAttributes() { Order = 1 })); // Performance | Max Bots @@ -372,31 +417,38 @@ private void SetupConfig() // Network - NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 7 })); + NativeSockets = Config.Bind(section: "Network", "Native Sockets", false, new ConfigDescription("Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", tags: new ConfigurationManagerAttributes() { Order = 8 })); + + 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 = 7 })); - 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.", new AcceptableValueList(GetLocalAddresses()), 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.", 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 = 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 })); + UDPPort = Config.Bind("Network", "UDP Port", 25565, new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 4 })); - UDPPort = Config.Bind("Network", "UDP Port", 25565, new ConfigDescription("Port to use for UDP gameplay packets.", tags: new ConfigurationManagerAttributes() { Order = 3 })); + UseUPnP = Config.Bind("Network", "Use UPnP", false, new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 3 })); - UseUPnP = Config.Bind("Network", "Use UPnP", false, new ConfigDescription("Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + UseNatPunching = Config.Bind("Network", "Use NAT Punching", false, new ConfigDescription("Use NAT punching when hosting a raid. Only works with fullcone NAT type routers and requires NatPunchServer to be running on the AKI server. UPnP, Force IP and Force Bind IP are disabled with this mode.", tags: new ConfigurationManagerAttributes() { Order = 2 })); ConnectionTimeout = Config.Bind("Network", "Connection Timeout", 15, new ConfigDescription("How long it takes for a connection to be considered dropped if no packets are received.", new AcceptableValueRange(5, 60), new ConfigurationManagerAttributes() { Order = 1 })); // Gameplay - HeadDamageMultiplier = Config.Bind("Gameplay", "Head Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the head collider. 0.2 = 20%", new AcceptableValueRange(0.2f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); + HeadDamageMultiplier = Config.Bind("Gameplay", "Head Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the head collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); - 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 = 3 })); + 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.05f, 1f), new ConfigurationManagerAttributes() { Order = 3 })); - StomachDamageMultiplier = Config.Bind("Gameplay", "Stomach Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the stomach collider. 0.2 = 20%", new AcceptableValueRange(0.2f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); + StomachDamageMultiplier = Config.Bind("Gameplay", "Stomach Damage Multiplier", 1f, new ConfigDescription("X multiplier to damage taken on the stomach collider. 0.2 = 20%", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); DisableBotMetabolism = Config.Bind("Gameplay", "Disable Bot Metabolism", false, new ConfigDescription("Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", tags: new ConfigurationManagerAttributes() { Order = 1 })); } + private void OfficialVersion_SettingChanged(object sender, EventArgs e) + { + FikaVersionLabel_Patch.UpdateVersionLabel(); + } + private string[] GetLocalAddresses() { List ips = []; @@ -452,6 +504,8 @@ private void DisableSPTPatches() new ArmorDamageCounterPatch().Disable(); new DogtagPatch().Disable(); new OfflineSaveProfilePatch().Disable(); // We handle this with our own exit manager + new ScavRepAdjustmentPatch().Disable(); + new DisablePvEPatch().Disable(); new BTRInteractionPatch().Disable(); new BTRExtractPassengersPatch().Disable(); @@ -469,36 +523,22 @@ private void EnableOverridePatches() new FikaAirdropFlare_Patch().Enable(); } - private void LogDependencyErrors() - { - // Skip if we've already shown the message, or there are no errors - if (ShownDependencyError || Chainloader.DependencyErrors.Count == 0) - { - return; - } - - StringBuilder stringBuilder = new(); - stringBuilder.AppendLine("Errors occurred during plugin loading"); - stringBuilder.AppendLine("-------------------------------------"); - stringBuilder.AppendLine(); - foreach (string error in Chainloader.DependencyErrors) - { - stringBuilder.AppendLine(error); - stringBuilder.AppendLine(); - } - string errorMessage = stringBuilder.ToString(); - - // Show an error in the BepInEx console/log file - Logger.LogError(errorMessage); - - ShownDependencyError = true; - } - - public enum DynamicAIRates + public enum EDynamicAIRates { Low, Medium, High } + + [Flags] + public enum EQuestSharingTypes + { + Kill = 1, + Item = 2, + Location = 4, + PlaceBeacon = 8, + + All = Kill | Item | Location | PlaceBeacon + } } } \ No newline at end of file diff --git a/Fika.Core/Models/BotDifficulties.cs b/Fika.Core/Models/BotDifficulties.cs index 24edd6fe..08b8caa9 100644 --- a/Fika.Core/Models/BotDifficulties.cs +++ b/Fika.Core/Models/BotDifficulties.cs @@ -1,6 +1,6 @@ -using Aki.Common.Http; -using EFT; +using EFT; using Newtonsoft.Json; +using SPT.Common.Http; using System.Collections.Generic; namespace Fika.Core.Models @@ -8,12 +8,12 @@ namespace Fika.Core.Models public class BotDifficulties : Dictionary { [JsonIgnore] - private GClass531 CoreSettings; + private CoreBotSettingsClass CoreSettings; public BotDifficulties() { string coreString = RequestHandler.GetJson("/singleplayer/settings/bot/difficulty/core/core"); - CoreSettings = JsonConvert.DeserializeObject(coreString); + CoreSettings = JsonConvert.DeserializeObject(coreString); } public BotSettingsComponents GetComponent(BotDifficulty botDifficulty, WildSpawnType role) @@ -31,7 +31,7 @@ public BotSettingsComponents GetComponent(BotDifficulty botDifficulty, WildSpawn return null; } - public GClass531 GetCoreSettings() + public CoreBotSettingsClass GetCoreSettings() { FikaPlugin.Instance.FikaLogger.LogInfo("Retrieving Core settings"); if (CoreSettings != null) diff --git a/Fika.Core/Models/ClientConfigModel.cs b/Fika.Core/Models/ClientConfigModel.cs index bdaa2e37..90b8c2de 100644 --- a/Fika.Core/Models/ClientConfigModel.cs +++ b/Fika.Core/Models/ClientConfigModel.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System; +using System.Reflection; using System.Runtime.Serialization; namespace Fika.Core.UI.Models @@ -21,26 +22,54 @@ public struct ClientConfigModel [DataMember(Name = "allowItemSending")] public bool AllowItemSending; + [DataMember(Name = "blacklistedItems")] + public string[] BlacklistedItems; + [DataMember(Name = "forceSaveOnDeath")] public bool ForceSaveOnDeath; - public ClientConfigModel(bool useBTR, bool friendlyFire, bool dynamicVExfils, bool allowFreeCam, bool allowItemSending, bool forceSaveOnDeath) + [DataMember(Name = "useInertia")] + public bool UseInertia; + + [DataMember(Name = "sharedQuestProgression")] + public bool SharedQuestProgression; + + public ClientConfigModel(bool useBTR, bool friendlyFire, bool dynamicVExfils, bool allowFreeCam, bool allowItemSending, string[] blacklistedItems, bool forceSaveOnDeath, bool useInertia, + bool sharedQuestProgression) { UseBTR = useBTR; FriendlyFire = friendlyFire; DynamicVExfils = dynamicVExfils; AllowFreeCam = allowFreeCam; AllowItemSending = allowItemSending; + BlacklistedItems = blacklistedItems; ForceSaveOnDeath = forceSaveOnDeath; + UseInertia = useInertia; + SharedQuestProgression = sharedQuestProgression; } - public new void ToString() + public void LogValues() { FikaPlugin.Instance.FikaLogger.LogInfo("Received config from server:"); FieldInfo[] fields = typeof(ClientConfigModel).GetFields(); foreach (FieldInfo field in fields) { object value = field.GetValue(this); + if (value is Array valueArray) + { + string values = ""; + for (int i = 0; i < valueArray.Length; i++) + { + if (i == 0) + { + values = valueArray.GetValue(i).ToString(); + continue; + } + values = values + ", " + valueArray.GetValue(i).ToString(); + } + FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + values); + continue; + } FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + value); } } diff --git a/Fika.Core/Models/NatPunchServerConfigModel.cs b/Fika.Core/Models/NatPunchServerConfigModel.cs new file mode 100644 index 00000000..0a237059 --- /dev/null +++ b/Fika.Core/Models/NatPunchServerConfigModel.cs @@ -0,0 +1,52 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; + +namespace Fika.Core.UI.Models +{ + [DataContract] + public struct NatPunchServerConfigModel + { + [DataMember(Name = "enable")] + public bool Enable; + + [DataMember(Name = "port")] + public int Port; + + [DataMember(Name = "natIntroduceAmount")] + public int NatIntroduceAmount; + + public NatPunchServerConfigModel(bool enable, int port, int natIntroduceAmount) + { + Enable = enable; + Port = port; + NatIntroduceAmount = natIntroduceAmount; + } + + public void LogValues() + { + FikaPlugin.Instance.FikaLogger.LogInfo("Received NatPunchServer config from server:"); + FieldInfo[] fields = typeof(NatPunchServerConfigModel).GetFields(); + foreach (FieldInfo field in fields) + { + object value = field.GetValue(this); + if (value is Array valueArray) + { + string values = ""; + for (int i = 0; i < valueArray.Length; i++) + { + if (i == 0) + { + values = valueArray.GetValue(i).ToString(); + continue; + } + values = values + ", " + valueArray.GetValue(i).ToString(); + } + FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + values); + continue; + } + FikaPlugin.Instance.FikaLogger.LogInfo(field.Name + ": " + value); + } + } + } +} \ No newline at end of file diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 42a1156f..8fb72c10 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -1,21 +1,24 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Custom.Airdrops; using BepInEx.Logging; using Comfort.Common; +using Coop.Airdrops; using EFT; using EFT.AssetsManager; using EFT.Interactive; using EFT.MovingPlatforms; using EFT.UI; using EFT.Weather; +using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.Components; +using Fika.Core.Coop.Custom; using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Modding; using Fika.Core.Modding.Events; -using Fika.Core.Networking.Http; -using Fika.Core.Networking.Http.Models; +using Fika.Core.Networking.Packets; +using Fika.Core.Networking.Packets.Communication; using Fika.Core.Networking.Packets.GameWorld; using Fika.Core.Networking.Packets.Player; using HarmonyLib; @@ -34,9 +37,9 @@ public class FikaClient : MonoBehaviour, INetEventListener { private NetManager _netClient; public NetDataWriter DataWriter = new(); - public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - public Dictionary Players => CoopHandler.Players; - private CoopHandler CoopHandler { get; set; } + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + private CoopHandler coopHandler; public NetPacketProcessor packetProcessor = new(); public int Ping = 0; public int ConnectedClients = 0; @@ -49,18 +52,28 @@ public NetManager NetClient } } public NetPeer ServerConnection { get; private set; } - public string IP { get; private set; } - public int Port { get; private set; } public bool SpawnPointsReceived { get; private set; } = false; - private readonly ManualLogSource clientLogger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); - public bool ClientReady = false; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); + public bool Started + { + get + { + if (_netClient == null) + { + return false; + } + return _netClient.IsRunning; + } + } + private FikaChat fikaChat; - protected void Start() + public void Init() { packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); @@ -79,6 +92,9 @@ protected void Start() packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); _netClient = new NetManager(this) { @@ -91,26 +107,65 @@ protected void Start() EnableStatistics = true }; - _netClient.Start(); + if (FikaBackendUtils.IsHostNatPunch) + { + NetManagerUtils.DestroyPingingClient(); + } - GetHostRequest body = new(CoopHandler.GetServerId()); - GetHostResponse result = FikaRequestHandler.GetHost(body); + _netClient.Start(FikaBackendUtils.LocalPort); - IP = result.Ip; - Port = result.Port; + string ip = FikaBackendUtils.RemoteIp; + int port = FikaBackendUtils.RemotePort; - if (string.IsNullOrEmpty(IP)) + if (string.IsNullOrEmpty(ip)) { Singleton.Instance.ShowErrorScreen("Network Error", "Unable to connect to the raid server. IP and/or Port was empty when requesting data!"); } else { - ServerConnection = _netClient.Connect(IP, Port, "fika.core"); + ServerConnection = _netClient.Connect(ip, port, "fika.core"); }; - Singleton.Create(this); FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); - ClientReady = true; + } + + private void OnQuestItemPacketReceived(QuestItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestItemPacket(ref packet); + } + } + } + + private void OnQuestConditionPacketReceived(QuestConditionPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestPacket(ref packet); + } + } + } + + private void OnTextMessagePacketReceived(TextMessagePacket packet) + { + logger.LogInfo($"Received message from: {packet.Nickname}, Message: {packet.Message}"); + + if (fikaChat != null) + { + fikaChat.ReceiveMessage(packet.Nickname, packet.Message); + } + } + + public void SetupGameVariables(CoopPlayer coopPlayer) + { + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + MyPlayer = coopPlayer; + fikaChat = gameObject.AddComponent(); } private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) @@ -141,7 +196,7 @@ private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) { FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); string allPlayers = ""; - foreach (KeyValuePair kvp in CoopHandler.Players) + foreach (KeyValuePair kvp in coopHandler.Players) { string toAdd = $"Key: {kvp.Key}, Nickname: {kvp.Value.Profile.Nickname}, NetId: {kvp.Value.NetId}"; allPlayers = string.Join(", ", allPlayers + toAdd); @@ -155,7 +210,7 @@ private void OnAssignNetIdPacketReceived(AssignNetIdPacket packet) FikaPlugin.Instance.FikaLogger.LogInfo($"OnAssignNetIdPacketReceived: Assigned NetId {packet.NetId} to my own client."); MyPlayer.NetId = packet.NetId; int i = -1; - foreach (var player in Players) + foreach (KeyValuePair player in Players) { if (player.Value == MyPlayer) { @@ -179,7 +234,7 @@ private void OnSendCharacterPacketReceived(SendCharacterPacket packet) { if (packet.PlayerInfo.Profile.ProfileId != MyPlayer.ProfileId) { - CoopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); } } @@ -199,7 +254,7 @@ private void OnBorderZonePacketReceived(BorderZonePacket packet) { if (player.ProfileId == packet.ProfileId) { - GInterface94 playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); + IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); borderZone.ProcessIncomingPacket(playerBridge, true); } } @@ -213,7 +268,7 @@ private void OnMinePacketReceived(MinePacket packet) { if (Singleton.Instance.MineManager != null) { - NetworkGame.Class1381 mineSeeker = new() + NetworkGame.Class1407 mineSeeker = new() { minePosition = packet.MinePositon }; @@ -236,9 +291,9 @@ private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) private void OnBTRPacketReceived(BTRPacket packet) { - if (CoopHandler.clientBTR != null) + if (coopHandler.clientBTR != null) { - CoopHandler.clientBTR.btrPackets.Enqueue(packet); + coopHandler.clientBTR.btrPackets.Enqueue(packet); } } @@ -248,11 +303,11 @@ private void OnWeatherPacketReceived(WeatherPacket packet) { if (WeatherController.Instance != null) { - WeatherController.Instance.method_4(packet.WeatherClasses); + WeatherController.Instance.method_0(packet.WeatherClasses); } else { - clientLogger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); + logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); } } } @@ -268,7 +323,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) if (exfilController.ExfiltrationPoints == null) return; - CoopGame coopGame = (CoopGame)Singleton.Instance; + CoopGame coopGame = coopHandler.LocalGameInstance; CarExtraction carExtraction = FindObjectOfType(); @@ -299,7 +354,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); + logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); } } @@ -327,7 +382,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); + logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); } } @@ -339,7 +394,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); } } } @@ -350,13 +405,13 @@ private void OnGenericPacketReceived(GenericPacket packet) { case EPackageType.ClientExtract: { - if (CoopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - CoopHandler.Players.Remove(packet.NetId); - if (!CoopHandler.ExtractedPlayers.Contains(packet.NetId)) + coopHandler.Players.Remove(packet.NetId); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) { - CoopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = (CoopGame)CoopHandler.LocalGameInstance; + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; coopGame.ExtractedPlayers.Add(packet.NetId); coopGame.ClearHostAI(playerToApply); @@ -391,7 +446,7 @@ private void OnGenericPacketReceived(GenericPacket packet) } else { - clientLogger.LogWarning("GenericPacketReceived: Could not find locomotive!"); + logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); } } break; @@ -404,7 +459,7 @@ private void OnGenericPacketReceived(GenericPacket packet) ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); if (exfilPoint != null) { - CoopGame game = (CoopGame)Singleton.Instance; + CoopGame game = coopHandler.LocalGameInstance; exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; if (exfilPoint.Status != EExfiltrationStatus.Countdown) @@ -417,35 +472,35 @@ private void OnGenericPacketReceived(GenericPacket packet) break; case EPackageType.TraderServiceNotification: { - if (CoopHandler.clientBTR) + if (coopHandler.clientBTR) { - CoopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); + coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); } } break; case EPackageType.DisposeBot: { - if (CoopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) { if (!botToDispose.gameObject.activeSelf) { botToDispose.gameObject.SetActive(true); } - if (CoopHandler.Players.Remove(packet.BotNetId)) + if (coopHandler.Players.Remove(packet.BotNetId)) { botToDispose.Dispose(); AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); - clientLogger.LogInfo("Disposing bot: " + packet.BotNetId); + logger.LogInfo("Disposing bot: " + packet.BotNetId); } else { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); } } else { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); } } break; @@ -459,36 +514,36 @@ private void OnGenericPacketReceived(GenericPacket packet) break; case EPackageType.EnableBot: { - if (CoopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) { if (!botToEnable.gameObject.activeSelf) { #if DEBUG - clientLogger.LogWarning("Enabling " + packet.BotNetId); + logger.LogWarning("Enabling " + packet.BotNetId); #endif botToEnable.gameObject.SetActive(true); } else { - clientLogger.LogWarning($"Received packet to enable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already enabled!"); + logger.LogWarning($"Received packet to enable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already enabled!"); } } } break; case EPackageType.DisableBot: { - if (CoopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) { if (botToEnable.gameObject.activeSelf) { #if DEBUG - clientLogger.LogWarning("Disabling " + packet.BotNetId); + logger.LogWarning("Disabling " + packet.BotNetId); #endif botToEnable.gameObject.SetActive(false); } else { - clientLogger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); + logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); } } } @@ -500,7 +555,7 @@ private void OnHealthSyncPacketReceived(HealthSyncPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.HealthSyncPackets?.Enqueue(packet); + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); } } @@ -512,7 +567,7 @@ private void OnAirdropLootPacketReceived(AirdropLootPacket packet) } else { - clientLogger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); + logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); } } @@ -539,7 +594,7 @@ private void OnAirdropPacketReceived(AirdropPacket packet) } else { - clientLogger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); + logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); } } @@ -550,29 +605,21 @@ private void OnInformationPacketReceived(InformationPacket packet) ConnectedClients = packet.NumberOfPlayers; ReadyClients = packet.ReadyPlayers; } - if (packet.ForceStart) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - coopGame.forceStart = true; - } - } } private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) { if (!packet.IsRequest) { - clientLogger.LogInfo($"Received CharacterRequest! ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); + logger.LogInfo($"Received CharacterRequest! ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); if (packet.ProfileId != MyPlayer.ProfileId) { - CoopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.NetId, packet.IsAlive, packet.IsAI); + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.NetId, packet.IsAlive, packet.IsAI); } } else if (packet.IsRequest) { - clientLogger.LogInfo($"Received CharacterRequest from server, send my Profile."); + logger.LogInfo($"Received CharacterRequest from server, send my Profile."); AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) { IsRequest = false, @@ -594,7 +641,7 @@ private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.CommonPlayerPackets?.Enqueue(packet); + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); } } @@ -602,7 +649,7 @@ private void OnInventoryPacketReceived(InventoryPacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.InventoryPackets?.Enqueue(packet); + playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); } } @@ -610,44 +657,45 @@ private void OnDamagePacketReceived(DamagePacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.DamagePackets?.Enqueue(packet); + playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); } } - private void OnFirearmPacketReceived(WeaponPacket packet) + private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.FirearmPackets?.Enqueue(packet); + playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); } } - private void OnGameTimerPacketReceived(GameTimerPacket packet) + private void OnFirearmPacketReceived(WeaponPacket packet) { - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler == null) + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - return; + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); } + } + private void OnGameTimerPacketReceived(GameTimerPacket packet) + { TimeSpan sessionTime = new(packet.Tick); - if (coopHandler.LocalGameInstance is CoopGame coopGame) + CoopGame coopGame = coopHandler.LocalGameInstance; + + 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); } } @@ -659,24 +707,25 @@ private void OnPlayerStatePacketReceived(PlayerStatePacket packet) } } - protected void Awake() - { - CoopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - } - protected void Update() { - _netClient.PollEvents(); + _netClient?.PollEvents(); - if (_netClient.FirstPeer == null) + /*if (_netClient.FirstPeer == null) { _netClient.SendBroadcast([1], Port); - } + }*/ } protected void OnDestroy() { _netClient?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); } @@ -694,7 +743,7 @@ public void OnPeerConnected(NetPeer peer) public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { - clientLogger.LogInfo("[CLIENT] We received error " + socketErrorCode); + logger.LogInfo("[CLIENT] We received error " + socketErrorCode); } public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) @@ -706,7 +755,7 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead { if (messageType == UnconnectedMessageType.BasicMessage && _netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) { - clientLogger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); + logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); _netClient.Connect(remoteEndPoint, "fika.core"); } } @@ -725,7 +774,7 @@ public void OnConnectionRequest(ConnectionRequest request) public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { - clientLogger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); + logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); if (disconnectInfo.Reason is DisconnectReason.Timeout) { NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); diff --git a/Fika.Core/Networking/FikaPingingClient.cs b/Fika.Core/Networking/FikaPingingClient.cs index ef77fa1a..fdeae689 100644 --- a/Fika.Core/Networking/FikaPingingClient.cs +++ b/Fika.Core/Networking/FikaPingingClient.cs @@ -1,64 +1,123 @@ using BepInEx.Logging; +using Fika.Core.Coop.Utils; using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; using LiteNetLib; using LiteNetLib.Utils; +using System.Collections; using System.Net; using System.Net.Sockets; +using System.Threading.Tasks; +using UnityEngine; namespace Fika.Core.Networking { - internal class FikaPingingClient(string serverId) : INetEventListener + public class FikaPingingClient : MonoBehaviour, INetEventListener, INatPunchListener { public NetManager NetClient; - private readonly ManualLogSource _logger = Logger.CreateLogSource("Fika.PingingClient"); - private readonly string serverId = serverId; + private readonly ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("Fika.PingingClient"); private IPEndPoint remoteEndPoint; + private IPEndPoint localEndPoint; public bool Received = false; + private Coroutine _keepAliveRoutine; - public bool Init() + public bool Init(string serverId) { NetClient = new(this) { - UnconnectedMessagesEnabled = true + UnconnectedMessagesEnabled = true, + NatPunchEnabled = true }; GetHostRequest body = new(serverId); GetHostResponse result = FikaRequestHandler.GetHost(body); - string ip = result.Ip; - int port = result.Port; + FikaBackendUtils.IsHostNatPunch = result.NatPunch; - if (string.IsNullOrEmpty(ip)) - { - _logger.LogError("IP was empty when pinging!"); - return false; - } + NetClient.Start(); - if (port == default) + if (FikaBackendUtils.IsHostNatPunch) { - _logger.LogError("Port was empty when pinging!"); - return false; + NetClient.NatPunchModule.Init(this); + + string natPunchServerIP = FikaPlugin.Instance.NatPunchServerIP; + int natPunchServerPort = FikaPlugin.Instance.NatPunchServerPort; + string token = $"client:{serverId}"; + + NetClient.NatPunchModule.SendNatIntroduceRequest(natPunchServerIP, natPunchServerPort, token); + + _logger.LogInfo($"SendNatIntroduceRequest: {natPunchServerIP}:{natPunchServerPort}"); } + else + { + string ip = result.Ips[0]; + string localIp = null; + if (result.Ips.Length > 1) + { + localIp = result.Ips[1]; + } + int port = result.Port; - remoteEndPoint = new(IPAddress.Parse(ip), port); + if (string.IsNullOrEmpty(ip)) + { + _logger.LogError("IP was empty when pinging!"); + return false; + } - NetClient.Start(); + if (port == default) + { + _logger.LogError("Port was empty when pinging!"); + return false; + } + + remoteEndPoint = new(IPAddress.Parse(ip), port); + if (!string.IsNullOrEmpty(localIp)) + { + localEndPoint = new(IPAddress.Parse(localIp), port); + } + } return true; } - public bool PingEndPoint() + public void PingEndPoint(string message) { - if (Received) + NetDataWriter writer = new(); + writer.Put(message); + + if (remoteEndPoint != null) + { + NetClient.SendUnconnectedMessage(writer, remoteEndPoint); + } + if (localEndPoint != null) { - return true; + NetClient.SendUnconnectedMessage(writer, localEndPoint); } + } - NetDataWriter writer = new(); - writer.Put("fika.hello"); + public void StartKeepAliveRoutine() + { + _keepAliveRoutine = StartCoroutine(KeepAlive()); + } + + public void StopKeepAliveRoutine() + { + if (_keepAliveRoutine != null) + { + StopCoroutine(_keepAliveRoutine); + } + } + + public IEnumerator KeepAlive() + { + while (true) + { + PingEndPoint("fika.keepalive"); + NetClient.PollEvents(); + NetClient.NatPunchModule.PollEvents(); - return NetClient.SendUnconnectedMessage(writer, remoteEndPoint); + yield return new WaitForSeconds(1.0f); + } } public void OnConnectionRequest(ConnectionRequest request) @@ -83,21 +142,22 @@ public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelN public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { - if (Received) - { - return; - } - _logger.LogInfo("Received response from server, parsing..."); - if (reader.TryGetString(out string result)) { - if (result == "fika.hello") - { - Received = true; - } - else + switch (result) { - _logger.LogError("Data was not as expected"); + case "fika.hello": + Received = true; + FikaBackendUtils.RemoteIp = remoteEndPoint.Address.ToString(); + FikaBackendUtils.RemotePort = remoteEndPoint.Port; + FikaBackendUtils.LocalPort = NetClient.LocalPort; + break; + case "fika.keepalive": + // Do nothing + break; + default: + _logger.LogError("Data was not as expected"); + break; } } else @@ -115,5 +175,36 @@ public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { // Do nothing } + + public void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) + { + // Do nothing + } + + public void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) + { + // Do nothing + } + + public void OnNatIntroductionResponse(IPEndPoint natLocalEndPoint, IPEndPoint natRemoteEndPoint, string token) + { + _logger.LogInfo($"OnNatIntroductionResponse: {remoteEndPoint}"); + + localEndPoint = natLocalEndPoint; + remoteEndPoint = natRemoteEndPoint; + + Task.Run(async () => + { + NetDataWriter data = new(); + data.Put("fika.hello"); + + for (int i = 0; i < 20; i++) + { + NetClient.SendUnconnectedMessage(data, localEndPoint); + NetClient.SendUnconnectedMessage(data, remoteEndPoint); + await Task.Delay(250); + } + }); + } } } diff --git a/Fika.Core/Networking/FikaSerialization.cs b/Fika.Core/Networking/FikaSerialization.cs index 846c70dc..505fe044 100644 --- a/Fika.Core/Networking/FikaSerialization.cs +++ b/Fika.Core/Networking/FikaSerialization.cs @@ -7,7 +7,7 @@ using System; using UnityEngine; -// GClass2800 +// GClass2801 namespace Fika.Core.Networking { @@ -17,14 +17,14 @@ public class FikaSerialization /*public class AddressUtils { - public static void SerializeGridItemAddressDescriptor(NetDataWriter writer, GClass1528 gridItemAddressDescriptor) + public static void SerializeGridItemAddressDescriptor(NetDataWriter writer, GridItemAddressDescriptorClass gridItemAddressDescriptor) { SerializeLocationInGrid(writer, gridItemAddressDescriptor.LocationInGrid); } - public static GClass1528 DeserializeGridItemAddressDescriptor(NetDataReader reader) + public static GridItemAddressDescriptorClass DeserializeGridItemAddressDescriptor(NetDataReader reader) { - return new GClass1528() + return new GridItemAddressDescriptorClass() { LocationInGrid = DeserializeLocationInGrid(reader), Container = DeserializeContainerDescriptor(reader) @@ -227,13 +227,13 @@ public static void SerializeInventory(NetDataWriter writer, Inventory inventory) { GClass1489 inventoryDescriptor = new GClass1489() { - Equipment = GClass1524.SerializeItem(inventory.Equipment), - Stash = GClass1524.SerializeItem(inventory.Stash), - QuestRaidItems = GClass1524.SerializeItem(inventory.QuestRaidItems), - QuestStashItems = GClass1524.SerializeItem(inventory.QuestStashItems), - SortingTable = GClass1524.SerializeItem(inventory.SortingTable), - FastAccess = GClass1524.SerializeFastAccess(inventory.FastAccess), - DiscardLimits = GClass1524.SerializeDiscardLimits(inventory.DiscardLimits) + Equipment = GClass1534.SerializeItem(inventory.Equipment), + Stash = GClass1534.SerializeItem(inventory.Stash), + QuestRaidItems = GClass1534.SerializeItem(inventory.QuestRaidItems), + QuestStashItems = GClass1534.SerializeItem(inventory.QuestStashItems), + SortingTable = GClass1534.SerializeItem(inventory.SortingTable), + FastAccess = GClass1534.SerializeFastAccess(inventory.FastAccess), + DiscardLimits = GClass1534.SerializeDiscardLimits(inventory.DiscardLimits) }; GClass1081 polyWriter = new(); polyWriter.WriteEFTInventoryDescriptor(inventoryDescriptor); @@ -244,14 +244,14 @@ public static Inventory DeserializeInventory(byte[] inventoryBytes) { using MemoryStream memoryStream = new(inventoryBytes); BinaryReader polyReader = new(memoryStream); - Inventory inventory = GClass1524.DeserializeInventory(Singleton.Instance, polyReader.ReadEFTInventoryDescriptor()); + Inventory inventory = GClass1534.DeserializeInventory(Singleton.Instance, polyReader.ReadEFTInventoryDescriptor()); return inventory; } }*/ - public struct PlayerInfoPacket() + public struct PlayerInfoPacket(Profile profile) { - public Profile Profile; + public Profile Profile = profile; public static void Serialize(NetDataWriter writer, PlayerInfoPacket packet) { @@ -262,10 +262,7 @@ public static void Serialize(NetDataWriter writer, PlayerInfoPacket packet) public static PlayerInfoPacket Deserialize(NetDataReader reader) { byte[] profileBytes = reader.GetByteArray(); - PlayerInfoPacket packet = new() - { - Profile = SimpleZlib.Decompress(profileBytes, null).ParseJsonTo() - }; + PlayerInfoPacket packet = new(SimpleZlib.Decompress(profileBytes, null).ParseJsonTo()); return packet; } } @@ -273,14 +270,16 @@ public static PlayerInfoPacket Deserialize(NetDataReader reader) public struct LightStatesPacket { public int Amount; - public GStruct163[] LightStates; + public FirearmLightStateStruct[] LightStates; public static LightStatesPacket Deserialize(NetDataReader reader) { - LightStatesPacket packet = new(); - packet.Amount = reader.GetInt(); + LightStatesPacket packet = new() + { + Amount = reader.GetInt() + }; if (packet.Amount > 0) { - packet.LightStates = new GStruct163[packet.Amount]; + packet.LightStates = new FirearmLightStateStruct[packet.Amount]; for (int i = 0; i < packet.Amount; i++) { packet.LightStates[i] = new() @@ -312,14 +311,16 @@ public static void Serialize(NetDataWriter writer, LightStatesPacket packet) public struct HeadLightsPacket { public int Amount; - public GStruct163[] LightStates; + public FirearmLightStateStruct[] LightStates; public static HeadLightsPacket Deserialize(NetDataReader reader) { - HeadLightsPacket packet = new(); - packet.Amount = reader.GetInt(); + HeadLightsPacket packet = new() + { + Amount = reader.GetInt() + }; if (packet.Amount > 0) { - packet.LightStates = new GStruct163[packet.Amount]; + packet.LightStates = new FirearmLightStateStruct[packet.Amount]; for (int i = 0; i < packet.Amount; i++) { packet.LightStates[i] = new() @@ -351,17 +352,17 @@ public static void Serialize(NetDataWriter writer, HeadLightsPacket packet) public struct ScopeStatesPacket { public int Amount; - public GStruct164[] GStruct164; + public FirearmScopeStateStruct[] FirearmScopeStateStruct; public static ScopeStatesPacket Deserialize(NetDataReader reader) { ScopeStatesPacket packet = new(); packet.Amount = reader.GetInt(); if (packet.Amount > 0) { - packet.GStruct164 = new GStruct164[packet.Amount]; + packet.FirearmScopeStateStruct = new FirearmScopeStateStruct[packet.Amount]; for (int i = 0; i < packet.Amount; i++) { - packet.GStruct164[i] = new() + packet.FirearmScopeStateStruct[i] = new() { Id = reader.GetString(), ScopeMode = reader.GetInt(), @@ -380,10 +381,10 @@ public static void Serialize(NetDataWriter writer, ScopeStatesPacket packet) { for (int i = 0; i < packet.Amount; i++) { - writer.Put(packet.GStruct164[i].Id); - writer.Put(packet.GStruct164[i].ScopeMode); - writer.Put(packet.GStruct164[i].ScopeIndexInsideSight); - writer.Put(packet.GStruct164[i].ScopeCalibrationIndex); + writer.Put(packet.FirearmScopeStateStruct[i].Id); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeMode); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeIndexInsideSight); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeCalibrationIndex); } } } @@ -509,7 +510,7 @@ public struct CylinderMagPacket public static CylinderMagPacket Deserialize(NetDataReader reader) { - CylinderMagPacket packet = new CylinderMagPacket(); + CylinderMagPacket packet = new(); packet.Changed = reader.GetBool(); if (packet.Changed) { @@ -634,72 +635,6 @@ public static void Serialize(NetDataWriter writer, GrenadePacket packet) } } - public struct ApplyShotPacket() - { - public EDamageType DamageType; - public float Damage; - public EBodyPart BodyPartType; - public EBodyPartColliderType ColliderType; - public EArmorPlateCollider ArmorPlateCollider; - public float Absorbed; - public Vector3 Direction = Vector3.zero; - public Vector3 Point = Vector3.zero; - public Vector3 HitNormal = Vector3.zero; - public float PenetrationPower = 0f; - public string BlockedBy; - public string DeflectedBy; - public string SourceId; - public string AmmoId; - public int FragmentIndex; - public float ArmorDamage = 0f; - public string ProfileId; - - public static ApplyShotPacket Deserialize(NetDataReader reader) - { - ApplyShotPacket packet = new() - { - DamageType = (EDamageType)reader.GetInt(), - Damage = reader.GetFloat(), - BodyPartType = (EBodyPart)reader.GetInt(), - ColliderType = (EBodyPartColliderType)reader.GetInt(), - ArmorPlateCollider = (EArmorPlateCollider)reader.GetInt(), - Absorbed = reader.GetFloat(), - Direction = reader.GetVector3(), - Point = reader.GetVector3(), - HitNormal = reader.GetVector3(), - PenetrationPower = reader.GetFloat(), - BlockedBy = reader.GetString(), - DeflectedBy = reader.GetString(), - SourceId = reader.GetString(), - AmmoId = reader.GetString(), - FragmentIndex = reader.GetInt(), - ArmorDamage = reader.GetFloat(), - ProfileId = reader.GetString() - }; - return packet; - } - public static void Serialize(NetDataWriter writer, ApplyShotPacket packet) - { - writer.Put((int)packet.DamageType); - writer.Put(packet.Damage); - writer.Put((int)packet.BodyPartType); - writer.Put((int)packet.ColliderType); - writer.Put((int)packet.ArmorPlateCollider); - writer.Put(packet.Absorbed); - writer.Put(packet.Direction); - writer.Put(packet.Point); - writer.Put(packet.HitNormal); - writer.Put(packet.PenetrationPower); - writer.Put(packet.BlockedBy); - writer.Put(packet.DeflectedBy); - writer.Put(packet.SourceId); - writer.Put(packet.AmmoId); - writer.Put(packet.FragmentIndex); - writer.Put(packet.ArmorDamage); - writer.Put(packet.ProfileId); - } - } - public struct ItemControllerExecutePacket { public uint CallbackId; @@ -892,7 +827,6 @@ public static void Serialize(NetDataWriter writer, KnifePacket packet) public struct ShotInfoPacket() { - public bool IsPrimaryActive = true; public EShotType ShotType = EShotType.Unknown; public int AmmoAfterShot = 0; public Vector3 ShotPosition = Vector3.zero; @@ -910,7 +844,6 @@ public static ShotInfoPacket Deserialize(NetDataReader reader) { ShotInfoPacket packet = new() { - IsPrimaryActive = reader.GetBool(), ShotType = (EShotType)reader.GetInt(), AmmoAfterShot = reader.GetInt(), ShotPosition = reader.GetVector3(), @@ -929,7 +862,6 @@ public static ShotInfoPacket Deserialize(NetDataReader reader) } public static void Serialize(NetDataWriter writer, ShotInfoPacket packet) { - writer.Put(packet.IsPrimaryActive); writer.Put((int)packet.ShotType); writer.Put(packet.AmmoAfterShot); writer.Put(packet.ShotPosition); @@ -953,7 +885,7 @@ public struct SearchPacket public static SearchPacket Deserialize(NetDataReader reader) { - SearchPacket packet = new SearchPacket() + SearchPacket packet = new() { IsStop = reader.GetBool(), ItemId = reader.GetString(), @@ -1119,8 +1051,8 @@ public static BTRDataPacket Deserialize(NetDataReader reader) LeftSideState = reader.GetByte(), RouteState = reader.GetByte(), State = reader.GetByte(), - gunsBlockRotation = reader.GetQuaternion(), - turretRotation = reader.GetQuaternion(), + gunsBlockRotation = reader.GetFloat(), + turretRotation = reader.GetFloat(), rotation = reader.GetQuaternion() }; } diff --git a/Fika.Core/Networking/FikaSerializationExtensions.cs b/Fika.Core/Networking/FikaSerializationExtensions.cs index 2a3ce5b6..f14173af 100644 --- a/Fika.Core/Networking/FikaSerializationExtensions.cs +++ b/Fika.Core/Networking/FikaSerializationExtensions.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; using UnityEngine; -using static GClass681; // Physical struct +using static BasePhysicalClass; // Physical struct namespace Fika.Core.Networking { @@ -107,11 +107,11 @@ public static Color GetColor(this NetDataReader reader) } /// - /// Serializes a (Physical) struct + /// Serializes a (Physical) struct /// /// /// - public static void Put(this NetDataWriter writer, GStruct35 physical) + public static void Put(this NetDataWriter writer, GStruct36 physical) { writer.Put(physical.StaminaExhausted); writer.Put(physical.OxygenExhausted); @@ -119,13 +119,13 @@ public static void Put(this NetDataWriter writer, GStruct35 physical) } /// - /// Deserializes a (Physical) struct + /// Deserializes a (Physical) struct /// /// - /// A (Physical) - public static GStruct35 GetPhysical(this NetDataReader reader) + /// A (Physical) + public static GStruct36 GetPhysical(this NetDataReader reader) { - return new GStruct35() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; + return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; } /// @@ -188,7 +188,7 @@ public static void PutAirdropItem(this NetDataWriter writer, Item item) { using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1524.SerializeItem(item)); + binaryWriter.Write(GClass1535.SerializeItem(item)); writer.PutByteArray(memoryStream.ToArray()); } @@ -202,12 +202,12 @@ public static Item GetAirdropItem(this NetDataReader reader) using MemoryStream memoryStream = new(reader.GetByteArray()); using BinaryReader binaryReader = new(memoryStream); - Item item = GClass1524.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); ContainerCollection[] containerCollections = [item as ContainerCollection]; ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() - .Concat(containerCollections.Where(new Func(AirdropSynchronizableObject.Class1802.class1802_0.method_2))) - .SelectMany(new Func>(AirdropSynchronizableObject.Class1802.class1802_0.method_3)) + .Concat(containerCollections.Where(new Func(AirdropSynchronizableObject.Class1832.class1832_0.method_2))) + .SelectMany(new Func>(AirdropSynchronizableObject.Class1832.class1832_0.method_3)) .ToArray(); Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Online, resourceKeys, JobPriority.Immediate, null, default); @@ -223,7 +223,7 @@ public static void PutItem(this NetDataWriter writer, Item item) { using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1524.SerializeItem(item)); + binaryWriter.Write(GClass1535.SerializeItem(item)); writer.PutByteArray(memoryStream.ToArray()); } @@ -237,7 +237,7 @@ public static Item GetItem(this NetDataReader reader) using MemoryStream memoryStream = new(reader.GetByteArray()); using BinaryReader binaryReader = new(memoryStream); - return GClass1524.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); } } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 3064f8fc..7a69e051 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -7,19 +7,24 @@ using EFT.Interactive; using EFT.InventoryLogic; using EFT.UI; +using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.Components; +using Fika.Core.Coop.Custom; using Fika.Core.Coop.GameMode; -using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; +using Fika.Core.Coop.Utils; using Fika.Core.Modding; using Fika.Core.Modding.Events; using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; +using Fika.Core.Networking.Packets; +using Fika.Core.Networking.Packets.Communication; using Fika.Core.Networking.Packets.GameWorld; using Fika.Core.Networking.Packets.Player; using LiteNetLib; using LiteNetLib.Utils; using Open.Nat; +using SPT.Common.Http; using System; using System.Collections.Generic; using System.IO; @@ -33,17 +38,17 @@ namespace Fika.Core.Networking { - public class FikaServer : MonoBehaviour, INetEventListener, INetLogger + public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener { private NetManager _netServer; public NetPacketProcessor packetProcessor = new(); private readonly NetDataWriter _dataWriter = new(); - public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - public Dictionary Players => CoopHandler.Players; + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; public List PlayersMissing = []; public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); private int Port => FikaPlugin.UDPPort.Value; - private CoopHandler CoopHandler { get; set; } + private CoopHandler coopHandler; public int ReadyClients = 0; public NetManager NetServer { @@ -54,21 +59,32 @@ public NetManager NetServer } public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); public bool hasHadPeer = false; - private readonly ManualLogSource serverLogger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); - public bool ServerReady = false; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); private int _currentNetId; + public bool Started + { + get + { + if (_netServer == null) + { + return false; + } + return _netServer.IsRunning; + } + } + private FikaChat fikaChat; + private CancellationTokenSource natIntroduceRoutineCts; - public async void Start() + public async Task Init() { // Start at 1 to avoid having 0 and making us think it's working when it's not _currentNetId = 1; - NetDebug.Logger = this; - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); @@ -82,6 +98,9 @@ public async void Start() packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); _netServer = new NetManager(this) { @@ -92,31 +111,29 @@ public async void Start() IPv6Enabled = false, DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true + EnableStatistics = true, + NatPunchEnabled = true }; - if (FikaPlugin.UseUPnP.Value) + if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) { bool upnpFailed = false; - await Task.Run(async () => + try { - try - { - NatDiscoverer discoverer = new(); - CancellationTokenSource cts = new(10000); - NatDevice device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); - IPAddress extIp = await device.GetExternalIPAsync(); - MyExternalIP = extIp.MapToIPv4().ToString(); + NatDiscoverer discoverer = new(); + CancellationTokenSource cts = new(10000); + NatDevice device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); + IPAddress extIp = await device.GetExternalIPAsync(); + MyExternalIP = extIp.MapToIPv4().ToString(); - await device.CreatePortMapAsync(new Mapping(Protocol.Udp, Port, Port, 300, "Fika UDP")); - } - catch (Exception ex) - { - serverLogger.LogError($"Error when attempting to map UPnP. Make sure the selected port is not already open! Error message: {ex.Message}"); - upnpFailed = true; - } - }); + await device.CreatePortMapAsync(new Mapping(Protocol.Udp, Port, Port, 300, "Fika UDP")); + } + catch (Exception ex) + { + logger.LogError($"Error when attempting to map UPnP. Make sure the selected port is not already open! Error message: {ex.Message}"); + upnpFailed = true; + } if (upnpFailed) { @@ -142,25 +159,114 @@ await Task.Run(async () => } } - if (FikaPlugin.ForceBindIP.Value != "Disabled") + if (FikaPlugin.UseNatPunching.Value) { - _netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); + _netServer.NatPunchModule.Init(this); + _netServer.Start(); + + natIntroduceRoutineCts = new CancellationTokenSource(); + + string natPunchServerIP = FikaPlugin.Instance.NatPunchServerIP; + int natPunchServerPort = FikaPlugin.Instance.NatPunchServerPort; + string token = $"server:{RequestHandler.SessionId}"; + + Task natIntroduceTask = Task.Run(() => NatIntroduceRoutine(natPunchServerIP, natPunchServerPort, token, natIntroduceRoutineCts.Token)); } else { - _netServer.Start(Port); + if (FikaPlugin.ForceBindIP.Value != "Disabled") + { + _netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); + } + else + { + _netServer.Start(Port); + } } - serverLogger.LogInfo("Started Fika Server"); + logger.LogInfo("Started Fika Server"); + NotificationManagerClass.DisplayMessageNotification($"Server started on port {_netServer.LocalPort}.", EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - SetHostRequest body = new(MyExternalIP, Port); + string[] Ips = []; + + foreach (string ip in FikaPlugin.Instance.LocalIPs) + { + if (ip.StartsWith("192.168")) // need to add more cases here later, for now only check normal range... + { + Ips = [MyExternalIP, ip]; + } + } + + if (Ips.Length < 1) + { + Ips = [MyExternalIP, ""]; + NotificationManagerClass.DisplayMessageNotification("Could not find a valid local IP!", + iconType: EFT.Communications.ENotificationIconType.Alert); + } + + SetHostRequest body = new(Ips, Port, FikaPlugin.UseNatPunching.Value); FikaRequestHandler.UpdateSetHost(body); - Singleton.Create(this); FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); - ServerReady = true; + } + + private async void NatIntroduceRoutine(string natPunchServerIP, int natPunchServerPort, string token, CancellationToken ct) + { + logger.LogInfo("NatIntroduceRoutine started."); + + while (!ct.IsCancellationRequested) + { + _netServer.NatPunchModule.SendNatIntroduceRequest(natPunchServerIP, natPunchServerPort, token); + + logger.LogInfo($"SendNatIntroduceRequest: {natPunchServerIP}:{natPunchServerPort}"); + + await Task.Delay(TimeSpan.FromSeconds(15)); + } + + logger.LogInfo("NatIntroduceRoutine ended."); + } + + private void OnQuestItemPacketReceived(QuestItemPacket packet, NetPeer peer) + { + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestItemPacket(ref packet); + } + } + } + + private void OnQuestConditionPacketReceived(QuestConditionPacket packet, NetPeer peer) + { + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestPacket(ref packet); + } + } + } + + private void OnTextMessagePacketReceived(TextMessagePacket packet, NetPeer peer) + { + logger.LogInfo($"Received message from: {packet.Nickname}, Message: {packet.Message}"); + + if (fikaChat != null) + { + fikaChat.ReceiveMessage(packet.Nickname, packet.Message); + } + + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); } public int PopNetId() @@ -171,13 +277,20 @@ public int PopNetId() return netId; } + public void SetupGameVariables(CoopPlayer coopPlayer) + { + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + MyPlayer = coopPlayer; + fikaChat = gameObject.AddComponent(); + } + private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) { int netId = PopNetId(); packet.netId = netId; if (packet.PlayerInfo.Profile.ProfileId != MyPlayer.ProfileId) { - CoopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); } _dataWriter.Reset(); @@ -202,7 +315,7 @@ private void OnMinePacketReceived(MinePacket packet, NetPeer peer) { if (Singleton.Instance.MineManager != null) { - NetworkGame.Class1381 mineSeeker = new() + NetworkGame.Class1407 mineSeeker = new() { minePosition = packet.MinePositon }; @@ -217,21 +330,21 @@ private void OnMinePacketReceived(MinePacket packet, NetPeer peer) private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) { - if (CoopHandler.serverBTR != null) + if (coopHandler.serverBTR != null) { - CoopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); + coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); } } private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) { - if (CoopHandler.serverBTR != null) + if (coopHandler.serverBTR != null) { if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) { - if (CoopHandler.serverBTR.CanPlayerEnter(player)) + if (coopHandler.serverBTR.CanPlayerEnter(player)) { - CoopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); + coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); _dataWriter.Reset(); SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableOrdered); @@ -254,14 +367,14 @@ private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) { if (packet.IsRequest) { - if (MatchmakerAcceptPatches.Nodes != null) + if (FikaBackendUtils.Nodes != null) { WeatherPacket weatherPacket2 = new() { IsRequest = false, HasData = true, - Amount = MatchmakerAcceptPatches.Nodes.Length, - WeatherClasses = MatchmakerAcceptPatches.Nodes + Amount = FikaBackendUtils.Nodes.Length, + WeatherClasses = FikaBackendUtils.Nodes }; _dataWriter.Reset(); SendDataToPeer(peer, _dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); @@ -308,7 +421,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer pee } else { - serverLogger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); } } } @@ -317,13 +430,13 @@ private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) { if (packet.PacketType == EPackageType.ClientExtract) { - if (CoopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - CoopHandler.Players.Remove(packet.NetId); - if (!CoopHandler.ExtractedPlayers.Contains(packet.NetId)) + coopHandler.Players.Remove(packet.NetId); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) { - CoopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = (CoopGame)CoopHandler.LocalGameInstance; + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; coopGame.ExtractedPlayers.Add(packet.NetId); coopGame.ClearHostAI(playerToApply); @@ -348,7 +461,7 @@ private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) } else if (packet.PacketType == EPackageType.LoadBot) { - CoopGame coopGame = (CoopGame)Singleton.Instance; + CoopGame coopGame = coopHandler.LocalGameInstance; coopGame.IncreaseLoadedPlayers(packet.BotNetId); return; @@ -362,7 +475,7 @@ private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); if (exfilPoint != null) { - CoopGame game = (CoopGame)Singleton.Instance; + CoopGame game = coopHandler.LocalGameInstance; exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; if (exfilPoint.Status != EExfiltrationStatus.Countdown) @@ -380,7 +493,7 @@ private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.HealthSyncPackets?.Enqueue(packet); + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); } _dataWriter.Reset(); @@ -391,13 +504,10 @@ private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) { ReadyClients += packet.ReadyPlayers; - CoopGame coopGame = (CoopGame)Singleton.Instance; - InformationPacket respondPackage = new(false) { NumberOfPlayers = _netServer.ConnectedPeersCount, ReadyPlayers = ReadyClients, - ForceStart = coopGame.forceStart }; _dataWriter.Reset(); @@ -408,7 +518,7 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe { if (packet.IsRequest) { - foreach (CoopPlayer player in CoopHandler.Players.Values) + foreach (CoopPlayer player in coopHandler.Players.Values) { if (player.ProfileId == packet.ProfileId) { @@ -436,20 +546,20 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe SendDataToPeer(peer, _dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); } } - if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !CoopHandler.ExtractedPlayers.Contains(packet.NetId)) + if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) { PlayersMissing.Add(packet.ProfileId); - serverLogger.LogInfo($"Requesting missing player from server."); + logger.LogInfo($"Requesting missing player from server."); AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId); _dataWriter.Reset(); SendDataToPeer(peer, _dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); } if (!packet.IsRequest && PlayersMissing.Contains(packet.ProfileId)) { - serverLogger.LogInfo($"Received CharacterRequest from client: ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); + logger.LogInfo($"Received CharacterRequest from client: ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); if (packet.ProfileId != MyPlayer.ProfileId) { - CoopHandler.QueueProfile(packet.PlayerInfo.Profile, new Vector3(packet.Position.x, packet.Position.y + 0.5f, packet.Position.y), packet.NetId, packet.IsAlive); + coopHandler.QueueProfile(packet.PlayerInfo.Profile, new Vector3(packet.Position.x, packet.Position.y + 0.5f, packet.Position.y), packet.NetId, packet.IsAlive); PlayersMissing.Remove(packet.ProfileId); } } @@ -459,7 +569,7 @@ private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer pee { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.CommonPlayerPackets?.Enqueue(packet); + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); } _dataWriter.Reset(); @@ -474,7 +584,7 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) using BinaryReader binaryReader = new(memoryStream); try { - GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); + GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); InventoryOperationHandler opHandler = new() { @@ -492,9 +602,9 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) // 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 (result.Value is UnloadOperationClass unloadOperation) { - if (unloadOperation.InternalOperation is GClass2872 internalSplitOperation) + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) { Item item = internalSplitOperation.To.Item; if (item != null) @@ -516,7 +626,7 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) } // TODO: Same as above. - if (result.Value is GClass2872 splitOperation) + if (result.Value is SplitOperationClass splitOperation) { Item item = splitOperation.To.Item; if (item != null) @@ -561,7 +671,18 @@ private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.DamagePackets?.Enqueue(packet); + playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); + } + + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnArmorDamagePacketReceived(ArmorDamagePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); } _dataWriter.Reset(); @@ -572,7 +693,7 @@ private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) { if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - playerToApply.PacketReceiver?.FirearmPackets?.Enqueue(packet); + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); } _dataWriter.Reset(); @@ -584,7 +705,7 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) if (!packet.IsRequest) return; - CoopGame game = (CoopGame)Singleton.Instance; + CoopGame game = coopHandler.LocalGameInstance; if (game != null) { GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks); @@ -593,7 +714,7 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) } else { - serverLogger.LogError("OnGameTimerPacketReceived: Game was null!"); + logger.LogError("OnGameTimerPacketReceived: Game was null!"); } } @@ -608,21 +729,21 @@ private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); } - protected void Awake() - { - CoopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - } - - void Update() + protected void Update() { - _netServer.PollEvents(); + _netServer?.PollEvents(); + _netServer?.NatPunchModule?.PollEvents(); } - void OnDestroy() + protected void OnDestroy() { - NetDebug.Logger = null; _netServer?.Stop(); + if (fikaChat != null) + { + Destroy(fikaChat); + } + FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); } @@ -653,21 +774,21 @@ 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}."); + logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); hasHadPeer = true; } public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { - serverLogger.LogError("[SERVER] error " + socketErrorCode); + logger.LogError("[SERVER] error " + socketErrorCode); } public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { if (messageType == UnconnectedMessageType.Broadcast) { - serverLogger.LogInfo("[SERVER] Received discovery request. Send discovery response"); + logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); NetDataWriter resp = new(); resp.Put(1); _netServer.SendUnconnectedMessage(resp, remoteEndPoint); @@ -676,21 +797,36 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead { if (reader.TryGetString(out string data)) { - if (data == "fika.hello") - { - NetDataWriter resp = new(); - resp.Put(data); - _netServer.SendUnconnectedMessage(resp, remoteEndPoint); - serverLogger.LogInfo("PingingRequest: Correct ping query, sending response"); - } - else + NetDataWriter resp; + + switch (data) { - serverLogger.LogError("PingingRequest: Data was not as expected"); + case "fika.hello": + resp = new(); + resp.Put(data); + _netServer.SendUnconnectedMessage(resp, remoteEndPoint); + logger.LogInfo("PingingRequest: Correct ping query, sending response"); + break; + + case "fika.keepalive": + resp = new(); + resp.Put(data); + _netServer.SendUnconnectedMessage(resp, remoteEndPoint); + + if (!natIntroduceRoutineCts.IsCancellationRequested) + { + natIntroduceRoutineCts.Cancel(); + } + break; + + default: + logger.LogError("PingingRequest: Data was not as expected"); + break; } } else { - serverLogger.LogError("PingingRequest: Could not parse string"); + logger.LogError("PingingRequest: Could not parse string"); } } } @@ -706,7 +842,7 @@ public void OnConnectionRequest(ConnectionRequest request) public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { - serverLogger.LogInfo("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason); + logger.LogInfo("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason); NotificationManagerClass.DisplayMessageNotification("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason, iconType: EFT.Communications.ENotificationIconType.Alert); if (_netServer.ConnectedPeersCount == 0) { @@ -724,6 +860,34 @@ public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelN packetProcessor.ReadAllPackets(reader, peer); } + public void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) + { + // Do nothing + } + + public void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) + { + // Do nothing + } + + public void OnNatIntroductionResponse(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) + { + logger.LogInfo($"OnNatIntroductionResponse: {remoteEndPoint}"); + + Task.Run(async () => + { + NetDataWriter data = new(); + data.Put("fika.hello"); + + for (int i = 0; i < 20; i++) + { + _netServer.SendUnconnectedMessage(data, localEndPoint); + _netServer.SendUnconnectedMessage(data, remoteEndPoint); + await Task.Delay(250); + } + }); + } + private class InventoryOperationHandler { public GStruct411 opResult; @@ -754,7 +918,7 @@ internal void HandleResult(IResult result) using MemoryStream memoryStream = new(); using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(GClass1632.FromInventoryOperation(opResult.Value, false)); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); byte[] opBytes = memoryStream.ToArray(); packet.ItemControllerExecutePacket = new() { diff --git a/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs b/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs index 88515aa6..01c10bd4 100644 --- a/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs +++ b/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs @@ -1,8 +1,8 @@ /* NOTES - - Using Aki zlib (de-)compression + - Using SPT zlib (de-)compression */ -using Aki.Common.Utils; +using SPT.Common.Utils; using System; using System.IO; using System.Net.Http; diff --git a/Fika.Core/Networking/Http/FikaHttpHandler.cs b/Fika.Core/Networking/Http/FikaHttpHandler.cs index 28abbce5..1dcfdc48 100644 --- a/Fika.Core/Networking/Http/FikaHttpHandler.cs +++ b/Fika.Core/Networking/Http/FikaHttpHandler.cs @@ -1,10 +1,10 @@ -using Aki.Common.Http; using EFT; using Fika.Core.Models; using Fika.Core.Networking.Http.Models; using Fika.Core.UI.Models; using Fuyu.Platform.Common.Http; using Newtonsoft.Json; +using SPT.Common.Http; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; @@ -76,6 +76,11 @@ public static ClientConfigModel GetClientConfig() return GetJson("/fika/client/config"); } + public static NatPunchServerConfigModel GetNatPunchServerConfig() + { + return GetJson("/fika/natpunchserver/config"); + } + public static async Task UpdatePing(PingRequest data) { await PutJsonAsync("/fika/update/ping", data); diff --git a/Fika.Core/Networking/LiteNetLib/NatPunchModule.cs b/Fika.Core/Networking/LiteNetLib/NatPunchModule.cs index 476d9ef6..46a67f0b 100644 --- a/Fika.Core/Networking/LiteNetLib/NatPunchModule.cs +++ b/Fika.Core/Networking/LiteNetLib/NatPunchModule.cs @@ -1,4 +1,5 @@ using LiteNetLib.Utils; +using System; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; @@ -15,15 +16,18 @@ public interface INatPunchListener { void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); + void OnNatIntroductionResponse(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); } public class EventBasedNatPunchListener : INatPunchListener { public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); + public delegate void OnNatIntroductionResponse(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); public event OnNatIntroductionRequest NatIntroductionRequest; public event OnNatIntroductionSuccess NatIntroductionSuccess; + public event OnNatIntroductionResponse NatIntroductionResponse; void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) { @@ -36,6 +40,12 @@ void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAd if (NatIntroductionSuccess != null) NatIntroductionSuccess(targetEndPoint, type, token); } + + void INatPunchListener.OnNatIntroductionResponse(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) + { + if (NatIntroductionResponse != null) + NatIntroductionResponse(localEndPoint, remoteEndPoint, token); + } } /// @@ -57,6 +67,13 @@ struct SuccessEventData public string Token; } + struct ResponseEventData + { + public IPEndPoint LocalEndPoint; + public IPEndPoint RemoteEndPoint; + public string Token; + } + class NatIntroduceRequestPacket { public IPEndPoint Internal { [Preserve] get; [Preserve] set; } @@ -79,6 +96,7 @@ class NatPunchPacket private readonly NetManager _socket; private readonly ConcurrentQueue _requestEvents = new ConcurrentQueue(); private readonly ConcurrentQueue _successEvents = new ConcurrentQueue(); + private readonly ConcurrentQueue _responseEvents = new ConcurrentQueue(); private readonly NetDataReader _cacheReader = new NetDataReader(); private readonly NetDataWriter _cacheWriter = new NetDataWriter(); private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength); @@ -152,7 +170,7 @@ public void PollEvents() if (UnsyncedEvents) return; - if (_natPunchListener == null || (_successEvents.IsEmpty && _requestEvents.IsEmpty)) + if (_natPunchListener == null || (_successEvents.IsEmpty && _requestEvents.IsEmpty && _responseEvents.IsEmpty)) return; while (_successEvents.TryDequeue(out var evt)) @@ -167,6 +185,11 @@ public void PollEvents() { _natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token); } + + while (_responseEvents.TryDequeue(out var evt)) + { + _natPunchListener.OnNatIntroductionResponse(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token); + } } public void SendNatIntroduceRequest(string host, int port, string additionalInfo) @@ -195,6 +218,7 @@ public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string addi //We got request and must introduce private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) { + Console.WriteLine("OnNatIntroductionRequest"); if (UnsyncedEvents) { _natPunchListener.OnNatIntroductionRequest( @@ -232,6 +256,24 @@ private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) punchPacket.IsExternal = true; Send(punchPacket, req.External); NetDebug.Write(NetLogLevel.Trace, $"[NAT] external punch sent to {req.External}"); + + if (UnsyncedEvents) + { + _natPunchListener.OnNatIntroductionResponse( + req.Internal, + req.External, + req.Token + ); + } + else + { + _responseEvents.Enqueue(new ResponseEventData + { + LocalEndPoint = req.Internal, + RemoteEndPoint = req.External, + Token = req.Token + }); + } } //We got punch and can connect diff --git a/Fika.Core/Networking/LiteNetLib/Utils/NetDataReader.cs b/Fika.Core/Networking/LiteNetLib/Utils/NetDataReader.cs index 86487f28..ca410e8e 100644 --- a/Fika.Core/Networking/LiteNetLib/Utils/NetDataReader.cs +++ b/Fika.Core/Networking/LiteNetLib/Utils/NetDataReader.cs @@ -195,6 +195,11 @@ public void Get(out string result, int maxLength) result = GetString(maxLength); } + public void Get(out Guid result) + { + result = GetGuid(); + } + public IPEndPoint GetNetEndPoint() { string host = GetString(1000); @@ -225,6 +230,30 @@ public T[] GetArray(ushort size) return result; } + public T[] GetArray() where T : INetSerializable, new() + { + ushort length = BitConverter.ToUInt16(_data, _position); + _position += 2; + T[] result = new T[length]; + for (int i = 0; i < length; i++) + { + var item = new T(); + item.Deserialize(this); + result[i] = item; + } + return result; + } + + public T[] GetArray(Func constructor) where T : class, INetSerializable + { + ushort length = BitConverter.ToUInt16(_data, _position); + _position += 2; + T[] result = new T[length]; + for (int i = 0; i < length; i++) + Get(out result[i], constructor); + return result; + } + public bool[] GetBoolArray() { return GetArray(1); @@ -370,40 +399,47 @@ public string GetString(int maxLength) { ushort size = GetUShort(); if (size == 0) - { return string.Empty; - } int actualSize = size - 1; - if (actualSize >= NetDataWriter.StringBufferMaxLength) - { - return null; - } - - ArraySegment data = GetBytesSegment(actualSize); - - return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ? + string result = maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position, actualSize) > maxLength ? string.Empty : - NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count); + NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, actualSize); + _position += actualSize; + return result; } public string GetString() { ushort size = GetUShort(); if (size == 0) - { return string.Empty; - } int actualSize = size - 1; - if (actualSize >= NetDataWriter.StringBufferMaxLength) - { - return null; - } + string result = NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, actualSize); + _position += actualSize; + return result; + } - ArraySegment data = GetBytesSegment(actualSize); + public string GetLargeString() + { + int size = GetInt(); + if (size <= 0) + return string.Empty; + string result = NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, size); + _position += size; + return result; + } - return NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count); + public Guid GetGuid() + { +#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + var result = new Guid(_data.AsSpan(_position, 16)); + _position += 16; + return result; +#else + return new Guid(GetBytesWithLength()); +#endif } public ArraySegment GetBytesSegment(int count) @@ -542,16 +578,9 @@ public string PeekString(int maxLength) { ushort size = PeekUShort(); if (size == 0) - { return string.Empty; - } int actualSize = size - 1; - if (actualSize >= NetDataWriter.StringBufferMaxLength) - { - return null; - } - return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, actualSize) > maxLength) ? string.Empty : NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize); @@ -561,16 +590,9 @@ public string PeekString() { ushort size = PeekUShort(); if (size == 0) - { return string.Empty; - } int actualSize = size - 1; - if (actualSize >= NetDataWriter.StringBufferMaxLength) - { - return null; - } - return NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize); } #endregion @@ -767,4 +789,4 @@ public void Clear() _data = null; } } -} +} \ No newline at end of file diff --git a/Fika.Core/Networking/LiteNetLib/Utils/NetDataWriter.cs b/Fika.Core/Networking/LiteNetLib/Utils/NetDataWriter.cs index 11ecd521..9bcd62f1 100644 --- a/Fika.Core/Networking/LiteNetLib/Utils/NetDataWriter.cs +++ b/Fika.Core/Networking/LiteNetLib/Utils/NetDataWriter.cs @@ -30,8 +30,6 @@ public int Length } public static readonly ThreadLocal uTF8Encoding = new ThreadLocal(() => new UTF8Encoding(false, true)); - public const int StringBufferMaxLength = 65535; - private readonly byte[] _stringBuffer = new byte[StringBufferMaxLength]; public NetDataWriter() : this(true, InitialSize) { @@ -216,6 +214,18 @@ public void Put(byte value) _position++; } + public void Put(Guid value) + { +#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + if (_autoResize) + ResizeIfNeed(_position + 16); + value.TryWriteBytes(_data.AsSpan(_position)); + _position += 16; +#else + PutBytesWithLength(value.ToByteArray()); +#endif + } + public void Put(byte[] data, int offset, int length) { if (_autoResize) @@ -338,12 +348,40 @@ public void PutArray(string[] value, int strMaxLength) Put(value[i], strMaxLength); } + public void PutArray(T[] value) where T : INetSerializable, new() + { + ushort strArrayLength = (ushort)(value?.Length ?? 0); + Put(strArrayLength); + for (int i = 0; i < strArrayLength; i++) + value[i].Serialize(this); + } + public void Put(IPEndPoint endPoint) { Put(endPoint.Address.ToString()); Put(endPoint.Port); } + public void PutLargeString(string value) + { + if (string.IsNullOrEmpty(value)) + { + Put(0); + return; + } + int size = uTF8Encoding.Value.GetByteCount(value); + if (size == 0) + { + Put(0); + return; + } + Put(size); + if (_autoResize) + ResizeIfNeed(_position + size); + uTF8Encoding.Value.GetBytes(value, 0, size, _data, _position); + _position += size; + } + public void Put(string value) { Put(value, 0); @@ -361,16 +399,17 @@ public void Put(string value, int maxLength) } int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length; - int size = uTF8Encoding.Value.GetBytes(value, 0, length, _stringBuffer, 0); - - if (size == 0 || size >= StringBufferMaxLength) + int maxSize = uTF8Encoding.Value.GetMaxByteCount(length); + if (_autoResize) + ResizeIfNeed(_position + maxSize + sizeof(ushort)); + int size = uTF8Encoding.Value.GetBytes(value, 0, length, _data, _position + sizeof(ushort)); + if (size == 0) { Put((ushort)0); return; } - Put(checked((ushort)(size + 1))); - Put(_stringBuffer, 0, size); + _position += size; } public void Put(T obj) where T : INetSerializable @@ -378,4 +417,4 @@ public void Put(T obj) where T : INetSerializable obj.Serialize(this); } } -} +} \ No newline at end of file diff --git a/Fika.Core/Networking/LiteNetLib/Utils/NetPacketProcessor.cs b/Fika.Core/Networking/LiteNetLib/Utils/NetPacketProcessor.cs index 36041d80..060f6328 100644 --- a/Fika.Core/Networking/LiteNetLib/Utils/NetPacketProcessor.cs +++ b/Fika.Core/Networking/LiteNetLib/Utils/NetPacketProcessor.cs @@ -265,7 +265,7 @@ public void SubscribeNetSerializable( } catch { - FikaPlugin.Instance.FikaLogger.LogError("Error when deserializing " + nameof(reference) + " packet."); + FikaPlugin.Instance.FikaLogger.LogError("Error when deserializing " + reference.GetType().Name + " packet."); } onReceive(reference, (TUserData)userData); }; @@ -283,7 +283,7 @@ public void SubscribeNetSerializable( } catch { - FikaPlugin.Instance.FikaLogger.LogError("Error when deserializing " + nameof(reference) + " packet."); + FikaPlugin.Instance.FikaLogger.LogError("Error when deserializing " + reference.GetType().Name + " packet."); } onReceive(reference); }; diff --git a/Fika.Core/Networking/LiteNetLib/Utils/NetSerializer.cs b/Fika.Core/Networking/LiteNetLib/Utils/NetSerializer.cs index 975b2b5f..e6494c67 100644 --- a/Fika.Core/Networking/LiteNetLib/Utils/NetSerializer.cs +++ b/Fika.Core/Networking/LiteNetLib/Utils/NetSerializer.cs @@ -422,6 +422,12 @@ private class IPEndPointSerializer : FastCallSpecificAuto protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); } } + private class GuidSerializer : FastCallSpecificAuto + { + protected override void ElementWrite(NetDataWriter w, ref Guid guid) { w.Put(guid); } + protected override void ElementRead(NetDataReader r, out Guid guid) { guid = r.GetGuid(); } + } + private class StringSerializer : FastCallSpecific { private readonly int _maxLength; @@ -644,6 +650,8 @@ private ClassInfo RegisterInternal< serialzer = new CharSerializer(); else if (elementType == typeof(IPEndPoint)) serialzer = new IPEndPointSerializer(); + else if (elementType == typeof(Guid)) + serialzer = new GuidSerializer(); else { _registeredTypes.TryGetValue(elementType, out var customType); @@ -758,4 +766,4 @@ public byte[] Serialize< return _writer.CopyData(); } } -} +} \ No newline at end of file diff --git a/Fika.Core/Networking/Models/CreateMatchRequest.cs b/Fika.Core/Networking/Models/CreateMatchRequest.cs index 0ffb2d7d..1c3cde97 100644 --- a/Fika.Core/Networking/Models/CreateMatchRequest.cs +++ b/Fika.Core/Networking/Models/CreateMatchRequest.cs @@ -9,6 +9,9 @@ namespace Fika.Core.Networking.Http.Models [DataContract] public struct CreateMatch { + [DataMember(Name = "raidCode")] + public string RaidCode; + [DataMember(Name = "serverId")] public string ServerId; @@ -36,8 +39,9 @@ public struct CreateMatch [DataMember(Name = "time")] public EDateTime Time; - public CreateMatch(string serverId, string hostUsername, long timestamp, RaidSettings settings, int expectedNumberOfPlayers, ESideType side, EDateTime time) + public CreateMatch(string raidCode, string serverId, string hostUsername, long timestamp, RaidSettings settings, int expectedNumberOfPlayers, ESideType side, EDateTime time) { + RaidCode = raidCode; ServerId = serverId; HostUsername = hostUsername; Timestamp = timestamp; diff --git a/Fika.Core/Networking/Models/GetHostResponse.cs b/Fika.Core/Networking/Models/GetHostResponse.cs index 11f82482..929c0068 100644 --- a/Fika.Core/Networking/Models/GetHostResponse.cs +++ b/Fika.Core/Networking/Models/GetHostResponse.cs @@ -5,16 +5,20 @@ namespace Fika.Core.Networking.Http.Models [DataContract] public struct GetHostResponse { - [DataMember(Name = "ip")] - public string Ip; + [DataMember(Name = "ips")] + public string[] Ips; [DataMember(Name = "port")] public int Port; - public GetHostResponse(string ip, int port) + [DataMember(Name = "natPunch")] + public bool NatPunch; + + public GetHostResponse(string[] ips, int port, bool natPunch) { - Ip = ip; + Ips = ips; Port = port; + NatPunch = natPunch; } } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/GetHostStunRequest.cs b/Fika.Core/Networking/Models/GetHostStunRequest.cs new file mode 100644 index 00000000..fe6c621e --- /dev/null +++ b/Fika.Core/Networking/Models/GetHostStunRequest.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Fika.Core.Networking.Http.Models +{ + [DataContract] + public struct GetHostStunRequest + { + [DataMember(Name = "requestType")] + public string RequestType; + + [DataMember(Name = "sessionId")] + public string SessionId; + + [DataMember(Name = "serverId")] + public string ServerId; + + [DataMember(Name = "stunIp")] + public string StunIp; + + [DataMember(Name = "stunPort")] + public int StunPort; + + public GetHostStunRequest(string serverId, string sessionId, string stunIp, int stunPort) + { + RequestType = GetType().Name; + SessionId = sessionId; + ServerId = serverId; + StunIp = stunIp; + StunPort = stunPort; + } + } +} \ No newline at end of file diff --git a/Fika.Core/Networking/Models/GetHostStunResponse.cs b/Fika.Core/Networking/Models/GetHostStunResponse.cs new file mode 100644 index 00000000..da74a9fb --- /dev/null +++ b/Fika.Core/Networking/Models/GetHostStunResponse.cs @@ -0,0 +1,25 @@ +using System.Runtime.Serialization; + +[DataContract] +public struct GetHostStunResponse +{ + [DataMember(Name = "requestType")] + public string RequestType; + + [DataMember(Name = "sessionId")] + public string SessionId; + + [DataMember(Name = "StunIp")] + public string StunIp; + + [DataMember(Name = "StunPort")] + public int StunPort; + + public GetHostStunResponse(string sessionId, string stunIp, int stunPort) + { + RequestType = GetType().Name; + SessionId = sessionId; + StunIp = stunIp; + StunPort = stunPort; + } +} \ No newline at end of file diff --git a/Fika.Core/Networking/Models/ModValidationResponse.cs b/Fika.Core/Networking/Models/ModValidationResponse.cs new file mode 100644 index 00000000..f5fba64c --- /dev/null +++ b/Fika.Core/Networking/Models/ModValidationResponse.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace Fika.Core.Networking.Http.Models +{ + [DataContract] + public struct ModValidationResponse + { + [DataMember(Name = "forbidden")] + public string[] Forbidden; + + [DataMember(Name = "missingRequired")] + public string[] MissingRequired; + + [DataMember(Name = "hashMismatch")] + public string[] HashMismatch; + } +} \ No newline at end of file diff --git a/Fika.Core/Networking/Models/PingRequest.cs b/Fika.Core/Networking/Models/PingRequest.cs index 1f858f19..45add8ba 100644 --- a/Fika.Core/Networking/Models/PingRequest.cs +++ b/Fika.Core/Networking/Models/PingRequest.cs @@ -1,4 +1,4 @@ -using Fika.Core.Coop.Components; +using Fika.Core.Coop.Utils; using System.Runtime.Serialization; namespace Fika.Core.Networking.Http.Models @@ -11,7 +11,7 @@ public struct PingRequest public PingRequest() { - ServerId = CoopHandler.GetServerId(); + ServerId = FikaBackendUtils.GetGroupId(); } } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/SetHostRequest.cs b/Fika.Core/Networking/Models/SetHostRequest.cs index aa539a09..df68b130 100644 --- a/Fika.Core/Networking/Models/SetHostRequest.cs +++ b/Fika.Core/Networking/Models/SetHostRequest.cs @@ -9,17 +9,21 @@ public struct SetHostRequest [DataMember(Name = "serverId")] public string ServerId; - [DataMember(Name = "ip")] - public string Ip; + [DataMember(Name = "ips")] + public string[] Ips; [DataMember(Name = "port")] public int Port; - public SetHostRequest(string ip, int port) + [DataMember(Name = "natPunch")] + public bool NatPunch; + + public SetHostRequest(string[] ips, int port, bool natPunch) { ServerId = CoopHandler.GetServerId(); - Ip = ip; + Ips = ips; Port = port; + NatPunch = natPunch; } } } \ No newline at end of file diff --git a/Fika.Core/Networking/Open.Nat/Utils/Extensions.cs b/Fika.Core/Networking/Open.Nat/Utils/Extensions.cs index 032f957c..b16acbbe 100644 --- a/Fika.Core/Networking/Open.Nat/Utils/Extensions.cs +++ b/Fika.Core/Networking/Open.Nat/Utils/Extensions.cs @@ -149,9 +149,11 @@ public static Task TimeoutAfter(this Task task, TimeS public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { #if DEBUG - return await task; + return await task; #endif +#pragma warning disable CS0162 // Unreachable code detected var timeoutCancellationTokenSource = new CancellationTokenSource(); +#pragma warning restore CS0162 // Unreachable code detected Task completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)); if (completedTask == task) diff --git a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs index f60fafae..3868c8a7 100644 --- a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs +++ b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs @@ -9,7 +9,6 @@ public struct InformationPacket(bool isRequest) : INetSerializable public bool IsRequest = isRequest; public int NumberOfPlayers = 0; public int ReadyPlayers = 0; - public bool ForceStart = false; public void Deserialize(NetDataReader reader) @@ -17,7 +16,6 @@ public void Deserialize(NetDataReader reader) IsRequest = reader.GetBool(); NumberOfPlayers = reader.GetInt(); ReadyPlayers = reader.GetInt(); - ForceStart = reader.GetBool(); } public void Serialize(NetDataWriter writer) @@ -25,7 +23,6 @@ public void Serialize(NetDataWriter writer) writer.Put(IsRequest); writer.Put(NumberOfPlayers); writer.Put(ReadyPlayers); - writer.Put(ForceStart); } } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs new file mode 100644 index 00000000..9bfc9e12 --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs @@ -0,0 +1,25 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets +{ + public struct QuestConditionPacket(string nickname, string id, string sourceId) : INetSerializable + { + public string Nickname = nickname; + public string Id = id; + public string SourceId = sourceId; + + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + Id = reader.GetString(); + SourceId = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(Id); + writer.Put(SourceId); + } + } +} diff --git a/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs new file mode 100644 index 00000000..cc6f618c --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets +{ + public struct QuestItemPacket(string nickname, string itemId) : INetSerializable + { + public string Nickname = nickname; + public string ItemId = itemId; + + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + ItemId = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(ItemId); + } + } +} diff --git a/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs new file mode 100644 index 00000000..5eeb787d --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets.Communication +{ + public struct TextMessagePacket(string nickname, string message) : INetSerializable + { + public string Nickname = nickname; + public string Message = message; + + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + Message = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(Message); + } + } +} diff --git a/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs b/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs index 268ed6cd..c47befcd 100644 --- a/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs @@ -1,7 +1,7 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Custom.Airdrops.Models; using ComponentAce.Compression.Libs.zlib; +using Fika.Core.Coop.Airdrops.Models; using LiteNetLib.Utils; using UnityEngine; diff --git a/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs b/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs new file mode 100644 index 00000000..3e7de4ff --- /dev/null +++ b/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs @@ -0,0 +1,25 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking +{ + public struct ArmorDamagePacket(int netId) : INetSerializable + { + public int NetId = netId; + public string[] ItemIds; + public float[] Durabilities; + + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + ItemIds = reader.GetStringArray(); + Durabilities = reader.GetFloatArray(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.PutArray(ItemIds); + writer.PutArray(Durabilities); + } + } +} diff --git a/Fika.Core/Networking/Packets/Player/DamagePacket.cs b/Fika.Core/Networking/Packets/Player/DamagePacket.cs index f1dcc5f2..91e011b4 100644 --- a/Fika.Core/Networking/Packets/Player/DamagePacket.cs +++ b/Fika.Core/Networking/Packets/Player/DamagePacket.cs @@ -1,23 +1,76 @@ -using LiteNetLib.Utils; -using static Fika.Core.Networking.FikaSerialization; +using EFT; +using EFT.Ballistics; +using LiteNetLib.Utils; +using UnityEngine; namespace Fika.Core.Networking { public struct DamagePacket(int netId) : INetSerializable { public int NetId = netId; - public ApplyShotPacket DamageInfo; + public EDamageType DamageType; + public float Damage; + public EBodyPart BodyPartType; + public EBodyPartColliderType ColliderType; + public EArmorPlateCollider ArmorPlateCollider; + public float Absorbed; + public Vector3 Direction = Vector3.zero; + public Vector3 Point = Vector3.zero; + public Vector3 HitNormal = Vector3.zero; + public float PenetrationPower = 0f; + public string BlockedBy; + public string DeflectedBy; + public string SourceId; + public string AmmoId; + public int FragmentIndex; + public float ArmorDamage = 0f; + public string ProfileId; + public MaterialType Material = 0; public void Deserialize(NetDataReader reader) { NetId = reader.GetInt(); - DamageInfo = ApplyShotPacket.Deserialize(reader); + DamageType = (EDamageType)reader.GetInt(); + Damage = reader.GetFloat(); + BodyPartType = (EBodyPart)reader.GetInt(); + ColliderType = (EBodyPartColliderType)reader.GetInt(); + ArmorPlateCollider = (EArmorPlateCollider)reader.GetInt(); + Absorbed = reader.GetFloat(); + Direction = reader.GetVector3(); + Point = reader.GetVector3(); + HitNormal = reader.GetVector3(); + PenetrationPower = reader.GetFloat(); + BlockedBy = reader.GetString(); + DeflectedBy = reader.GetString(); + SourceId = reader.GetString(); + AmmoId = reader.GetString(); + FragmentIndex = reader.GetInt(); + ArmorDamage = reader.GetFloat(); + ProfileId = reader.GetString(); + Material = (MaterialType)reader.GetInt(); } public void Serialize(NetDataWriter writer) { writer.Put(NetId); - ApplyShotPacket.Serialize(writer, DamageInfo); + writer.Put((int)DamageType); + writer.Put(Damage); + writer.Put((int)BodyPartType); + writer.Put((int)ColliderType); + writer.Put((int)ArmorPlateCollider); + writer.Put(Absorbed); + writer.Put(Direction); + writer.Put(Point); + writer.Put(HitNormal); + writer.Put(PenetrationPower); + writer.Put(BlockedBy); + writer.Put(DeflectedBy); + writer.Put(SourceId); + writer.Put(AmmoId); + writer.Put(FragmentIndex); + writer.Put(ArmorDamage); + writer.Put(ProfileId); + writer.Put((int)Material); } } } diff --git a/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs b/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs index 9f2d12c2..333afcc7 100644 --- a/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs +++ b/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs @@ -9,7 +9,6 @@ public struct HealthSyncPacket(int netId) : INetSerializable public int NetId = netId; public GStruct346 Packet; public string KillerId; - public string KillerWeaponId; public RagdollPacket RagdollPacket; public EquipmentClass Equipment; public string[] TriggerZones; @@ -136,7 +135,6 @@ public void Deserialize(NetDataReader reader) { packet.Data.IsAlive.DamageType = (EDamageType)reader.GetInt(); KillerId = reader.GetString(); - KillerWeaponId = reader.GetString(); RagdollPacket = RagdollPacket.Deserialize(reader); Equipment = (EquipmentClass)reader.GetItem(); TriggerZones = reader.GetStringArray(); @@ -348,7 +346,6 @@ public void Serialize(NetDataWriter writer) { writer.Put((int)packet.IsAlive.DamageType); writer.Put(KillerId); - writer.Put(KillerWeaponId); RagdollPacket.Serialize(writer, RagdollPacket); writer.PutItem(Equipment); writer.PutArray(TriggerZones); diff --git a/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs b/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs index cf9ff22b..6b0f2fe3 100644 --- a/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs +++ b/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs @@ -6,7 +6,7 @@ namespace Fika.Core.Networking { public struct PlayerStatePacket(int netId, Vector3 position, Vector2 rotation, Vector2 headRotation, Vector2 movementDirection, EPlayerState state, float tilt, int step, int animatorStateIndex, float characterMovementSpeed, - bool isProne, float poseLevel, bool isSprinting, GClass681.GStruct35 stamina, int blindfire, + bool isProne, float poseLevel, bool isSprinting, BasePhysicalClass.GStruct36 stamina, int blindfire, float weaponOverlap, bool leftStanceDisabled, bool isGrounded, bool hasGround, ESurfaceSound surfaceSound, Vector3 surfaceNormal) : INetSerializable { public int NetId = netId; @@ -22,7 +22,7 @@ public struct PlayerStatePacket(int netId, Vector3 position, Vector2 rotation, V public bool IsProne = isProne; public float PoseLevel = poseLevel; public bool IsSprinting = isSprinting; - public GClass681.GStruct35 Stamina = stamina; + public BasePhysicalClass.GStruct36 Stamina = stamina; public int Blindfire = blindfire; public float WeaponOverlap = weaponOverlap; public bool LeftStanceDisabled = leftStanceDisabled; diff --git a/Fika.Core/UI/Custom/MatchMakerUI.cs b/Fika.Core/UI/Custom/MatchMakerUI.cs index 90e4efe0..78dd350e 100644 --- a/Fika.Core/UI/Custom/MatchMakerUI.cs +++ b/Fika.Core/UI/Custom/MatchMakerUI.cs @@ -17,6 +17,8 @@ public class MatchMakerUI : MonoBehaviour [SerializeField] public Button StartButton; [SerializeField] + public Button CloseButton; + [SerializeField] public TextMeshProUGUI PlayerAmountText; public void SetServerNumberText(string text) diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 18697328..074047af 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -2,7 +2,7 @@ using EFT; using EFT.UI; using Fika.Core.Bundles; -using Fika.Core.Coop.Matchmaker; +using Fika.Core.Coop.Utils; using Fika.Core.Networking; using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; @@ -30,7 +30,7 @@ internal class MatchMakerUIScript : MonoBehaviour public DefaultUIButton AcceptButton { get; internal set; } public GameObject NewBackButton { get; internal set; } - private string ProfileId => MatchmakerAcceptPatches.Profile.ProfileId; + private string ProfileId => FikaBackendUtils.Profile.ProfileId; private float _lastRefreshed; @@ -68,7 +68,9 @@ protected void OnDestroy() { StopQuery = true; if (NewBackButton != null) + { Destroy(NewBackButton); + } } private void CreateMatchMakerUI() @@ -102,6 +104,15 @@ private void CreateMatchMakerUI() } }); + fikaMatchMakerUi.CloseButton.onClick.AddListener(() => + { + Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); + if (fikaMatchMakerUi.PlayerAmountSelection.active) + { + fikaMatchMakerUi.PlayerAmountSelection.SetActive(false); + } + }); + fikaMatchMakerUi.StartButton.onClick.AddListener(() => { Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); @@ -124,10 +135,9 @@ private void CreateMatchMakerUI() 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); + 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; } } @@ -135,15 +145,14 @@ private void CreateMatchMakerUI() { 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); + 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); + FikaBackendUtils.HostExpectedNumberOfPlayers = int.Parse(fikaMatchMakerUi.PlayerAmountText.text); + FikaBackendUtils.CreateMatch(FikaBackendUtils.Profile.ProfileId, FikaBackendUtils.PMCName, RaidSettings); AcceptButton.OnClick.Invoke(); DestroyThis(); }); @@ -165,11 +174,22 @@ private void CreateMatchMakerUI() Traverse.Create(newButtonComponent).Field("OnClick").SetValue(newEvent); if (!NewBackButton.active) + { NewBackButton.SetActive(true); + } BackButton.gameObject.SetActive(false); } + private void AutoRefresh() + { + Matches = FikaRequestHandler.LocationRaids(RaidSettings); + + _lastRefreshed = Time.time; + + RefreshUI(); + } + private void ManualRefresh() { Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); @@ -189,8 +209,11 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) NotificationManagerClass.DisplayMessageNotification("Connecting to session...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); - FikaPingingClient pingingClient = new(serverId); - if (pingingClient.Init()) + NetManagerUtils.CreatePingingClient(); + + FikaPingingClient pingingClient = Singleton.Instance; + + if (pingingClient.Init(serverId)) { int attempts = 0; bool success; @@ -201,7 +224,7 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) { attempts++; - pingingClient.PingEndPoint(); + pingingClient.PingEndPoint("fika.hello"); pingingClient.NetClient.PollEvents(); success = pingingClient.Received; @@ -229,15 +252,20 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) ConsoleScreen.Log("ERROR"); } - pingingClient.NetClient?.Stop(); - pingingClient = null; - - if (MatchmakerAcceptPatches.JoinMatch(profileId, serverId, out CreateMatch result, out string errorMessage)) + if (FikaBackendUtils.JoinMatch(profileId, serverId, out CreateMatch result, out string errorMessage)) { - MatchmakerAcceptPatches.SetGroupId(result.ServerId); - MatchmakerAcceptPatches.SetTimestamp(result.Timestamp); - MatchmakerAcceptPatches.MatchingType = EMatchmakerType.GroupPlayer; - MatchmakerAcceptPatches.HostExpectedNumberOfPlayers = result.ExpectedNumberOfPlayers; + FikaBackendUtils.SetGroupId(result.ServerId); + FikaBackendUtils.MatchingType = EMatchmakerType.GroupPlayer; + FikaBackendUtils.HostExpectedNumberOfPlayers = result.ExpectedNumberOfPlayers; + + if (FikaBackendUtils.IsHostNatPunch) + { + pingingClient.StartKeepAliveRoutine(); + } + else + { + NetManagerUtils.DestroyPingingClient(); + } DestroyThis(); @@ -245,6 +273,8 @@ private IEnumerator JoinMatch(string profileId, string serverId, Button button) } else { + NetManagerUtils.DestroyPingingClient(); + Singleton.Instance.ShowCriticalErrorScreen("ERROR JOINING", errorMessage, ErrorScreen.EButtonType.OkButton, 15, null, null); } } @@ -442,7 +472,7 @@ public IEnumerator ServerQuery() { while (!StopQuery) { - ManualRefresh(); + AutoRefresh(); while (Time.time < _lastRefreshed + FikaPlugin.AutoRefreshRate.Value) { diff --git a/Fika.Core/UI/FikaUIUtils.cs b/Fika.Core/UI/FikaUIUtils.cs index bbb6c72e..5cf41581 100644 --- a/Fika.Core/UI/FikaUIUtils.cs +++ b/Fika.Core/UI/FikaUIUtils.cs @@ -39,12 +39,12 @@ public static TextMeshProUGUI CreateOverlayText(string overlayText) return text; } - public static GClass3085 ShowFikaMessage(this PreloaderUI preloaderUI, string header, string message, + public static GClass3107 ShowFikaMessage(this PreloaderUI preloaderUI, string header, string message, ErrorScreen.EButtonType buttonType, float waitingTime, Action acceptCallback, Action endTimeCallback) { Traverse preloaderUiTraverse = Traverse.Create(preloaderUI); - PreloaderUI.Class2561 messageHandler = new() + PreloaderUI.Class2594 messageHandler = new() { preloaderUI_0 = preloaderUI, acceptCallback = acceptCallback, @@ -54,7 +54,7 @@ public static GClass3085 ShowFikaMessage(this PreloaderUI preloaderUI, string he if (!AsyncWorker.CheckIsMainThread()) { FikaPlugin.Instance.FikaLogger.LogError("You are trying to show error screen from non-main thread!"); - return new GClass3085(); + return new GClass3107(); } ErrorScreen errorScreenTemplate = preloaderUiTraverse.Field("_criticalErrorScreenTemplate").GetValue(); @@ -65,19 +65,19 @@ public static GClass3085 ShowFikaMessage(this PreloaderUI preloaderUI, string he return messageHandler.errorScreen.ShowFikaMessage(header, message, new Action(messageHandler.method_1), waitingTime, new Action(messageHandler.method_2), buttonType, true); } - public static GClass3087 ShowFikaMessage(this ErrorScreen errorScreen, string title, string message, + public static GClass3109 ShowFikaMessage(this ErrorScreen errorScreen, string title, string message, Action closeManuallyCallback = null, float waitingTime = 0f, Action timeOutCallback = null, ErrorScreen.EButtonType buttonType = ErrorScreen.EButtonType.OkButton, bool removeHtml = true) { Traverse errorScreenTraverse = Traverse.Create(errorScreen); - ErrorScreen.Class2352 errorScreenHandler = new() + ErrorScreen.Class2382 errorScreenHandler = new() { errorScreen_0 = errorScreen }; if (!MonoBehaviourSingleton.Instance.CanShowErrorScreen) { - return new GClass3087(); + return new GClass3109(); } if (removeHtml) { @@ -89,12 +89,12 @@ public static GClass3087 ShowFikaMessage(this ErrorScreen errorScreen, string ti errorScreenTraverse.Field("action_1").SetValue(action_1); MethodBase baseShow = typeof(ErrorScreen).BaseType.GetMethod("Show"); - errorScreenHandler.context = (GClass3087)baseShow.Invoke(errorScreen, [closeManuallyCallback]); + errorScreenHandler.context = (GClass3109)baseShow.Invoke(errorScreen, [closeManuallyCallback]); errorScreenHandler.context.OnAccept += errorScreen.method_3; errorScreenHandler.context.OnDecline += errorScreen.method_4; errorScreenHandler.context.OnCloseSilent += errorScreen.method_4; - GClass767 ui = Traverse.Create(errorScreen).Field("UI").GetValue(); + CompositeDisposableClass ui = Traverse.Create(errorScreen).Field("UI").Value; ui.AddDisposable(new Action(errorScreenHandler.method_0)); string text = buttonType switch @@ -115,7 +115,7 @@ public static GClass3087 ShowFikaMessage(this ErrorScreen errorScreen, string ti string string_1 = message.SubstringIfNecessary(500); errorScreenTraverse.Field("string_1").SetValue(string_1); - TextMeshProUGUI errorDescription = Traverse.Create(errorScreen).Field("_errorDescription").GetValue(); + TextMeshProUGUI errorDescription = Traverse.Create(errorScreen).Field("_errorDescription").Value; errorDescription.text = string_1; Coroutine coroutine_0 = errorScreenTraverse.Field("coroutine_0").GetValue(); @@ -125,7 +125,7 @@ public static GClass3087 ShowFikaMessage(this ErrorScreen errorScreen, string ti } if (waitingTime > 0f) { - errorScreenTraverse.Field("coroutine_0").SetValue(errorScreen.StartCoroutine(errorScreen.method_2(GClass1296.Now.AddSeconds((double)waitingTime)))); + errorScreenTraverse.Field("coroutine_0").SetValue(errorScreen.StartCoroutine(errorScreen.method_2(EFTDateTimeClass.Now.AddSeconds((double)waitingTime)))); } return errorScreenHandler.context; } diff --git a/Fika.Core/UI/Patches/ChangeGameModeButton_Patch.cs b/Fika.Core/UI/Patches/ChangeGameModeButton_Patch.cs new file mode 100644 index 00000000..7bc27dad --- /dev/null +++ b/Fika.Core/UI/Patches/ChangeGameModeButton_Patch.cs @@ -0,0 +1,28 @@ +using EFT.UI; +using SPT.Reflection.Patching; +using System.Reflection; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Fika.Core.UI.Patches +{ + public class ChangeGameModeButton_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(ChangeGameModeButton).GetMethod(nameof(ChangeGameModeButton.Show)); + } + + [PatchPrefix] + private static bool PrefixChange(TextMeshProUGUI ____buttonLabel, TextMeshProUGUI ____buttonDescription, Image ____buttonDescriptionIcon, + GameObject ____availableState) + { + ____buttonLabel.text = "PvE"; + ____buttonDescription.text = "Fika will always be PvE"; + ____buttonDescriptionIcon.gameObject.SetActive(false); + ____availableState.SetActive(true); + return false; + } + } +} \ No newline at end of file diff --git a/Fika.Core/UI/Patches/DisableInsuranceReadyButton_Patch.cs b/Fika.Core/UI/Patches/DisableInsuranceReadyButton_Patch.cs index a2ce9500..e7ff801a 100644 --- a/Fika.Core/UI/Patches/DisableInsuranceReadyButton_Patch.cs +++ b/Fika.Core/UI/Patches/DisableInsuranceReadyButton_Patch.cs @@ -1,9 +1,9 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Reflection.Patching; using EFT.UI; +using EFT.UI.Matchmaker; +using SPT.Reflection.Patching; using System.Reflection; -using UnityEngine; namespace Fika.Core.UI.Patches { @@ -12,28 +12,18 @@ namespace Fika.Core.UI.Patches /// public class DisableInsuranceReadyButton_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(MainMenuController).GetMethod(nameof(MainMenuController.method_42)); + protected override MethodBase GetTargetMethod() + { + return typeof(MatchmakerInsuranceScreen).GetMethod(nameof(MatchmakerInsuranceScreen.Awake)); + } [PatchPostfix] - static void PatchPostfix() + static void Postfix(DefaultUIButton ____readyButton) { - var readyButton = GameObject.Find("ReadyButton"); - - if (readyButton != null) - { - readyButton.SetActive(false); - DefaultUIButton uiButton = readyButton.GetComponent(); - if (uiButton != null) - { - uiButton.SetDisabledTooltip("Disabled with Fika"); - uiButton.SetEnabledTooltip("Disabled with Fika"); + ____readyButton.SetDisabledTooltip("Disabled with Fika"); + ____readyButton.SetEnabledTooltip("Disabled with Fika"); - if (uiButton.Interactable == true) - { - uiButton.Interactable = false; - } - } - } + ____readyButton.Interactable = false; } } } \ No newline at end of file diff --git a/Fika.Core/UI/Patches/DisableMatchSettingsReadyButton_Patch.cs b/Fika.Core/UI/Patches/DisableMatchSettingsReadyButton_Patch.cs index a10a62f2..e933ab4d 100644 --- a/Fika.Core/UI/Patches/DisableMatchSettingsReadyButton_Patch.cs +++ b/Fika.Core/UI/Patches/DisableMatchSettingsReadyButton_Patch.cs @@ -1,10 +1,9 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Reflection.Patching; using EFT.UI; using EFT.UI.Matchmaker; +using SPT.Reflection.Patching; using System.Reflection; -using UnityEngine; namespace Fika.Core.UI { @@ -13,28 +12,18 @@ namespace Fika.Core.UI /// public class DisableMatchSettingsReadyButton_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(MatchmakerOfflineRaidScreen).GetMethod(nameof(MatchmakerOfflineRaidScreen.Awake)); + protected override MethodBase GetTargetMethod() + { + return typeof(MatchmakerOfflineRaidScreen).GetMethod(nameof(MatchmakerOfflineRaidScreen.Awake)); + } [PatchPostfix] - static void PatchPostfix() + static void Postfix(DefaultUIButton ____readyButton) { - var readyButton = GameObject.Find("ReadyButton"); - - if (readyButton != null) - { - readyButton.SetActive(false); - DefaultUIButton uiButton = readyButton.GetComponent(); - if (uiButton != null) - { - uiButton.SetDisabledTooltip("Disabled with Fika"); - uiButton.SetEnabledTooltip("Disabled with Fika"); + ____readyButton.SetDisabledTooltip("Disabled with Fika"); + ____readyButton.SetEnabledTooltip("Disabled with Fika"); - if (uiButton.Interactable == true) - { - uiButton.Interactable = false; - } - } - } + ____readyButton.Interactable = false; } } } \ No newline at end of file diff --git a/Fika.Core/UI/Patches/DisableReadyButton_Patch.cs b/Fika.Core/UI/Patches/DisableReadyButton_Patch.cs index a0f2d4cd..0005f481 100644 --- a/Fika.Core/UI/Patches/DisableReadyButton_Patch.cs +++ b/Fika.Core/UI/Patches/DisableReadyButton_Patch.cs @@ -1,10 +1,9 @@ // © 2024 Lacyway All Rights Reserved -using Aki.Reflection.Patching; using EFT.UI; using EFT.UI.Matchmaker; +using SPT.Reflection.Patching; using System.Reflection; -using UnityEngine; namespace Fika.Core.UI { @@ -13,28 +12,18 @@ namespace Fika.Core.UI /// public class DisableReadyButton_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(MatchMakerSelectionLocationScreen).GetMethod("method_7", BindingFlags.Public | BindingFlags.Instance); + protected override MethodBase GetTargetMethod() + { + return typeof(MatchMakerSelectionLocationScreen).GetMethod(nameof(MatchMakerSelectionLocationScreen.Awake)); + } [PatchPostfix] - static void PatchPostfix() + static void Postfix(DefaultUIButton ____readyButton) { - var readyButton = GameObject.Find("ReadyButton"); - - if (readyButton != null) - { - readyButton.SetActive(false); - DefaultUIButton uiButton = readyButton.GetComponent(); - if (uiButton != null) - { - uiButton.SetDisabledTooltip("Disabled with Fika"); - uiButton.SetEnabledTooltip("Disabled with Fika"); + ____readyButton.SetDisabledTooltip("Disabled with Fika"); + ____readyButton.SetEnabledTooltip("Disabled with Fika"); - if (uiButton.Interactable == true) - { - uiButton.Interactable = false; - } - } - } + ____readyButton.Interactable = false; } } } \ No newline at end of file diff --git a/Fika.Core/UI/Patches/DisconnectButton_Patch.cs b/Fika.Core/UI/Patches/DisconnectButton_Patch.cs new file mode 100644 index 00000000..cb8690af --- /dev/null +++ b/Fika.Core/UI/Patches/DisconnectButton_Patch.cs @@ -0,0 +1,32 @@ +using Comfort.Common; +using EFT.UI; +using Fika.Core.Coop.Utils; +using Fika.Core.Networking; +using SPT.Reflection.Patching; +using System.Reflection; + +namespace Fika.Core.UI.Patches +{ + public class DisconnectButton_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(MenuScreen).GetMethod(nameof(MenuScreen.method_11)); + } + + [PatchPrefix] + static bool Prefix() + { + if (FikaBackendUtils.IsServer) + { + FikaServer server = Singleton.Instance; + if (server != null && server.NetServer.ConnectedPeersCount > 0) + { + NotificationManagerClass.DisplayWarningNotification($"You cannot disconnect while there are still peers connected! Remaining: {server.NetServer.ConnectedPeersCount}"); + return false; + } + } + return true; + } + } +} diff --git a/Fika.Core/UI/Patches/FikaVersionLabel_Patch.cs b/Fika.Core/UI/Patches/FikaVersionLabel_Patch.cs new file mode 100644 index 00000000..8905bcba --- /dev/null +++ b/Fika.Core/UI/Patches/FikaVersionLabel_Patch.cs @@ -0,0 +1,72 @@ +using EFT.UI; +using HarmonyLib; +using SPT.Common.Http; +using SPT.Common.Utils; +using SPT.Custom.Models; +using SPT.Reflection.Patching; +using System.Reflection; + +namespace Fika.Core.EssentialPatches +{ + /// + /// Originally developed by SPT team + /// + public class FikaVersionLabel_Patch : ModulePatch + { + private static string versionLabel; + private static Traverse versionNumberTraverse; + private static string fikaVersion; + private static string officialVersion; + + protected override MethodBase GetTargetMethod() + { + return typeof(VersionNumberClass).GetMethod(nameof(VersionNumberClass.Create), + BindingFlags.Static | BindingFlags.Public); + } + + [PatchPostfix] + internal static void PatchPostfix(string major, object __result) + { + FikaPlugin.EFTVersionMajor = major; + + if (string.IsNullOrEmpty(versionLabel)) + { + string json = RequestHandler.GetJson("/singleplayer/settings/version"); + versionLabel = Json.Deserialize(json).Version; + Logger.LogInfo($"Server version: {versionLabel}"); + } + + fikaVersion = Assembly.GetAssembly(typeof(FikaVersionLabel_Patch)).GetName().Version.ToString(); + + Traverse preloaderUiTraverse = Traverse.Create(MonoBehaviourSingleton.Instance); + + preloaderUiTraverse.Field("_alphaVersionLabel").Property("LocalizationKey").SetValue("{0}"); + + versionNumberTraverse = Traverse.Create(__result); + + officialVersion = versionNumberTraverse.Field("Major").Value; + + UpdateVersionLabel(); + } + + public static void UpdateVersionLabel() + { + Traverse preloaderUiTraverse = Traverse.Create(MonoBehaviourSingleton.Instance); + if (FikaPlugin.OfficialVersion.Value) + { + preloaderUiTraverse.Field("string_2").SetValue($"{officialVersion} Beta version"); + versionNumberTraverse.Field("Major").SetValue(officialVersion); + } + else + { + preloaderUiTraverse.Field("string_2").SetValue($"FIKA BETA {fikaVersion} | {versionLabel}"); + versionNumberTraverse.Field("Major").SetValue($"{fikaVersion} {versionLabel}"); + } + + // Game mode + preloaderUiTraverse.Field("string_4").SetValue("PvE"); + // Update version label + preloaderUiTraverse.Method("method_6").GetValue(); + } + } +} \ No newline at end of file diff --git a/Fika.Core/UI/Patches/InventoryScroll_Patch.cs b/Fika.Core/UI/Patches/InventoryScroll_Patch.cs index ff319aeb..2998303e 100644 --- a/Fika.Core/UI/Patches/InventoryScroll_Patch.cs +++ b/Fika.Core/UI/Patches/InventoryScroll_Patch.cs @@ -1,5 +1,5 @@ -using Aki.Reflection.Patching; -using EFT.UI; +using EFT.UI; +using SPT.Reflection.Patching; using System.Reflection; using UnityEngine.UI; diff --git a/Fika.Core/UI/Patches/ItemContext_Patch.cs b/Fika.Core/UI/Patches/ItemContext_Patch.cs index e0aff788..4f037a43 100644 --- a/Fika.Core/UI/Patches/ItemContext_Patch.cs +++ b/Fika.Core/UI/Patches/ItemContext_Patch.cs @@ -1,5 +1,4 @@ -using Aki.Reflection.Patching; -using Comfort.Common; +using Comfort.Common; using EFT; using EFT.InventoryLogic; using EFT.UI; @@ -7,6 +6,7 @@ using Fika.Core.Networking.Http; using Fika.Core.UI.Models; using HarmonyLib; +using SPT.Reflection.Patching; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -27,12 +27,13 @@ protected override MethodBase GetTargetMethod() [PatchPrefix] private static void Prefix(ItemInfoInteractionsAbstractClass contextInteractions, Item item) { - if (contextInteractions is not GClass3021 gclass) + if (contextInteractions is not GClass3042 gclass) { return; } - ItemContextAbstractClass itemContext = Traverse.Create(contextInteractions).Field("gclass2813_0").Value; + // Check for GClass increments + ItemContextAbstractClass itemContext = Traverse.Create(contextInteractions).Field("itemContextAbstractClass").Value; if (itemContext.ViewType == EItemViewType.Inventory) { if (Singleton.Instantiated && Singleton.Instance is not HideoutGameWorld) @@ -54,20 +55,31 @@ private static void Prefix(ItemInfoInteractionsAbstractClass co return; } - Dictionary dynamicInteractions = Traverse.Create(contextInteractions).Field>("dictionary_0").Value; + // Check for GClass increments + Dictionary dynamicInteractions = Traverse.Create(contextInteractions) + .Field>("dictionary_0").Value; if (dynamicInteractions == null) { - dynamicInteractions = new Dictionary(); + dynamicInteractions = []; } dynamicInteractions["SEND"] = new("SEND", "SEND", () => { - var body = new AvailableReceiversRequest(itemContext.Item.Id); - var availableUsers = FikaRequestHandler.AvailableReceivers(body); + foreach (string itemId in FikaPlugin.Instance.BlacklistedItems) + { + if (itemId == item.TemplateId) + { + NotificationManagerClass.DisplayMessageNotification($"{item.ShortName.Localized()} is blacklisted from being sent.", iconType: EFT.Communications.ENotificationIconType.Alert); + return; + } + } + + AvailableReceiversRequest body = new(itemContext.Item.Id); + Dictionary availableUsers = FikaRequestHandler.AvailableReceivers(body); // convert availableUsers.Keys List optionDatas = []; - foreach (var user in availableUsers.Keys) + foreach (string user in availableUsers.Keys) { optionDatas.Add(new() { @@ -86,7 +98,7 @@ private static void Prefix(ItemInfoInteractionsAbstractClass co GameObject matchMakerUiPrefab = InternalBundleLoader.Instance.GetAssetBundle("senditemmenu").LoadAsset("SendItemMenu"); GameObject uiGameObj = Object.Instantiate(matchMakerUiPrefab); uiGameObj.transform.SetParent(GameObject.Find("Preloader UI/Preloader UI/UIContext/").transform); - InventoryScreen.GClass3116 screenController = Traverse.Create(CommonUI.Instance.InventoryScreen).Field("ScreenController").Value; + InventoryScreen.GClass3142 screenController = Traverse.Create(CommonUI.Instance.InventoryScreen).Field("ScreenController").Value; screenController.OnClose += () => { Object.Destroy(uiGameObj); }; SendItemUI sendItemUI = uiGameObj.GetComponent(); sendItemUI.PlayersDropdown.ClearOptions(); @@ -140,4 +152,4 @@ private static void Prefix(ItemInfoInteractionsAbstractClass co } } } -} +} \ No newline at end of file diff --git a/Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Awake_Patch.cs b/Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Awake_Patch.cs new file mode 100644 index 00000000..b5619d64 --- /dev/null +++ b/Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Awake_Patch.cs @@ -0,0 +1,43 @@ +using EFT.UI.Matchmaker; +using Fika.Core.Coop.Utils; +using Newtonsoft.Json; +using SPT.Reflection.Patching; +using System; +using System.Reflection; + +namespace Fika.Core.UI.Patches.MatchmakerAcceptScreen +{ + public class MatchmakerAcceptScreen_Awake_Patch : ModulePatch + { + [Serializable] + private class ServerStatus + { + [JsonProperty("ip")] + public string Ip { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + } + + protected override MethodBase GetTargetMethod() => typeof(MatchMakerAcceptScreen).GetMethod("Awake"); + + [PatchPrefix] + private static bool PatchPrefix(MatchMakerAcceptScreen __instance, PlayersRaidReadyPanel ____playersRaidReadyPanel, MatchMakerGroupPreview ____groupPreview) + { + FikaBackendUtils.MatchMakerAcceptScreenInstance = __instance; + FikaBackendUtils.PlayersRaidReadyPanel = ____playersRaidReadyPanel; + FikaBackendUtils.MatchMakerGroupPreview = ____groupPreview; + return true; + } + + } +} + + + + + + + + + diff --git a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Show_Patch.cs b/Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Show_Patch.cs similarity index 66% rename from Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Show_Patch.cs rename to Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Show_Patch.cs index 247f50a3..db6334ec 100644 --- a/Fika.Core/Coop/Matchmaker/MatchmakerAccept/MatchmakerAcceptScreen_Show_Patch.cs +++ b/Fika.Core/UI/Patches/MatchmakerAcceptScreen/MatchmakerAcceptScreen_Show_Patch.cs @@ -1,17 +1,23 @@ -using Aki.Reflection.Patching; -using EFT; +using EFT; using EFT.UI; using EFT.UI.Matchmaker; +using Fika.Core.Coop.Utils; using Fika.Core.UI.Custom; +using SPT.Reflection.Patching; using System.Linq; using System.Reflection; using UnityEngine; -namespace Fika.Core.Coop.Matchmaker +namespace Fika.Core.UI.Patches.MatchmakerAcceptScreen { public class MatchmakerAcceptScreen_Show_Patch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(MatchMakerAcceptScreen).GetMethods(BindingFlags.Public | BindingFlags.Instance).First(x => x.Name == "Show" && x.GetParameters()[0].Name == "session"); + protected override MethodBase GetTargetMethod() + { + return typeof(MatchMakerAcceptScreen).GetMethods(BindingFlags.Public | BindingFlags.Instance) + .First(x => x.Name == "Show" && x.GetParameters()[0].Name == "session"); + } + private static GameObject MatchmakerObject { get; set; } [PatchPrefix] @@ -36,9 +42,9 @@ private static void PreFix(ref RaidSettings raidSettings, DefaultUIButton ____ac [PatchPostfix] private static void PostFix(ref ISession session, Profile ___profile_0, MatchMakerAcceptScreen __instance) { - MatchmakerAcceptPatches.MatchMakerAcceptScreenInstance = __instance; - MatchmakerAcceptPatches.Profile = ___profile_0; - MatchmakerAcceptPatches.PMCName = session.Profile.Nickname; + FikaBackendUtils.MatchMakerAcceptScreenInstance = __instance; + FikaBackendUtils.Profile = ___profile_0; + FikaBackendUtils.PMCName = session.Profile.Nickname; } } diff --git a/Fika.Core/UI/Patches/TOS_Patch.cs b/Fika.Core/UI/Patches/TOS_Patch.cs index ad221cb2..a89f248a 100644 --- a/Fika.Core/UI/Patches/TOS_Patch.cs +++ b/Fika.Core/UI/Patches/TOS_Patch.cs @@ -1,7 +1,7 @@ -using Aki.Reflection.Patching; -using Comfort.Common; +using Comfort.Common; using EFT; using EFT.UI; +using SPT.Reflection.Patching; using System; using System.Reflection; using System.Text; diff --git a/Fika.Core/Utils/FikaModHandler.cs b/Fika.Core/Utils/FikaModHandler.cs index d364eeb7..84ecb295 100644 --- a/Fika.Core/Utils/FikaModHandler.cs +++ b/Fika.Core/Utils/FikaModHandler.cs @@ -1,44 +1,111 @@ -using BepInEx.Bootstrap; +using BepInEx; +using BepInEx.Bootstrap; using BepInEx.Logging; +using Comfort.Common; +using EFT; +using EFT.UI; +using Fika.Core.Networking.Http.Models; +using LiteNetLib.Utils; +using Newtonsoft.Json; +using SPT.Common.Http; +using System.Collections; using System.Collections.Generic; +using System.IO; +using UnityEngine; +using Logger = BepInEx.Logging.Logger; namespace Fika.Core.Utils { public class FikaModHandler { - private string[] loadedMods; private readonly ManualLogSource logger = Logger.CreateLogSource("FikaModHandler"); public bool QuestingBotsLoaded = false; public bool SAINLoaded = false; - public void Run() + public void VerifyMods() { - // Store all loaded plugins (mods) to improve compatibility - List tempPluginInfos = []; + PluginInfo[] pluginInfos = [.. Chainloader.PluginInfos.Values]; - foreach (string key in Chainloader.PluginInfos.Keys) + // Set capacity to avoid unnecessarily resizing for people who have a lot of mods loaded + Dictionary loadedMods = new(pluginInfos.Length); + + foreach (PluginInfo pluginInfo in pluginInfos) + { + string location = pluginInfo.Location; + byte[] fileBytes = File.ReadAllBytes(location); + uint crc32 = CRC32C.Compute(fileBytes, 0, fileBytes.Length); + loadedMods.Add(pluginInfo.Metadata.GUID, crc32); + logger.LogInfo($"Loaded plugin: [{pluginInfo.Metadata.Name}] with GUID [{pluginInfo.Metadata.GUID}] and crc32 [{crc32}]"); + CheckSpecialMods(pluginInfo.Metadata.GUID); + } + + string modValidationRequestJson = JsonConvert.SerializeObject(loadedMods); + logger.LogDebug(modValidationRequestJson); + + string validationJson = RequestHandler.PostJson("/fika/client/check/mods", modValidationRequestJson); + logger.LogDebug(validationJson); + + ModValidationResponse validationResult = + JsonConvert.DeserializeObject(validationJson); + + // If any errors were detected we will print what has happened + bool installationError = + validationResult.Forbidden.Length > 0 || + validationResult.MissingRequired.Length > 0 || + validationResult.HashMismatch.Length > 0; + + if (validationResult.Forbidden.Length > 0) { - logger.LogInfo($"Adding {key}, {Chainloader.PluginInfos[key].Metadata.Name} to loaded mods."); - tempPluginInfos.Add(key); - CheckSpecialMods(key); + logger.LogError($"{validationResult.Forbidden.Length} forbidden mod(s) are loaded, have the server host allow or remove the following mods: {string.Join(", ", validationResult.Forbidden)}"); } - loadedMods = [.. tempPluginInfos]; + if (validationResult.MissingRequired.Length > 0) + { + logger.LogError($"{validationResult.MissingRequired.Length} missing required mod(s), verify the following mods are present: {string.Join(", ", validationResult.MissingRequired)}"); + } - logger.LogInfo($"Loaded {loadedMods.Length} mods!"); + if (validationResult.HashMismatch.Length > 0) + { + logger.LogWarning($"{validationResult.HashMismatch.Length} mismatched mod(s) are loaded, verify the following mods are up to date with the server host: {string.Join(", ", validationResult.HashMismatch)}"); + } + + if (installationError) + { + StaticManager.BeginCoroutine(InformInstallationError()); + } } - private void CheckSpecialMods(string key) + private IEnumerator InformInstallationError() { - if (key == "com.DanW.QuestingBots") + while (!Singleton.Instantiated) { - QuestingBotsLoaded = true; + yield return null; } - if (key == "me.sol.sain") + string message = "Your client doesn't meet server requirements, check logs for more details"; + + // -1f time makes the message permanent + Singleton.Instance.ShowCriticalErrorScreen("INSTALLATION ERROR", message, + ErrorScreen.EButtonType.QuitButton, -1f, Application.Quit, null); + } + + private void CheckSpecialMods(string key) + { + switch (key) { - SAINLoaded = true; + case "com.DanW.QuestingBots": + { + QuestingBotsLoaded = true; + + break; + } + case "me.sol.sain": + { + SAINLoaded = true; + + break; + } } } } diff --git a/Fika.Core/Utils/WorldToScreen.cs b/Fika.Core/Utils/WorldToScreen.cs index d40b6b98..b1c5318e 100644 --- a/Fika.Core/Utils/WorldToScreen.cs +++ b/Fika.Core/Utils/WorldToScreen.cs @@ -8,7 +8,7 @@ namespace Fika.Core.Utils { public static class WorldToScreen { - public static bool GetScreenPoint(Vector3 worldPosition, CoopPlayer mainPlayer, out Vector3 screenPoint) + public static bool GetScreenPoint(Vector3 worldPosition, CoopPlayer mainPlayer, out Vector3 screenPoint, bool useOpticCamera = true) { CameraClass worldCameraInstance = CameraClass.Instance; Camera worldCamera = worldCameraInstance.Camera; @@ -22,7 +22,7 @@ public static bool GetScreenPoint(Vector3 worldPosition, CoopPlayer mainPlayer, ProceduralWeaponAnimation weaponAnimation = mainPlayer.ProceduralWeaponAnimation; - if (weaponAnimation != null) + if (useOpticCamera && weaponAnimation != null) { if (weaponAnimation.IsAiming && weaponAnimation.CurrentScope.IsOptic) { @@ -33,8 +33,8 @@ public static bool GetScreenPoint(Vector3 worldPosition, CoopPlayer mainPlayer, Vector3 opticCenterScreenPosition = GetOpticCenterScreenPosition(weaponAnimation, worldCamera); Vector3 opticCenterScreenOffset = opticCenterScreenPosition - (new Vector3(Screen.width, Screen.height, 0f) / 2); - float opticScale = (Screen.height / opticCamera.scaledPixelHeight); - Vector3 opticCameraOffset = new Vector3((worldCamera.pixelWidth / 2 - opticCamera.pixelWidth / 2), (worldCamera.pixelHeight / 2 - opticCamera.pixelHeight / 2), 0); + float opticScale = Screen.height / opticCamera.scaledPixelHeight; + Vector3 opticCameraOffset = new(worldCamera.pixelWidth / 2 - opticCamera.pixelWidth / 2, worldCamera.pixelHeight / 2 - opticCamera.pixelHeight / 2, 0); Vector3 opticScreenPoint = (opticCamera.WorldToScreenPoint(worldPosition) + opticCameraOffset) * opticScale; if (opticScreenPoint.z > 0f) diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 7719fef8..5129c19c 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,7 +1,7 @@ using System.Reflection; using System.Runtime.InteropServices; -[assembly: AssemblyDescription("Play with friends in Aki")] +[assembly: AssemblyDescription("Play with friends in SPT")] [assembly: AssemblyCompany("https://github.com/project-fika")] [assembly: AssemblyCopyright("Copyright © Lacyway 2024")] [assembly: ComVisible(false)] diff --git a/References b/References deleted file mode 160000 index 1064f858..00000000 --- a/References +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1064f8589c9ce0b1d5c1ffe015e9c33f914f3f95 diff --git a/References/AnimationSystem.Types.dll b/References/AnimationSystem.Types.dll new file mode 100644 index 00000000..6c7feebe Binary files /dev/null and b/References/AnimationSystem.Types.dll differ diff --git a/References/Comfort.Unity.dll b/References/Comfort.Unity.dll new file mode 100644 index 00000000..5e1fc19e Binary files /dev/null and b/References/Comfort.Unity.dll differ diff --git a/References/Comfort.dll b/References/Comfort.dll new file mode 100644 index 00000000..7171dd47 Binary files /dev/null and b/References/Comfort.dll differ diff --git a/References/CommonExtensions.dll b/References/CommonExtensions.dll new file mode 100644 index 00000000..fc298449 Binary files /dev/null and b/References/CommonExtensions.dll differ diff --git a/References/DissonanceVoip.dll b/References/DissonanceVoip.dll new file mode 100644 index 00000000..3837e4bd Binary files /dev/null and b/References/DissonanceVoip.dll differ diff --git a/References/ItemComponent.Types.dll b/References/ItemComponent.Types.dll new file mode 100644 index 00000000..1b861cfe Binary files /dev/null and b/References/ItemComponent.Types.dll differ diff --git a/References/Newtonsoft.Json.dll b/References/Newtonsoft.Json.dll new file mode 100644 index 00000000..1ba1ec91 Binary files /dev/null and b/References/Newtonsoft.Json.dll differ diff --git a/References/Sirenix.Serialization.dll b/References/Sirenix.Serialization.dll new file mode 100644 index 00000000..d1ea4b0e Binary files /dev/null and b/References/Sirenix.Serialization.dll differ diff --git a/References/Unity.Postprocessing.Runtime.dll b/References/Unity.Postprocessing.Runtime.dll new file mode 100644 index 00000000..b0d0c58d Binary files /dev/null and b/References/Unity.Postprocessing.Runtime.dll differ diff --git a/References/Unity.TextMeshPro.dll b/References/Unity.TextMeshPro.dll new file mode 100644 index 00000000..cee0cb7b Binary files /dev/null and b/References/Unity.TextMeshPro.dll differ diff --git a/References/UnityEngine.UI.dll b/References/UnityEngine.UI.dll new file mode 100644 index 00000000..7fbcba97 Binary files /dev/null and b/References/UnityEngine.UI.dll differ diff --git a/References/UnityEngine.dll b/References/UnityEngine.dll new file mode 100644 index 00000000..6ff1d0fb Binary files /dev/null and b/References/UnityEngine.dll differ diff --git a/References/bsg.componentace.compression.libs.zlib.dll b/References/bsg.componentace.compression.libs.zlib.dll new file mode 100644 index 00000000..a7973147 Binary files /dev/null and b/References/bsg.componentace.compression.libs.zlib.dll differ diff --git a/References/bsg.console.core.dll b/References/bsg.console.core.dll new file mode 100644 index 00000000..10028fa2 Binary files /dev/null and b/References/bsg.console.core.dll differ diff --git a/References/hollowed.dll b/References/hollowed.dll new file mode 100644 index 00000000..6cd67004 Binary files /dev/null and b/References/hollowed.dll differ diff --git a/References/spt-common.dll b/References/spt-common.dll new file mode 100644 index 00000000..3c3a48c0 Binary files /dev/null and b/References/spt-common.dll differ diff --git a/References/spt-core.dll b/References/spt-core.dll new file mode 100644 index 00000000..9581a83d Binary files /dev/null and b/References/spt-core.dll differ diff --git a/References/spt-custom.dll b/References/spt-custom.dll new file mode 100644 index 00000000..2faddbb6 Binary files /dev/null and b/References/spt-custom.dll differ diff --git a/References/spt-debugging.dll b/References/spt-debugging.dll new file mode 100644 index 00000000..f29d980e Binary files /dev/null and b/References/spt-debugging.dll differ diff --git a/References/spt-reflection.dll b/References/spt-reflection.dll new file mode 100644 index 00000000..bcde6e82 Binary files /dev/null and b/References/spt-reflection.dll differ diff --git a/References/spt-singleplayer.dll b/References/spt-singleplayer.dll new file mode 100644 index 00000000..b73ef99c Binary files /dev/null and b/References/spt-singleplayer.dll differ diff --git a/References/websocket-sharp.dll b/References/websocket-sharp.dll new file mode 100644 index 00000000..1a677258 Binary files /dev/null and b/References/websocket-sharp.dll differ