From 2ca53eaf3e72149551a535545bfb1c6169cd6687 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:35:11 +0200 Subject: [PATCH 01/83] Do not run Easy Kill conditions if aggressor is yourself --- Fika.Core/Coop/Players/CoopBot.cs | 2 +- Fika.Core/Coop/Players/ObservedCoopPlayer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index 064d84f5..d38c2e7b 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -100,7 +100,7 @@ public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damag if (FikaPlugin.EasyKillConditions.Value) { - if (aggressor.Profile.Info.GroupId == "Fika") + if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) { CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; if (mainPlayer != null) diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index b61445b0..7db9adb4 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -731,7 +731,7 @@ public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damag if (FikaPlugin.EasyKillConditions.Value) { - if (aggressor.Profile.Info.GroupId == "Fika") + if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) { CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; if (mainPlayer != null) From 65ebf70aea5b18bde90716b1a22a9591c8411a2a Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:22:47 +0200 Subject: [PATCH 02/83] Initial packet setup --- Fika.Core/Coop/GameMode/CoopGame.cs | 1 + Fika.Core/Coop/Utils/FikaBackendUtils.cs | 1 + .../Networking/FikaSerializationExtensions.cs | 519 +++++++++++------- .../Packets/GameWorld/ReconnectPacket.cs | 72 +++ 4 files changed, 389 insertions(+), 204 deletions(-) create mode 100644 Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index ca4d349e..309448ce 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -2172,6 +2172,7 @@ public override void Dispose() FikaBackendUtils.Nodes = null; FikaBackendUtils.HostExpectedNumberOfPlayers = 1; FikaBackendUtils.RequestFikaWorld = false; + FikaBackendUtils.IsReconnect = false; if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { diff --git a/Fika.Core/Coop/Utils/FikaBackendUtils.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs index 058e2586..6ef99e5d 100644 --- a/Fika.Core/Coop/Utils/FikaBackendUtils.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -26,6 +26,7 @@ public static class FikaBackendUtils public static bool IsServer => MatchingType == EMatchmakerType.GroupLeader; public static bool IsClient => MatchingType == EMatchmakerType.GroupPlayer; public static bool IsDedicated = false; + public static bool IsReconnect = false; public static bool IsSinglePlayer { get diff --git a/Fika.Core/Networking/FikaSerializationExtensions.cs b/Fika.Core/Networking/FikaSerializationExtensions.cs index f14173af..eb4e5a90 100644 --- a/Fika.Core/Networking/FikaSerializationExtensions.cs +++ b/Fika.Core/Networking/FikaSerializationExtensions.cs @@ -1,5 +1,6 @@ using Comfort.Common; using EFT; +using EFT.Interactive; using EFT.InventoryLogic; using EFT.SynchronizableObjects; using LiteNetLib.Utils; @@ -12,232 +13,342 @@ namespace Fika.Core.Networking { - /// - /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika - /// - public static class FikaSerializationExtensions - { - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector3 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - writer.Put(vector.z); - } + /// + /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika + /// + public static class FikaSerializationExtensions + { + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector3 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + writer.Put(vector.z); + } - /// - /// Deserializes a - /// - /// - /// A - public static Vector3 GetVector3(this NetDataReader reader) - { - return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } + /// + /// Deserializes a + /// + /// + /// A + public static Vector3 GetVector3(this NetDataReader reader) + { + return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector2 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - } + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector2 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + } - /// - /// Deserializes a - /// - /// - /// A - public static Vector2 GetVector2(this NetDataReader reader) - { - return new Vector2(reader.GetFloat(), reader.GetFloat()); - } + /// + /// Deserializes a + /// + /// + /// A + public static Vector2 GetVector2(this NetDataReader reader) + { + return new Vector2(reader.GetFloat(), reader.GetFloat()); + } - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Quaternion quaternion) - { - writer.Put(quaternion.x); - writer.Put(quaternion.y); - writer.Put(quaternion.z); - writer.Put(quaternion.w); - } + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Quaternion quaternion) + { + writer.Put(quaternion.x); + writer.Put(quaternion.y); + writer.Put(quaternion.z); + writer.Put(quaternion.w); + } - /// - /// Deserializes a - /// - /// - /// A - public static Quaternion GetQuaternion(this NetDataReader reader) - { - return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } + /// + /// Deserializes a + /// + /// + /// A + public static Quaternion GetQuaternion(this NetDataReader reader) + { + return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Color color) - { - writer.Put(color.r); - writer.Put(color.g); - writer.Put(color.b); - writer.Put(color.a); - } + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Color color) + { + writer.Put(color.r); + writer.Put(color.g); + writer.Put(color.b); + writer.Put(color.a); + } - /// - /// Deserializes a - /// - /// - /// A /returns> - public static Color GetColor(this NetDataReader reader) - { - return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } + /// + /// Deserializes a + /// + /// + /// A /returns> + public static Color GetColor(this NetDataReader reader) + { + return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } - /// - /// Serializes a (Physical) struct - /// - /// - /// - public static void Put(this NetDataWriter writer, GStruct36 physical) - { - writer.Put(physical.StaminaExhausted); - writer.Put(physical.OxygenExhausted); - writer.Put(physical.HandsExhausted); - } + /// + /// Serializes a (Physical) struct + /// + /// + /// + public static void Put(this NetDataWriter writer, GStruct36 physical) + { + writer.Put(physical.StaminaExhausted); + writer.Put(physical.OxygenExhausted); + writer.Put(physical.HandsExhausted); + } - /// - /// Deserializes a (Physical) struct - /// - /// - /// A (Physical) - public static GStruct36 GetPhysical(this NetDataReader reader) - { - return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; - } + /// + /// Deserializes a (Physical) struct + /// + /// + /// A (Physical) + public static GStruct36 GetPhysical(this NetDataReader reader) + { + return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; + } - /// - /// Serialize a array - /// - /// - /// - public static void PutByteArray(this NetDataWriter writer, byte[] bytes) - { - writer.Put(bytes.Length); - if (bytes.Length > 0) - { - writer.Put(bytes); - } - } + /// + /// Serialize a array + /// + /// + /// + public static void PutByteArray(this NetDataWriter writer, byte[] bytes) + { + writer.Put(bytes.Length); + if (bytes.Length > 0) + { + writer.Put(bytes); + } + } + + /// + /// Deserializes a array + /// + /// + /// A array + public static byte[] GetByteArray(this NetDataReader reader) + { + int length = reader.GetInt(); + if (length > 0) + { + byte[] bytes = new byte[length]; + reader.GetBytes(bytes, length); + return bytes; + } + return Array.Empty(); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, DateTime dateTime) + { + writer.Put(dateTime.ToOADate()); + } + + /// + /// Deserializes a + /// + /// + /// A + public static DateTime GetDateTime(this NetDataReader reader) + { + return DateTime.FromOADate(reader.GetDouble()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// The to serialize + public static void PutAirdropItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// An + public static Item GetAirdropItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); - /// - /// Deserializes a array - /// - /// - /// A array - public static byte[] GetByteArray(this NetDataReader reader) - { - int length = reader.GetInt(); - if (length > 0) + Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + + ContainerCollection[] containerCollections = [item as ContainerCollection]; + ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() + .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); + + return item; + } + + /// + /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. + /// + /// + /// The to serialize + public static void PutItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Gets a serialized + /// + /// + /// An + public static Item GetItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); + + return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + } + + public static void PutThrowableData(this NetDataWriter writer, List throwables) + { + writer.Put(throwables.Count); + foreach (GStruct35 data in throwables) + { + writer.Put(data.Id); + writer.Put(data.Position); + writer.Put(data.Template); + writer.Put(data.Time); + writer.Put(data.Orientation); + writer.Put(data.PlatformId); + } + } + + public static List GetThrowableData(this NetDataReader reader) + { + int amount = reader.GetInt(); + List throwables = new(amount); + for (int i = 0; i < amount; i++) + { + GStruct35 data = new() + { + Id = reader.GetString(), + Position = reader.GetVector3(), + Template = reader.GetString(), + Time = reader.GetInt(), + Orientation = reader.GetQuaternion(), + PlatformId = reader.GetShort() + }; + throwables.Add(data); + } + + return throwables; + } + + public static void PutInteractivesStates(this NetDataWriter writer, List interactiveObjectsData) + { + writer.Put(interactiveObjectsData.Count); + for (int i = 0; i < interactiveObjectsData.Count; i++) + { + writer.Put(interactiveObjectsData[i].NetId); + writer.Put(interactiveObjectsData[i].State); + writer.Put(interactiveObjectsData[i].IsBroken); + } + } + + public static List GetInteractivesStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + List interactivesStates = new(amount); + for (int i = 0; i < amount; i++) { - byte[] bytes = new byte[length]; - reader.GetBytes(bytes, length); - return bytes; + WorldInteractiveObject.GStruct384 data = new() + { + NetId = reader.GetInt(), + State = reader.GetByte(), + IsBroken = reader.GetBool() + }; + interactivesStates.Add(data); } - return Array.Empty(); - } - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, DateTime dateTime) - { - writer.Put(dateTime.ToOADate()); + return interactivesStates; } - /// - /// Deserializes a - /// - /// - /// A - public static DateTime GetDateTime(this NetDataReader reader) - { - return DateTime.FromOADate(reader.GetDouble()); + public static void PutLampStates(this NetDataWriter writer, Dictionary lampStates) + { + int amount = lampStates.Count; + writer.Put(amount); + foreach (KeyValuePair lampState in lampStates) + { + writer.Put(lampState.Key); + writer.Put(lampState.Value); + } } - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// The to serialize - public static void PutAirdropItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); - } + public static Dictionary GetLampStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetByte()); + } - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// An - public static Item GetAirdropItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); - - ContainerCollection[] containerCollections = [item as ContainerCollection]; - ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() - .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); - - return item; + return states; } - /// - /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. - /// - /// - /// The to serialize - public static void PutItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); + public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) + { + int amount = windowBreakerStates.Count; + writer.Put(amount); + foreach (KeyValuePair windowBreakerState in windowBreakerStates) + { + writer.Put(windowBreakerState.Key); + writer.Put(windowBreakerState.Value); + } } - /// - /// Gets a serialized - /// - /// - /// An - public static Item GetItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + public static Dictionary GetWindowBreakerStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetVector3()); + } + + return states; } - } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs new file mode 100644 index 00000000..1241c5cc --- /dev/null +++ b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs @@ -0,0 +1,72 @@ +using EFT.Interactive; +using LiteNetLib.Utils; +using System.Collections.Generic; +using UnityEngine; + +namespace Fika.Core.Networking.Packets.GameWorld +{ + public struct ReconnectPacket : INetSerializable + { + public bool IsRequest; + public EReconnectDataType Type; + + public List ThrowableData; + public List InteractivesData; + public Dictionary LampStates; + public Dictionary WindowBreakerStates; + + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + Type = (EReconnectDataType)reader.GetByte(); + switch (Type) + { + case EReconnectDataType.Throwable: + ThrowableData = reader.GetThrowableData(); + break; + case EReconnectDataType.Interactives: + InteractivesData = reader.GetInteractivesStates(); + break; + case EReconnectDataType.LampControllers: + LampStates = reader.GetLampStates(); + break; + case EReconnectDataType.Windows: + WindowBreakerStates = reader.GetWindowBreakerStates(); + break; + default: + break; + } + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put((byte)Type); + switch (Type) + { + case EReconnectDataType.Throwable: + writer.PutThrowableData(ThrowableData); + break; + case EReconnectDataType.Interactives: + writer.PutInteractivesStates(InteractivesData); + break; + case EReconnectDataType.LampControllers: + writer.PutLampStates(LampStates); + break; + case EReconnectDataType.Windows: + writer.PutWindowBreakerStates(WindowBreakerStates); + break; + default: + break; + } + } + + public enum EReconnectDataType + { + Throwable, + Interactives, + LampControllers, + Windows + } + } +} From a05e49e303c0ff678d875c456f0ffe04f62c783b Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:23:43 +0200 Subject: [PATCH 03/83] Cleanup --- .../Networking/FikaSerializationExtensions.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Fika.Core/Networking/FikaSerializationExtensions.cs b/Fika.Core/Networking/FikaSerializationExtensions.cs index eb4e5a90..5fcc3b99 100644 --- a/Fika.Core/Networking/FikaSerializationExtensions.cs +++ b/Fika.Core/Networking/FikaSerializationExtensions.cs @@ -291,8 +291,8 @@ public static void PutInteractivesStates(this NetDataWriter writer, List interactivesStates = new(amount); - for (int i = 0; i < amount; i++) - { + for (int i = 0; i < amount; i++) + { WorldInteractiveObject.GStruct384 data = new() { NetId = reader.GetInt(), @@ -300,10 +300,10 @@ public static void PutInteractivesStates(this NetDataWriter writer, List lampStates) { @@ -314,19 +314,19 @@ public static void PutLampStates(this NetDataWriter writer, Dictionary GetLampStates(this NetDataReader reader) { int amount = reader.GetInt(); Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { + for (int i = 0; i < amount; i++) + { states.Add(reader.GetInt(), reader.GetByte()); - } + } return states; - } + } public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) { @@ -337,18 +337,18 @@ public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary< writer.Put(windowBreakerState.Key); writer.Put(windowBreakerState.Value); } - } + } public static Dictionary GetWindowBreakerStates(this NetDataReader reader) { int amount = reader.GetInt(); Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { + for (int i = 0; i < amount; i++) + { states.Add(reader.GetInt(), reader.GetVector3()); - } + } return states; - } + } } } From 21ec1ad7e57fcb8e4f6ced817414c0b26fe072e4 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:59:17 +0200 Subject: [PATCH 04/83] Working reconnection --- Fika.Core/Coop/Components/CoopHandler.cs | 82 +- Fika.Core/Coop/Custom/FikaHealthBar.cs | 11 +- Fika.Core/Coop/GameMode/CoopGame.cs | 168 +- .../ObservedHealthController.cs | 75 +- Fika.Core/Coop/Players/ObservedCoopPlayer.cs | 24 +- Fika.Core/Coop/Utils/FikaBackendUtils.cs | 4 +- Fika.Core/Coop/Utils/NetManagerUtils.cs | 39 +- Fika.Core/Networking/FikaClient.cs | 109 +- Fika.Core/Networking/FikaServer.cs | 2221 +++++++++-------- .../Models/RegisterPlayerRequest.cs | 4 +- .../Packets/GameWorld/GenericPacket.cs | 63 +- .../Packets/GameWorld/ReconnectPacket.cs | 104 +- Fika.Core/UI/Custom/MatchMakerUIScript.cs | 8 +- 13 files changed, 1697 insertions(+), 1215 deletions(-) diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index d1cf872a..1248a857 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -24,17 +24,21 @@ namespace Fika.Core.Coop.Components /// public class CoopHandler : MonoBehaviour { - #region Fields/Properties + #region Fields/Properties public CoopGame LocalGameInstance { get; internal set; } public string ServerId { get; set; } = null; public Dictionary Players = []; public List HumanPlayers = []; public int AmountOfHumans = 1; public List ExtractedPlayers = []; - ManualLogSource Logger; public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; public List queuedProfileIds = []; + + private ManualLogSource Logger; private Queue spawnQueue = new(50); + private bool ready; + private bool isClient; + private float charSyncCounter; public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool isAI, int netId) { @@ -49,7 +53,6 @@ public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool i internal FikaBTRManager_Client clientBTR = null; internal FikaBTRManager_Host serverBTR = null; - internal static GameObject CoopHandlerParent; #endregion @@ -92,28 +95,26 @@ public static string GetServerId() #region Unity Component Methods - /// - /// Unity Component Awake Method - /// protected void Awake() { - // ---------------------------------------------------- - // Create a BepInEx Logger for CoopHandler Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHandler"); } - /// - /// Unity Component Start Method - /// protected void Start() { + RunAsyncTasks = true; + if (FikaBackendUtils.IsClient) { - _ = Task.Run(ReadFromServerCharactersLoop); + //_ = Task.Run(ReadFromServerCharactersLoop); + isClient = true; + charSyncCounter = 0f; } if (FikaBackendUtils.IsServer) { + isClient = false; + ready = true; Singleton.Instance.World_0.RegisterNetworkInteractionObjects(null); } } @@ -186,7 +187,7 @@ public EQuitState GetQuitState() /// /// This handles the ways of exiting the active game session /// - void ProcessQuitting() + private void ProcessQuitting() { EQuitState quitState = GetQuitState(); @@ -230,6 +231,11 @@ void ProcessQuitting() } } + public void SetReady(bool state) + { + ready = state; + } + protected private void Update() { if (LocalGameInstance == null) @@ -237,38 +243,40 @@ protected private void Update() return; } + if (!ready) + { + return; + } + if (spawnQueue.Count > 0) { SpawnPlayer(spawnQueue.Dequeue()); } ProcessQuitting(); - } - - #endregion - private async Task ReadFromServerCharactersLoop() - { - while (RunAsyncTasks) + if (isClient) { - CoopGame coopGame = LocalGameInstance; - int waitTime = 2500; - if (coopGame.Status == GameStatus.Started) - { - waitTime = 15000; - } - await Task.Delay(waitTime); + charSyncCounter += Time.deltaTime; + int waitTime = LocalGameInstance.Status == GameStatus.Started ? 15 : 2; - if (Players == null) + if (charSyncCounter > waitTime) { - continue; - } + charSyncCounter = 0f; - ReadFromServerCharacters(); + if (Players == null) + { + return; + } + + SyncPlayersWithServer(); + } } } - private void ReadFromServerCharacters() + #endregion + + private void SyncPlayersWithServer() { AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId); @@ -345,6 +353,8 @@ await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.Pool { // TODO: Spawn them as corpses? // Run 'OnDead' + otherPlayer.OnDead(EDamageType.Undefined); + otherPlayer.NetworkHealthController.IsAlive = false; } if (FikaBackendUtils.IsServer) @@ -376,7 +386,13 @@ await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.Pool public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAlive = true, bool isAI = false) { - foreach (IPlayer player in Singleton.Instance.RegisteredPlayers) + GameWorld gameWorld = Singleton.Instance; + if (gameWorld == null) + { + return; + } + + foreach (IPlayer player in gameWorld.RegisteredPlayers) { if (player.ProfileId == profile.ProfileId) { @@ -384,7 +400,7 @@ public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAl } } - foreach (IPlayer player in Singleton.Instance.AllAlivePlayersList) + foreach (IPlayer player in gameWorld.AllAlivePlayersList) { if (player.ProfileId == profile.ProfileId) { diff --git a/Fika.Core/Coop/Custom/FikaHealthBar.cs b/Fika.Core/Coop/Custom/FikaHealthBar.cs index 782dcdb4..1d354aa5 100644 --- a/Fika.Core/Coop/Custom/FikaHealthBar.cs +++ b/Fika.Core/Coop/Custom/FikaHealthBar.cs @@ -42,6 +42,15 @@ protected void Awake() CreateHealthBar(); } + public void ClearEffects() + { + foreach (HealthBarEffect effect in effects) + { + effect.Remove(); + } + effects.Clear(); + } + protected void Update() { if (currentPlayer != null) @@ -467,8 +476,6 @@ protected void OnDestroy() currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; - - playerPlate.gameObject.SetActive(false); effects.Clear(); Destroy(this); diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 309448ce..3c5546a0 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -246,49 +246,6 @@ public override void SetMatchmakerStatus(string status, float? progress = null) } } - /// - /// Creates and initializes the - /// - /// - /// If no ServerId was found - public Task CreateCoopHandler() - { - Logger.LogInfo("Creating CoopHandler..."); - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler != null) - { - Destroy(coopHandler); - } - - if (CoopHandler.CoopHandlerParent != null) - { - Destroy(CoopHandler.CoopHandlerParent); - CoopHandler.CoopHandlerParent = null; - } - - if (CoopHandler.CoopHandlerParent == null) - { - CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); - DontDestroyOnLoad(CoopHandler.CoopHandlerParent); - } - - coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); - coopHandler.LocalGameInstance = this; - - if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) - { - coopHandler.ServerId = FikaBackendUtils.GetGroupId(); - } - else - { - Destroy(coopHandler); - Logger.LogError("No Server Id found, Deleting Coop Handler"); - throw new MissingReferenceException("No Server Id found"); - } - - return Task.CompletedTask; - } - #region Bot /// /// Returns all human players @@ -701,7 +658,7 @@ public override IEnumerator vmethod_1() CoopPlayer coopPlayer = (CoopPlayer)PlayerOwner.Player; coopPlayer.PacketSender.Init(); - int timeBeforeDeployLocal = Singleton.Instance.TimeBeforeDeployLocal; + int timeBeforeDeployLocal = FikaBackendUtils.IsReconnect ? 3 : Singleton.Instance.TimeBeforeDeployLocal; DateTime dateTime = EFTDateTimeClass.Now.AddSeconds(timeBeforeDeployLocal); new MatchmakerFinalCountdown.FinalCountdownScreenClass(Profile_0, dateTime).ShowScreen(EScreenState.Root); MonoBehaviourSingleton.Instance.FadeInVolumeBeforeRaid(timeBeforeDeployLocal); @@ -891,10 +848,8 @@ public override async Task vmethod_2(int playerId, Vector3 position EUpdateQueue updateQueue, Player.EUpdateMode armsUpdateMode, Player.EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, Func getAimingSensitivity, IStatisticsManager statisticsManager, AbstractQuestControllerClass questController, AbstractAchievementControllerClass achievementsController) - { - await CreateCoopHandler(); - - profile.SetSpawnedInSession(profile.Side == EPlayerSide.Savage); + { + 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, @@ -909,7 +864,6 @@ public override async Task vmethod_2(int playerId, Vector3 position throw new MissingComponentException("CoopHandler was missing during CoopGame init"); } - if (RaidSettings.MetabolismDisabled) { myPlayer.HealthController.DisableMetabolism(); @@ -929,10 +883,9 @@ public override async Task vmethod_2(int playerId, Vector3 position GameObject customButton = null; await NetManagerUtils.SetupGameVariables(isServer, coopPlayer); - customButton = CreateCancelButton(myPlayer, coopPlayer, customButton); + customButton = CreateCancelButton(myPlayer, customButton); - - if (!isServer) + if (!isServer && !FikaBackendUtils.IsReconnect) { SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(coopPlayer.Profile), coopPlayer.HealthController.IsAlive, false, coopPlayer.Transform.position, coopPlayer.NetId); FikaClient client = Singleton.Instance; @@ -952,6 +905,11 @@ public override async Task vmethod_2(int playerId, Vector3 position Destroy(customButton); + if (FikaBackendUtils.IsReconnect && !FikaBackendUtils.ReconnectPosition.Equals(Vector3.zero)) + { + myPlayer.Teleport(FikaBackendUtils.ReconnectPosition); + } + return myPlayer; } @@ -962,7 +920,7 @@ public override async Task vmethod_2(int playerId, Vector3 position /// /// /// - private GameObject CreateCancelButton(LocalPlayer myPlayer, CoopPlayer coopPlayer, GameObject customButton) + private GameObject CreateCancelButton(LocalPlayer myPlayer, GameObject customButton) { if (myPlayer.Side is EPlayerSide.Savage) { @@ -1010,7 +968,22 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU Status = GameStatus.Running; UnityEngine.Random.InitState((int)EFTDateTimeClass.Now.Ticks); - LocationSettingsClass.Location location; + if (isServer) + { + await NetManagerUtils.CreateCoopHandler(); + } + + CoopHandler handler = CoopHandler.GetCoopHandler(); + if (handler != null) + { + handler.LocalGameInstance = this; + } + else + { + throw new NullReferenceException("CoopHandler was missing!"); + } + + LocationSettingsClass.Location location; if (Location_0.IsHideout) { location = Location_0; @@ -1029,7 +1002,14 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU else { FikaBackendUtils.ScreenController.ChangeStatus("Retrieving loot from server..."); - await AwaitLootFromServer(); + if (!FikaBackendUtils.IsReconnect) + { + await RetrieveLootFromServer(true); + } + else + { + await RetrieveLootFromServer(false); + } location = new() { Loot = LootItems @@ -1038,12 +1018,17 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU } } - ApplicationConfigClass config = BackendConfigAbstractClass.Config; + ApplicationConfigClass config = BackendConfigAbstractClass.Config; if (config.FixedFrameRate > 0f) { FixedDeltaTime = 1f / config.FixedFrameRate; } + if (FikaBackendUtils.IsReconnect) + { + await GetReconnectProfile(ProfileId); + } + using (CounterCreatorAbstractClass.StartWithToken("player create")) { Player player = await CreateLocalPlayer(); @@ -1056,10 +1041,54 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU StartHandler startHandler = new(this, botsSettings, SpawnSystem, runCallback); + if (FikaBackendUtils.IsReconnect) + { + await Reconnect(); + } + + handler.SetReady(true); + await method_11(location, startHandler.FinishLoading); } - private async Task AwaitLootFromServer() + private async Task GetReconnectProfile(string profileId) + { + Profile_0 = null; + + ReconnectPacket reconnectPacket = new(true) + { + InitialRequest = true, + ProfileId = profileId + }; + FikaClient client = Singleton.Instance; + client.Writer.Reset(); + client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); + + do + { + await Task.Delay(250); + } while (Profile_0 == null); + } + + private async Task Reconnect() + { + FikaBackendUtils.ScreenController.ChangeStatus($"Reconnecting..."); + + ReconnectPacket reconnectPacket = new(true) + { + ProfileId = ProfileId + }; + FikaClient client = Singleton.Instance; + client.Writer.Reset(); + client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); + + do + { + await Task.Delay(1000); + } while (!client.ReconnectDone); + } + + private async Task RetrieveLootFromServer(bool register) { FikaClient client = Singleton.Instance; WorldLootPacket packet = new(true); @@ -1074,8 +1103,11 @@ private async Task AwaitLootFromServer() } } while (!HasReceivedLoot); - RegisterPlayerRequest request = new(0, Location_0.Id, 0); - await FikaRequestHandler.RegisterPlayer(request); + if (register) + { + RegisterPlayerRequest request = new(0, Location_0.Id, 0); + await FikaRequestHandler.RegisterPlayer(request); + } } /// @@ -1112,7 +1144,8 @@ private async Task CreateLocalPlayer() eupdateMode = Player.EUpdateMode.Manual; } - spawnPoints = SpawnPointManagerClass.CreateFromScene(new DateTime?(EFTDateTimeClass.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; 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); @@ -2173,6 +2206,7 @@ public override void Dispose() FikaBackendUtils.HostExpectedNumberOfPlayers = 1; FikaBackendUtils.RequestFikaWorld = false; FikaBackendUtils.IsReconnect = false; + FikaBackendUtils.ReconnectPosition = Vector3.zero; if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { @@ -2273,5 +2307,17 @@ public void ExitOverride() Logger.LogDebug("method_6"); return; } - } + + public byte[] GetHostLootItems() + { + if (HostLootItems == null || HostLootItems.Length == 0) + { + GClass1211 lootItems = new(Singleton.Instance.GetJsonLootItems() + .Where(x => x as CorpseLootItemClass is null)); + return SimpleZlib.CompressToBytes(lootItems.ToJson([]), 6); + } + + return HostLootItems; + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs index 27524938..66f58683 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs @@ -1,7 +1,9 @@ // © 2024 Lacyway All Rights Reserved using EFT; +using EFT.HealthSystem; using EFT.InventoryLogic; +using System.Collections.Generic; namespace Fika.Core.Coop.ObservedClasses { @@ -16,5 +18,76 @@ public override void CancelApplyingItem() { // Do nothing } - } + + public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass health = null) + { + Profile.ProfileHealthClass profileHealthClass; + if ((profileHealthClass = health) == null) + { + Profile.ProfileHealthClass profileHealthClass2 = new() + { + BodyParts = GClass769.GetDictWith(), + Energy = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_0.Current, + Minimum = healthValue_0.Minimum, + Maximum = healthValue_0.Maximum + }, + Hydration = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_1.Current, + Minimum = healthValue_1.Minimum, + Maximum = healthValue_1.Maximum + }, + Temperature = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_2.Current, + Minimum = healthValue_2.Minimum, + Maximum = healthValue_2.Maximum + } + }; + profileHealthClass = profileHealthClass2; + profileHealthClass2.Poison = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_3.Current, + Minimum = healthValue_3.Minimum, + Maximum = healthValue_3.Maximum + }; + } + health = profileHealthClass; + foreach (KeyValuePair keyValuePair in Dictionary_0) + { + keyValuePair.Deconstruct(out EBodyPart ebodyPart, out BodyPartState bodyPartState); + EBodyPart ebodyPart2 = ebodyPart; + BodyPartState bodyPartState2 = bodyPartState; + if (!health.BodyParts.TryGetValue(ebodyPart2, out Profile.ProfileHealthClass.GClass1770 gclass)) + { + gclass = new Profile.ProfileHealthClass.GClass1770(); + health.BodyParts.Add(ebodyPart2, gclass); + } + gclass.Health = new Profile.ProfileHealthClass.ValueInfo + { + Current = bodyPartState2.Health.Current, + Maximum = bodyPartState2.Health.Maximum + }; + gclass.Effects ??= []; + } + + foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) + { + if (gclass.State != EEffectState.Residued) + { + Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; + gclass2.Effects ??= []; + gclass2.Effects.Add(gclass.GetType().Name, new() + { + Time = gclass.TimeLeft, + ExtraData = gclass.StoreObj + }); + } + } + + return health; + } + } } diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index 7db9adb4..936f1292 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -36,12 +36,16 @@ public class ObservedCoopPlayer : CoopPlayer #region Fields and Properties public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; private float observedFixedTime = 0f; - private FikaHealthBar healthBar = null; + public FikaHealthBar HealthBar + { + get => healthBar; + } + private FikaHealthBar healthBar = null; private Coroutine waitForStartRoutine; private bool isServer; - public NetworkHealthControllerAbstractClass NetworkHealthController + public ObservedHealthController NetworkHealthController { - get => HealthController as NetworkHealthControllerAbstractClass; + get => HealthController as ObservedHealthController; } private readonly ObservedVaultingParametersClass ObservedVaultingParameters = new(); public override bool CanBeSnapped => false; @@ -114,10 +118,10 @@ public override float SqrCameraDistance } return base.SqrCameraDistance; } - } - #endregion + } + #endregion - public static async Task CreateObservedPlayer(int playerId, Vector3 position, Quaternion rotation, + public static async Task CreateObservedPlayer(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, @@ -646,9 +650,9 @@ public override void OnDead(EDamageType damageType) { StartCoroutine(DestroyNetworkedComponents()); - if (healthBar != null) + if (HealthBar != null) { - Destroy(healthBar); + Destroy(HealthBar); } if (FikaPlugin.ShowNotifications.Value) @@ -1139,9 +1143,9 @@ public override void OnDestroy() HandsController.Destroy(); } } - if (healthBar != null) + if (HealthBar != null) { - Destroy(healthBar); + Destroy(HealthBar); } if (Singleton.Instantiated) { diff --git a/Fika.Core/Coop/Utils/FikaBackendUtils.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs index 6ef99e5d..cef95128 100644 --- a/Fika.Core/Coop/Utils/FikaBackendUtils.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -7,6 +7,7 @@ using System; using System.Reflection; using System.Threading.Tasks; +using UnityEngine; namespace Fika.Core.Coop.Utils { @@ -46,6 +47,7 @@ public static bool IsSinglePlayer public static bool IsHostNatPunch = false; public static string HostLocationId; public static bool RequestFikaWorld = false; + public static Vector3 ReconnectPosition = Vector3.zero; private static string groupId; private static string raidCode; @@ -120,7 +122,7 @@ public static async Task CreateMatch(string profileId, string hostUsername, Raid public static string GenerateRaidCode(int length) { - Random random = new(); + System.Random random = new(); char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray(); string raidCode = ""; for (int i = 0; i < length; i++) diff --git a/Fika.Core/Coop/Utils/NetManagerUtils.cs b/Fika.Core/Coop/Utils/NetManagerUtils.cs index f9b83dfd..2e338bb9 100644 --- a/Fika.Core/Coop/Utils/NetManagerUtils.cs +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -198,5 +198,42 @@ public static void StopPinger() } } } - } + + public static Task CreateCoopHandler() + { + logger.LogInfo("Creating CoopHandler..."); + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler != null) + { + GameObject.Destroy(coopHandler); + } + + if (CoopHandler.CoopHandlerParent != null) + { + GameObject.Destroy(CoopHandler.CoopHandlerParent); + CoopHandler.CoopHandlerParent = null; + } + + if (CoopHandler.CoopHandlerParent == null) + { + CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); + GameObject.DontDestroyOnLoad(CoopHandler.CoopHandlerParent); + } + + coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); + + if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) + { + coopHandler.ServerId = FikaBackendUtils.GetGroupId(); + } + else + { + GameObject.Destroy(coopHandler); + logger.LogError("No Server Id found, Deleting Coop Handler"); + throw new MissingReferenceException("No Server Id found"); + } + + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index ca05c7e5..eb44359a 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -32,6 +32,7 @@ using System.Net.Sockets; using System.Threading.Tasks; using UnityEngine; +using UnityEngine.Networking.Match; using static Fika.Core.Utils.ColorUtils; namespace Fika.Core.Networking @@ -47,6 +48,7 @@ public class FikaClient : MonoBehaviour, INetEventListener public int ConnectedClients = 0; public int ReadyClients = 0; public bool HostReady = false; + public bool ReconnectDone = false; public NetManager NetClient { get @@ -73,12 +75,15 @@ public bool Started private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); private NetDataWriter dataWriter = new(); private FikaChat fikaChat; + private string myProfileId; public async void Init() { NetworkGameSession.RTT = 0; NetworkGameSession.LossPercent = 0; + myProfileId = FikaBackendUtils.Profile.ProfileId; + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); @@ -112,8 +117,9 @@ public async void Init() packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); - netClient = new NetManager(this) + netClient = new NetManager(this) { UnconnectedMessagesEnabled = true, UpdateTime = 15, @@ -126,6 +132,9 @@ public async void Init() ReconnectDelay = 1 * 1000 }; + await NetManagerUtils.CreateCoopHandler(); + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + if (FikaBackendUtils.IsHostNatPunch) { NetManagerUtils.DestroyPingingClient(); @@ -157,7 +166,89 @@ public async void Init() FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); } - private void OnWorldLootPacketReceived(WorldLootPacket packet) + private void OnReconnectPacketReceived(ReconnectPacket packet) + { + if (!packet.IsRequest) + { + switch (packet.Type) + { + case ReconnectPacket.EReconnectDataType.Throwable: + Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); + break; + case ReconnectPacket.EReconnectDataType.Interactives: + { + WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; + Dictionary netIdDictionary = []; + { + foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) + { + netIdDictionary.Add(data.NetId, data); + } + } + foreach (WorldInteractiveObject item in worldInteractiveObjects) + { + if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) + { + item.SetInitialSyncState(value); + } + } + break; + } + case ReconnectPacket.EReconnectDataType.LampControllers: + { + Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) + .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); + + foreach (KeyValuePair lampState in packet.LampStates) + { + if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) + { + if (lampController.LampState != (Turnable.EState)lampState.Value) + { + lampController.Switch((Turnable.EState)lampState.Value); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.Windows: + { + Dictionary windowBreakerStates = packet.WindowBreakerStates; + foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) + { + if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) + { + try + { + DamageInfo damageInfo = default; + damageInfo.HitPoint = hitPosition; + windowBreaker.MakeHit(in damageInfo, true); + } + catch (Exception ex) + { + logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.OwnCharacter: + coopHandler.LocalGameInstance.Profile_0 = packet.Profile; + coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; + FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; + break; + case ReconnectPacket.EReconnectDataType.Finished: + ReconnectDone = true; + break; + default: + break; + } + } + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet) { if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) { @@ -259,7 +350,6 @@ private void OnTextMessagePacketReceived(TextMessagePacket packet) public void SetupGameVariables(CoopPlayer coopPlayer) { - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); MyPlayer = coopPlayer; if (FikaPlugin.EnableChat.Value) { @@ -336,7 +426,7 @@ private void OnSendCharacterPacketReceived(SendCharacterPacket packet) return; } - if (packet.PlayerInfo.Profile.ProfileId != MyPlayer.ProfileId) + if (packet.PlayerInfo.Profile.ProfileId != myProfileId) { coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); } @@ -650,6 +740,17 @@ private void OnGenericPacketReceived(GenericPacket packet) } } break; + case EPackageType.ClearEffects: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + if (playerToApply is ObservedCoopPlayer observedPlayer) + { + observedPlayer.HealthBar.ClearEffects(); + } + } + } + break; } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 522e581c..36a53871 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -2,6 +2,7 @@ using BepInEx.Logging; using Comfort.Common; +using ComponentAce.Compression.Libs.zlib; using EFT; using EFT.AssetsManager; using EFT.Interactive; @@ -20,6 +21,7 @@ using Fika.Core.Networking.Http.Models; using Fika.Core.Networking.Packets.GameWorld; using Fika.Core.Utils; +using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; using Open.Nat; @@ -34,723 +36,884 @@ using System.Threading; using System.Threading.Tasks; using UnityEngine; +using static Fika.Core.Networking.Packets.GameWorld.ReconnectPacket; using static Fika.Core.Utils.ColorUtils; namespace Fika.Core.Networking { - public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener - { - public NetPacketProcessor packetProcessor = new(); - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public List PlayersMissing = []; - public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); - public int ReadyClients = 0; - public NetManager NetServer - { - get - { - return netServer; - } - } - public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); - public bool hasHadPeer = false; - public bool Started - { - get - { - if (netServer == null) - { - return false; - } - return netServer.IsRunning; - } - } - - private NetManager netServer; - public NetDataWriter Writer => dataWriter; - private readonly NetDataWriter dataWriter = new(); - private int Port => FikaPlugin.UDPPort.Value; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); - private int currentNetId; - private FikaChat fikaChat; - private CancellationTokenSource natIntroduceRoutineCts; - private int statisticsCounter = 0; - - public async Task Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - // Start at 1 to avoid having 0 and making us think it's working when it's not - currentNetId = 1; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); - - netServer = new NetManager(this) - { - BroadcastReceiveEnabled = true, - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - AutoRecycle = true, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - NatPunchEnabled = true - }; - - if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) - { - bool upnpFailed = false; - - 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(); - - 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) - { - Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); - } - } - else if (FikaPlugin.ForceIP.Value != "") - { - MyExternalIP = FikaPlugin.ForceIP.Value; - } - else - { - try - { - HttpClient client = new(); - string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); - MyExternalIP = ipAdress.Replace("\n", ""); - client.Dispose(); - } - catch (Exception) - { - Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); - } - } - - if (FikaPlugin.UseNatPunching.Value) - { - 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 - { - if (FikaPlugin.ForceBindIP.Value != "Disabled") - { - netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); - } - else - { - netServer.Start(Port); - } - } - - logger.LogInfo("Started Fika Server"); - - NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - - string[] Ips = []; - - foreach (string ip in FikaPlugin.Instance.LocalIPs) - { - if (ValidateLocalIP(ip)) - { - 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, FikaBackendUtils.IsDedicatedGame); - FikaRequestHandler.UpdateSetHost(body); - - FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); - } - - private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - WorldLootPacket response = new(false) - { - Data = coopGame.HostLootItems - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (Singleton.Instantiated) - { - World world = Singleton.Instance.World_0; - if (world.Interactables != null) - { - InteractableInitPacket response = new(false) - { - Interactables = world.Interactables - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - if (packet.IsRequest) - { - SpawnpointPacket response = new(false) - { - Name = coopGame.GetSpawnpointName() - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) - { - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - private bool ValidateLocalIP(string LocalIP) - { - if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) - { - return true; - } - - //Check for RFC1918's 20 bit block. - int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); - - if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) - { - return true; - } - - return false; - } - - 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() - { - int netId = currentNetId; - currentNetId++; - - return netId; - } - - public void SetupGameVariables(CoopPlayer coopPlayer) - { - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } - - private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - 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); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - AssignNetIdPacket assignNetIdPacket = new() - { - NetId = netId - }; - - dataWriter.Reset(); - packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); - peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); - } - - private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) - { - // This shouldn't happen - } - - private void OnMinePacketReceived(MinePacket packet, NetPeer peer) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (coopHandler.serverBTR.CanPlayerEnter(player)) - { - coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - BTRInteractionPacket newPacket = new(packet.NetId) - { - HasInteractPacket = false - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); - } - } - } - } - - private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (FikaBackendUtils.Nodes != null) - { - WeatherPacket weatherPacket2 = new() - { - IsRequest = false, - HasData = true, - Amount = FikaBackendUtils.Nodes.Length, - WeatherClasses = FikaBackendUtils.Nodes - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); - }; - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - ExfiltrationPacket exfilPacket = new(false) - { - ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, - ExfiltrationPoints = [] - }; - - foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) - { - exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) - { - exfilPacket.HasScavExfils = true; - exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; - exfilPacket.ScavExfiltrationPoints = []; - - foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) - { - exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); - } - } - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) - { - if (packet.PacketType == EPackageType.ClientExtract) - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); + public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener + { + public NetPacketProcessor packetProcessor = new(); + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public List PlayersMissing = []; + public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); + public int ReadyClients = 0; + public NetManager NetServer + { + get + { + return netServer; + } + } + public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); + public bool hasHadPeer = false; + public bool Started + { + get + { + if (netServer == null) + { + return false; + } + return netServer.IsRunning; + } + } + + private NetManager netServer; + public NetDataWriter Writer => dataWriter; + private readonly NetDataWriter dataWriter = new(); + private int Port => FikaPlugin.UDPPort.Value; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); + private int currentNetId; + private FikaChat fikaChat; + private CancellationTokenSource natIntroduceRoutineCts; + private int statisticsCounter = 0; + + public async Task Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + // Start at 1 to avoid having 0 and making us think it's working when it's not + currentNetId = 1; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); + + netServer = new NetManager(this) + { + BroadcastReceiveEnabled = true, + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + AutoRecycle = true, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + NatPunchEnabled = true + }; + + if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) + { + bool upnpFailed = false; + + 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(); + + 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) + { + Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); + } + } + else if (FikaPlugin.ForceIP.Value != "") + { + MyExternalIP = FikaPlugin.ForceIP.Value; + } + else + { + try + { + HttpClient client = new(); + string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); + MyExternalIP = ipAdress.Replace("\n", ""); + client.Dispose(); + } + catch (Exception) + { + Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); + } + } + + if (FikaPlugin.UseNatPunching.Value) + { + 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 + { + if (FikaPlugin.ForceBindIP.Value != "Disabled") + { + netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); + } + else + { + netServer.Start(Port); + } + } + + logger.LogInfo("Started Fika Server"); + + NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + + string[] Ips = []; + + foreach (string ip in FikaPlugin.Instance.LocalIPs) + { + if (ValidateLocalIP(ip)) + { + 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, FikaBackendUtils.IsDedicatedGame); + FikaRequestHandler.UpdateSetHost(body); + + FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); + } + + private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (packet.InitialRequest) + { + NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId && player is ObservedCoopPlayer observedCoopPlayer) + { + ReconnectPacket ownCharacterPacket = new(false) + { + Type = EReconnectDataType.OwnCharacter, + Profile = observedCoopPlayer.Profile, + ProfileHealthClass = observedCoopPlayer.NetworkHealthController.Store(), + PlayerPosition = observedCoopPlayer.Position + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref ownCharacterPacket, DeliveryMethod.ReliableOrdered); + + observedCoopPlayer.HealthBar.ClearEffects(); + GenericPacket clearEffectsPacket = new(EPackageType.ClearEffects) + { + NetId = observedCoopPlayer.NetId + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref clearEffectsPacket, DeliveryMethod.ReliableUnordered, peer); + } + } + + return; + } + + GameWorld gameWorld = Singleton.Instance; + Traverse worldTraverse = Traverse.Create(gameWorld.World_0); + + GClass724.GStruct43 grenades = gameWorld.Grenades.GetValuesEnumerator(); + List smokeData = []; + foreach (Throwable item in grenades) + { + if (item is SmokeGrenade smokeGrenade) + { + smokeData.Add(smokeGrenade.NetworkData); + } + } + + if (smokeData.Count > 0) + { + ReconnectPacket throwablePacket = new(false) + { + Type = EReconnectDataType.Throwable, + ThrowableData = smokeData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref throwablePacket, DeliveryMethod.ReliableOrdered); + } + + List interactivesData = []; + WorldInteractiveObject[] worldInteractiveObjects = worldTraverse.Field("worldInteractiveObject_0").Value; + foreach (WorldInteractiveObject interactiveObject in worldInteractiveObjects) + { + if ((interactiveObject.DoorState != interactiveObject.InitialDoorState && interactiveObject.DoorState != EDoorState.Interacting) + || (interactiveObject is Door door && door.IsBroken)) + { + interactivesData.Add(interactiveObject.GetStatusInfo(true)); + } + } + + if (interactivesData.Count > 0) + { + ReconnectPacket interactivePacket = new(false) + { + Type = EReconnectDataType.Interactives, + InteractivesData = interactivesData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref interactivePacket, DeliveryMethod.ReliableOrdered); + } + + IEnumerable lampControllers = LocationScene.GetAllObjects(false); + Dictionary lampStates = []; + foreach (LampController controller in lampControllers) + { + lampStates.Add(controller.NetId, (byte)controller.LampState); + } + + if (lampStates.Count > 0) + { + ReconnectPacket lampPacket = new(false) + { + Type = EReconnectDataType.LampControllers, + LampStates = lampStates + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref lampPacket, DeliveryMethod.ReliableOrdered); + } + + GClass724.GStruct43 windows = gameWorld.Windows.GetValuesEnumerator(); + Dictionary windowData = []; + foreach (WindowBreaker window in windows) + { + if (window.AvailableToSync && window.IsDamaged) + { + windowData.Add(window.NetId, window.FirstHitPosition.Value); + } + } + + if (windowData.Count > 0) + { + ReconnectPacket windowPacket = new(false) + { + Type = EReconnectDataType.Windows, + WindowBreakerStates = windowData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); + } - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - else if (packet.PacketType == EPackageType.LoadBot) - { - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.IncreaseLoadedPlayers(packet.BotNetId); - - return; - } - else if (packet.PacketType == EPackageType.ExfilCountdown) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) - { - ReadyClients += packet.ReadyPlayers; - - InformationPacket respondPackage = new(false) - { - NumberOfPlayers = netServer.ConnectedPeersCount, - ReadyPlayers = ReadyClients, - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - if (packet.IsRequest) - { foreach (CoopPlayer player in coopHandler.Players.Values) { - if (player.ProfileId == packet.ProfileId) - { - continue; - } - - if (packet.Characters.Contains(player.ProfileId)) - { - continue; - } - - AllCharacterRequestPacket requestPacket = new(player.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = player.Profile - }, - IsAlive = player.HealthController.IsAlive, - IsAI = player is CoopBot, - Position = player.Transform.position, - NetId = player.NetId - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - PlayersMissing.Add(packet.ProfileId); - 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)) - { - 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); - PlayersMissing.Remove(packet.ProfileId); - } - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), + player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref characterPacket, DeliveryMethod.ReliableOrdered); + } + + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId) + { + AssignNetIdPacket assignPacket = new() + { + NetId = player.NetId + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref assignPacket, DeliveryMethod.ReliableOrdered); + } + } + + ReconnectPacket finishPacket = new(false) + { + Type = EReconnectDataType.Finished + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); - using BinaryReader binaryReader = new(memoryStream); - try - { - GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); - - InventoryOperationHandler opHandler = new() - { - opResult = result, - operationId = packet.ItemControllerExecutePacket.CallbackId, - netId = playerToApply.NetId, - peer = peer, - server = this - }; - - OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); - SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); - - // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. - // Unknown what problems this might cause so far. - if (result.Value is UnloadOperationClass unloadOperation) - { - if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) - { - Item item = internalSplitOperation.To.Item; - if (item != null) - { - if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) - { - item.Id = internalSplitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - } - - // TODO: Same as above. - if (result.Value is SplitOperationClass splitOperation) - { - Item item = splitOperation.To.Item; - if (item != null) - { - if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) - { - item.Id = splitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - - /*// Fix for folding not replicating + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + WorldLootPacket response = new(false) + { + Data = coopGame.GetHostLootItems() + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + + private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (Singleton.Instantiated) + { + World world = Singleton.Instance.World_0; + if (world.Interactables != null) + { + InteractableInitPacket response = new(false) + { + Interactables = world.Interactables + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + } + + private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + if (packet.IsRequest) + { + SpawnpointPacket response = new(false) + { + Name = coopGame.GetSpawnpointName() + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) + { + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + + private bool ValidateLocalIP(string LocalIP) + { + if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) + { + return true; + } + + //Check for RFC1918's 20 bit block. + int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); + + if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) + { + return true; + } + + return false; + } + + 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() + { + int netId = currentNetId; + currentNetId++; + + return netId; + } + + public void SetupGameVariables(CoopPlayer coopPlayer) + { + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + 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); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + AssignNetIdPacket assignNetIdPacket = new() + { + NetId = netId + }; + + dataWriter.Reset(); + packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); + peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) + { + // This shouldn't happen + } + + private void OnMinePacketReceived(MinePacket packet, NetPeer peer) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (coopHandler.serverBTR.CanPlayerEnter(player)) + { + coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + BTRInteractionPacket newPacket = new(packet.NetId) + { + HasInteractPacket = false + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); + } + } + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (FikaBackendUtils.Nodes != null) + { + WeatherPacket weatherPacket2 = new() + { + IsRequest = false, + HasData = true, + Amount = FikaBackendUtils.Nodes.Length, + WeatherClasses = FikaBackendUtils.Nodes + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); + }; + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + ExfiltrationPacket exfilPacket = new(false) + { + ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, + ExfiltrationPoints = [] + }; + + foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) + { + exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) + { + exfilPacket.HasScavExfils = true; + exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; + exfilPacket.ScavExfiltrationPoints = []; + + foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) + { + exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); + } + } + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) + { + if (packet.PacketType == EPackageType.ClientExtract) + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + else if (packet.PacketType == EPackageType.LoadBot) + { + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.IncreaseLoadedPlayers(packet.BotNetId); + + return; + } + else if (packet.PacketType == EPackageType.ExfilCountdown) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) + { + ReadyClients += packet.ReadyPlayers; + + InformationPacket respondPackage = new(false) + { + NumberOfPlayers = netServer.ConnectedPeersCount, + ReadyPlayers = ReadyClients, + HostReady = coopHandler != null && coopHandler.LocalGameInstance.Status == GameStatus.Started + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); + } + + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + if (packet.IsRequest) + { + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (player.ProfileId == packet.ProfileId) + { + continue; + } + + if (packet.Characters.Contains(player.ProfileId)) + { + continue; + } + + AllCharacterRequestPacket requestPacket = new(player.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = player.Profile + }, + IsAlive = player.HealthController.IsAlive, + IsAI = player is CoopBot, + Position = player.Transform.position, + NetId = player.NetId + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + PlayersMissing.Add(packet.ProfileId); + 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)) + { + 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); + PlayersMissing.Remove(packet.ProfileId); + } + } + } + + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); + using BinaryReader binaryReader = new(memoryStream); + try + { + GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); + + InventoryOperationHandler opHandler = new() + { + opResult = result, + operationId = packet.ItemControllerExecutePacket.CallbackId, + netId = playerToApply.NetId, + peer = peer, + server = this + }; + + OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); + SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); + + // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. + // Unknown what problems this might cause so far. + if (result.Value is UnloadOperationClass unloadOperation) + { + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) + { + Item item = internalSplitOperation.To.Item; + if (item != null) + { + if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) + { + item.Id = internalSplitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + + // TODO: Same as above. + if (result.Value is SplitOperationClass splitOperation) + { + Item item = splitOperation.To.Item; + if (item != null) + { + if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) + { + item.Id = splitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + + /*// Fix for folding not replicating if (result.Value is GClass2858 foldOperation) { if (playerToApply.HandsController is CoopObservedFirearmController observedFirearmController) @@ -761,324 +924,324 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) } } }*/ - } - catch (Exception exception) - { - FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); - OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); - SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - 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(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) - { - if (!packet.IsRequest) - return; - - CoopGame game = coopHandler.LocalGameInstance; - if (game != null) - { - GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks); - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError("OnGameTimerPacketReceived: Game was null!"); - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - protected void Update() - { - netServer?.PollEvents(); - netServer?.NatPunchModule?.PollEvents(); - - statisticsCounter++; - if (statisticsCounter > 600) - { - statisticsCounter = 0; - SendStatisticsPacket(); - } - } - - private void SendStatisticsPacket() - { - int fps = (int)(1f / Time.unscaledDeltaTime); - StatisticsPacket packet = new(fps); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void OnDestroy() - { - netServer?.Stop(); - - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); - } - - public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable - { - if (peerToExclude != null) - { - if (NetServer.ConnectedPeersCount > 1) - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod, peerToExclude); - } - } - else - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod); - } - } - - public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - peer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); - logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); - - hasHadPeer = true; - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogError("[SERVER] error " + socketErrorCode); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.Broadcast) - { - logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); - NetDataWriter resp = new(); - resp.Put(1); - netServer.SendUnconnectedMessage(resp, remoteEndPoint); - } - else - { - if (reader.TryGetString(out string data)) - { - NetDataWriter resp; - - switch (data) - { - 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 - { - logger.LogError("PingingRequest: Could not parse string"); - } - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - } - - public void OnConnectionRequest(ConnectionRequest request) - { - request.AcceptIfKey("fika.core"); - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - 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) - { - timeSinceLastPeerDisconnected = DateTime.Now; - } - - if (FikaBackendUtils.IsDedicatedGame) - { - if (netServer.ConnectedPeersCount == 0) - { - foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) - { - if (profile is null) - { - continue; - } - - if (profile.ProfileId == RequestHandler.SessionId) - { - foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) - { - bodyPartHealth.Effects.Clear(); - bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; - } - } - } - - // End the raid - Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, - Singleton.Instance.MyExitStatus, - Singleton.Instance.MyExitLocation, 0); - } - } - } - - public void WriteNet(NetLogLevel level, string str, params object[] args) - { - Debug.LogFormat(str, args); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - 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; - public uint operationId; - public int netId; - public NetPeer peer; - public FikaServer server; - - internal void HandleResult(IResult result) - { - NetDataWriter writer = new(); - OperationCallbackPacket operationCallbackPacket; - - if (!result.Succeed) - { - FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); - operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - return; - } - - InventoryPacket packet = new(netId) - { - HasItemControllerExecutePacket = true - }; - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operationId, - OperationBytes = opBytes - }; - - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); - - operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - } - } - } + } + catch (Exception exception) + { + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + 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(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) + { + if (!packet.IsRequest) + return; + + CoopGame game = coopHandler.LocalGameInstance; + if (game != null) + { + GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks); + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError("OnGameTimerPacketReceived: Game was null!"); + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + protected void Update() + { + netServer?.PollEvents(); + netServer?.NatPunchModule?.PollEvents(); + + statisticsCounter++; + if (statisticsCounter > 600) + { + statisticsCounter = 0; + SendStatisticsPacket(); + } + } + + private void SendStatisticsPacket() + { + int fps = (int)(1f / Time.unscaledDeltaTime); + StatisticsPacket packet = new(fps); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void OnDestroy() + { + netServer?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); + } + + public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable + { + if (peerToExclude != null) + { + if (NetServer.ConnectedPeersCount > 1) + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod, peerToExclude); + } + } + else + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod); + } + } + + public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + peer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); + logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); + + hasHadPeer = true; + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogError("[SERVER] error " + socketErrorCode); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.Broadcast) + { + logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); + NetDataWriter resp = new(); + resp.Put(1); + netServer.SendUnconnectedMessage(resp, remoteEndPoint); + } + else + { + if (reader.TryGetString(out string data)) + { + NetDataWriter resp; + + switch (data) + { + 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 + { + logger.LogError("PingingRequest: Could not parse string"); + } + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + } + + public void OnConnectionRequest(ConnectionRequest request) + { + request.AcceptIfKey("fika.core"); + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + 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) + { + timeSinceLastPeerDisconnected = DateTime.Now; + } + + if (FikaBackendUtils.IsDedicatedGame) + { + if (netServer.ConnectedPeersCount == 0) + { + foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) + { + if (profile is null) + { + continue; + } + + if (profile.ProfileId == RequestHandler.SessionId) + { + foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) + { + bodyPartHealth.Effects.Clear(); + bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; + } + } + } + + // End the raid + Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, + Singleton.Instance.MyExitStatus, + Singleton.Instance.MyExitLocation, 0); + } + } + } + + public void WriteNet(NetLogLevel level, string str, params object[] args) + { + Debug.LogFormat(str, args); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + 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; + public uint operationId; + public int netId; + public NetPeer peer; + public FikaServer server; + + internal void HandleResult(IResult result) + { + NetDataWriter writer = new(); + OperationCallbackPacket operationCallbackPacket; + + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); + operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + return; + } + + InventoryPacket packet = new(netId) + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationId, + OperationBytes = opBytes + }; + + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); + + operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + } + } + } } diff --git a/Fika.Core/Networking/Models/RegisterPlayerRequest.cs b/Fika.Core/Networking/Models/RegisterPlayerRequest.cs index 7f0c59b8..9f38292b 100644 --- a/Fika.Core/Networking/Models/RegisterPlayerRequest.cs +++ b/Fika.Core/Networking/Models/RegisterPlayerRequest.cs @@ -14,10 +14,10 @@ public struct RegisterPlayerRequest [DataMember(Name = "variantId")] public int VariantId; - public RegisterPlayerRequest(int crc, string profileId, int variantId) + public RegisterPlayerRequest(int crc, string locationId, int variantId) { Crc = crc; - LocationId = profileId; + LocationId = locationId; VariantId = variantId; } } diff --git a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs index c108764f..98938d2e 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs @@ -30,35 +30,35 @@ public void Deserialize(NetDataReader reader) { NetId = reader.GetInt(); PacketType = (EPackageType)reader.GetInt(); - switch (PacketType) - { - case EPackageType.Ping: - PingLocation = reader.GetVector3(); - PingType = (PingFactory.EPingType)reader.GetByte(); - PingColor = reader.GetColor(); - Nickname = reader.GetString(); - LocaleId = reader.GetString(); - break; - case EPackageType.TrainSync: - DepartureTime = reader.GetLong(); - break; - case EPackageType.ExfilCountdown: - ExfilName = reader.GetString(); - ExfilStartTime = reader.GetFloat(); - break; - case EPackageType.TraderServiceNotification: - TraderServiceType = (ETraderServiceType)reader.GetInt(); - break; - case EPackageType.LoadBot: - case EPackageType.DisposeBot: - case EPackageType.EnableBot: - case EPackageType.DisableBot: - BotNetId = reader.GetInt(); - break; - } - } + switch (PacketType) + { + case EPackageType.Ping: + PingLocation = reader.GetVector3(); + PingType = (PingFactory.EPingType)reader.GetByte(); + PingColor = reader.GetColor(); + Nickname = reader.GetString(); + LocaleId = reader.GetString(); + break; + case EPackageType.TrainSync: + DepartureTime = reader.GetLong(); + break; + case EPackageType.ExfilCountdown: + ExfilName = reader.GetString(); + ExfilStartTime = reader.GetFloat(); + break; + case EPackageType.TraderServiceNotification: + TraderServiceType = (ETraderServiceType)reader.GetInt(); + break; + case EPackageType.LoadBot: + case EPackageType.DisposeBot: + case EPackageType.EnableBot: + case EPackageType.DisableBot: + BotNetId = reader.GetInt(); + break; + } + } - public void Serialize(NetDataWriter writer) + public void Serialize(NetDataWriter writer) { writer.Put(NetId); writer.Put((int)PacketType); @@ -89,9 +89,9 @@ public void Serialize(NetDataWriter writer) break; } } - } + } - public enum EPackageType + public enum EPackageType { ClientExtract, Ping, @@ -102,6 +102,7 @@ public enum EPackageType DisposeBot, EnableBot, DisableBot, - RemoveAirdropManager + RemoveAirdropManager, + ClearEffects } } diff --git a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs index 1241c5cc..fcc0d8cb 100644 --- a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs @@ -1,15 +1,23 @@ -using EFT.Interactive; +using ComponentAce.Compression.Libs.zlib; +using EFT; +using EFT.Interactive; using LiteNetLib.Utils; using System.Collections.Generic; using UnityEngine; namespace Fika.Core.Networking.Packets.GameWorld { - public struct ReconnectPacket : INetSerializable + public struct ReconnectPacket(bool isRequest) : INetSerializable { - public bool IsRequest; + public bool IsRequest = isRequest; + public bool InitialRequest = false; public EReconnectDataType Type; + public string ProfileId; + public Profile Profile; + public Profile.ProfileHealthClass ProfileHealthClass; + public Vector3 PlayerPosition; + public List ThrowableData; public List InteractivesData; public Dictionary LampStates; @@ -18,46 +26,68 @@ public struct ReconnectPacket : INetSerializable public void Deserialize(NetDataReader reader) { IsRequest = reader.GetBool(); - Type = (EReconnectDataType)reader.GetByte(); - switch (Type) + InitialRequest = reader.GetBool(); + ProfileId = reader.GetString(); + if (!IsRequest) { - case EReconnectDataType.Throwable: - ThrowableData = reader.GetThrowableData(); - break; - case EReconnectDataType.Interactives: - InteractivesData = reader.GetInteractivesStates(); - break; - case EReconnectDataType.LampControllers: - LampStates = reader.GetLampStates(); - break; - case EReconnectDataType.Windows: - WindowBreakerStates = reader.GetWindowBreakerStates(); - break; - default: - break; + Type = (EReconnectDataType)reader.GetByte(); + switch (Type) + { + case EReconnectDataType.Throwable: + ThrowableData = reader.GetThrowableData(); + break; + case EReconnectDataType.Interactives: + InteractivesData = reader.GetInteractivesStates(); + break; + case EReconnectDataType.LampControllers: + LampStates = reader.GetLampStates(); + break; + case EReconnectDataType.Windows: + WindowBreakerStates = reader.GetWindowBreakerStates(); + break; + case EReconnectDataType.OwnCharacter: + Profile = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + ProfileHealthClass = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + PlayerPosition = reader.GetVector3(); + break; + case EReconnectDataType.Finished: + default: + break; + } } } public void Serialize(NetDataWriter writer) { writer.Put(IsRequest); - writer.Put((byte)Type); - switch (Type) + writer.Put(InitialRequest); + writer.Put(ProfileId); + if (!IsRequest) { - case EReconnectDataType.Throwable: - writer.PutThrowableData(ThrowableData); - break; - case EReconnectDataType.Interactives: - writer.PutInteractivesStates(InteractivesData); - break; - case EReconnectDataType.LampControllers: - writer.PutLampStates(LampStates); - break; - case EReconnectDataType.Windows: - writer.PutWindowBreakerStates(WindowBreakerStates); - break; - default: - break; + writer.Put((byte)Type); + switch (Type) + { + case EReconnectDataType.Throwable: + writer.PutThrowableData(ThrowableData); + break; + case EReconnectDataType.Interactives: + writer.PutInteractivesStates(InteractivesData); + break; + case EReconnectDataType.LampControllers: + writer.PutLampStates(LampStates); + break; + case EReconnectDataType.Windows: + writer.PutWindowBreakerStates(WindowBreakerStates); + break; + case EReconnectDataType.OwnCharacter: + writer.PutByteArray(SimpleZlib.CompressToBytes(Profile.ToJson(), 4)); + writer.PutByteArray(SimpleZlib.CompressToBytes(ProfileHealthClass.ToJson(), 4)); + writer.Put(PlayerPosition); + break; + case EReconnectDataType.Finished: + default: + break; + } } } @@ -66,7 +96,9 @@ public enum EReconnectDataType Throwable, Interactives, LampControllers, - Windows + Windows, + OwnCharacter, + Finished } } } diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 098c93bf..e32afe72 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -605,7 +605,7 @@ private void RefreshUI() switch (entry.Status) { case LobbyEntry.ELobbyStatus.LOADING: - { + /*{ tooltipTextGetter = new() { TooltipText = "Host is still loading." @@ -621,9 +621,9 @@ private void RefreshUI() tooltipArea.enabled = true; tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); } - break; + break;*/ case LobbyEntry.ELobbyStatus.IN_GAME: - tooltipTextGetter = new() + /*tooltipTextGetter = new() { TooltipText = "Raid is already in progress." }; @@ -637,7 +637,7 @@ private void RefreshUI() tooltipArea = joinButton.GetOrAddComponent(); tooltipArea.enabled = true; tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); - break; + break;*/ case LobbyEntry.ELobbyStatus.COMPLETE: tooltipTextGetter = new() { From ff7614e1c35e6cd2a51120995c0ecfe942288f33 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:22:09 +0200 Subject: [PATCH 05/83] Handle reconnect from server --- Fika.Core/Coop/Components/CoopHandler.cs | 6 -- Fika.Core/Coop/GameMode/CoopGame.cs | 3 - Fika.Core/Coop/Players/CoopPlayer.cs | 16 ++++ .../Networking/Http/FikaRequestHandler.cs | 5 ++ .../Websocket/DedicatedRaidWebSocketClient.cs | 2 +- Fika.Core/UI/Custom/MatchMakerUIScript.cs | 89 +++++++++++++++---- Fika.Core/UI/Models/LobbyEntry.cs | 8 +- 7 files changed, 99 insertions(+), 30 deletions(-) diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index 1248a857..975b00d2 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -49,8 +49,6 @@ public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool i public int NetId { get; set; } = netId; } - public bool RunAsyncTasks { get; set; } = true; - internal FikaBTRManager_Client clientBTR = null; internal FikaBTRManager_Host serverBTR = null; internal static GameObject CoopHandlerParent; @@ -102,8 +100,6 @@ protected void Awake() protected void Start() { - RunAsyncTasks = true; - if (FikaBackendUtils.IsClient) { //_ = Task.Run(ReadFromServerCharactersLoop); @@ -123,8 +119,6 @@ protected void OnDestroy() { Players.Clear(); HumanPlayers.Clear(); - - RunAsyncTasks = false; } private bool requestQuitGame = false; diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 3c5546a0..3a2f144b 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -1949,7 +1949,6 @@ public override void Stop(string profileId, ExitStatus exitStatus, string exitNa Logger.LogError("Stop: Could not find CoopHandler!"); } - coopHandler.RunAsyncTasks = false; Destroy(coopHandler); if (CoopHandler.CoopHandlerParent != null) @@ -2070,7 +2069,6 @@ public void StopFromCancel(string profileId, ExitStatus exitStatus) Logger.LogError("Stop: Could not find CoopHandler!"); } - coopHandler.RunAsyncTasks = false; Destroy(coopHandler); if (CoopHandler.CoopHandlerParent != null) @@ -2210,7 +2208,6 @@ public override void Dispose() if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) { - coopHandler.RunAsyncTasks = false; Destroy(coopHandler); if (CoopHandler.CoopHandlerParent != null) diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index 0fed64c8..65bce2e1 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -16,6 +16,8 @@ using Fika.Core.Coop.PacketHandlers; using Fika.Core.Coop.Utils; using Fika.Core.Networking; +using Fika.Core.Networking.Http; +using Fika.Core.Networking.Http.Models; using System; using System.Collections; using System.Collections.Generic; @@ -702,6 +704,10 @@ public override void OnDead(EDamageType damageType) base.OnDead(damageType); StartCoroutine(DisableNetworkedComponents()); + if (IsYourPlayer) + { + StartCoroutine(LocalPlayerDied()); + } } private IEnumerator DisableNetworkedComponents() @@ -714,6 +720,16 @@ private IEnumerator DisableNetworkedComponents() } } + private IEnumerator LocalPlayerDied() + { + AddPlayerRequest request = new(FikaBackendUtils.GetGroupId(), ProfileId); + Task diedTask = FikaRequestHandler.PlayerDied(request); + while (!diedTask.IsCompleted) + { + yield return new WaitForEndOfFrame(); + } + } + public override void Move(Vector2 direction) { if (direction.sqrMagnitude > 0) diff --git a/Fika.Core/Networking/Http/FikaRequestHandler.cs b/Fika.Core/Networking/Http/FikaRequestHandler.cs index c390b3e0..6349b314 100644 --- a/Fika.Core/Networking/Http/FikaRequestHandler.cs +++ b/Fika.Core/Networking/Http/FikaRequestHandler.cs @@ -172,5 +172,10 @@ public static async Task RegisterPlayer(RegisterPlayerRequest request) { await PutJsonAsync("/fika/raid/registerPlayer", request); } + + public static async Task PlayerDied(AddPlayerRequest request) + { + await PutJsonAsync("/fika/update/playerdied", request); + } } } \ No newline at end of file diff --git a/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs b/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs index 146bb579..a93fa370 100644 --- a/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs +++ b/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs @@ -116,7 +116,7 @@ private void WebSocket_OnMessage(object sender, MessageEventArgs e) // Matchmaker next screen (accept) matchMakerAcceptScreen.method_22(); - })); + }, false)); } else { diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index e32afe72..87b71bcb 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -380,13 +380,15 @@ private void ManualRefresh() RefreshUI(); } - public static IEnumerator JoinMatch(string profileId, string serverId, Button button, Action successCallback) + public static IEnumerator JoinMatch(string profileId, string serverId, Button button, Action successCallback, bool reconnect) { if (button != null) { button.enabled = false; } + FikaBackendUtils.IsReconnect = reconnect; + NotificationManagerClass.DisplayMessageNotification("Connecting to session...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); NetManagerUtils.CreatePingingClient(); @@ -490,6 +492,11 @@ private void RefreshUI() { LobbyEntry entry = Matches[i]; + if (entry.ServerId == ProfileId) + { + continue; + } + // server object GameObject server = Instantiate(fikaMatchMakerUi.RaidGroupDefaultToClone, fikaMatchMakerUi.RaidGroupDefaultToClone.transform.parent); server.SetActive(true); @@ -497,8 +504,19 @@ private void RefreshUI() server.name = entry.ServerId; - // player label - GameObject playerLabel = GameObject.Find("PlayerLabel"); + bool localPlayerInRaid = false; + bool localPlayerDead = false; + foreach (KeyValuePair player in entry.Players) + { + if (player.Key == ProfileId) + { + localPlayerInRaid = true; + localPlayerDead = player.Value; + } + } + + // player label + GameObject playerLabel = GameObject.Find("PlayerLabel"); playerLabel.name = "PlayerLabel" + i; playerLabel.GetComponentInChildren().text = entry.HostUsername; @@ -519,13 +537,12 @@ private void RefreshUI() } Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); - //StartCoroutine(JoinMatch(ProfileId, server.name, button)); FikaBackendUtils.HostLocationId = entry.Location; StartCoroutine(JoinMatch(ProfileId, server.name, button, () => { DestroyThis(); AcceptButton.OnClick.Invoke(); - })); + }, localPlayerInRaid)); }); TooltipTextGetter tooltipTextGetter; @@ -600,12 +617,12 @@ private void RefreshUI() tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); continue; - } + } switch (entry.Status) { case LobbyEntry.ELobbyStatus.LOADING: - /*{ + { tooltipTextGetter = new() { TooltipText = "Host is still loading." @@ -621,23 +638,57 @@ private void RefreshUI() tooltipArea.enabled = true; tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); } - break;*/ + break; case LobbyEntry.ELobbyStatus.IN_GAME: - /*tooltipTextGetter = new() + if (!localPlayerInRaid) { - TooltipText = "Raid is already in progress." - }; + tooltipTextGetter = new() + { + TooltipText = "Raid is already in progress." + }; - button.enabled = false; - if (image != null) + button.enabled = false; + if (image != null) + { + image.color = new(0.5f, image.color.g / 2, image.color.b / 2, 0.75f); + } + + tooltipArea = joinButton.GetOrAddComponent(); + tooltipArea.enabled = true; + tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); + } + else { - image.color = new(0.5f, image.color.g / 2, image.color.b / 2, 0.75f); + if (!localPlayerDead) + { + tooltipTextGetter = new() + { + TooltipText = "Click to rejoin raid." + }; + + tooltipArea = joinButton.GetOrAddComponent(); + tooltipArea.enabled = true; + tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); + } + else + { + tooltipTextGetter = new() + { + TooltipText = "Cannot rejoin a raid where you died." + }; + + button.enabled = false; + if (image != null) + { + image.color = new(0.5f, image.color.g / 2, image.color.b / 2, 0.75f); + } + + tooltipArea = joinButton.GetOrAddComponent(); + tooltipArea.enabled = true; + tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); + } } - - tooltipArea = joinButton.GetOrAddComponent(); - tooltipArea.enabled = true; - tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); - break;*/ + break; case LobbyEntry.ELobbyStatus.COMPLETE: tooltipTextGetter = new() { diff --git a/Fika.Core/UI/Models/LobbyEntry.cs b/Fika.Core/UI/Models/LobbyEntry.cs index 5b7ef5dd..d3a82952 100644 --- a/Fika.Core/UI/Models/LobbyEntry.cs +++ b/Fika.Core/UI/Models/LobbyEntry.cs @@ -1,5 +1,7 @@ using EFT; +using EFT.UI; using JsonType; +using System.Collections.Generic; using System.Runtime.Serialization; namespace Fika.Core.UI.Models @@ -28,7 +30,10 @@ public struct LobbyEntry [DataMember] public EDateTime Time; - public LobbyEntry(string serverId, string hostUsername, int playerCount, ELobbyStatus status, string location, ESideType side, EDateTime time) + [DataMember] + public Dictionary Players; + + public LobbyEntry(string serverId, string hostUsername, int playerCount, ELobbyStatus status, string location, ESideType side, EDateTime time, Dictionary players) { ServerId = serverId; HostUsername = hostUsername; @@ -37,6 +42,7 @@ public LobbyEntry(string serverId, string hostUsername, int playerCount, ELobbyS Location = location; Side = side; Time = time; + Players = players; } public enum ELobbyStatus From 1385acd180b5d989017b9d46b4eabcf0ae3c97ce Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:51:23 +0200 Subject: [PATCH 06/83] Filter effects on reconnect and black destroyed limbs --- Fika.Core/Coop/GameMode/CoopGame.cs | 8 ++++++++ .../Coop/ObservedClasses/ObservedHealthController.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 3a2f144b..9e21f164 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -10,6 +10,7 @@ using EFT.Counters; using EFT.EnvironmentEffect; using EFT.Game.Spawning; +using EFT.HealthSystem; using EFT.Interactive; using EFT.InventoryLogic; using EFT.UI; @@ -1044,6 +1045,13 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU if (FikaBackendUtils.IsReconnect) { await Reconnect(); + foreach (KeyValuePair.BodyPartState> item in gparam_0.Player.ActiveHealthController.Dictionary_0) + { + if (item.Value.Health.AtMinimum) + { + item.Value.IsDestroyed = true; + } + } } handler.SetReady(true); diff --git a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs index 66f58683..9b81c5a3 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs @@ -75,7 +75,7 @@ public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass heal foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) { - if (gclass.State != EEffectState.Residued) + if (gclass is GInterface251 && gclass.State != EEffectState.Residued) // We only resync effects that are in-game effects, check for GClass increments { Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; gclass2.Effects ??= []; From 27c1ab6ca951408f610d741e3f3983d78c6d40fc Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:57:38 +0200 Subject: [PATCH 07/83] Update gametimer packet --- Fika.Core/Networking/FikaClient.cs | 24 ++++++++++++------- Fika.Core/Networking/FikaServer.cs | 2 +- .../Packets/GameWorld/GameTimerPacket.cs | 5 +++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index eb44359a..6d35bdb8 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -9,6 +9,7 @@ using EFT.Interactive; using EFT.MovingPlatforms; using EFT.UI; +using EFT.UI.BattleTimer; using EFT.Weather; using Fika.Core.Coop.ClientClasses; using Fika.Core.Coop.Components; @@ -895,18 +896,23 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet) GameTimerClass gameTimer = coopGame.GameTimer; if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) { - if (gameTimer.PastTime.TotalSeconds < 3) - { - return; - } + TimeSpan timeRemain = gameTimer.PastTime + sessionTime; - TimeSpan timeRemain = gameTimer.PastTime + sessionTime; + gameTimer.ChangeSessionTime(timeRemain); - gameTimer.ChangeSessionTime(timeRemain); + Traverse timerPanel = Traverse.Create(coopGame.GameUi.TimerPanel); + timerPanel.Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds)); - Traverse.Create(coopGame.GameUi.TimerPanel).Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value); - } - } + MainTimerPanel mainTimerPanel = timerPanel.Field("_mainTimerPanel").Value; + if (mainTimerPanel != null) + { + Traverse.Create(mainTimerPanel).Field("dateTime_0").Value = gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds); + mainTimerPanel.UpdateTimer(); + } + + Traverse.Create(gameTimer).Field("nullable_0").Value = new DateTime(packet.StartTime); + } + } private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) { diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 36a53871..0c3a9250 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -975,7 +975,7 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) CoopGame game = coopHandler.LocalGameInstance; if (game != null) { - GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks); + GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks, game.GameTimer.StartDateTime.Value.Ticks); dataWriter.Reset(); SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); } diff --git a/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs index 2e794eb4..af39b9ce 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs @@ -4,21 +4,24 @@ namespace Fika.Core.Networking { - public struct GameTimerPacket(bool isRequest, long tick = 0) : INetSerializable + public struct GameTimerPacket(bool isRequest, long tick = 0, long startTime = 0) : INetSerializable { public bool IsRequest = isRequest; public long Tick = tick; + public long StartTime = startTime; public void Deserialize(NetDataReader reader) { IsRequest = reader.GetBool(); Tick = reader.GetLong(); + StartTime = reader.GetLong(); } public void Serialize(NetDataWriter writer) { writer.Put(IsRequest); writer.Put(Tick); + writer.Put(StartTime); } } } From 22b0819c0f74be467bf26dec0802bb1b8e7440a0 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:02:17 +0200 Subject: [PATCH 08/83] Cleanup --- Fika.Core/Networking/FikaClient.cs | 1788 ++++++++++++++-------------- Fika.Core/Networking/FikaServer.cs | 9 +- 2 files changed, 897 insertions(+), 900 deletions(-) diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 6d35bdb8..c2758077 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -25,7 +25,6 @@ using HarmonyLib; using LiteNetLib; using LiteNetLib.Utils; -using SPT.Common.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -33,869 +32,868 @@ using System.Net.Sockets; using System.Threading.Tasks; using UnityEngine; -using UnityEngine.Networking.Match; using static Fika.Core.Utils.ColorUtils; namespace Fika.Core.Networking { - public class FikaClient : MonoBehaviour, INetEventListener - { - public NetDataWriter Writer => dataWriter; - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public NetPacketProcessor packetProcessor = new(); - public int Ping = 0; - public int ServerFPS = 0; - public int ConnectedClients = 0; - public int ReadyClients = 0; - public bool HostReady = false; - public bool ReconnectDone = false; - public NetManager NetClient - { - get - { - return netClient; - } - } - public NetPeer ServerConnection { get; private set; } - public bool SpawnPointsReceived { get; private set; } = false; - public bool Started - { - get - { - if (netClient == null) - { - return false; - } - return netClient.IsRunning; - } - } - - private NetManager netClient; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); - private NetDataWriter dataWriter = new(); - private FikaChat fikaChat; - private string myProfileId; - - public async void Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - myProfileId = FikaBackendUtils.Profile.ProfileId; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + public class FikaClient : MonoBehaviour, INetEventListener + { + public NetDataWriter Writer => dataWriter; + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public NetPacketProcessor packetProcessor = new(); + public int Ping = 0; + public int ServerFPS = 0; + public int ConnectedClients = 0; + public int ReadyClients = 0; + public bool HostReady = false; + public bool ReconnectDone = false; + public NetManager NetClient + { + get + { + return netClient; + } + } + public NetPeer ServerConnection { get; private set; } + public bool SpawnPointsReceived { get; private set; } = false; + public bool Started + { + get + { + if (netClient == null) + { + return false; + } + return netClient.IsRunning; + } + } + + private NetManager netClient; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); + private NetDataWriter dataWriter = new(); + private FikaChat fikaChat; + private string myProfileId; + + public async void Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + myProfileId = FikaBackendUtils.Profile.ProfileId; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); netClient = new NetManager(this) - { - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - NatPunchEnabled = false, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - MaxConnectAttempts = 5, - ReconnectDelay = 1 * 1000 - }; - - await NetManagerUtils.CreateCoopHandler(); - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - - if (FikaBackendUtils.IsHostNatPunch) - { - NetManagerUtils.DestroyPingingClient(); - } + { + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + NatPunchEnabled = false, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + MaxConnectAttempts = 5, + ReconnectDelay = 1 * 1000 + }; + + await NetManagerUtils.CreateCoopHandler(); + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + + if (FikaBackendUtils.IsHostNatPunch) + { + NetManagerUtils.DestroyPingingClient(); + } - netClient.Start(FikaBackendUtils.LocalPort); + netClient.Start(FikaBackendUtils.LocalPort); - string ip = FikaBackendUtils.RemoteIp; - int port = FikaBackendUtils.RemotePort; + string ip = FikaBackendUtils.RemoteIp; + int port = FikaBackendUtils.RemotePort; - 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"); - }; + 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"); + }; - while (ServerConnection.ConnectionState != ConnectionState.Connected) - { + while (ServerConnection.ConnectionState != ConnectionState.Connected) + { #if DEBUG FikaPlugin.Instance.FikaLogger.LogWarning("FikaClient was not able to connect in time!"); #endif - await Task.Delay(1 * 6000); - ServerConnection = netClient.Connect(ip, port, "fika.core"); - } + await Task.Delay(1 * 6000); + ServerConnection = netClient.Connect(ip, port, "fika.core"); + } - FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); - } + FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); + } private void OnReconnectPacketReceived(ReconnectPacket packet) { if (!packet.IsRequest) - { + { switch (packet.Type) - { - case ReconnectPacket.EReconnectDataType.Throwable: + { + case ReconnectPacket.EReconnectDataType.Throwable: Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); break; case ReconnectPacket.EReconnectDataType.Interactives: - { - WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; - Dictionary netIdDictionary = []; - { - foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) - { - netIdDictionary.Add(data.NetId, data); - } - } - foreach (WorldInteractiveObject item in worldInteractiveObjects) - { + { + WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; + Dictionary netIdDictionary = []; + { + foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) + { + netIdDictionary.Add(data.NetId, data); + } + } + foreach (WorldInteractiveObject item in worldInteractiveObjects) + { if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) - { - item.SetInitialSyncState(value); - } - } - break; - } - case ReconnectPacket.EReconnectDataType.LampControllers: - { + { + item.SetInitialSyncState(value); + } + } + break; + } + case ReconnectPacket.EReconnectDataType.LampControllers: + { Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); - foreach (KeyValuePair lampState in packet.LampStates) - { - if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) - { - if (lampController.LampState != (Turnable.EState)lampState.Value) - { - lampController.Switch((Turnable.EState)lampState.Value); - } - } - } - break; + foreach (KeyValuePair lampState in packet.LampStates) + { + if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) + { + if (lampController.LampState != (Turnable.EState)lampState.Value) + { + lampController.Switch((Turnable.EState)lampState.Value); + } + } + } + break; } - case ReconnectPacket.EReconnectDataType.Windows: - { + case ReconnectPacket.EReconnectDataType.Windows: + { Dictionary windowBreakerStates = packet.WindowBreakerStates; foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) - { + { if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) - { - try - { - DamageInfo damageInfo = default; - damageInfo.HitPoint = hitPosition; - windowBreaker.MakeHit(in damageInfo, true); - } - catch (Exception ex) - { - logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); - } - } - } + { + try + { + DamageInfo damageInfo = default; + damageInfo.HitPoint = hitPosition; + windowBreaker.MakeHit(in damageInfo, true); + } + catch (Exception ex) + { + logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); + } + } + } break; - } - case ReconnectPacket.EReconnectDataType.OwnCharacter: - coopHandler.LocalGameInstance.Profile_0 = packet.Profile; - coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; - FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; - break; - case ReconnectPacket.EReconnectDataType.Finished: - ReconnectDone = true; - break; - default: - break; - } - } + } + case ReconnectPacket.EReconnectDataType.OwnCharacter: + coopHandler.LocalGameInstance.Profile_0 = packet.Profile; + coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; + FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; + break; + case ReconnectPacket.EReconnectDataType.Finished: + ReconnectDone = true; + break; + default: + break; + } + } } private void OnWorldLootPacketReceived(WorldLootPacket packet) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); - coopGame.LootItems = lootItems; - coopGame.HasReceivedLoot = true; - } - } - - private void OnStatisticsPacketReceived(ThrowablePacket packet) - { - GClass724 grenades = Singleton.Instance.Grenades; - if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) - { - throwable.ApplyNetPacket(packet.Data); - } - } - - private void OnStatisticsPacketReceived(StatisticsPacket packet) - { - ServerFPS = packet.ServerFPS; - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet) - { - if (!packet.IsRequest) - { - World world = Singleton.Instance.World_0; - if (world.Interactables == null) - { - world.RegisterNetworkInteractionObjects(packet.Interactables); - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - coopGame.InteractablesInitialized = true; - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) - { - coopGame.SpawnId = packet.Name; - } - } - else - { - logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; - } - } + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); + coopGame.LootItems = lootItems; + coopGame.HasReceivedLoot = true; + } + } - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) - { - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - 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) - { - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } + private void OnStatisticsPacketReceived(ThrowablePacket packet) + { + GClass724 grenades = Singleton.Instance.Grenades; + if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) + { + throwable.ApplyNetPacket(packet.Data); + } + } - private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) - { - player.HandleCallbackFromServer(in packet); - } - } - - private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) - { - Dictionary newPlayers = Players; - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (player.ProfileId != packet.ProfileId) - { - FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); - for (int i = 0; i < Players.Count; i++) - { - KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); - Players.Remove(playerToReorganize.Key); - Players[packet.NetId] = playerToReorganize.Value; - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); - string allPlayers = ""; - 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); - } - FikaPlugin.Instance.FikaLogger.LogError(allPlayers); - } - } - - 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 (KeyValuePair player in Players) - { - if (player.Value == MyPlayer) - { - i = player.Key; + private void OnStatisticsPacketReceived(StatisticsPacket packet) + { + ServerFPS = packet.ServerFPS; + } - break; - } - } + private void OnInteractableInitPacketReceived(InteractableInitPacket packet) + { + if (!packet.IsRequest) + { + World world = Singleton.Instance.World_0; + if (world.Interactables == null) + { + world.RegisterNetworkInteractionObjects(packet.Interactables); + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + coopGame.InteractablesInitialized = true; + } + } + } + } - if (i == -1) - { - FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); - return; - } + private void OnSpawnPointPacketReceived(SpawnpointPacket packet) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) + { + coopGame.SpawnId = packet.Name; + } + } + else + { + logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; + } + } - Players.Remove(i); - Players[packet.NetId] = MyPlayer; - } + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } - private void OnSendCharacterPacketReceived(SendCharacterPacket packet) - { - if (coopHandler == null) - { - return; - } + private void OnQuestItemPacketReceived(QuestItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestItemPacket(ref packet); + } + } + } - if (packet.PlayerInfo.Profile.ProfileId != myProfileId) - { - coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); - } - } + private void OnQuestConditionPacketReceived(QuestConditionPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestPacket(ref packet); + } + } + } - private void OnBorderZonePacketReceived(BorderZonePacket packet) - { - if (Singleton.Instantiated) - { - BorderZone[] borderZones = Singleton.Instance.BorderZones; - if (borderZones != null && borderZones.Length > 0) - { - foreach (BorderZone borderZone in borderZones) - { - if (borderZone.Id == packet.ZoneId) - { - List players = Singleton.Instance.RegisteredPlayers; - foreach (IPlayer player in players) - { - if (player.ProfileId == packet.ProfileId) - { - IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); - borderZone.ProcessIncomingPacket(playerBridge, true); - } - } - } - } - } - } - } - - private void OnMinePacketReceived(MinePacket packet) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - player.ProcessInteractWithBTR(packet); - } - } + private void OnTextMessagePacketReceived(TextMessagePacket packet) + { + logger.LogInfo($"Received message from: {packet.Nickname}, Message: {packet.Message}"); - private void OnBTRPacketReceived(BTRPacket packet) - { - if (coopHandler.clientBTR != null) - { - coopHandler.clientBTR.btrPackets.Enqueue(packet); - } - } + if (fikaChat != null) + { + fikaChat.ReceiveMessage(packet.Nickname, packet.Message); + } + } - private void OnWeatherPacketReceived(WeatherPacket packet) - { - if (!packet.IsRequest) - { - if (WeatherController.Instance != null) - { - WeatherController.Instance.method_0(packet.WeatherClasses); - } - else - { - logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); - } - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) - { - if (!packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - CoopGame coopGame = coopHandler.LocalGameInstance; - - CarExtraction carExtraction = FindObjectOfType(); - - foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) - { - ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); - if (point != null || point != default) - { - if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) - { - point.Enable(); - point.Status = exfilPoint.Value; - } - else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) - { - point.Disable(); - point.Status = exfilPoint.Value; - - if (carExtraction != null) - { - if (carExtraction.Subscribee == point) - { - carExtraction.Play(true); - } - } - } - coopGame.UpdateExfiltrationUi(point, false, true); - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); - } - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) - { - foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) - { - ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); - if (scavPoint != null || scavPoint != default) - { - if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) - { - scavPoint.Enable(); - scavPoint.EligibleIds.Add(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, true); - } - else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) - { - scavPoint.Disable(); - scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, false); - } - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); - } - } - - ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); - coopGame.ResetExfilPointsFromServer(points); - } - - SpawnPointsReceived = true; - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet) - { - switch (packet.PacketType) - { - case EPackageType.ClientExtract: - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); - - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - break; - case EPackageType.Ping: - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - break; - case EPackageType.TrainSync: - { - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - DateTime depart = new(packet.DepartureTime); - Traverse.Create(locomotive).Field("_depart").SetValue(depart); - } - else - { - logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); - } - } - break; - case EPackageType.ExfilCountdown: - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - break; - case EPackageType.TraderServiceNotification: - { - if (coopHandler.clientBTR) - { - coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); - } - } - break; - case EPackageType.DisposeBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) - { - if (!botToDispose.gameObject.activeSelf) - { - botToDispose.gameObject.SetActive(true); - } - - if (coopHandler.Players.Remove(packet.BotNetId)) - { - botToDispose.Dispose(); - AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); - logger.LogInfo("Disposing bot: " + packet.BotNetId); - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); - } - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); - } - } - break; - case EPackageType.RemoveAirdropManager: - { - if (Singleton.Instance != null) - { - Destroy(Singleton.Instance); - } - } - break; - case EPackageType.EnableBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) - { - if (!botToEnable.gameObject.activeSelf) - { + public void SetupGameVariables(CoopPlayer coopPlayer) + { + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) + { + player.HandleCallbackFromServer(in packet); + } + } + + private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) + { + Dictionary newPlayers = Players; + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (player.ProfileId != packet.ProfileId) + { + FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); + for (int i = 0; i < Players.Count; i++) + { + KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); + Players.Remove(playerToReorganize.Key); + Players[packet.NetId] = playerToReorganize.Value; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); + string allPlayers = ""; + 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); + } + FikaPlugin.Instance.FikaLogger.LogError(allPlayers); + } + } + + 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 (KeyValuePair player in Players) + { + if (player.Value == MyPlayer) + { + i = player.Key; + + break; + } + } + + if (i == -1) + { + FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); + return; + } + + Players.Remove(i); + Players[packet.NetId] = MyPlayer; + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet) + { + if (coopHandler == null) + { + return; + } + + if (packet.PlayerInfo.Profile.ProfileId != myProfileId) + { + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); + } + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet) + { + if (Singleton.Instantiated) + { + BorderZone[] borderZones = Singleton.Instance.BorderZones; + if (borderZones != null && borderZones.Length > 0) + { + foreach (BorderZone borderZone in borderZones) + { + if (borderZone.Id == packet.ZoneId) + { + List players = Singleton.Instance.RegisteredPlayers; + foreach (IPlayer player in players) + { + if (player.ProfileId == packet.ProfileId) + { + IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); + borderZone.ProcessIncomingPacket(playerBridge, true); + } + } + } + } + } + } + } + + private void OnMinePacketReceived(MinePacket packet) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + player.ProcessInteractWithBTR(packet); + } + } + + private void OnBTRPacketReceived(BTRPacket packet) + { + if (coopHandler.clientBTR != null) + { + coopHandler.clientBTR.btrPackets.Enqueue(packet); + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet) + { + if (!packet.IsRequest) + { + if (WeatherController.Instance != null) + { + WeatherController.Instance.method_0(packet.WeatherClasses); + } + else + { + logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); + } + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) + { + if (!packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + CoopGame coopGame = coopHandler.LocalGameInstance; + + CarExtraction carExtraction = FindObjectOfType(); + + foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) + { + ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); + if (point != null || point != default) + { + if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) + { + point.Enable(); + point.Status = exfilPoint.Value; + } + else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) + { + point.Disable(); + point.Status = exfilPoint.Value; + + if (carExtraction != null) + { + if (carExtraction.Subscribee == point) + { + carExtraction.Play(true); + } + } + } + coopGame.UpdateExfiltrationUi(point, false, true); + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); + } + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) + { + foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) + { + ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); + if (scavPoint != null || scavPoint != default) + { + if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) + { + scavPoint.Enable(); + scavPoint.EligibleIds.Add(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, true); + } + else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) + { + scavPoint.Disable(); + scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, false); + } + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); + } + } + + ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); + coopGame.ResetExfilPointsFromServer(points); + } + + SpawnPointsReceived = true; + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet) + { + switch (packet.PacketType) + { + case EPackageType.ClientExtract: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + break; + case EPackageType.Ping: + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + break; + case EPackageType.TrainSync: + { + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) + { + DateTime depart = new(packet.DepartureTime); + Traverse.Create(locomotive).Field("_depart").SetValue(depart); + } + else + { + logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); + } + } + break; + case EPackageType.ExfilCountdown: + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + break; + case EPackageType.TraderServiceNotification: + { + if (coopHandler.clientBTR) + { + coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); + } + } + break; + case EPackageType.DisposeBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) + { + if (!botToDispose.gameObject.activeSelf) + { + botToDispose.gameObject.SetActive(true); + } + + if (coopHandler.Players.Remove(packet.BotNetId)) + { + botToDispose.Dispose(); + AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); + logger.LogInfo("Disposing bot: " + packet.BotNetId); + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + } + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + } + } + break; + case EPackageType.RemoveAirdropManager: + { + if (Singleton.Instance != null) + { + Destroy(Singleton.Instance); + } + } + break; + case EPackageType.EnableBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) + { + if (!botToEnable.gameObject.activeSelf) + { #if DEBUG logger.LogWarning("Enabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(true); - } - else - { - 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 (botToEnable.gameObject.activeSelf) - { + botToEnable.gameObject.SetActive(true); + } + else + { + 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 (botToEnable.gameObject.activeSelf) + { #if DEBUG logger.LogWarning("Disabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(false); - } - else - { - logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); - } - } - } - break; - case EPackageType.ClearEffects: - { + botToEnable.gameObject.SetActive(false); + } + else + { + logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); + } + } + } + break; + case EPackageType.ClearEffects: + { if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) { - if (playerToApply is ObservedCoopPlayer observedPlayer) - { - observedPlayer.HealthBar.ClearEffects(); - } + if (playerToApply is ObservedCoopPlayer observedPlayer) + { + observedPlayer.HealthBar.ClearEffects(); + } } } - break; - } - } + break; + } + } - private void OnHealthSyncPacketReceived(HealthSyncPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - } + private void OnHealthSyncPacketReceived(HealthSyncPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + } - private void OnAirdropLootPacketReceived(AirdropLootPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.ReceiveBuildLootContainer(packet); - } - else - { - logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); - } - } + private void OnAirdropLootPacketReceived(AirdropLootPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.ReceiveBuildLootContainer(packet); + } + else + { + logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); + } + } - private void OnAirdropPacketReceived(AirdropPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.AirdropParameters = new() - { - Config = packet.Config, - AirdropAvailable = packet.AirdropAvailable, - PlaneSpawned = packet.PlaneSpawned, - BoxSpawned = packet.BoxSpawned, - DistanceTraveled = packet.DistanceTraveled, - DistanceToTravel = packet.DistanceToTravel, - DistanceToDrop = packet.DistanceToDrop, - Timer = packet.Timer, - DropHeight = packet.DropHeight, - TimeToStart = packet.TimeToStart, - RandomAirdropPoint = packet.BoxPoint, - SpawnPoint = packet.SpawnPoint, - LookPoint = packet.LookPoint - }; - } - else - { - logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); - } - } + private void OnAirdropPacketReceived(AirdropPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.AirdropParameters = new() + { + Config = packet.Config, + AirdropAvailable = packet.AirdropAvailable, + PlaneSpawned = packet.PlaneSpawned, + BoxSpawned = packet.BoxSpawned, + DistanceTraveled = packet.DistanceTraveled, + DistanceToTravel = packet.DistanceToTravel, + DistanceToDrop = packet.DistanceToDrop, + Timer = packet.Timer, + DropHeight = packet.DropHeight, + TimeToStart = packet.TimeToStart, + RandomAirdropPoint = packet.BoxPoint, + SpawnPoint = packet.SpawnPoint, + LookPoint = packet.LookPoint + }; + } + else + { + logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); + } + } - private void OnInformationPacketReceived(InformationPacket packet) - { - if (!packet.IsRequest) - { - ConnectedClients = packet.NumberOfPlayers; - ReadyClients = packet.ReadyPlayers; - HostReady = packet.HostReady; - } - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) - { - if (coopHandler == null) - { - return; - } + private void OnInformationPacketReceived(InformationPacket packet) + { + if (!packet.IsRequest) + { + ConnectedClients = packet.NumberOfPlayers; + ReadyClients = packet.ReadyPlayers; + HostReady = packet.HostReady; + } + } - if (!packet.IsRequest) - { - 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); - } - } - else if (packet.IsRequest) - { - logger.LogInfo($"Received CharacterRequest from server, send my Profile."); - AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = MyPlayer.Profile - }, - IsAlive = MyPlayer.ActiveHealthController.IsAlive, - IsAI = MyPlayer.IsAI, - Position = MyPlayer.Transform.position, - NetId = MyPlayer.NetId - }; - dataWriter.Reset(); - SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); - } - } + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) + { + if (coopHandler == null) + { + return; + } - private void OnInventoryPacketReceived(InventoryPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); - } - } + if (!packet.IsRequest) + { + 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); + } + } + else if (packet.IsRequest) + { + logger.LogInfo($"Received CharacterRequest from server, send my Profile."); + AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = MyPlayer.Profile + }, + IsAlive = MyPlayer.ActiveHealthController.IsAlive, + IsAI = MyPlayer.IsAI, + Position = MyPlayer.Transform.position, + NetId = MyPlayer.NetId + }; + dataWriter.Reset(); + SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } - private void OnDamagePacketReceived(DamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); - } - } + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + } - private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); - } - } + private void OnInventoryPacketReceived(InventoryPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); + } + } - private void OnFirearmPacketReceived(WeaponPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - } + private void OnDamagePacketReceived(DamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); + } + } + + private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); + } + } + + private void OnFirearmPacketReceived(WeaponPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + } - private void OnGameTimerPacketReceived(GameTimerPacket packet) - { - TimeSpan sessionTime = new(packet.Tick); + private void OnGameTimerPacketReceived(GameTimerPacket packet) + { + TimeSpan sessionTime = new(packet.Tick); - CoopGame coopGame = coopHandler.LocalGameInstance; + 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) + { TimeSpan timeRemain = gameTimer.PastTime + sessionTime; gameTimer.ChangeSessionTime(timeRemain); @@ -914,113 +912,113 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet) } } - private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) - { - HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; + private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) + { + HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; - if (controller == null) - { - return; - } + if (controller == null) + { + return; + } - switch (packet.PacketType) - { - case EHalloweenPacketType.Summon: - controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); - break; - case EHalloweenPacketType.Sync: - controller.SetEventState(packet.EventState, false); - break; - case EHalloweenPacketType.Exit: - controller.method_3(packet.Exit); - break; - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - } + switch (packet.PacketType) + { + case EHalloweenPacketType.Summon: + controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); + break; + case EHalloweenPacketType.Sync: + controller.SetEventState(packet.EventState, false); + break; + case EHalloweenPacketType.Exit: + controller.method_3(packet.Exit); + break; + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + } - protected void Update() - { - netClient?.PollEvents(); + protected void Update() + { + netClient?.PollEvents(); - /*if (_netClient.FirstPeer == null) + /*if (_netClient.FirstPeer == null) { _netClient.SendBroadcast([1], Port); }*/ - } + } - protected void OnDestroy() - { - netClient?.Stop(); + protected void OnDestroy() + { + netClient?.Stop(); - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); - } - - public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netClient.FirstPeer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogInfo("[CLIENT] We received error " + socketErrorCode); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - packetProcessor.ReadAllPackets(reader, peer); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) - { - logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); - netClient.Connect(remoteEndPoint, "fika.core"); - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - Ping = latency; - NetworkGameSession.RTT = peer.RoundTripTime; - NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; - } - - public void OnConnectionRequest(ConnectionRequest request) - { - - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); - if (disconnectInfo.Reason is DisconnectReason.Timeout) - { - NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); - Destroy(MyPlayer.PacketReceiver); - MyPlayer.PacketSender.DestroyThis(); - Destroy(this); - Singleton.Release(this); - } - } - } + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); + } + + public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netClient.FirstPeer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogInfo("[CLIENT] We received error " + socketErrorCode); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + packetProcessor.ReadAllPackets(reader, peer); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) + { + logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); + netClient.Connect(remoteEndPoint, "fika.core"); + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + Ping = latency; + NetworkGameSession.RTT = peer.RoundTripTime; + NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; + } + + public void OnConnectionRequest(ConnectionRequest request) + { + + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); + if (disconnectInfo.Reason is DisconnectReason.Timeout) + { + NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); + Destroy(MyPlayer.PacketReceiver); + MyPlayer.PacketSender.DestroyThis(); + Destroy(this); + Singleton.Release(this); + } + } + } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 0c3a9250..2132d7da 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -2,7 +2,6 @@ using BepInEx.Logging; using Comfort.Common; -using ComponentAce.Compression.Libs.zlib; using EFT; using EFT.AssetsManager; using EFT.Interactive; @@ -229,7 +228,7 @@ public async Task Init() private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) { if (packet.IsRequest) - { + { if (packet.InitialRequest) { NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); @@ -351,8 +350,8 @@ private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); } - foreach (CoopPlayer player in coopHandler.Players.Values) - { + foreach (CoopPlayer player in coopHandler.Players.Values) + { SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); @@ -381,7 +380,7 @@ private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) dataWriter.Reset(); SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); - } + } } private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) From e0242f40c436e9b542f89caf8f1655f95e2a8f4f Mon Sep 17 00:00:00 2001 From: Archangel Date: Wed, 7 Aug 2024 23:03:44 +0200 Subject: [PATCH 09/83] Run cleanup --- Fika.Core/Console/FikaCommands.cs | 2 +- Fika.Core/Coop/Components/CoopHandler.cs | 3 +- Fika.Core/Coop/GameMode/CoopGame.cs | 54 +- .../ObservedHealthController.cs | 138 +- .../Coop/PacketHandlers/BotPacketSender.cs | 2 +- .../Coop/PacketHandlers/ClientPacketSender.cs | 2 +- .../Coop/PacketHandlers/ServerPacketSender.cs | 2 +- Fika.Core/Coop/Players/ObservedCoopPlayer.cs | 16 +- Fika.Core/Coop/Utils/NetManagerUtils.cs | 74 +- Fika.Core/Networking/FikaClient.cs | 1946 +++++++------- .../Networking/FikaSerializationExtensions.cs | 676 ++--- Fika.Core/Networking/FikaServer.cs | 2386 ++++++++--------- .../Packets/GameWorld/GenericPacket.cs | 60 +- .../Packets/GameWorld/ReconnectPacket.cs | 178 +- Fika.Core/UI/Custom/MatchMakerUIScript.cs | 76 +- Fika.Core/UI/Models/LobbyEntry.cs | 1 - 16 files changed, 2807 insertions(+), 2809 deletions(-) diff --git a/Fika.Core/Console/FikaCommands.cs b/Fika.Core/Console/FikaCommands.cs index 07c56e20..db4aba25 100644 --- a/Fika.Core/Console/FikaCommands.cs +++ b/Fika.Core/Console/FikaCommands.cs @@ -159,7 +159,7 @@ public static void StopTimer() { ConsoleScreen.Log("GameTimer stopped at: " + game.GameTimer.PastTime.ToString()); } - } + } } #endif diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index 975b00d2..c4a6d7c4 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -14,7 +14,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using UnityEngine; namespace Fika.Core.Coop.Components @@ -381,7 +380,7 @@ await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.Pool public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAlive = true, bool isAI = false) { GameWorld gameWorld = Singleton.Instance; - if (gameWorld == null) + if (gameWorld == null) { return; } diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index 9e21f164..af20452f 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -849,8 +849,8 @@ public override async Task vmethod_2(int playerId, Vector3 position EUpdateQueue updateQueue, Player.EUpdateMode armsUpdateMode, Player.EUpdateMode bodyUpdateMode, CharacterControllerSpawner.Mode characterControllerMode, Func getSensitivity, Func getAimingSensitivity, IStatisticsManager statisticsManager, AbstractQuestControllerClass questController, AbstractAchievementControllerClass achievementsController) - { - profile.SetSpawnedInSession(profile.Side == EPlayerSide.Savage); + { + 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, @@ -977,14 +977,14 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU CoopHandler handler = CoopHandler.GetCoopHandler(); if (handler != null) { - handler.LocalGameInstance = this; - } + handler.LocalGameInstance = this; + } else { throw new NullReferenceException("CoopHandler was missing!"); } - LocationSettingsClass.Location location; + LocationSettingsClass.Location location; if (Location_0.IsHideout) { location = Location_0; @@ -1005,7 +1005,7 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU FikaBackendUtils.ScreenController.ChangeStatus("Retrieving loot from server..."); if (!FikaBackendUtils.IsReconnect) { - await RetrieveLootFromServer(true); + await RetrieveLootFromServer(true); } else { @@ -1019,7 +1019,7 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU } } - ApplicationConfigClass config = BackendConfigAbstractClass.Config; + ApplicationConfigClass config = BackendConfigAbstractClass.Config; if (config.FixedFrameRate > 0f) { FixedDeltaTime = 1f / config.FixedFrameRate; @@ -1050,7 +1050,7 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU if (item.Value.Health.AtMinimum) { item.Value.IsDestroyed = true; - } + } } } @@ -1059,8 +1059,8 @@ public async Task InitPlayer(BotControllerSettings botsSettings, string backendU await method_11(location, startHandler.FinishLoading); } - private async Task GetReconnectProfile(string profileId) - { + private async Task GetReconnectProfile(string profileId) + { Profile_0 = null; ReconnectPacket reconnectPacket = new(true) @@ -1068,19 +1068,19 @@ private async Task GetReconnectProfile(string profileId) InitialRequest = true, ProfileId = profileId }; - FikaClient client = Singleton.Instance; - client.Writer.Reset(); - client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); + FikaClient client = Singleton.Instance; + client.Writer.Reset(); + client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); - do + do { await Task.Delay(250); } while (Profile_0 == null); - } + } - private async Task Reconnect() - { - FikaBackendUtils.ScreenController.ChangeStatus($"Reconnecting..."); + private async Task Reconnect() + { + FikaBackendUtils.ScreenController.ChangeStatus($"Reconnecting..."); ReconnectPacket reconnectPacket = new(true) { @@ -1094,9 +1094,9 @@ private async Task Reconnect() { await Task.Delay(1000); } while (!client.ReconnectDone); - } + } - private async Task RetrieveLootFromServer(bool register) + private async Task RetrieveLootFromServer(bool register) { FikaClient client = Singleton.Instance; WorldLootPacket packet = new(true); @@ -1114,7 +1114,7 @@ private async Task RetrieveLootFromServer(bool register) if (register) { RegisterPlayerRequest request = new(0, Location_0.Id, 0); - await FikaRequestHandler.RegisterPlayer(request); + await FikaRequestHandler.RegisterPlayer(request); } } @@ -2313,16 +2313,16 @@ public void ExitOverride() return; } - public byte[] GetHostLootItems() - { + public byte[] GetHostLootItems() + { if (HostLootItems == null || HostLootItems.Length == 0) { GClass1211 lootItems = new(Singleton.Instance.GetJsonLootItems() .Where(x => x as CorpseLootItemClass is null)); - return SimpleZlib.CompressToBytes(lootItems.ToJson([]), 6); - } + return SimpleZlib.CompressToBytes(lootItems.ToJson([]), 6); + } return HostLootItems; - } - } + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs index 9b81c5a3..b07bf4d0 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs @@ -19,75 +19,75 @@ public override void CancelApplyingItem() // Do nothing } - public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass health = null) - { - Profile.ProfileHealthClass profileHealthClass; - if ((profileHealthClass = health) == null) - { - Profile.ProfileHealthClass profileHealthClass2 = new() - { - BodyParts = GClass769.GetDictWith(), - Energy = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_0.Current, - Minimum = healthValue_0.Minimum, - Maximum = healthValue_0.Maximum - }, - Hydration = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_1.Current, - Minimum = healthValue_1.Minimum, - Maximum = healthValue_1.Maximum - }, - Temperature = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_2.Current, - Minimum = healthValue_2.Minimum, - Maximum = healthValue_2.Maximum - } - }; - profileHealthClass = profileHealthClass2; - profileHealthClass2.Poison = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_3.Current, - Minimum = healthValue_3.Minimum, - Maximum = healthValue_3.Maximum - }; - } - health = profileHealthClass; - foreach (KeyValuePair keyValuePair in Dictionary_0) - { - keyValuePair.Deconstruct(out EBodyPart ebodyPart, out BodyPartState bodyPartState); - EBodyPart ebodyPart2 = ebodyPart; - BodyPartState bodyPartState2 = bodyPartState; - if (!health.BodyParts.TryGetValue(ebodyPart2, out Profile.ProfileHealthClass.GClass1770 gclass)) - { - gclass = new Profile.ProfileHealthClass.GClass1770(); - health.BodyParts.Add(ebodyPart2, gclass); - } - gclass.Health = new Profile.ProfileHealthClass.ValueInfo - { - Current = bodyPartState2.Health.Current, - Maximum = bodyPartState2.Health.Maximum - }; - gclass.Effects ??= []; - } + public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass health = null) + { + Profile.ProfileHealthClass profileHealthClass; + if ((profileHealthClass = health) == null) + { + Profile.ProfileHealthClass profileHealthClass2 = new() + { + BodyParts = GClass769.GetDictWith(), + Energy = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_0.Current, + Minimum = healthValue_0.Minimum, + Maximum = healthValue_0.Maximum + }, + Hydration = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_1.Current, + Minimum = healthValue_1.Minimum, + Maximum = healthValue_1.Maximum + }, + Temperature = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_2.Current, + Minimum = healthValue_2.Minimum, + Maximum = healthValue_2.Maximum + } + }; + profileHealthClass = profileHealthClass2; + profileHealthClass2.Poison = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_3.Current, + Minimum = healthValue_3.Minimum, + Maximum = healthValue_3.Maximum + }; + } + health = profileHealthClass; + foreach (KeyValuePair keyValuePair in Dictionary_0) + { + keyValuePair.Deconstruct(out EBodyPart ebodyPart, out BodyPartState bodyPartState); + EBodyPart ebodyPart2 = ebodyPart; + BodyPartState bodyPartState2 = bodyPartState; + if (!health.BodyParts.TryGetValue(ebodyPart2, out Profile.ProfileHealthClass.GClass1770 gclass)) + { + gclass = new Profile.ProfileHealthClass.GClass1770(); + health.BodyParts.Add(ebodyPart2, gclass); + } + gclass.Health = new Profile.ProfileHealthClass.ValueInfo + { + Current = bodyPartState2.Health.Current, + Maximum = bodyPartState2.Health.Maximum + }; + gclass.Effects ??= []; + } - foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) - { - if (gclass is GInterface251 && gclass.State != EEffectState.Residued) // We only resync effects that are in-game effects, check for GClass increments - { - Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; - gclass2.Effects ??= []; - gclass2.Effects.Add(gclass.GetType().Name, new() - { - Time = gclass.TimeLeft, - ExtraData = gclass.StoreObj - }); - } - } + foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) + { + if (gclass is GInterface251 && gclass.State != EEffectState.Residued) // We only resync effects that are in-game effects, check for GClass increments + { + Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; + gclass2.Effects ??= []; + gclass2.Effects.Add(gclass.GetType().Name, new() + { + Time = gclass.TimeLeft, + ExtraData = gclass.StoreObj + }); + } + } - return health; - } - } + return health; + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs index d5cb36d2..702236c7 100644 --- a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs @@ -69,7 +69,7 @@ protected void FixedUpdate() Writer.Reset(); Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); - + if (!mover.IsMoving || mover.Pause) { player.LastDirection = Vector2.zero; diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index e20734d7..73d7c40e 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -216,7 +216,7 @@ private void SendPing() string localeId = null; #if DEBUG - ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); + ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); #endif if (LayerMask.LayerToName(hitLayer) == "Player") diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index 263648d3..23f5e8db 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -219,7 +219,7 @@ private void SendPing() string localeId = null; #if DEBUG - ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); + ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); #endif if (LayerMask.LayerToName(hitLayer) == "Player") diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index 936f1292..b9b32a74 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -36,11 +36,11 @@ public class ObservedCoopPlayer : CoopPlayer #region Fields and Properties public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; private float observedFixedTime = 0f; - public FikaHealthBar HealthBar - { - get => healthBar; - } - private FikaHealthBar healthBar = null; + public FikaHealthBar HealthBar + { + get => healthBar; + } + private FikaHealthBar healthBar = null; private Coroutine waitForStartRoutine; private bool isServer; public ObservedHealthController NetworkHealthController @@ -118,10 +118,10 @@ public override float SqrCameraDistance } return base.SqrCameraDistance; } - } - #endregion + } + #endregion - public static async Task CreateObservedPlayer(int playerId, Vector3 position, Quaternion rotation, + public static async Task CreateObservedPlayer(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, diff --git a/Fika.Core/Coop/Utils/NetManagerUtils.cs b/Fika.Core/Coop/Utils/NetManagerUtils.cs index 2e338bb9..fdf76d9d 100644 --- a/Fika.Core/Coop/Utils/NetManagerUtils.cs +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -199,41 +199,41 @@ public static void StopPinger() } } - public static Task CreateCoopHandler() - { - logger.LogInfo("Creating CoopHandler..."); - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler != null) - { - GameObject.Destroy(coopHandler); - } - - if (CoopHandler.CoopHandlerParent != null) - { - GameObject.Destroy(CoopHandler.CoopHandlerParent); - CoopHandler.CoopHandlerParent = null; - } - - if (CoopHandler.CoopHandlerParent == null) - { - CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); - GameObject.DontDestroyOnLoad(CoopHandler.CoopHandlerParent); - } - - coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); - - if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) - { - coopHandler.ServerId = FikaBackendUtils.GetGroupId(); - } - else - { - GameObject.Destroy(coopHandler); - logger.LogError("No Server Id found, Deleting Coop Handler"); - throw new MissingReferenceException("No Server Id found"); - } - - return Task.CompletedTask; - } - } + public static Task CreateCoopHandler() + { + logger.LogInfo("Creating CoopHandler..."); + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler != null) + { + GameObject.Destroy(coopHandler); + } + + if (CoopHandler.CoopHandlerParent != null) + { + GameObject.Destroy(CoopHandler.CoopHandlerParent); + CoopHandler.CoopHandlerParent = null; + } + + if (CoopHandler.CoopHandlerParent == null) + { + CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); + GameObject.DontDestroyOnLoad(CoopHandler.CoopHandlerParent); + } + + coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); + + if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) + { + coopHandler.ServerId = FikaBackendUtils.GetGroupId(); + } + else + { + GameObject.Destroy(coopHandler); + logger.LogError("No Server Id found, Deleting Coop Handler"); + throw new MissingReferenceException("No Server Id found"); + } + + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index c2758077..e3a64d22 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -36,989 +36,989 @@ namespace Fika.Core.Networking { - public class FikaClient : MonoBehaviour, INetEventListener - { - public NetDataWriter Writer => dataWriter; - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public NetPacketProcessor packetProcessor = new(); - public int Ping = 0; - public int ServerFPS = 0; - public int ConnectedClients = 0; - public int ReadyClients = 0; - public bool HostReady = false; - public bool ReconnectDone = false; - public NetManager NetClient - { - get - { - return netClient; - } - } - public NetPeer ServerConnection { get; private set; } - public bool SpawnPointsReceived { get; private set; } = false; - public bool Started - { - get - { - if (netClient == null) - { - return false; - } - return netClient.IsRunning; - } - } - - private NetManager netClient; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); - private NetDataWriter dataWriter = new(); - private FikaChat fikaChat; - private string myProfileId; - - public async void Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - myProfileId = FikaBackendUtils.Profile.ProfileId; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); - - netClient = new NetManager(this) - { - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - NatPunchEnabled = false, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - MaxConnectAttempts = 5, - ReconnectDelay = 1 * 1000 - }; - - await NetManagerUtils.CreateCoopHandler(); - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - - if (FikaBackendUtils.IsHostNatPunch) - { - NetManagerUtils.DestroyPingingClient(); - } - - netClient.Start(FikaBackendUtils.LocalPort); - - string ip = FikaBackendUtils.RemoteIp; - int port = FikaBackendUtils.RemotePort; - - 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"); - }; - - while (ServerConnection.ConnectionState != ConnectionState.Connected) - { + public class FikaClient : MonoBehaviour, INetEventListener + { + public NetDataWriter Writer => dataWriter; + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public NetPacketProcessor packetProcessor = new(); + public int Ping = 0; + public int ServerFPS = 0; + public int ConnectedClients = 0; + public int ReadyClients = 0; + public bool HostReady = false; + public bool ReconnectDone = false; + public NetManager NetClient + { + get + { + return netClient; + } + } + public NetPeer ServerConnection { get; private set; } + public bool SpawnPointsReceived { get; private set; } = false; + public bool Started + { + get + { + if (netClient == null) + { + return false; + } + return netClient.IsRunning; + } + } + + private NetManager netClient; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); + private NetDataWriter dataWriter = new(); + private FikaChat fikaChat; + private string myProfileId; + + public async void Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + myProfileId = FikaBackendUtils.Profile.ProfileId; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); + + netClient = new NetManager(this) + { + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + NatPunchEnabled = false, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + MaxConnectAttempts = 5, + ReconnectDelay = 1 * 1000 + }; + + await NetManagerUtils.CreateCoopHandler(); + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + + if (FikaBackendUtils.IsHostNatPunch) + { + NetManagerUtils.DestroyPingingClient(); + } + + netClient.Start(FikaBackendUtils.LocalPort); + + string ip = FikaBackendUtils.RemoteIp; + int port = FikaBackendUtils.RemotePort; + + 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"); + }; + + while (ServerConnection.ConnectionState != ConnectionState.Connected) + { #if DEBUG FikaPlugin.Instance.FikaLogger.LogWarning("FikaClient was not able to connect in time!"); #endif - await Task.Delay(1 * 6000); - ServerConnection = netClient.Connect(ip, port, "fika.core"); - } - - FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); - } - - private void OnReconnectPacketReceived(ReconnectPacket packet) - { - if (!packet.IsRequest) - { - switch (packet.Type) - { - case ReconnectPacket.EReconnectDataType.Throwable: - Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); - break; - case ReconnectPacket.EReconnectDataType.Interactives: - { - WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; - Dictionary netIdDictionary = []; - { - foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) - { - netIdDictionary.Add(data.NetId, data); - } - } - foreach (WorldInteractiveObject item in worldInteractiveObjects) - { - if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) - { - item.SetInitialSyncState(value); - } - } - break; - } - case ReconnectPacket.EReconnectDataType.LampControllers: - { - Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) - .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) - .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); - - foreach (KeyValuePair lampState in packet.LampStates) - { - if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) - { - if (lampController.LampState != (Turnable.EState)lampState.Value) - { - lampController.Switch((Turnable.EState)lampState.Value); - } - } - } - break; - } - case ReconnectPacket.EReconnectDataType.Windows: - { - Dictionary windowBreakerStates = packet.WindowBreakerStates; - foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) - .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) - { - if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) - { - try - { - DamageInfo damageInfo = default; - damageInfo.HitPoint = hitPosition; - windowBreaker.MakeHit(in damageInfo, true); - } - catch (Exception ex) - { - logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); - } - } - } - break; - } - case ReconnectPacket.EReconnectDataType.OwnCharacter: - coopHandler.LocalGameInstance.Profile_0 = packet.Profile; - coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; - FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; - break; - case ReconnectPacket.EReconnectDataType.Finished: - ReconnectDone = true; - break; - default: - break; - } - } - } - - private void OnWorldLootPacketReceived(WorldLootPacket packet) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); - coopGame.LootItems = lootItems; - coopGame.HasReceivedLoot = true; - } - } - - private void OnStatisticsPacketReceived(ThrowablePacket packet) - { - GClass724 grenades = Singleton.Instance.Grenades; - if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) - { - throwable.ApplyNetPacket(packet.Data); - } - } - - private void OnStatisticsPacketReceived(StatisticsPacket packet) - { - ServerFPS = packet.ServerFPS; - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet) - { - if (!packet.IsRequest) - { - World world = Singleton.Instance.World_0; - if (world.Interactables == null) - { - world.RegisterNetworkInteractionObjects(packet.Interactables); - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - coopGame.InteractablesInitialized = true; - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) - { - coopGame.SpawnId = packet.Name; - } - } - else - { - logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; - } - } - - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) - { - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - 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) - { - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } - - private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) - { - player.HandleCallbackFromServer(in packet); - } - } - - private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) - { - Dictionary newPlayers = Players; - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (player.ProfileId != packet.ProfileId) - { - FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); - for (int i = 0; i < Players.Count; i++) - { - KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); - Players.Remove(playerToReorganize.Key); - Players[packet.NetId] = playerToReorganize.Value; - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); - string allPlayers = ""; - 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); - } - FikaPlugin.Instance.FikaLogger.LogError(allPlayers); - } - } - - 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 (KeyValuePair player in Players) - { - if (player.Value == MyPlayer) - { - i = player.Key; - - break; - } - } - - if (i == -1) - { - FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); - return; - } - - Players.Remove(i); - Players[packet.NetId] = MyPlayer; - } - - private void OnSendCharacterPacketReceived(SendCharacterPacket packet) - { - if (coopHandler == null) - { - return; - } - - if (packet.PlayerInfo.Profile.ProfileId != myProfileId) - { - coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); - } - } - - private void OnBorderZonePacketReceived(BorderZonePacket packet) - { - if (Singleton.Instantiated) - { - BorderZone[] borderZones = Singleton.Instance.BorderZones; - if (borderZones != null && borderZones.Length > 0) - { - foreach (BorderZone borderZone in borderZones) - { - if (borderZone.Id == packet.ZoneId) - { - List players = Singleton.Instance.RegisteredPlayers; - foreach (IPlayer player in players) - { - if (player.ProfileId == packet.ProfileId) - { - IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); - borderZone.ProcessIncomingPacket(playerBridge, true); - } - } - } - } - } - } - } - - private void OnMinePacketReceived(MinePacket packet) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - player.ProcessInteractWithBTR(packet); - } - } - - private void OnBTRPacketReceived(BTRPacket packet) - { - if (coopHandler.clientBTR != null) - { - coopHandler.clientBTR.btrPackets.Enqueue(packet); - } - } - - private void OnWeatherPacketReceived(WeatherPacket packet) - { - if (!packet.IsRequest) - { - if (WeatherController.Instance != null) - { - WeatherController.Instance.method_0(packet.WeatherClasses); - } - else - { - logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); - } - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) - { - if (!packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - CoopGame coopGame = coopHandler.LocalGameInstance; - - CarExtraction carExtraction = FindObjectOfType(); - - foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) - { - ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); - if (point != null || point != default) - { - if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) - { - point.Enable(); - point.Status = exfilPoint.Value; - } - else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) - { - point.Disable(); - point.Status = exfilPoint.Value; - - if (carExtraction != null) - { - if (carExtraction.Subscribee == point) - { - carExtraction.Play(true); - } - } - } - coopGame.UpdateExfiltrationUi(point, false, true); - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); - } - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) - { - foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) - { - ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); - if (scavPoint != null || scavPoint != default) - { - if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) - { - scavPoint.Enable(); - scavPoint.EligibleIds.Add(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, true); - } - else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) - { - scavPoint.Disable(); - scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, false); - } - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); - } - } - - ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); - coopGame.ResetExfilPointsFromServer(points); - } - - SpawnPointsReceived = true; - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet) - { - switch (packet.PacketType) - { - case EPackageType.ClientExtract: - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); - - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - break; - case EPackageType.Ping: - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - break; - case EPackageType.TrainSync: - { - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - DateTime depart = new(packet.DepartureTime); - Traverse.Create(locomotive).Field("_depart").SetValue(depart); - } - else - { - logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); - } - } - break; - case EPackageType.ExfilCountdown: - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - break; - case EPackageType.TraderServiceNotification: - { - if (coopHandler.clientBTR) - { - coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); - } - } - break; - case EPackageType.DisposeBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) - { - if (!botToDispose.gameObject.activeSelf) - { - botToDispose.gameObject.SetActive(true); - } - - if (coopHandler.Players.Remove(packet.BotNetId)) - { - botToDispose.Dispose(); - AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); - logger.LogInfo("Disposing bot: " + packet.BotNetId); - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); - } - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); - } - } - break; - case EPackageType.RemoveAirdropManager: - { - if (Singleton.Instance != null) - { - Destroy(Singleton.Instance); - } - } - break; - case EPackageType.EnableBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) - { - if (!botToEnable.gameObject.activeSelf) - { + await Task.Delay(1 * 6000); + ServerConnection = netClient.Connect(ip, port, "fika.core"); + } + + FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); + } + + private void OnReconnectPacketReceived(ReconnectPacket packet) + { + if (!packet.IsRequest) + { + switch (packet.Type) + { + case ReconnectPacket.EReconnectDataType.Throwable: + Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); + break; + case ReconnectPacket.EReconnectDataType.Interactives: + { + WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; + Dictionary netIdDictionary = []; + { + foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) + { + netIdDictionary.Add(data.NetId, data); + } + } + foreach (WorldInteractiveObject item in worldInteractiveObjects) + { + if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) + { + item.SetInitialSyncState(value); + } + } + break; + } + case ReconnectPacket.EReconnectDataType.LampControllers: + { + Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) + .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); + + foreach (KeyValuePair lampState in packet.LampStates) + { + if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) + { + if (lampController.LampState != (Turnable.EState)lampState.Value) + { + lampController.Switch((Turnable.EState)lampState.Value); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.Windows: + { + Dictionary windowBreakerStates = packet.WindowBreakerStates; + foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) + { + if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) + { + try + { + DamageInfo damageInfo = default; + damageInfo.HitPoint = hitPosition; + windowBreaker.MakeHit(in damageInfo, true); + } + catch (Exception ex) + { + logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.OwnCharacter: + coopHandler.LocalGameInstance.Profile_0 = packet.Profile; + coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; + FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; + break; + case ReconnectPacket.EReconnectDataType.Finished: + ReconnectDone = true; + break; + default: + break; + } + } + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); + coopGame.LootItems = lootItems; + coopGame.HasReceivedLoot = true; + } + } + + private void OnStatisticsPacketReceived(ThrowablePacket packet) + { + GClass724 grenades = Singleton.Instance.Grenades; + if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) + { + throwable.ApplyNetPacket(packet.Data); + } + } + + private void OnStatisticsPacketReceived(StatisticsPacket packet) + { + ServerFPS = packet.ServerFPS; + } + + private void OnInteractableInitPacketReceived(InteractableInitPacket packet) + { + if (!packet.IsRequest) + { + World world = Singleton.Instance.World_0; + if (world.Interactables == null) + { + world.RegisterNetworkInteractionObjects(packet.Interactables); + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + coopGame.InteractablesInitialized = true; + } + } + } + } + + private void OnSpawnPointPacketReceived(SpawnpointPacket packet) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) + { + coopGame.SpawnId = packet.Name; + } + } + else + { + logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; + } + } + + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + + 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) + { + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) + { + player.HandleCallbackFromServer(in packet); + } + } + + private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) + { + Dictionary newPlayers = Players; + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (player.ProfileId != packet.ProfileId) + { + FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); + for (int i = 0; i < Players.Count; i++) + { + KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); + Players.Remove(playerToReorganize.Key); + Players[packet.NetId] = playerToReorganize.Value; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); + string allPlayers = ""; + 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); + } + FikaPlugin.Instance.FikaLogger.LogError(allPlayers); + } + } + + 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 (KeyValuePair player in Players) + { + if (player.Value == MyPlayer) + { + i = player.Key; + + break; + } + } + + if (i == -1) + { + FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); + return; + } + + Players.Remove(i); + Players[packet.NetId] = MyPlayer; + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet) + { + if (coopHandler == null) + { + return; + } + + if (packet.PlayerInfo.Profile.ProfileId != myProfileId) + { + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); + } + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet) + { + if (Singleton.Instantiated) + { + BorderZone[] borderZones = Singleton.Instance.BorderZones; + if (borderZones != null && borderZones.Length > 0) + { + foreach (BorderZone borderZone in borderZones) + { + if (borderZone.Id == packet.ZoneId) + { + List players = Singleton.Instance.RegisteredPlayers; + foreach (IPlayer player in players) + { + if (player.ProfileId == packet.ProfileId) + { + IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); + borderZone.ProcessIncomingPacket(playerBridge, true); + } + } + } + } + } + } + } + + private void OnMinePacketReceived(MinePacket packet) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + player.ProcessInteractWithBTR(packet); + } + } + + private void OnBTRPacketReceived(BTRPacket packet) + { + if (coopHandler.clientBTR != null) + { + coopHandler.clientBTR.btrPackets.Enqueue(packet); + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet) + { + if (!packet.IsRequest) + { + if (WeatherController.Instance != null) + { + WeatherController.Instance.method_0(packet.WeatherClasses); + } + else + { + logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); + } + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) + { + if (!packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + CoopGame coopGame = coopHandler.LocalGameInstance; + + CarExtraction carExtraction = FindObjectOfType(); + + foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) + { + ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); + if (point != null || point != default) + { + if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) + { + point.Enable(); + point.Status = exfilPoint.Value; + } + else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) + { + point.Disable(); + point.Status = exfilPoint.Value; + + if (carExtraction != null) + { + if (carExtraction.Subscribee == point) + { + carExtraction.Play(true); + } + } + } + coopGame.UpdateExfiltrationUi(point, false, true); + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); + } + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) + { + foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) + { + ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); + if (scavPoint != null || scavPoint != default) + { + if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) + { + scavPoint.Enable(); + scavPoint.EligibleIds.Add(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, true); + } + else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) + { + scavPoint.Disable(); + scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, false); + } + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); + } + } + + ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); + coopGame.ResetExfilPointsFromServer(points); + } + + SpawnPointsReceived = true; + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet) + { + switch (packet.PacketType) + { + case EPackageType.ClientExtract: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + break; + case EPackageType.Ping: + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + break; + case EPackageType.TrainSync: + { + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) + { + DateTime depart = new(packet.DepartureTime); + Traverse.Create(locomotive).Field("_depart").SetValue(depart); + } + else + { + logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); + } + } + break; + case EPackageType.ExfilCountdown: + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + break; + case EPackageType.TraderServiceNotification: + { + if (coopHandler.clientBTR) + { + coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); + } + } + break; + case EPackageType.DisposeBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) + { + if (!botToDispose.gameObject.activeSelf) + { + botToDispose.gameObject.SetActive(true); + } + + if (coopHandler.Players.Remove(packet.BotNetId)) + { + botToDispose.Dispose(); + AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); + logger.LogInfo("Disposing bot: " + packet.BotNetId); + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + } + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + } + } + break; + case EPackageType.RemoveAirdropManager: + { + if (Singleton.Instance != null) + { + Destroy(Singleton.Instance); + } + } + break; + case EPackageType.EnableBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) + { + if (!botToEnable.gameObject.activeSelf) + { #if DEBUG logger.LogWarning("Enabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(true); - } - else - { - 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 (botToEnable.gameObject.activeSelf) - { + botToEnable.gameObject.SetActive(true); + } + else + { + 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 (botToEnable.gameObject.activeSelf) + { #if DEBUG logger.LogWarning("Disabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(false); - } - else - { - logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); - } - } - } - break; - case EPackageType.ClearEffects: - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - if (playerToApply is ObservedCoopPlayer observedPlayer) - { - observedPlayer.HealthBar.ClearEffects(); - } - } - } - break; - } - } - - private void OnHealthSyncPacketReceived(HealthSyncPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - } - - private void OnAirdropLootPacketReceived(AirdropLootPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.ReceiveBuildLootContainer(packet); - } - else - { - logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); - } - } - - private void OnAirdropPacketReceived(AirdropPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.AirdropParameters = new() - { - Config = packet.Config, - AirdropAvailable = packet.AirdropAvailable, - PlaneSpawned = packet.PlaneSpawned, - BoxSpawned = packet.BoxSpawned, - DistanceTraveled = packet.DistanceTraveled, - DistanceToTravel = packet.DistanceToTravel, - DistanceToDrop = packet.DistanceToDrop, - Timer = packet.Timer, - DropHeight = packet.DropHeight, - TimeToStart = packet.TimeToStart, - RandomAirdropPoint = packet.BoxPoint, - SpawnPoint = packet.SpawnPoint, - LookPoint = packet.LookPoint - }; - } - else - { - logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); - } - } - - private void OnInformationPacketReceived(InformationPacket packet) - { - if (!packet.IsRequest) - { - ConnectedClients = packet.NumberOfPlayers; - ReadyClients = packet.ReadyPlayers; - HostReady = packet.HostReady; - } - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) - { - if (coopHandler == null) - { - return; - } - - if (!packet.IsRequest) - { - 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); - } - } - else if (packet.IsRequest) - { - logger.LogInfo($"Received CharacterRequest from server, send my Profile."); - AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = MyPlayer.Profile - }, - IsAlive = MyPlayer.ActiveHealthController.IsAlive, - IsAI = MyPlayer.IsAI, - Position = MyPlayer.Transform.position, - NetId = MyPlayer.NetId - }; - dataWriter.Reset(); - SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); - } - } - - private void OnInventoryPacketReceived(InventoryPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); - } - } - - private void OnDamagePacketReceived(DamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); - } - } - - private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); - } - } - - private void OnFirearmPacketReceived(WeaponPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - } - - private void OnGameTimerPacketReceived(GameTimerPacket packet) - { - TimeSpan sessionTime = new(packet.Tick); - - CoopGame coopGame = coopHandler.LocalGameInstance; - - GameTimerClass gameTimer = coopGame.GameTimer; - if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) - { - TimeSpan timeRemain = gameTimer.PastTime + sessionTime; - - gameTimer.ChangeSessionTime(timeRemain); - - Traverse timerPanel = Traverse.Create(coopGame.GameUi.TimerPanel); - timerPanel.Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds)); - - MainTimerPanel mainTimerPanel = timerPanel.Field("_mainTimerPanel").Value; - if (mainTimerPanel != null) - { - Traverse.Create(mainTimerPanel).Field("dateTime_0").Value = gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds); - mainTimerPanel.UpdateTimer(); - } - - Traverse.Create(gameTimer).Field("nullable_0").Value = new DateTime(packet.StartTime); - } - } - - private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) - { - HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; - - if (controller == null) - { - return; - } - - switch (packet.PacketType) - { - case EHalloweenPacketType.Summon: - controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); - break; - case EHalloweenPacketType.Sync: - controller.SetEventState(packet.EventState, false); - break; - case EHalloweenPacketType.Exit: - controller.method_3(packet.Exit); - break; - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - } - - protected void Update() - { - netClient?.PollEvents(); - - /*if (_netClient.FirstPeer == null) + botToEnable.gameObject.SetActive(false); + } + else + { + logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); + } + } + } + break; + case EPackageType.ClearEffects: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + if (playerToApply is ObservedCoopPlayer observedPlayer) + { + observedPlayer.HealthBar.ClearEffects(); + } + } + } + break; + } + } + + private void OnHealthSyncPacketReceived(HealthSyncPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + } + + private void OnAirdropLootPacketReceived(AirdropLootPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.ReceiveBuildLootContainer(packet); + } + else + { + logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); + } + } + + private void OnAirdropPacketReceived(AirdropPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.AirdropParameters = new() + { + Config = packet.Config, + AirdropAvailable = packet.AirdropAvailable, + PlaneSpawned = packet.PlaneSpawned, + BoxSpawned = packet.BoxSpawned, + DistanceTraveled = packet.DistanceTraveled, + DistanceToTravel = packet.DistanceToTravel, + DistanceToDrop = packet.DistanceToDrop, + Timer = packet.Timer, + DropHeight = packet.DropHeight, + TimeToStart = packet.TimeToStart, + RandomAirdropPoint = packet.BoxPoint, + SpawnPoint = packet.SpawnPoint, + LookPoint = packet.LookPoint + }; + } + else + { + logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); + } + } + + private void OnInformationPacketReceived(InformationPacket packet) + { + if (!packet.IsRequest) + { + ConnectedClients = packet.NumberOfPlayers; + ReadyClients = packet.ReadyPlayers; + HostReady = packet.HostReady; + } + } + + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) + { + if (coopHandler == null) + { + return; + } + + if (!packet.IsRequest) + { + 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); + } + } + else if (packet.IsRequest) + { + logger.LogInfo($"Received CharacterRequest from server, send my Profile."); + AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = MyPlayer.Profile + }, + IsAlive = MyPlayer.ActiveHealthController.IsAlive, + IsAI = MyPlayer.IsAI, + Position = MyPlayer.Transform.position, + NetId = MyPlayer.NetId + }; + dataWriter.Reset(); + SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + } + + private void OnInventoryPacketReceived(InventoryPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); + } + } + + private void OnDamagePacketReceived(DamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); + } + } + + private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); + } + } + + private void OnFirearmPacketReceived(WeaponPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + } + + private void OnGameTimerPacketReceived(GameTimerPacket packet) + { + TimeSpan sessionTime = new(packet.Tick); + + CoopGame coopGame = coopHandler.LocalGameInstance; + + GameTimerClass gameTimer = coopGame.GameTimer; + if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) + { + TimeSpan timeRemain = gameTimer.PastTime + sessionTime; + + gameTimer.ChangeSessionTime(timeRemain); + + Traverse timerPanel = Traverse.Create(coopGame.GameUi.TimerPanel); + timerPanel.Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds)); + + MainTimerPanel mainTimerPanel = timerPanel.Field("_mainTimerPanel").Value; + if (mainTimerPanel != null) + { + Traverse.Create(mainTimerPanel).Field("dateTime_0").Value = gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds); + mainTimerPanel.UpdateTimer(); + } + + Traverse.Create(gameTimer).Field("nullable_0").Value = new DateTime(packet.StartTime); + } + } + + private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) + { + HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; + + if (controller == null) + { + return; + } + + switch (packet.PacketType) + { + case EHalloweenPacketType.Summon: + controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); + break; + case EHalloweenPacketType.Sync: + controller.SetEventState(packet.EventState, false); + break; + case EHalloweenPacketType.Exit: + controller.method_3(packet.Exit); + break; + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + } + + protected void Update() + { + netClient?.PollEvents(); + + /*if (_netClient.FirstPeer == null) { _netClient.SendBroadcast([1], Port); }*/ - } - - protected void OnDestroy() - { - netClient?.Stop(); - - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); - } - - public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netClient.FirstPeer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogInfo("[CLIENT] We received error " + socketErrorCode); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - packetProcessor.ReadAllPackets(reader, peer); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) - { - logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); - netClient.Connect(remoteEndPoint, "fika.core"); - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - Ping = latency; - NetworkGameSession.RTT = peer.RoundTripTime; - NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; - } - - public void OnConnectionRequest(ConnectionRequest request) - { - - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); - if (disconnectInfo.Reason is DisconnectReason.Timeout) - { - NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); - Destroy(MyPlayer.PacketReceiver); - MyPlayer.PacketSender.DestroyThis(); - Destroy(this); - Singleton.Release(this); - } - } - } + } + + protected void OnDestroy() + { + netClient?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); + } + + public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netClient.FirstPeer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogInfo("[CLIENT] We received error " + socketErrorCode); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + packetProcessor.ReadAllPackets(reader, peer); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) + { + logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); + netClient.Connect(remoteEndPoint, "fika.core"); + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + Ping = latency; + NetworkGameSession.RTT = peer.RoundTripTime; + NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; + } + + public void OnConnectionRequest(ConnectionRequest request) + { + + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); + if (disconnectInfo.Reason is DisconnectReason.Timeout) + { + NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); + Destroy(MyPlayer.PacketReceiver); + MyPlayer.PacketSender.DestroyThis(); + Destroy(this); + Singleton.Release(this); + } + } + } } diff --git a/Fika.Core/Networking/FikaSerializationExtensions.cs b/Fika.Core/Networking/FikaSerializationExtensions.cs index 5fcc3b99..fd130a3f 100644 --- a/Fika.Core/Networking/FikaSerializationExtensions.cs +++ b/Fika.Core/Networking/FikaSerializationExtensions.cs @@ -13,342 +13,342 @@ namespace Fika.Core.Networking { - /// - /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika - /// - public static class FikaSerializationExtensions - { - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector3 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - writer.Put(vector.z); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Vector3 GetVector3(this NetDataReader reader) - { - return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector2 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Vector2 GetVector2(this NetDataReader reader) - { - return new Vector2(reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Quaternion quaternion) - { - writer.Put(quaternion.x); - writer.Put(quaternion.y); - writer.Put(quaternion.z); - writer.Put(quaternion.w); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Quaternion GetQuaternion(this NetDataReader reader) - { - return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Color color) - { - writer.Put(color.r); - writer.Put(color.g); - writer.Put(color.b); - writer.Put(color.a); - } - - /// - /// Deserializes a - /// - /// - /// A /returns> - public static Color GetColor(this NetDataReader reader) - { - return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a (Physical) struct - /// - /// - /// - public static void Put(this NetDataWriter writer, GStruct36 physical) - { - writer.Put(physical.StaminaExhausted); - writer.Put(physical.OxygenExhausted); - writer.Put(physical.HandsExhausted); - } - - /// - /// Deserializes a (Physical) struct - /// - /// - /// A (Physical) - public static GStruct36 GetPhysical(this NetDataReader reader) - { - return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; - } - - /// - /// Serialize a array - /// - /// - /// - public static void PutByteArray(this NetDataWriter writer, byte[] bytes) - { - writer.Put(bytes.Length); - if (bytes.Length > 0) - { - writer.Put(bytes); - } - } - - /// - /// Deserializes a array - /// - /// - /// A array - public static byte[] GetByteArray(this NetDataReader reader) - { - int length = reader.GetInt(); - if (length > 0) - { - byte[] bytes = new byte[length]; - reader.GetBytes(bytes, length); - return bytes; - } - return Array.Empty(); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, DateTime dateTime) - { - writer.Put(dateTime.ToOADate()); - } - - /// - /// Deserializes a - /// - /// - /// A - public static DateTime GetDateTime(this NetDataReader reader) - { - return DateTime.FromOADate(reader.GetDouble()); - } - - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// The to serialize - public static void PutAirdropItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); - } - - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// An - public static Item GetAirdropItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); - - ContainerCollection[] containerCollections = [item as ContainerCollection]; - ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() - .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); - - return item; - } - - /// - /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. - /// - /// - /// The to serialize - public static void PutItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); - } - - /// - /// Gets a serialized - /// - /// - /// An - public static Item GetItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); - } - - public static void PutThrowableData(this NetDataWriter writer, List throwables) - { - writer.Put(throwables.Count); - foreach (GStruct35 data in throwables) - { - writer.Put(data.Id); - writer.Put(data.Position); - writer.Put(data.Template); - writer.Put(data.Time); - writer.Put(data.Orientation); - writer.Put(data.PlatformId); - } - } - - public static List GetThrowableData(this NetDataReader reader) - { - int amount = reader.GetInt(); - List throwables = new(amount); - for (int i = 0; i < amount; i++) - { - GStruct35 data = new() - { - Id = reader.GetString(), - Position = reader.GetVector3(), - Template = reader.GetString(), - Time = reader.GetInt(), - Orientation = reader.GetQuaternion(), - PlatformId = reader.GetShort() - }; - throwables.Add(data); - } - - return throwables; - } - - public static void PutInteractivesStates(this NetDataWriter writer, List interactiveObjectsData) - { - writer.Put(interactiveObjectsData.Count); - for (int i = 0; i < interactiveObjectsData.Count; i++) - { - writer.Put(interactiveObjectsData[i].NetId); - writer.Put(interactiveObjectsData[i].State); - writer.Put(interactiveObjectsData[i].IsBroken); - } - } - - public static List GetInteractivesStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - List interactivesStates = new(amount); - for (int i = 0; i < amount; i++) - { - WorldInteractiveObject.GStruct384 data = new() - { - NetId = reader.GetInt(), - State = reader.GetByte(), - IsBroken = reader.GetBool() - }; - interactivesStates.Add(data); - } - - return interactivesStates; - } - - public static void PutLampStates(this NetDataWriter writer, Dictionary lampStates) - { - int amount = lampStates.Count; - writer.Put(amount); - foreach (KeyValuePair lampState in lampStates) - { - writer.Put(lampState.Key); - writer.Put(lampState.Value); - } - } - - public static Dictionary GetLampStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { - states.Add(reader.GetInt(), reader.GetByte()); - } - - return states; - } - - public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) - { - int amount = windowBreakerStates.Count; - writer.Put(amount); - foreach (KeyValuePair windowBreakerState in windowBreakerStates) - { - writer.Put(windowBreakerState.Key); - writer.Put(windowBreakerState.Value); - } - } - - public static Dictionary GetWindowBreakerStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { - states.Add(reader.GetInt(), reader.GetVector3()); - } - - return states; - } - } + /// + /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika + /// + public static class FikaSerializationExtensions + { + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector3 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + writer.Put(vector.z); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Vector3 GetVector3(this NetDataReader reader) + { + return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector2 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Vector2 GetVector2(this NetDataReader reader) + { + return new Vector2(reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Quaternion quaternion) + { + writer.Put(quaternion.x); + writer.Put(quaternion.y); + writer.Put(quaternion.z); + writer.Put(quaternion.w); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Quaternion GetQuaternion(this NetDataReader reader) + { + return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Color color) + { + writer.Put(color.r); + writer.Put(color.g); + writer.Put(color.b); + writer.Put(color.a); + } + + /// + /// Deserializes a + /// + /// + /// A /returns> + public static Color GetColor(this NetDataReader reader) + { + return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a (Physical) struct + /// + /// + /// + public static void Put(this NetDataWriter writer, GStruct36 physical) + { + writer.Put(physical.StaminaExhausted); + writer.Put(physical.OxygenExhausted); + writer.Put(physical.HandsExhausted); + } + + /// + /// Deserializes a (Physical) struct + /// + /// + /// A (Physical) + public static GStruct36 GetPhysical(this NetDataReader reader) + { + return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; + } + + /// + /// Serialize a array + /// + /// + /// + public static void PutByteArray(this NetDataWriter writer, byte[] bytes) + { + writer.Put(bytes.Length); + if (bytes.Length > 0) + { + writer.Put(bytes); + } + } + + /// + /// Deserializes a array + /// + /// + /// A array + public static byte[] GetByteArray(this NetDataReader reader) + { + int length = reader.GetInt(); + if (length > 0) + { + byte[] bytes = new byte[length]; + reader.GetBytes(bytes, length); + return bytes; + } + return Array.Empty(); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, DateTime dateTime) + { + writer.Put(dateTime.ToOADate()); + } + + /// + /// Deserializes a + /// + /// + /// A + public static DateTime GetDateTime(this NetDataReader reader) + { + return DateTime.FromOADate(reader.GetDouble()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// The to serialize + public static void PutAirdropItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// An + public static Item GetAirdropItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); + + Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + + ContainerCollection[] containerCollections = [item as ContainerCollection]; + ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() + .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); + + return item; + } + + /// + /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. + /// + /// + /// The to serialize + public static void PutItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Gets a serialized + /// + /// + /// An + public static Item GetItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); + + return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + } + + public static void PutThrowableData(this NetDataWriter writer, List throwables) + { + writer.Put(throwables.Count); + foreach (GStruct35 data in throwables) + { + writer.Put(data.Id); + writer.Put(data.Position); + writer.Put(data.Template); + writer.Put(data.Time); + writer.Put(data.Orientation); + writer.Put(data.PlatformId); + } + } + + public static List GetThrowableData(this NetDataReader reader) + { + int amount = reader.GetInt(); + List throwables = new(amount); + for (int i = 0; i < amount; i++) + { + GStruct35 data = new() + { + Id = reader.GetString(), + Position = reader.GetVector3(), + Template = reader.GetString(), + Time = reader.GetInt(), + Orientation = reader.GetQuaternion(), + PlatformId = reader.GetShort() + }; + throwables.Add(data); + } + + return throwables; + } + + public static void PutInteractivesStates(this NetDataWriter writer, List interactiveObjectsData) + { + writer.Put(interactiveObjectsData.Count); + for (int i = 0; i < interactiveObjectsData.Count; i++) + { + writer.Put(interactiveObjectsData[i].NetId); + writer.Put(interactiveObjectsData[i].State); + writer.Put(interactiveObjectsData[i].IsBroken); + } + } + + public static List GetInteractivesStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + List interactivesStates = new(amount); + for (int i = 0; i < amount; i++) + { + WorldInteractiveObject.GStruct384 data = new() + { + NetId = reader.GetInt(), + State = reader.GetByte(), + IsBroken = reader.GetBool() + }; + interactivesStates.Add(data); + } + + return interactivesStates; + } + + public static void PutLampStates(this NetDataWriter writer, Dictionary lampStates) + { + int amount = lampStates.Count; + writer.Put(amount); + foreach (KeyValuePair lampState in lampStates) + { + writer.Put(lampState.Key); + writer.Put(lampState.Value); + } + } + + public static Dictionary GetLampStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetByte()); + } + + return states; + } + + public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) + { + int amount = windowBreakerStates.Count; + writer.Put(amount); + foreach (KeyValuePair windowBreakerState in windowBreakerStates) + { + writer.Put(windowBreakerState.Key); + writer.Put(windowBreakerState.Value); + } + } + + public static Dictionary GetWindowBreakerStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetVector3()); + } + + return states; + } + } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 2132d7da..dd6a2a64 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -40,879 +40,879 @@ namespace Fika.Core.Networking { - public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener - { - public NetPacketProcessor packetProcessor = new(); - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public List PlayersMissing = []; - public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); - public int ReadyClients = 0; - public NetManager NetServer - { - get - { - return netServer; - } - } - public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); - public bool hasHadPeer = false; - public bool Started - { - get - { - if (netServer == null) - { - return false; - } - return netServer.IsRunning; - } - } - - private NetManager netServer; - public NetDataWriter Writer => dataWriter; - private readonly NetDataWriter dataWriter = new(); - private int Port => FikaPlugin.UDPPort.Value; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); - private int currentNetId; - private FikaChat fikaChat; - private CancellationTokenSource natIntroduceRoutineCts; - private int statisticsCounter = 0; - - public async Task Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - // Start at 1 to avoid having 0 and making us think it's working when it's not - currentNetId = 1; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); - - netServer = new NetManager(this) - { - BroadcastReceiveEnabled = true, - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - AutoRecycle = true, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - NatPunchEnabled = true - }; - - if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) - { - bool upnpFailed = false; - - 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(); - - 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) - { - Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); - } - } - else if (FikaPlugin.ForceIP.Value != "") - { - MyExternalIP = FikaPlugin.ForceIP.Value; - } - else - { - try - { - HttpClient client = new(); - string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); - MyExternalIP = ipAdress.Replace("\n", ""); - client.Dispose(); - } - catch (Exception) - { - Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); - } - } - - if (FikaPlugin.UseNatPunching.Value) - { - 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 - { - if (FikaPlugin.ForceBindIP.Value != "Disabled") - { - netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); - } - else - { - netServer.Start(Port); - } - } - - logger.LogInfo("Started Fika Server"); - - NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - - string[] Ips = []; - - foreach (string ip in FikaPlugin.Instance.LocalIPs) - { - if (ValidateLocalIP(ip)) - { - 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, FikaBackendUtils.IsDedicatedGame); - FikaRequestHandler.UpdateSetHost(body); - - FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); - } - - private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (packet.InitialRequest) - { - NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); - foreach (CoopPlayer player in coopHandler.HumanPlayers) - { - if (player.ProfileId == packet.ProfileId && player is ObservedCoopPlayer observedCoopPlayer) - { - ReconnectPacket ownCharacterPacket = new(false) - { - Type = EReconnectDataType.OwnCharacter, - Profile = observedCoopPlayer.Profile, - ProfileHealthClass = observedCoopPlayer.NetworkHealthController.Store(), - PlayerPosition = observedCoopPlayer.Position - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref ownCharacterPacket, DeliveryMethod.ReliableOrdered); - - observedCoopPlayer.HealthBar.ClearEffects(); - GenericPacket clearEffectsPacket = new(EPackageType.ClearEffects) - { - NetId = observedCoopPlayer.NetId - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref clearEffectsPacket, DeliveryMethod.ReliableUnordered, peer); - } - } - - return; - } - - GameWorld gameWorld = Singleton.Instance; - Traverse worldTraverse = Traverse.Create(gameWorld.World_0); - - GClass724.GStruct43 grenades = gameWorld.Grenades.GetValuesEnumerator(); - List smokeData = []; - foreach (Throwable item in grenades) - { - if (item is SmokeGrenade smokeGrenade) - { - smokeData.Add(smokeGrenade.NetworkData); - } - } - - if (smokeData.Count > 0) - { - ReconnectPacket throwablePacket = new(false) - { - Type = EReconnectDataType.Throwable, - ThrowableData = smokeData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref throwablePacket, DeliveryMethod.ReliableOrdered); - } - - List interactivesData = []; - WorldInteractiveObject[] worldInteractiveObjects = worldTraverse.Field("worldInteractiveObject_0").Value; - foreach (WorldInteractiveObject interactiveObject in worldInteractiveObjects) - { - if ((interactiveObject.DoorState != interactiveObject.InitialDoorState && interactiveObject.DoorState != EDoorState.Interacting) - || (interactiveObject is Door door && door.IsBroken)) - { - interactivesData.Add(interactiveObject.GetStatusInfo(true)); - } - } - - if (interactivesData.Count > 0) - { - ReconnectPacket interactivePacket = new(false) - { - Type = EReconnectDataType.Interactives, - InteractivesData = interactivesData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref interactivePacket, DeliveryMethod.ReliableOrdered); - } - - IEnumerable lampControllers = LocationScene.GetAllObjects(false); - Dictionary lampStates = []; - foreach (LampController controller in lampControllers) - { - lampStates.Add(controller.NetId, (byte)controller.LampState); - } - - if (lampStates.Count > 0) - { - ReconnectPacket lampPacket = new(false) - { - Type = EReconnectDataType.LampControllers, - LampStates = lampStates - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref lampPacket, DeliveryMethod.ReliableOrdered); - } - - GClass724.GStruct43 windows = gameWorld.Windows.GetValuesEnumerator(); - Dictionary windowData = []; - foreach (WindowBreaker window in windows) - { - if (window.AvailableToSync && window.IsDamaged) - { - windowData.Add(window.NetId, window.FirstHitPosition.Value); - } - } - - if (windowData.Count > 0) - { - ReconnectPacket windowPacket = new(false) - { - Type = EReconnectDataType.Windows, - WindowBreakerStates = windowData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); - } - - foreach (CoopPlayer player in coopHandler.Players.Values) - { - SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), - player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref characterPacket, DeliveryMethod.ReliableOrdered); - } - - foreach (CoopPlayer player in coopHandler.HumanPlayers) - { - if (player.ProfileId == packet.ProfileId) - { - AssignNetIdPacket assignPacket = new() - { - NetId = player.NetId - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref assignPacket, DeliveryMethod.ReliableOrdered); - } - } - - ReconnectPacket finishPacket = new(false) - { - Type = EReconnectDataType.Finished - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); - } - } - - private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - WorldLootPacket response = new(false) - { - Data = coopGame.GetHostLootItems() - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (Singleton.Instantiated) - { - World world = Singleton.Instance.World_0; - if (world.Interactables != null) - { - InteractableInitPacket response = new(false) - { - Interactables = world.Interactables - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - if (packet.IsRequest) - { - SpawnpointPacket response = new(false) - { - Name = coopGame.GetSpawnpointName() - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) - { - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - private bool ValidateLocalIP(string LocalIP) - { - if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) - { - return true; - } - - //Check for RFC1918's 20 bit block. - int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); - - if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) - { - return true; - } - - return false; - } - - 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() - { - int netId = currentNetId; - currentNetId++; - - return netId; - } - - public void SetupGameVariables(CoopPlayer coopPlayer) - { - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } - - private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - 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); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - AssignNetIdPacket assignNetIdPacket = new() - { - NetId = netId - }; - - dataWriter.Reset(); - packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); - peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); - } - - private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) - { - // This shouldn't happen - } - - private void OnMinePacketReceived(MinePacket packet, NetPeer peer) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (coopHandler.serverBTR.CanPlayerEnter(player)) - { - coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - BTRInteractionPacket newPacket = new(packet.NetId) - { - HasInteractPacket = false - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); - } - } - } - } - - private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (FikaBackendUtils.Nodes != null) - { - WeatherPacket weatherPacket2 = new() - { - IsRequest = false, - HasData = true, - Amount = FikaBackendUtils.Nodes.Length, - WeatherClasses = FikaBackendUtils.Nodes - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); - }; - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - ExfiltrationPacket exfilPacket = new(false) - { - ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, - ExfiltrationPoints = [] - }; - - foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) - { - exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) - { - exfilPacket.HasScavExfils = true; - exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; - exfilPacket.ScavExfiltrationPoints = []; - - foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) - { - exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); - } - } - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) - { - if (packet.PacketType == EPackageType.ClientExtract) - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); - - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - else if (packet.PacketType == EPackageType.LoadBot) - { - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.IncreaseLoadedPlayers(packet.BotNetId); - - return; - } - else if (packet.PacketType == EPackageType.ExfilCountdown) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) - { - ReadyClients += packet.ReadyPlayers; - - InformationPacket respondPackage = new(false) - { - NumberOfPlayers = netServer.ConnectedPeersCount, - ReadyPlayers = ReadyClients, - HostReady = coopHandler != null && coopHandler.LocalGameInstance.Status == GameStatus.Started - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - if (packet.IsRequest) - { - foreach (CoopPlayer player in coopHandler.Players.Values) - { - if (player.ProfileId == packet.ProfileId) - { - continue; - } - - if (packet.Characters.Contains(player.ProfileId)) - { - continue; - } - - AllCharacterRequestPacket requestPacket = new(player.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = player.Profile - }, - IsAlive = player.HealthController.IsAlive, - IsAI = player is CoopBot, - Position = player.Transform.position, - NetId = player.NetId - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - PlayersMissing.Add(packet.ProfileId); - 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)) - { - 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); - PlayersMissing.Remove(packet.ProfileId); - } - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); - using BinaryReader binaryReader = new(memoryStream); - try - { - GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); - - InventoryOperationHandler opHandler = new() - { - opResult = result, - operationId = packet.ItemControllerExecutePacket.CallbackId, - netId = playerToApply.NetId, - peer = peer, - server = this - }; - - OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); - SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); - - // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. - // Unknown what problems this might cause so far. - if (result.Value is UnloadOperationClass unloadOperation) - { - if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) - { - Item item = internalSplitOperation.To.Item; - if (item != null) - { - if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) - { - item.Id = internalSplitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - } - - // TODO: Same as above. - if (result.Value is SplitOperationClass splitOperation) - { - Item item = splitOperation.To.Item; - if (item != null) - { - if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) - { - item.Id = splitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - - /*// Fix for folding not replicating + public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener + { + public NetPacketProcessor packetProcessor = new(); + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public List PlayersMissing = []; + public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); + public int ReadyClients = 0; + public NetManager NetServer + { + get + { + return netServer; + } + } + public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); + public bool hasHadPeer = false; + public bool Started + { + get + { + if (netServer == null) + { + return false; + } + return netServer.IsRunning; + } + } + + private NetManager netServer; + public NetDataWriter Writer => dataWriter; + private readonly NetDataWriter dataWriter = new(); + private int Port => FikaPlugin.UDPPort.Value; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); + private int currentNetId; + private FikaChat fikaChat; + private CancellationTokenSource natIntroduceRoutineCts; + private int statisticsCounter = 0; + + public async Task Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + // Start at 1 to avoid having 0 and making us think it's working when it's not + currentNetId = 1; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); + + netServer = new NetManager(this) + { + BroadcastReceiveEnabled = true, + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + AutoRecycle = true, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + NatPunchEnabled = true + }; + + if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) + { + bool upnpFailed = false; + + 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(); + + 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) + { + Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); + } + } + else if (FikaPlugin.ForceIP.Value != "") + { + MyExternalIP = FikaPlugin.ForceIP.Value; + } + else + { + try + { + HttpClient client = new(); + string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); + MyExternalIP = ipAdress.Replace("\n", ""); + client.Dispose(); + } + catch (Exception) + { + Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); + } + } + + if (FikaPlugin.UseNatPunching.Value) + { + 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 + { + if (FikaPlugin.ForceBindIP.Value != "Disabled") + { + netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); + } + else + { + netServer.Start(Port); + } + } + + logger.LogInfo("Started Fika Server"); + + NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + + string[] Ips = []; + + foreach (string ip in FikaPlugin.Instance.LocalIPs) + { + if (ValidateLocalIP(ip)) + { + 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, FikaBackendUtils.IsDedicatedGame); + FikaRequestHandler.UpdateSetHost(body); + + FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); + } + + private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (packet.InitialRequest) + { + NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId && player is ObservedCoopPlayer observedCoopPlayer) + { + ReconnectPacket ownCharacterPacket = new(false) + { + Type = EReconnectDataType.OwnCharacter, + Profile = observedCoopPlayer.Profile, + ProfileHealthClass = observedCoopPlayer.NetworkHealthController.Store(), + PlayerPosition = observedCoopPlayer.Position + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref ownCharacterPacket, DeliveryMethod.ReliableOrdered); + + observedCoopPlayer.HealthBar.ClearEffects(); + GenericPacket clearEffectsPacket = new(EPackageType.ClearEffects) + { + NetId = observedCoopPlayer.NetId + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref clearEffectsPacket, DeliveryMethod.ReliableUnordered, peer); + } + } + + return; + } + + GameWorld gameWorld = Singleton.Instance; + Traverse worldTraverse = Traverse.Create(gameWorld.World_0); + + GClass724.GStruct43 grenades = gameWorld.Grenades.GetValuesEnumerator(); + List smokeData = []; + foreach (Throwable item in grenades) + { + if (item is SmokeGrenade smokeGrenade) + { + smokeData.Add(smokeGrenade.NetworkData); + } + } + + if (smokeData.Count > 0) + { + ReconnectPacket throwablePacket = new(false) + { + Type = EReconnectDataType.Throwable, + ThrowableData = smokeData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref throwablePacket, DeliveryMethod.ReliableOrdered); + } + + List interactivesData = []; + WorldInteractiveObject[] worldInteractiveObjects = worldTraverse.Field("worldInteractiveObject_0").Value; + foreach (WorldInteractiveObject interactiveObject in worldInteractiveObjects) + { + if ((interactiveObject.DoorState != interactiveObject.InitialDoorState && interactiveObject.DoorState != EDoorState.Interacting) + || (interactiveObject is Door door && door.IsBroken)) + { + interactivesData.Add(interactiveObject.GetStatusInfo(true)); + } + } + + if (interactivesData.Count > 0) + { + ReconnectPacket interactivePacket = new(false) + { + Type = EReconnectDataType.Interactives, + InteractivesData = interactivesData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref interactivePacket, DeliveryMethod.ReliableOrdered); + } + + IEnumerable lampControllers = LocationScene.GetAllObjects(false); + Dictionary lampStates = []; + foreach (LampController controller in lampControllers) + { + lampStates.Add(controller.NetId, (byte)controller.LampState); + } + + if (lampStates.Count > 0) + { + ReconnectPacket lampPacket = new(false) + { + Type = EReconnectDataType.LampControllers, + LampStates = lampStates + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref lampPacket, DeliveryMethod.ReliableOrdered); + } + + GClass724.GStruct43 windows = gameWorld.Windows.GetValuesEnumerator(); + Dictionary windowData = []; + foreach (WindowBreaker window in windows) + { + if (window.AvailableToSync && window.IsDamaged) + { + windowData.Add(window.NetId, window.FirstHitPosition.Value); + } + } + + if (windowData.Count > 0) + { + ReconnectPacket windowPacket = new(false) + { + Type = EReconnectDataType.Windows, + WindowBreakerStates = windowData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); + } + + foreach (CoopPlayer player in coopHandler.Players.Values) + { + SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), + player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref characterPacket, DeliveryMethod.ReliableOrdered); + } + + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId) + { + AssignNetIdPacket assignPacket = new() + { + NetId = player.NetId + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref assignPacket, DeliveryMethod.ReliableOrdered); + } + } + + ReconnectPacket finishPacket = new(false) + { + Type = EReconnectDataType.Finished + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); + } + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + WorldLootPacket response = new(false) + { + Data = coopGame.GetHostLootItems() + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + + private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (Singleton.Instantiated) + { + World world = Singleton.Instance.World_0; + if (world.Interactables != null) + { + InteractableInitPacket response = new(false) + { + Interactables = world.Interactables + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + } + + private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + if (packet.IsRequest) + { + SpawnpointPacket response = new(false) + { + Name = coopGame.GetSpawnpointName() + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) + { + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + + private bool ValidateLocalIP(string LocalIP) + { + if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) + { + return true; + } + + //Check for RFC1918's 20 bit block. + int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); + + if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) + { + return true; + } + + return false; + } + + 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() + { + int netId = currentNetId; + currentNetId++; + + return netId; + } + + public void SetupGameVariables(CoopPlayer coopPlayer) + { + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + 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); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + AssignNetIdPacket assignNetIdPacket = new() + { + NetId = netId + }; + + dataWriter.Reset(); + packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); + peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) + { + // This shouldn't happen + } + + private void OnMinePacketReceived(MinePacket packet, NetPeer peer) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (coopHandler.serverBTR.CanPlayerEnter(player)) + { + coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + BTRInteractionPacket newPacket = new(packet.NetId) + { + HasInteractPacket = false + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); + } + } + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (FikaBackendUtils.Nodes != null) + { + WeatherPacket weatherPacket2 = new() + { + IsRequest = false, + HasData = true, + Amount = FikaBackendUtils.Nodes.Length, + WeatherClasses = FikaBackendUtils.Nodes + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); + }; + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + ExfiltrationPacket exfilPacket = new(false) + { + ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, + ExfiltrationPoints = [] + }; + + foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) + { + exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) + { + exfilPacket.HasScavExfils = true; + exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; + exfilPacket.ScavExfiltrationPoints = []; + + foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) + { + exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); + } + } + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) + { + if (packet.PacketType == EPackageType.ClientExtract) + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + else if (packet.PacketType == EPackageType.LoadBot) + { + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.IncreaseLoadedPlayers(packet.BotNetId); + + return; + } + else if (packet.PacketType == EPackageType.ExfilCountdown) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) + { + ReadyClients += packet.ReadyPlayers; + + InformationPacket respondPackage = new(false) + { + NumberOfPlayers = netServer.ConnectedPeersCount, + ReadyPlayers = ReadyClients, + HostReady = coopHandler != null && coopHandler.LocalGameInstance.Status == GameStatus.Started + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); + } + + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + if (packet.IsRequest) + { + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (player.ProfileId == packet.ProfileId) + { + continue; + } + + if (packet.Characters.Contains(player.ProfileId)) + { + continue; + } + + AllCharacterRequestPacket requestPacket = new(player.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = player.Profile + }, + IsAlive = player.HealthController.IsAlive, + IsAI = player is CoopBot, + Position = player.Transform.position, + NetId = player.NetId + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + PlayersMissing.Add(packet.ProfileId); + 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)) + { + 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); + PlayersMissing.Remove(packet.ProfileId); + } + } + } + + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); + using BinaryReader binaryReader = new(memoryStream); + try + { + GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); + + InventoryOperationHandler opHandler = new() + { + opResult = result, + operationId = packet.ItemControllerExecutePacket.CallbackId, + netId = playerToApply.NetId, + peer = peer, + server = this + }; + + OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); + SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); + + // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. + // Unknown what problems this might cause so far. + if (result.Value is UnloadOperationClass unloadOperation) + { + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) + { + Item item = internalSplitOperation.To.Item; + if (item != null) + { + if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) + { + item.Id = internalSplitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + + // TODO: Same as above. + if (result.Value is SplitOperationClass splitOperation) + { + Item item = splitOperation.To.Item; + if (item != null) + { + if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) + { + item.Id = splitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + + /*// Fix for folding not replicating if (result.Value is GClass2858 foldOperation) { if (playerToApply.HandsController is CoopObservedFirearmController observedFirearmController) @@ -923,324 +923,324 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) } } }*/ - } - catch (Exception exception) - { - FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); - OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); - SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - 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(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) - { - if (!packet.IsRequest) - return; - - CoopGame game = coopHandler.LocalGameInstance; - if (game != null) - { - GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks, game.GameTimer.StartDateTime.Value.Ticks); - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError("OnGameTimerPacketReceived: Game was null!"); - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - protected void Update() - { - netServer?.PollEvents(); - netServer?.NatPunchModule?.PollEvents(); - - statisticsCounter++; - if (statisticsCounter > 600) - { - statisticsCounter = 0; - SendStatisticsPacket(); - } - } - - private void SendStatisticsPacket() - { - int fps = (int)(1f / Time.unscaledDeltaTime); - StatisticsPacket packet = new(fps); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void OnDestroy() - { - netServer?.Stop(); - - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); - } - - public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable - { - if (peerToExclude != null) - { - if (NetServer.ConnectedPeersCount > 1) - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod, peerToExclude); - } - } - else - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod); - } - } - - public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - peer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); - logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); - - hasHadPeer = true; - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogError("[SERVER] error " + socketErrorCode); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.Broadcast) - { - logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); - NetDataWriter resp = new(); - resp.Put(1); - netServer.SendUnconnectedMessage(resp, remoteEndPoint); - } - else - { - if (reader.TryGetString(out string data)) - { - NetDataWriter resp; - - switch (data) - { - 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 - { - logger.LogError("PingingRequest: Could not parse string"); - } - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - } - - public void OnConnectionRequest(ConnectionRequest request) - { - request.AcceptIfKey("fika.core"); - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - 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) - { - timeSinceLastPeerDisconnected = DateTime.Now; - } - - if (FikaBackendUtils.IsDedicatedGame) - { - if (netServer.ConnectedPeersCount == 0) - { - foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) - { - if (profile is null) - { - continue; - } - - if (profile.ProfileId == RequestHandler.SessionId) - { - foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) - { - bodyPartHealth.Effects.Clear(); - bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; - } - } - } - - // End the raid - Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, - Singleton.Instance.MyExitStatus, - Singleton.Instance.MyExitLocation, 0); - } - } - } - - public void WriteNet(NetLogLevel level, string str, params object[] args) - { - Debug.LogFormat(str, args); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - 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; - public uint operationId; - public int netId; - public NetPeer peer; - public FikaServer server; - - internal void HandleResult(IResult result) - { - NetDataWriter writer = new(); - OperationCallbackPacket operationCallbackPacket; - - if (!result.Succeed) - { - FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); - operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - return; - } - - InventoryPacket packet = new(netId) - { - HasItemControllerExecutePacket = true - }; - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operationId, - OperationBytes = opBytes - }; - - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); - - operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - } - } - } + } + catch (Exception exception) + { + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + 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(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) + { + if (!packet.IsRequest) + return; + + CoopGame game = coopHandler.LocalGameInstance; + if (game != null) + { + GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks, game.GameTimer.StartDateTime.Value.Ticks); + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError("OnGameTimerPacketReceived: Game was null!"); + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + protected void Update() + { + netServer?.PollEvents(); + netServer?.NatPunchModule?.PollEvents(); + + statisticsCounter++; + if (statisticsCounter > 600) + { + statisticsCounter = 0; + SendStatisticsPacket(); + } + } + + private void SendStatisticsPacket() + { + int fps = (int)(1f / Time.unscaledDeltaTime); + StatisticsPacket packet = new(fps); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void OnDestroy() + { + netServer?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); + } + + public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable + { + if (peerToExclude != null) + { + if (NetServer.ConnectedPeersCount > 1) + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod, peerToExclude); + } + } + else + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod); + } + } + + public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + peer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); + logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); + + hasHadPeer = true; + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogError("[SERVER] error " + socketErrorCode); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.Broadcast) + { + logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); + NetDataWriter resp = new(); + resp.Put(1); + netServer.SendUnconnectedMessage(resp, remoteEndPoint); + } + else + { + if (reader.TryGetString(out string data)) + { + NetDataWriter resp; + + switch (data) + { + 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 + { + logger.LogError("PingingRequest: Could not parse string"); + } + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + } + + public void OnConnectionRequest(ConnectionRequest request) + { + request.AcceptIfKey("fika.core"); + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + 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) + { + timeSinceLastPeerDisconnected = DateTime.Now; + } + + if (FikaBackendUtils.IsDedicatedGame) + { + if (netServer.ConnectedPeersCount == 0) + { + foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) + { + if (profile is null) + { + continue; + } + + if (profile.ProfileId == RequestHandler.SessionId) + { + foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) + { + bodyPartHealth.Effects.Clear(); + bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; + } + } + } + + // End the raid + Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, + Singleton.Instance.MyExitStatus, + Singleton.Instance.MyExitLocation, 0); + } + } + } + + public void WriteNet(NetLogLevel level, string str, params object[] args) + { + Debug.LogFormat(str, args); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + 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; + public uint operationId; + public int netId; + public NetPeer peer; + public FikaServer server; + + internal void HandleResult(IResult result) + { + NetDataWriter writer = new(); + OperationCallbackPacket operationCallbackPacket; + + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); + operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + return; + } + + InventoryPacket packet = new(netId) + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationId, + OperationBytes = opBytes + }; + + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); + + operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs index 98938d2e..27e8071c 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs @@ -30,35 +30,35 @@ public void Deserialize(NetDataReader reader) { NetId = reader.GetInt(); PacketType = (EPackageType)reader.GetInt(); - switch (PacketType) - { - case EPackageType.Ping: - PingLocation = reader.GetVector3(); - PingType = (PingFactory.EPingType)reader.GetByte(); - PingColor = reader.GetColor(); - Nickname = reader.GetString(); - LocaleId = reader.GetString(); - break; - case EPackageType.TrainSync: - DepartureTime = reader.GetLong(); - break; - case EPackageType.ExfilCountdown: - ExfilName = reader.GetString(); - ExfilStartTime = reader.GetFloat(); - break; - case EPackageType.TraderServiceNotification: - TraderServiceType = (ETraderServiceType)reader.GetInt(); - break; - case EPackageType.LoadBot: - case EPackageType.DisposeBot: - case EPackageType.EnableBot: - case EPackageType.DisableBot: - BotNetId = reader.GetInt(); - break; - } - } + switch (PacketType) + { + case EPackageType.Ping: + PingLocation = reader.GetVector3(); + PingType = (PingFactory.EPingType)reader.GetByte(); + PingColor = reader.GetColor(); + Nickname = reader.GetString(); + LocaleId = reader.GetString(); + break; + case EPackageType.TrainSync: + DepartureTime = reader.GetLong(); + break; + case EPackageType.ExfilCountdown: + ExfilName = reader.GetString(); + ExfilStartTime = reader.GetFloat(); + break; + case EPackageType.TraderServiceNotification: + TraderServiceType = (ETraderServiceType)reader.GetInt(); + break; + case EPackageType.LoadBot: + case EPackageType.DisposeBot: + case EPackageType.EnableBot: + case EPackageType.DisableBot: + BotNetId = reader.GetInt(); + break; + } + } - public void Serialize(NetDataWriter writer) + public void Serialize(NetDataWriter writer) { writer.Put(NetId); writer.Put((int)PacketType); @@ -89,9 +89,9 @@ public void Serialize(NetDataWriter writer) break; } } - } + } - public enum EPackageType + public enum EPackageType { ClientExtract, Ping, diff --git a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs index fcc0d8cb..96111c51 100644 --- a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs @@ -7,98 +7,98 @@ namespace Fika.Core.Networking.Packets.GameWorld { - public struct ReconnectPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public bool InitialRequest = false; - public EReconnectDataType Type; + public struct ReconnectPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public bool InitialRequest = false; + public EReconnectDataType Type; - public string ProfileId; - public Profile Profile; - public Profile.ProfileHealthClass ProfileHealthClass; - public Vector3 PlayerPosition; + public string ProfileId; + public Profile Profile; + public Profile.ProfileHealthClass ProfileHealthClass; + public Vector3 PlayerPosition; - public List ThrowableData; - public List InteractivesData; - public Dictionary LampStates; - public Dictionary WindowBreakerStates; + public List ThrowableData; + public List InteractivesData; + public Dictionary LampStates; + public Dictionary WindowBreakerStates; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - InitialRequest = reader.GetBool(); - ProfileId = reader.GetString(); - if (!IsRequest) - { - Type = (EReconnectDataType)reader.GetByte(); - switch (Type) - { - case EReconnectDataType.Throwable: - ThrowableData = reader.GetThrowableData(); - break; - case EReconnectDataType.Interactives: - InteractivesData = reader.GetInteractivesStates(); - break; - case EReconnectDataType.LampControllers: - LampStates = reader.GetLampStates(); - break; - case EReconnectDataType.Windows: - WindowBreakerStates = reader.GetWindowBreakerStates(); - break; - case EReconnectDataType.OwnCharacter: - Profile = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); - ProfileHealthClass = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); - PlayerPosition = reader.GetVector3(); - break; - case EReconnectDataType.Finished: - default: - break; - } - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + InitialRequest = reader.GetBool(); + ProfileId = reader.GetString(); + if (!IsRequest) + { + Type = (EReconnectDataType)reader.GetByte(); + switch (Type) + { + case EReconnectDataType.Throwable: + ThrowableData = reader.GetThrowableData(); + break; + case EReconnectDataType.Interactives: + InteractivesData = reader.GetInteractivesStates(); + break; + case EReconnectDataType.LampControllers: + LampStates = reader.GetLampStates(); + break; + case EReconnectDataType.Windows: + WindowBreakerStates = reader.GetWindowBreakerStates(); + break; + case EReconnectDataType.OwnCharacter: + Profile = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + ProfileHealthClass = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + PlayerPosition = reader.GetVector3(); + break; + case EReconnectDataType.Finished: + default: + break; + } + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(InitialRequest); - writer.Put(ProfileId); - if (!IsRequest) - { - writer.Put((byte)Type); - switch (Type) - { - case EReconnectDataType.Throwable: - writer.PutThrowableData(ThrowableData); - break; - case EReconnectDataType.Interactives: - writer.PutInteractivesStates(InteractivesData); - break; - case EReconnectDataType.LampControllers: - writer.PutLampStates(LampStates); - break; - case EReconnectDataType.Windows: - writer.PutWindowBreakerStates(WindowBreakerStates); - break; - case EReconnectDataType.OwnCharacter: - writer.PutByteArray(SimpleZlib.CompressToBytes(Profile.ToJson(), 4)); - writer.PutByteArray(SimpleZlib.CompressToBytes(ProfileHealthClass.ToJson(), 4)); - writer.Put(PlayerPosition); - break; - case EReconnectDataType.Finished: - default: - break; - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(InitialRequest); + writer.Put(ProfileId); + if (!IsRequest) + { + writer.Put((byte)Type); + switch (Type) + { + case EReconnectDataType.Throwable: + writer.PutThrowableData(ThrowableData); + break; + case EReconnectDataType.Interactives: + writer.PutInteractivesStates(InteractivesData); + break; + case EReconnectDataType.LampControllers: + writer.PutLampStates(LampStates); + break; + case EReconnectDataType.Windows: + writer.PutWindowBreakerStates(WindowBreakerStates); + break; + case EReconnectDataType.OwnCharacter: + writer.PutByteArray(SimpleZlib.CompressToBytes(Profile.ToJson(), 4)); + writer.PutByteArray(SimpleZlib.CompressToBytes(ProfileHealthClass.ToJson(), 4)); + writer.Put(PlayerPosition); + break; + case EReconnectDataType.Finished: + default: + break; + } + } + } - public enum EReconnectDataType - { - Throwable, - Interactives, - LampControllers, - Windows, - OwnCharacter, - Finished - } - } + public enum EReconnectDataType + { + Throwable, + Interactives, + LampControllers, + Windows, + OwnCharacter, + Finished + } + } } diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 87b71bcb..86564817 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -504,19 +504,19 @@ private void RefreshUI() server.name = entry.ServerId; - bool localPlayerInRaid = false; - bool localPlayerDead = false; - foreach (KeyValuePair player in entry.Players) - { - if (player.Key == ProfileId) - { - localPlayerInRaid = true; - localPlayerDead = player.Value; - } - } - - // player label - GameObject playerLabel = GameObject.Find("PlayerLabel"); + bool localPlayerInRaid = false; + bool localPlayerDead = false; + foreach (KeyValuePair player in entry.Players) + { + if (player.Key == ProfileId) + { + localPlayerInRaid = true; + localPlayerDead = player.Value; + } + } + + // player label + GameObject playerLabel = GameObject.Find("PlayerLabel"); playerLabel.name = "PlayerLabel" + i; playerLabel.GetComponentInChildren().text = entry.HostUsername; @@ -617,7 +617,7 @@ private void RefreshUI() tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); continue; - } + } switch (entry.Status) { @@ -661,32 +661,32 @@ private void RefreshUI() { if (!localPlayerDead) { - tooltipTextGetter = new() - { - TooltipText = "Click to rejoin raid." - }; - - tooltipArea = joinButton.GetOrAddComponent(); - tooltipArea.enabled = true; - tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); - } + tooltipTextGetter = new() + { + TooltipText = "Click to rejoin raid." + }; + + tooltipArea = joinButton.GetOrAddComponent(); + tooltipArea.enabled = true; + tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); + } else { - tooltipTextGetter = new() - { - TooltipText = "Cannot rejoin a raid where you died." - }; - - button.enabled = false; - if (image != null) - { - image.color = new(0.5f, image.color.g / 2, image.color.b / 2, 0.75f); - } - - tooltipArea = joinButton.GetOrAddComponent(); - tooltipArea.enabled = true; - tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); - } + tooltipTextGetter = new() + { + TooltipText = "Cannot rejoin a raid where you died." + }; + + button.enabled = false; + if (image != null) + { + image.color = new(0.5f, image.color.g / 2, image.color.b / 2, 0.75f); + } + + tooltipArea = joinButton.GetOrAddComponent(); + tooltipArea.enabled = true; + tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); + } } break; case LobbyEntry.ELobbyStatus.COMPLETE: diff --git a/Fika.Core/UI/Models/LobbyEntry.cs b/Fika.Core/UI/Models/LobbyEntry.cs index d3a82952..f8149d15 100644 --- a/Fika.Core/UI/Models/LobbyEntry.cs +++ b/Fika.Core/UI/Models/LobbyEntry.cs @@ -1,5 +1,4 @@ using EFT; -using EFT.UI; using JsonType; using System.Collections.Generic; using System.Runtime.Serialization; From 3bd9733acf522483f814b969f859733167bfb47d Mon Sep 17 00:00:00 2001 From: ZHL <45361542+zhliau@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:54:49 -0400 Subject: [PATCH 10/83] Refactor free camera a bit to make it more readable, and to remove unnecessary looping through coopplayers list --- Fika.Core/Coop/FreeCamera/FreeCamera.cs | 259 +++++++++--------------- 1 file changed, 94 insertions(+), 165 deletions(-) diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index 4cd75405..42d7a574 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -1,4 +1,5 @@ -using BSG.CameraEffects; +using BepInEx.Logging; +using BSG.CameraEffects; using Comfort.Common; using EFT; using EFT.UI; @@ -104,6 +105,94 @@ protected void OnGUI() } } + public void SwitchSpectateMode() + { + bool shouldHeadCam = Input.GetKey(KeyCode.Space); + bool should3rdPerson = Input.GetKey(KeyCode.LeftControl); + if (shouldHeadCam) + { + AttachToPlayer(); + } + else if (should3rdPerson) + { + Attach3rdPerson(); + } + else + { + JumpToPlayer(); + } + } + + /// + /// Helper method to cycle spectating players + /// + /// + /// If true, cycle players in reverse direction + /// + public void CycleSpectatePlayers(bool reverse = false) + { + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler == null) + { + return; + } + List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; + + FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: There are {players.Count} players"); + if (players.Count() <= 0 ) + { + // Clear out all spectate positions + CurrentPlayer = null; + if (isFollowing) + { + isFollowing = false; + transform.parent = null; + } + return; + } + + if (CurrentPlayer == null && players[0]) + { + CurrentPlayer = players[0]; + FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); + SwitchSpectateMode(); + } + + int nextIndex = reverse ? players.IndexOf(CurrentPlayer) - 1 : players.IndexOf(CurrentPlayer) + 1; + if (!reverse) + { + if (nextIndex <= players.Count - 1) + { + FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Setting to next player"); + CurrentPlayer = players[nextIndex]; + SwitchSpectateMode(); + } + else + { + // hit end of list, loop from start + FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Looping back to start player"); + CurrentPlayer = players[0]; + SwitchSpectateMode(); + } + } + else + { + if (nextIndex >= 0) + { + FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Setting to previous player"); + CurrentPlayer = players[nextIndex]; + SwitchSpectateMode(); + } + else + { + // hit beginning of list, loop from end + FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Looping back to end player"); + CurrentPlayer = players[players.Count - 1]; + SwitchSpectateMode(); + } + } + } + protected void Update() { if (!IsActive) @@ -131,175 +220,13 @@ protected void Update() // Spectate next player if (Input.GetKeyDown(KeyCode.Mouse0)) { - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler == null) - { - return; - } - - List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; - - if (players.Count > 0) - { - 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 (shouldHeadCam) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - - int nextPlayer = players.IndexOf(CurrentPlayer) + 1; - - if (players.Count - 1 >= nextPlayer) - { - CurrentPlayer = players[nextPlayer]; - if (shouldHeadCam) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - else - { - CurrentPlayer = players[0]; - if (shouldHeadCam) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - } - } - else - { - if (CurrentPlayer != null) - { - CurrentPlayer = null; - } - if (isFollowing) - { - isFollowing = false; - transform.parent = null; - } - } + CycleSpectatePlayers(false); } // Spectate previous player if (Input.GetKeyDown(KeyCode.Mouse1)) { - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler == null) - { - return; - } - - List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; - - if (players.Count > 0) - { - bool shouldFollow = 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) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - - int previousPlayer = players.IndexOf(CurrentPlayer) - 1; - - if (previousPlayer >= 0) - { - CurrentPlayer = players[previousPlayer]; - if (shouldFollow) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - else - { - CurrentPlayer = players[players.Count - 1]; - if (shouldFollow) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - break; - } - } - } - else - { - if (CurrentPlayer != null) - { - CurrentPlayer = null; - } - if (isFollowing) - { - isFollowing = false; - transform.parent = null; - } - } + CycleSpectatePlayers(true); } // Toggle vision @@ -474,6 +401,7 @@ public void JumpToPlayer() public void AttachToPlayer() { + FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: Attaching to helmet cam current player {CurrentPlayer.Profile.Nickname}"); transform.parent = CurrentPlayer.PlayerBones.Head.Original; transform.localPosition = new Vector3(-0.1f, -0.07f, -0.17f); transform.localEulerAngles = new Vector3(260, 80, 0); @@ -482,6 +410,7 @@ public void AttachToPlayer() public void Attach3rdPerson() { + FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: Attaching to 3rd person current player {CurrentPlayer.Profile.Nickname}"); transform.parent = CurrentPlayer.RaycastCameraTransform; transform.localPosition = new Vector3(0.3f, 0.2f, -0.65f); transform.localEulerAngles = new Vector3(4.3f, 5.9f, 0f); From ba172fb8b81e8717ec265dea499160d2d8212a21 Mon Sep 17 00:00:00 2001 From: ZHL <45361542+zhliau@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:07:27 -0400 Subject: [PATCH 11/83] Change logging to debug --- Fika.Core/Coop/FreeCamera/FreeCamera.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index 42d7a574..8014f2af 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -124,7 +124,7 @@ public void SwitchSpectateMode() } /// - /// Helper method to cycle spectating players + /// Helper method to cycle spectating players /// /// /// If true, cycle players in reverse direction @@ -138,7 +138,7 @@ public void CycleSpectatePlayers(bool reverse = false) } List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; - FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: There are {players.Count} players"); + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: There are {players.Count} players"); if (players.Count() <= 0 ) { // Clear out all spectate positions @@ -154,7 +154,7 @@ public void CycleSpectatePlayers(bool reverse = false) if (CurrentPlayer == null && players[0]) { CurrentPlayer = players[0]; - FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); SwitchSpectateMode(); } @@ -163,30 +163,30 @@ public void CycleSpectatePlayers(bool reverse = false) { if (nextIndex <= players.Count - 1) { - FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Setting to next player"); + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to next player"); CurrentPlayer = players[nextIndex]; SwitchSpectateMode(); } else { // hit end of list, loop from start - FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Looping back to start player"); + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to start player"); CurrentPlayer = players[0]; SwitchSpectateMode(); } } - else + else { if (nextIndex >= 0) { - FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Setting to previous player"); + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to previous player"); CurrentPlayer = players[nextIndex]; SwitchSpectateMode(); } else { // hit beginning of list, loop from end - FikaPlugin.Instance.FikaLogger.LogInfo("Freecam: Looping back to end player"); + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to end player"); CurrentPlayer = players[players.Count - 1]; SwitchSpectateMode(); } @@ -401,7 +401,7 @@ public void JumpToPlayer() public void AttachToPlayer() { - FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: Attaching to helmet cam current player {CurrentPlayer.Profile.Nickname}"); + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to helmet cam current player {CurrentPlayer.Profile.Nickname}"); transform.parent = CurrentPlayer.PlayerBones.Head.Original; transform.localPosition = new Vector3(-0.1f, -0.07f, -0.17f); transform.localEulerAngles = new Vector3(260, 80, 0); @@ -410,7 +410,7 @@ public void AttachToPlayer() public void Attach3rdPerson() { - FikaPlugin.Instance.FikaLogger.LogInfo($"Freecam: Attaching to 3rd person current player {CurrentPlayer.Profile.Nickname}"); + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to 3rd person current player {CurrentPlayer.Profile.Nickname}"); transform.parent = CurrentPlayer.RaycastCameraTransform; transform.localPosition = new Vector3(0.3f, 0.2f, -0.65f); transform.localEulerAngles = new Vector3(4.3f, 5.9f, 0f); From ba850cd8598f811f52a0f6df8d0778ab6a8a6b1e Mon Sep 17 00:00:00 2001 From: ZHL <45361542+zhliau@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:28:10 -0400 Subject: [PATCH 12/83] Remove unused import, return right after setting the currentplayer from null, and add some left shoulder debug logging --- Fika.Core/Coop/FreeCamera/FreeCamera.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index 8014f2af..1a731fbc 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -1,5 +1,4 @@ -using BepInEx.Logging; -using BSG.CameraEffects; +using BSG.CameraEffects; using Comfort.Common; using EFT; using EFT.UI; @@ -151,13 +150,16 @@ public void CycleSpectatePlayers(bool reverse = false) return; } + // Start spectating a player if we haven't before if (CurrentPlayer == null && players[0]) { CurrentPlayer = players[0]; FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); SwitchSpectateMode(); + return; } + // Cycle through spectate-able players int nextIndex = reverse ? players.IndexOf(CurrentPlayer) - 1 : players.IndexOf(CurrentPlayer) + 1; if (!reverse) { @@ -250,10 +252,12 @@ protected void Update() { if (CurrentPlayer.MovementContext.LeftStanceEnabled && !leftMode) { + FikaPlugin.Instance.FikaLogger.LogDebug("Setting left shoulder mode"); SetLeftShoulderMode(true); } else if (!CurrentPlayer.MovementContext.LeftStanceEnabled && leftMode) { + FikaPlugin.Instance.FikaLogger.LogDebug("Unsetting left shoulder mode"); SetLeftShoulderMode(false); } } From 47839808047b7134fa9c6f156b8d4cbdc32634ef Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:08:27 +0200 Subject: [PATCH 13/83] Fix free cam jumping - I hate math --- Fika.Core/Coop/FreeCamera/FreeCamera.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index 1a731fbc..e2fba2e7 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -223,18 +223,21 @@ protected void Update() if (Input.GetKeyDown(KeyCode.Mouse0)) { CycleSpectatePlayers(false); + return; } // Spectate previous player if (Input.GetKeyDown(KeyCode.Mouse1)) { CycleSpectatePlayers(true); + return; } // Toggle vision if (Input.GetKeyDown(KeyCode.N)) { ToggleVision(); + return; } // Disable culling @@ -243,6 +246,7 @@ protected void Update() if (freeCameraController != null) { freeCameraController.DisableAllCullingObjects(); + return; } } @@ -393,9 +397,14 @@ private void ToggleVision() public void JumpToPlayer() { - transform.position = new Vector3(CurrentPlayer.Transform.position.x - 2, CurrentPlayer.Transform.position.y + 2, CurrentPlayer.Transform.position.z); - transform.LookAt(new Vector3(CurrentPlayer.Transform.position.x, CurrentPlayer.Transform.position.y + 1, CurrentPlayer.Transform.position.z)); - if (isFollowing) + Vector3 position = CurrentPlayer.PlayerBones.Neck.position; + transform.position = position + Vector3.back + (Vector3.up / 2); + transform.LookAt(position); + + pitch = -transform.eulerAngles.x; + yaw = transform.eulerAngles.y; + + if (isFollowing) { isFollowing = false; leftMode = false; From 4281a470b9241ca7feb2649d4ae6100b5cd309a3 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:08:50 +0200 Subject: [PATCH 14/83] Cleanup --- Fika.Core/Coop/FreeCamera/FreeCamera.cs | 938 ++++++++++++------------ 1 file changed, 469 insertions(+), 469 deletions(-) diff --git a/Fika.Core/Coop/FreeCamera/FreeCamera.cs b/Fika.Core/Coop/FreeCamera/FreeCamera.cs index e2fba2e7..ad767d85 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCamera.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCamera.cs @@ -11,478 +11,478 @@ namespace Fika.Core.Coop.FreeCamera { - /// - /// A simple free camera to be added to a Unity game object.

- /// - /// Full credit to Ashley Davis on GitHub for the inital code:
- /// https://gist.github.com/ashleydavis/f025c03a9221bc840a2b

- /// - /// This is HEAVILY based on Terkoiz's work found here. Thanks for your work Terkoiz!
- /// https://dev.sp-tarkov.com/Terkoiz/Freecam/raw/branch/master/project/Terkoiz.Freecam/FreecamController.cs - ///
- 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 float yaw = 0f; - private float pitch = 0f; - private float lookSensitivity = 3f; - - private KeyCode forwardKey = KeyCode.W; - private KeyCode backKey = KeyCode.S; - private KeyCode leftKey = KeyCode.A; - private KeyCode rightKey = KeyCode.D; - private KeyCode relUpKey = KeyCode.E; - private KeyCode relDownKey = KeyCode.Q; - private readonly KeyCode upKey = KeyCode.R; - private readonly KeyCode downKey = KeyCode.F; - - protected void Start() - { - if (FikaPlugin.AZERTYMode.Value) - { - forwardKey = KeyCode.Z; - backKey = KeyCode.S; - leftKey = KeyCode.Q; - rightKey = KeyCode.D; - - 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(); - } - } - - public void SwitchSpectateMode() - { - bool shouldHeadCam = Input.GetKey(KeyCode.Space); - bool should3rdPerson = Input.GetKey(KeyCode.LeftControl); - if (shouldHeadCam) - { - AttachToPlayer(); - } - else if (should3rdPerson) - { - Attach3rdPerson(); - } - else - { - JumpToPlayer(); - } - } - - /// - /// Helper method to cycle spectating players - /// - /// - /// If true, cycle players in reverse direction - /// - public void CycleSpectatePlayers(bool reverse = false) - { - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler == null) - { - return; - } - List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; - - FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: There are {players.Count} players"); - if (players.Count() <= 0 ) - { - // Clear out all spectate positions - CurrentPlayer = null; - if (isFollowing) - { - isFollowing = false; - transform.parent = null; - } - return; - } - - // Start spectating a player if we haven't before - if (CurrentPlayer == null && players[0]) - { - CurrentPlayer = players[0]; - FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); - SwitchSpectateMode(); - return; - } - - // Cycle through spectate-able players - int nextIndex = reverse ? players.IndexOf(CurrentPlayer) - 1 : players.IndexOf(CurrentPlayer) + 1; - if (!reverse) - { - if (nextIndex <= players.Count - 1) - { - FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to next player"); - CurrentPlayer = players[nextIndex]; - SwitchSpectateMode(); - } - else - { - // hit end of list, loop from start - FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to start player"); - CurrentPlayer = players[0]; - SwitchSpectateMode(); - } - } - else - { - if (nextIndex >= 0) - { - FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to previous player"); - CurrentPlayer = players[nextIndex]; - SwitchSpectateMode(); - } - else - { - // hit beginning of list, loop from end - FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to end player"); - CurrentPlayer = players[players.Count - 1]; - SwitchSpectateMode(); - } - } - } - - protected void Update() - { - if (!IsActive) - { - return; - } - - // Toggle input - if (Input.GetKeyDown(KeyCode.Home)) - { - disableInput = !disableInput; - NotificationManagerClass.DisplayMessageNotification($"Free cam input is now {(disableInput ? "disabled" : "enabled")}."); - } - - if (disableInput) - { - return; - } - - if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) - { - return; - } - - // Spectate next player - if (Input.GetKeyDown(KeyCode.Mouse0)) - { - CycleSpectatePlayers(false); - return; - } - - // Spectate previous player - if (Input.GetKeyDown(KeyCode.Mouse1)) - { - CycleSpectatePlayers(true); - return; - } - - // Toggle vision - if (Input.GetKeyDown(KeyCode.N)) - { - ToggleVision(); - return; - } - - // Disable culling - if (Input.GetKeyDown(KeyCode.M)) - { - if (freeCameraController != null) - { - freeCameraController.DisableAllCullingObjects(); - return; - } - } - - if (isFollowing) - { - if (CurrentPlayer != null) - { - if (CurrentPlayer.MovementContext.LeftStanceEnabled && !leftMode) - { - FikaPlugin.Instance.FikaLogger.LogDebug("Setting left shoulder mode"); - SetLeftShoulderMode(true); - } - else if (!CurrentPlayer.MovementContext.LeftStanceEnabled && leftMode) - { - FikaPlugin.Instance.FikaLogger.LogDebug("Unsetting left shoulder mode"); - 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; - } - - if (Input.GetKey(leftKey) || Input.GetKey(KeyCode.LeftArrow)) - { - transform.position += -transform.right * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(rightKey) || Input.GetKey(KeyCode.RightArrow)) - { - transform.position += transform.right * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(forwardKey) || Input.GetKey(KeyCode.UpArrow)) - { - transform.position += transform.forward * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(backKey) || Input.GetKey(KeyCode.DownArrow)) - { - transform.position += -transform.forward * (movementSpeed * Time.deltaTime); - } - - // Teleportation - if (Input.GetKeyDown(KeyCode.T)) - { - if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - return; - } - - Player player = Singleton.Instance.MainPlayer; - - if (!coopHandler.ExtractedPlayers.Contains(((CoopPlayer)player).NetId) && player.HealthController.IsAlive) - { - player?.Teleport(transform.position); - } - } - - if (true) - { - if (Input.GetKey(relUpKey)) - { - transform.position += transform.up * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(relDownKey)) - { - transform.position += -transform.up * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(upKey) || Input.GetKey(KeyCode.PageUp)) - { - transform.position += Vector3.up * (movementSpeed * Time.deltaTime); - } - - if (Input.GetKey(downKey) || Input.GetKey(KeyCode.PageDown)) - { - transform.position += -Vector3.up * (movementSpeed * Time.deltaTime); - } - } - - float x = Input.GetAxis("Mouse X"); - float y = Input.GetAxis("Mouse Y"); - - pitch += y * lookSensitivity; - pitch = Mathf.Clamp(pitch, -89, 89); - transform.eulerAngles = new(-pitch, yaw, 0); - yaw = (yaw + x * lookSensitivity) % 360f; - } - - 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() - { - Vector3 position = CurrentPlayer.PlayerBones.Neck.position; + /// + /// A simple free camera to be added to a Unity game object.

+ /// + /// Full credit to Ashley Davis on GitHub for the inital code:
+ /// https://gist.github.com/ashleydavis/f025c03a9221bc840a2b

+ /// + /// This is HEAVILY based on Terkoiz's work found here. Thanks for your work Terkoiz!
+ /// https://dev.sp-tarkov.com/Terkoiz/Freecam/raw/branch/master/project/Terkoiz.Freecam/FreecamController.cs + ///
+ 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 float yaw = 0f; + private float pitch = 0f; + private float lookSensitivity = 3f; + + private KeyCode forwardKey = KeyCode.W; + private KeyCode backKey = KeyCode.S; + private KeyCode leftKey = KeyCode.A; + private KeyCode rightKey = KeyCode.D; + private KeyCode relUpKey = KeyCode.E; + private KeyCode relDownKey = KeyCode.Q; + private readonly KeyCode upKey = KeyCode.R; + private readonly KeyCode downKey = KeyCode.F; + + protected void Start() + { + if (FikaPlugin.AZERTYMode.Value) + { + forwardKey = KeyCode.Z; + backKey = KeyCode.S; + leftKey = KeyCode.Q; + rightKey = KeyCode.D; + + 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(); + } + } + + public void SwitchSpectateMode() + { + bool shouldHeadCam = Input.GetKey(KeyCode.Space); + bool should3rdPerson = Input.GetKey(KeyCode.LeftControl); + if (shouldHeadCam) + { + AttachToPlayer(); + } + else if (should3rdPerson) + { + Attach3rdPerson(); + } + else + { + JumpToPlayer(); + } + } + + /// + /// Helper method to cycle spectating players + /// + /// + /// If true, cycle players in reverse direction + /// + public void CycleSpectatePlayers(bool reverse = false) + { + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler == null) + { + return; + } + List players = [.. coopHandler.HumanPlayers.Where(x => !x.IsYourPlayer && x.HealthController.IsAlive)]; + + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: There are {players.Count} players"); + if (players.Count() <= 0) + { + // Clear out all spectate positions + CurrentPlayer = null; + if (isFollowing) + { + isFollowing = false; + transform.parent = null; + } + return; + } + + // Start spectating a player if we haven't before + if (CurrentPlayer == null && players[0]) + { + CurrentPlayer = players[0]; + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: CurrentPlayer was null, setting to first player {players[0].Profile.Nickname}"); + SwitchSpectateMode(); + return; + } + + // Cycle through spectate-able players + int nextIndex = reverse ? players.IndexOf(CurrentPlayer) - 1 : players.IndexOf(CurrentPlayer) + 1; + if (!reverse) + { + if (nextIndex <= players.Count - 1) + { + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to next player"); + CurrentPlayer = players[nextIndex]; + SwitchSpectateMode(); + } + else + { + // hit end of list, loop from start + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to start player"); + CurrentPlayer = players[0]; + SwitchSpectateMode(); + } + } + else + { + if (nextIndex >= 0) + { + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Setting to previous player"); + CurrentPlayer = players[nextIndex]; + SwitchSpectateMode(); + } + else + { + // hit beginning of list, loop from end + FikaPlugin.Instance.FikaLogger.LogDebug("Freecam: Looping back to end player"); + CurrentPlayer = players[players.Count - 1]; + SwitchSpectateMode(); + } + } + } + + protected void Update() + { + if (!IsActive) + { + return; + } + + // Toggle input + if (Input.GetKeyDown(KeyCode.Home)) + { + disableInput = !disableInput; + NotificationManagerClass.DisplayMessageNotification($"Free cam input is now {(disableInput ? "disabled" : "enabled")}."); + } + + if (disableInput) + { + return; + } + + if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) + { + return; + } + + // Spectate next player + if (Input.GetKeyDown(KeyCode.Mouse0)) + { + CycleSpectatePlayers(false); + return; + } + + // Spectate previous player + if (Input.GetKeyDown(KeyCode.Mouse1)) + { + CycleSpectatePlayers(true); + return; + } + + // Toggle vision + if (Input.GetKeyDown(KeyCode.N)) + { + ToggleVision(); + return; + } + + // Disable culling + if (Input.GetKeyDown(KeyCode.M)) + { + if (freeCameraController != null) + { + freeCameraController.DisableAllCullingObjects(); + return; + } + } + + if (isFollowing) + { + if (CurrentPlayer != null) + { + if (CurrentPlayer.MovementContext.LeftStanceEnabled && !leftMode) + { + FikaPlugin.Instance.FikaLogger.LogDebug("Setting left shoulder mode"); + SetLeftShoulderMode(true); + } + else if (!CurrentPlayer.MovementContext.LeftStanceEnabled && leftMode) + { + FikaPlugin.Instance.FikaLogger.LogDebug("Unsetting left shoulder mode"); + 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; + } + + if (Input.GetKey(leftKey) || Input.GetKey(KeyCode.LeftArrow)) + { + transform.position += -transform.right * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(rightKey) || Input.GetKey(KeyCode.RightArrow)) + { + transform.position += transform.right * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(forwardKey) || Input.GetKey(KeyCode.UpArrow)) + { + transform.position += transform.forward * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(backKey) || Input.GetKey(KeyCode.DownArrow)) + { + transform.position += -transform.forward * (movementSpeed * Time.deltaTime); + } + + // Teleportation + if (Input.GetKeyDown(KeyCode.T)) + { + if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + return; + } + + Player player = Singleton.Instance.MainPlayer; + + if (!coopHandler.ExtractedPlayers.Contains(((CoopPlayer)player).NetId) && player.HealthController.IsAlive) + { + player?.Teleport(transform.position); + } + } + + if (true) + { + if (Input.GetKey(relUpKey)) + { + transform.position += transform.up * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(relDownKey)) + { + transform.position += -transform.up * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(upKey) || Input.GetKey(KeyCode.PageUp)) + { + transform.position += Vector3.up * (movementSpeed * Time.deltaTime); + } + + if (Input.GetKey(downKey) || Input.GetKey(KeyCode.PageDown)) + { + transform.position += -Vector3.up * (movementSpeed * Time.deltaTime); + } + } + + float x = Input.GetAxis("Mouse X"); + float y = Input.GetAxis("Mouse Y"); + + pitch += y * lookSensitivity; + pitch = Mathf.Clamp(pitch, -89, 89); + transform.eulerAngles = new(-pitch, yaw, 0); + yaw = (yaw + x * lookSensitivity) % 360f; + } + + 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() + { + Vector3 position = CurrentPlayer.PlayerBones.Neck.position; transform.position = position + Vector3.back + (Vector3.up / 2); transform.LookAt(position); - pitch = -transform.eulerAngles.x; - yaw = transform.eulerAngles.y; + pitch = -transform.eulerAngles.x; + yaw = transform.eulerAngles.y; if (isFollowing) - { - isFollowing = false; - leftMode = false; - transform.parent = null; - } - } - - public void AttachToPlayer() - { - FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to helmet cam current player {CurrentPlayer.Profile.Nickname}"); - transform.parent = CurrentPlayer.PlayerBones.Head.Original; - transform.localPosition = new Vector3(-0.1f, -0.07f, -0.17f); - transform.localEulerAngles = new Vector3(260, 80, 0); - isFollowing = true; - } - - public void Attach3rdPerson() - { - FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to 3rd person current player {CurrentPlayer.Profile.Nickname}"); - transform.parent = CurrentPlayer.RaycastCameraTransform; - transform.localPosition = new Vector3(0.3f, 0.2f, -0.65f); - transform.localEulerAngles = new Vector3(4.3f, 5.9f, 0f); - isFollowing = true; - } - - 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; - } - - protected void OnDestroy() - { - Destroy(this); - } - } + { + isFollowing = false; + leftMode = false; + transform.parent = null; + } + } + + public void AttachToPlayer() + { + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to helmet cam current player {CurrentPlayer.Profile.Nickname}"); + transform.parent = CurrentPlayer.PlayerBones.Head.Original; + transform.localPosition = new Vector3(-0.1f, -0.07f, -0.17f); + transform.localEulerAngles = new Vector3(260, 80, 0); + isFollowing = true; + } + + public void Attach3rdPerson() + { + FikaPlugin.Instance.FikaLogger.LogDebug($"Freecam: Attaching to 3rd person current player {CurrentPlayer.Profile.Nickname}"); + transform.parent = CurrentPlayer.RaycastCameraTransform; + transform.localPosition = new Vector3(0.3f, 0.2f, -0.65f); + transform.localEulerAngles = new Vector3(4.3f, 5.9f, 0f); + isFollowing = true; + } + + 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; + } + + protected void OnDestroy() + { + Destroy(this); + } + } } \ No newline at end of file From bb07ac934f43d42e7cca23f5e1813a9b887fcb64 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:13:37 +0200 Subject: [PATCH 15/83] Cleanup --- Fika.Core/Bundles/InternalBundleLoader.cs | 74 +- Fika.Core/ConfigurationManagerAttributes.cs | 208 +- Fika.Core/Console/FikaCommands.cs | 352 +- Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs | 312 +- .../Coop/Airdrops/FikaAirdropsManager.cs | 682 +-- .../Airdrops/Models/FikaAirdropConfigModel.cs | 84 +- .../Airdrops/Models/FikaAirdropLootModel.cs | 44 +- .../Models/FikaAirdropParametersModel.cs | 44 +- .../Coop/Airdrops/Utils/FikaAirdropUtil.cs | 272 +- .../Airdrops/Utils/FikaItemFactoryUtil.cs | 132 +- Fika.Core/Coop/BTR/FikaBTRManager_Client.cs | 1000 ++-- Fika.Core/Coop/BTR/FikaBTRManager_Host.cs | 1604 +++--- .../Coop/BotClasses/BotFirearmController.cs | 28 +- .../Coop/BotClasses/BotMovementContext.cs | 36 +- .../BotClasses/CoopBotHealthController.cs | 48 +- .../BotClasses/CoopBotInventoryController.cs | 44 +- .../ClientClasses/ClientMovementContext.cs | 52 +- .../Coop/ClientClasses/CoopClientGameWorld.cs | 50 +- .../CoopClientHealthController.cs | 48 +- .../CoopClientInventoryController.cs | 424 +- .../CoopClientSharedQuestController.cs | 608 +-- .../CoopClientStatisticsManager.cs | 32 +- .../CoopClientFirearmController.cs | 1590 +++--- .../CoopClientGrenadeController.cs | 196 +- .../CoopClientKnifeController.cs | 136 +- .../CoopClientQuickGrenadeController.cs | 64 +- .../ClientClasses/NoInertiaMovementContext.cs | 94 +- .../Coop/ClientClasses/NoInertiaPhysical.cs | 104 +- Fika.Core/Coop/Components/CoopExfilManager.cs | 422 +- .../Components/CoopHalloweenEventManager.cs | 152 +- Fika.Core/Coop/Components/CoopHandler.cs | 1114 ++--- Fika.Core/Coop/Components/CoopTimeManager.cs | 44 +- Fika.Core/Coop/Components/FikaPinger.cs | 72 +- Fika.Core/Coop/Custom/FikaChat.cs | 224 +- Fika.Core/Coop/Custom/FikaDebug.cs | 326 +- Fika.Core/Coop/Custom/FikaDynamicAI.cs | 438 +- Fika.Core/Coop/Custom/FikaHealthBar.cs | 1060 ++-- Fika.Core/Coop/Custom/FikaPing.cs | 22 +- Fika.Core/Coop/Custom/PlayerPlateUI.cs | 60 +- .../Coop/Factories/HandsControllerFactory.cs | 200 +- Fika.Core/Coop/Factories/PingFactory.cs | 442 +- .../Coop/FreeCamera/FreeCameraController.cs | 996 ++-- .../FreeCamera/Patches/DeathFade_Patch.cs | 34 +- .../Patches/FadeBlackScreen_Patch.cs | 90 +- Fika.Core/Coop/GameMode/CoopGame.cs | 4430 ++++++++--------- Fika.Core/Coop/GameMode/FikaHostWorld.cs | 132 +- Fika.Core/Coop/GameMode/FikaWorld.cs | 32 +- Fika.Core/Coop/GameMode/IFikaGame.cs | 14 +- .../Coop/HostClasses/CoopHostGameWorld.cs | 50 +- Fika.Core/Coop/HostClasses/CoopHostGrenade.cs | 8 +- .../Coop/HostClasses/CoopHostSmokeGrenade.cs | 8 +- .../Coop/HostClasses/CoopHostStunGrenade.cs | 8 +- .../Coop/HostClasses/HostGrenadeFactory.cs | 30 +- .../ObservedClasses/CoopObservedGrenade.cs | 14 +- .../CoopObservedStatisticsManager.cs | 112 +- .../CoopObservedEmptyHandsController.cs | 48 +- .../CoopObservedFirearmController.cs | 1312 ++--- .../CoopObservedGrenadeController.cs | 110 +- .../CoopObservedKnifeController.cs | 24 +- .../CoopObservedMedsController.cs | 48 +- .../CoopObservedQuickGrenadeController.cs | 110 +- .../MovementStates/ObservedRunState.cs | 36 +- .../MovementStates/ObservedSprintState.cs | 86 +- .../ObservedHealthController.cs | 158 +- .../ObservedInventoryController.cs | 50 +- .../ObservedMovementContext.cs | 282 +- .../Coop/PacketHandlers/BotPacketSender.cs | 322 +- .../Coop/PacketHandlers/ClientPacketSender.cs | 676 +-- .../PacketHandlers/DedicatedPacketSender.cs | 366 +- .../Coop/PacketHandlers/IPacketSender.cs | 32 +- .../PacketHandlers/ObservedPacketSender.cs | 200 +- .../Coop/PacketHandlers/PacketReceiver.cs | 238 +- .../Coop/PacketHandlers/ServerPacketSender.cs | 664 +-- .../AbstractGame/AbstractGame_InRaid_Patch.cs | 32 +- .../Coop/Patches/Airdrop/AirdropBox_Patch.cs | 30 +- .../Patches/Airdrop/FikaAirdropFlare_Patch.cs | 40 +- Fika.Core/Coop/Patches/BotCacher_Patch.cs | 124 +- .../LocalGame/GameWorld_Create_Patch.cs | 78 +- .../LocalGame/NonWaveSpawnScenario_Patch.cs | 22 +- ...arkovApplication_LocalGameCreator_Patch.cs | 264 +- ...rkovApplication_LocalGamePreparer_Patch.cs | 106 +- .../LocalGame/WaveSpawnScenario_Patch.cs | 22 +- .../World_AddSpawnQuestLootPacket_Patch.cs | 30 +- .../Minefield/Minefield_method_2_Patch.cs | 156 +- ...EnemyToAllGroupsInBotZonePatch_Override.cs | 116 +- .../Overrides/BotDifficultyPatch_Override.cs | 50 +- .../BotTemplateLimitPatch_Override.cs | 64 +- .../Patches/Overrides/MaxBotPatch_Override.cs | 90 +- .../OfflineRaidSettingsMenuPatch_Override.cs | 170 +- .../Overrides/ScavProfileLoad_Override.cs | 24 +- .../Coop/Patches/Weather/WeatherNode_Patch.cs | 26 +- Fika.Core/Coop/Players/CoopBot.cs | 662 +-- Fika.Core/Coop/Players/CoopPlayer.cs | 3782 +++++++------- Fika.Core/Coop/Players/ObservedCoopPlayer.cs | 2822 +++++------ Fika.Core/Coop/Utils/FikaBackendUtils.cs | 246 +- Fika.Core/Coop/Utils/NetManagerUtils.cs | 450 +- Fika.Core/FikaPlugin.cs | 1120 ++--- .../Events/AbstractGameCreatedEvent.cs | 16 +- .../Modding/Events/FikaClientCreatedEvent.cs | 16 +- .../Events/FikaClientDestroyedEvent.cs | 16 +- Fika.Core/Modding/Events/FikaEvent.cs | 6 +- .../Modding/Events/FikaGameCreatedEvent.cs | 16 +- .../Modding/Events/FikaServerCreatedEvent.cs | 16 +- .../Events/FikaServerDestroyedEvent.cs | 16 +- .../Modding/Events/GameWorldStartedEvent.cs | 16 +- Fika.Core/Modding/FikaEventDispatcher.cs | 60 +- Fika.Core/Models/BotDifficulties.cs | 84 +- Fika.Core/Models/ClientConfigModel.cs | 120 +- Fika.Core/Models/NatPunchServerConfigModel.cs | 82 +- Fika.Core/Networking/FikaClient.cs | 1952 ++++---- Fika.Core/Networking/FikaPingingClient.cs | 392 +- Fika.Core/Networking/FikaSerialization.cs | 1870 +++---- .../Networking/FikaSerializationExtensions.cs | 676 +-- Fika.Core/Networking/FikaServer.cs | 2386 ++++----- .../Fuyu.Platform.Common/Http/FuyuClient.cs | 290 +- .../Networking/Http/FikaRequestHandler.cs | 330 +- .../Networking/Models/AddPlayerRequest.cs | 26 +- .../Networking/Models/CreateMatchRequest.cs | 94 +- .../Models/Dedicated/DedicatedStatus.cs | 4 +- .../Dedicated/GetDedicatedStatusResponse.cs | 12 +- .../Dedicated/SetDedicatedStatusRequest.cs | 26 +- .../Dedicated/SetDedicatedStatusResponse.cs | 16 +- .../Models/Dedicated/StartDedicatedRequest.cs | 48 +- .../Dedicated/StartDedicatedResponse.cs | 16 +- Fika.Core/Networking/Models/GetHostRequest.cs | 20 +- .../Networking/Models/GetHostResponse.cs | 38 +- .../Networking/Models/GetHostStunRequest.cs | 44 +- .../Networking/Models/GetHostStunResponse.cs | 30 +- .../Networking/Models/MatchJoinRequest.cs | 26 +- .../Networking/Models/MatchJoinResponse.cs | 26 +- .../Models/ModValidationResponse.cs | 20 +- Fika.Core/Networking/Models/PingRequest.cs | 20 +- .../Networking/Models/PlayerLeftRequest.cs | 26 +- .../Networking/Models/PlayerSpawnRequest.cs | 32 +- .../Networking/Models/RaidSettingsRequest.cs | 20 +- .../Networking/Models/RaidSettingsResponse.cs | 16 +- .../Models/RegisterPlayerRequest.cs | 32 +- Fika.Core/Networking/Models/SetHostRequest.cs | 44 +- Fika.Core/Networking/Models/SetStatusModel.cs | 26 +- .../Models/UpdateSpawnPointRequest.cs | 26 +- .../Packets/Backend/InformationPacket.cs | 42 +- .../Packets/Backend/StatisticsPacket.cs | 24 +- .../AllCharacterRequestPacket.cs | 96 +- .../Communication/AssignNetIdPacket.cs | 24 +- .../Communication/QuestConditionPacket.cs | 36 +- .../Communication/QuestDropItemPacket.cs | 36 +- .../Packets/Communication/QuestItemPacket.cs | 30 +- .../Communication/SendCharacterPacket.cs | 48 +- .../Packets/Communication/SpawnpointPacket.cs | 30 +- .../Packets/Communication/SyncNetIdPacket.cs | 30 +- .../Communication/TextMessagePacket.cs | 30 +- .../Packets/FirearmController/WeaponPacket.cs | 378 +- .../Packets/GameWorld/AirdropLootPacket.cs | 42 +- .../Packets/GameWorld/AirdropPacket.cs | 100 +- .../Packets/GameWorld/AirdropUpdatePacket.cs | 94 +- .../Packets/GameWorld/BTRInteractionPacket.cs | 70 +- .../Networking/Packets/GameWorld/BTRPacket.cs | 70 +- .../Packets/GameWorld/BTRServicePacket.cs | 54 +- .../Packets/GameWorld/BorderZonePacket.cs | 30 +- .../Packets/GameWorld/ExfiltrationPacket.cs | 110 +- .../Packets/GameWorld/GameTimerPacket.cs | 36 +- .../Packets/GameWorld/GenericPacket.cs | 190 +- .../Packets/GameWorld/HalloweenEventPacket.cs | 90 +- .../GameWorld/InteractableInitPacket.cs | 48 +- .../Packets/GameWorld/MinePacket.cs | 24 +- .../Packets/GameWorld/ReconnectPacket.cs | 178 +- .../Packets/GameWorld/ThrowablePacket.cs | 72 +- .../Packets/GameWorld/WeatherPacket.cs | 110 +- .../Packets/GameWorld/WorldLootPacket.cs | 42 +- .../Packets/Player/ArmorDamagePacket.cs | 36 +- .../Packets/Player/CommonPlayerPacket.cs | 184 +- .../Networking/Packets/Player/DamagePacket.cs | 132 +- .../Packets/Player/HealthSyncPacket.cs | 852 ++-- .../Packets/Player/InventoryPacket.cs | 56 +- .../Packets/Player/OperationCallbackPacket.cs | 54 +- .../Packets/Player/PlayerStatePacket.cs | 158 +- .../Websocket/DedicatedRaidWebSocketClient.cs | 232 +- Fika.Core/UI/Custom/ButtonHandler.cs | 94 +- Fika.Core/UI/Custom/IntUpDown.cs | 40 +- Fika.Core/UI/Custom/MatchMakerUI.cs | 48 +- Fika.Core/UI/Custom/MatchMakerUIScript.cs | 1410 +++--- Fika.Core/UI/Custom/SendItemUI.cs | 12 +- Fika.Core/UI/FikaUIUtils.cs | 238 +- .../UI/Models/AvailableReceiversRequest.cs | 20 +- Fika.Core/UI/Models/FikaLocalization.cs | 10 +- Fika.Core/UI/Models/LobbyEntry.cs | 92 +- .../UI/Patches/ChangeGameModeButton_Patch.cs | 34 +- .../DisableInsuranceReadyButton_Patch.cs | 34 +- .../DisableMatchSettingsReadyButton_Patch.cs | 34 +- .../UI/Patches/DisableReadyButton_Patch.cs | 34 +- .../UI/Patches/DisconnectButton_Patch.cs | 42 +- .../UI/Patches/FikaVersionLabel_Patch.cs | 100 +- Fika.Core/UI/Patches/ItemContext_Patch.cs | 314 +- .../MatchmakerAcceptScreen_Awake_Patch.cs | 48 +- .../MatchmakerAcceptScreen_Show_Patch.cs | 74 +- Fika.Core/UI/Patches/MenuTaskBar_Patch.cs | 170 +- Fika.Core/UI/Patches/TOS_Patch.cs | 70 +- Fika.Core/Utils/ColorUtils.cs | 58 +- Fika.Core/Utils/FikaModHandler.cs | 198 +- Fika.Core/Utils/LocaleUtils.cs | 26 +- Fika.Core/Utils/MainThreadDispatcher.cs | 72 +- Fika.Core/Utils/WorldToScreen.cs | 164 +- 202 files changed, 25268 insertions(+), 25268 deletions(-) diff --git a/Fika.Core/Bundles/InternalBundleLoader.cs b/Fika.Core/Bundles/InternalBundleLoader.cs index 8c1deef3..58188285 100644 --- a/Fika.Core/Bundles/InternalBundleLoader.cs +++ b/Fika.Core/Bundles/InternalBundleLoader.cs @@ -8,46 +8,46 @@ namespace Fika.Core.Bundles { - /// - /// Created by Nexus / pandahhcorp
- /// Refactored by Lacyway to load bundles directly from memory - ///
- public class InternalBundleLoader - { - public Dictionary _loadedBundles; - public static InternalBundleLoader Instance { get; private set; } + /// + /// Created by Nexus / pandahhcorp
+ /// Refactored by Lacyway to load bundles directly from memory + ///
+ public class InternalBundleLoader + { + public Dictionary _loadedBundles; + public static InternalBundleLoader Instance { get; private set; } - public void Create() - { - Instance = this; - Awake(); - } + public void Create() + { + Instance = this; + Awake(); + } - private void Awake() - { - Assembly assembly = Assembly.GetExecutingAssembly(); - _loadedBundles = []; + private void Awake() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + _loadedBundles = []; - assembly.GetManifestResourceNames().ToList().ForEach(name => - { - using Stream stream = assembly.GetManifestResourceStream(name); - using MemoryStream memoryStream = new(); - { - string bundlename = name.Replace("Fika.Core.Bundles.Files.", "").Replace(".bundle", ""); - stream.CopyTo(memoryStream); - _loadedBundles.Add(bundlename, AssetBundle.LoadFromMemoryAsync(memoryStream.ToArray())); - } - }); - } + assembly.GetManifestResourceNames().ToList().ForEach(name => + { + using Stream stream = assembly.GetManifestResourceStream(name); + using MemoryStream memoryStream = new(); + { + string bundlename = name.Replace("Fika.Core.Bundles.Files.", "").Replace(".bundle", ""); + stream.CopyTo(memoryStream); + _loadedBundles.Add(bundlename, AssetBundle.LoadFromMemoryAsync(memoryStream.ToArray())); + } + }); + } - public AssetBundle GetAssetBundle(string bundleName) - { - if (_loadedBundles.TryGetValue(bundleName, out AssetBundleCreateRequest request) && request.isDone) - { - return request.assetBundle; - } + public AssetBundle GetAssetBundle(string bundleName) + { + if (_loadedBundles.TryGetValue(bundleName, out AssetBundleCreateRequest request) && request.isDone) + { + return request.assetBundle; + } - return null; - } - } + return null; + } + } } \ No newline at end of file diff --git a/Fika.Core/ConfigurationManagerAttributes.cs b/Fika.Core/ConfigurationManagerAttributes.cs index 74afd842..8ca78847 100644 --- a/Fika.Core/ConfigurationManagerAttributes.cs +++ b/Fika.Core/ConfigurationManagerAttributes.cs @@ -27,123 +27,123 @@ #pragma warning disable 0169, 0414, 0649 internal sealed class ConfigurationManagerAttributes { - /// - /// Should the setting be shown as a percentage (only use with value range settings). - /// - public bool? ShowRangeAsPercent; + /// + /// Should the setting be shown as a percentage (only use with value range settings). + /// + public bool? ShowRangeAsPercent; - /// - /// Custom setting editor (OnGUI code that replaces the default editor provided by ConfigurationManager). - /// See below for a deeper explanation. Using a custom drawer will cause many of the other fields to do nothing. - /// - public System.Action CustomDrawer; + /// + /// Custom setting editor (OnGUI code that replaces the default editor provided by ConfigurationManager). + /// See below for a deeper explanation. Using a custom drawer will cause many of the other fields to do nothing. + /// + public System.Action CustomDrawer; - /// - /// Custom setting editor that allows polling keyboard input with the Input (or UnityInput) class. - /// Use either CustomDrawer or CustomHotkeyDrawer, using both at the same time leads to undefined behaviour. - /// - public CustomHotkeyDrawerFunc CustomHotkeyDrawer; + /// + /// Custom setting editor that allows polling keyboard input with the Input (or UnityInput) class. + /// Use either CustomDrawer or CustomHotkeyDrawer, using both at the same time leads to undefined behaviour. + /// + public CustomHotkeyDrawerFunc CustomHotkeyDrawer; - /// - /// Custom setting draw action that allows polling keyboard input with the Input class. - /// Note: Make sure to focus on your UI control when you are accepting input so user doesn't type in the search box or in another setting (best to do this on every frame). - /// If you don't draw any selectable UI controls You can use `GUIUtility.keyboardControl = -1;` on every frame to make sure that nothing is selected. - /// - /// - /// CustomHotkeyDrawer = (ConfigEntryBase setting, ref bool isEditing) => - /// { - /// if (isEditing) - /// { - /// // Make sure nothing else is selected since we aren't focusing on a text box with GUI.FocusControl. - /// GUIUtility.keyboardControl = -1; - /// - /// // Use Input.GetKeyDown and others here, remember to set isEditing to false after you're done! - /// // It's best to check Input.anyKeyDown and set isEditing to false immediately if it's true, - /// // so that the input doesn't have a chance to propagate to the game itself. - /// - /// if (GUILayout.Button("Stop")) - /// isEditing = false; - /// } - /// else - /// { - /// if (GUILayout.Button("Start")) - /// isEditing = true; - /// } - /// - /// // This will only be true when isEditing is true and you hold any key - /// GUILayout.Label("Any key pressed: " + Input.anyKey); - /// } - /// - /// - /// Setting currently being set (if available). - /// - /// - /// Set this ref parameter to true when you want the current setting drawer to receive Input events. - /// The value will persist after being set, use it to see if the current instance is being edited. - /// Remember to set it to false after you are done! - /// - public delegate void CustomHotkeyDrawerFunc(BepInEx.Configuration.ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); + /// + /// Custom setting draw action that allows polling keyboard input with the Input class. + /// Note: Make sure to focus on your UI control when you are accepting input so user doesn't type in the search box or in another setting (best to do this on every frame). + /// If you don't draw any selectable UI controls You can use `GUIUtility.keyboardControl = -1;` on every frame to make sure that nothing is selected. + /// + /// + /// CustomHotkeyDrawer = (ConfigEntryBase setting, ref bool isEditing) => + /// { + /// if (isEditing) + /// { + /// // Make sure nothing else is selected since we aren't focusing on a text box with GUI.FocusControl. + /// GUIUtility.keyboardControl = -1; + /// + /// // Use Input.GetKeyDown and others here, remember to set isEditing to false after you're done! + /// // It's best to check Input.anyKeyDown and set isEditing to false immediately if it's true, + /// // so that the input doesn't have a chance to propagate to the game itself. + /// + /// if (GUILayout.Button("Stop")) + /// isEditing = false; + /// } + /// else + /// { + /// if (GUILayout.Button("Start")) + /// isEditing = true; + /// } + /// + /// // This will only be true when isEditing is true and you hold any key + /// GUILayout.Label("Any key pressed: " + Input.anyKey); + /// } + /// + /// + /// Setting currently being set (if available). + /// + /// + /// Set this ref parameter to true when you want the current setting drawer to receive Input events. + /// The value will persist after being set, use it to see if the current instance is being edited. + /// Remember to set it to false after you are done! + /// + public delegate void CustomHotkeyDrawerFunc(BepInEx.Configuration.ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); - /// - /// Show this setting in the settings screen at all? If false, don't show. - /// - public bool? Browsable; + /// + /// Show this setting in the settings screen at all? If false, don't show. + /// + public bool? Browsable; - /// - /// Category the setting is under. Null to be directly under the plugin. - /// - public string Category; + /// + /// Category the setting is under. Null to be directly under the plugin. + /// + public string Category; - /// - /// If set, a "Default" button will be shown next to the setting to allow resetting to default. - /// - public object DefaultValue; + /// + /// If set, a "Default" button will be shown next to the setting to allow resetting to default. + /// + public object DefaultValue; - /// - /// Force the "Reset" button to not be displayed, even if a valid DefaultValue is available. - /// - public bool? HideDefaultButton; + /// + /// Force the "Reset" button to not be displayed, even if a valid DefaultValue is available. + /// + public bool? HideDefaultButton; - /// - /// Force the setting name to not be displayed. Should only be used with a to get more space. - /// Can be used together with to gain even more space. - /// - public bool? HideSettingName; + /// + /// Force the setting name to not be displayed. Should only be used with a to get more space. + /// Can be used together with to gain even more space. + /// + public bool? HideSettingName; - /// - /// Optional description shown when hovering over the setting. - /// Not recommended, provide the description when creating the setting instead. - /// - public string Description; + /// + /// Optional description shown when hovering over the setting. + /// Not recommended, provide the description when creating the setting instead. + /// + public string Description; - /// - /// Name of the setting. - /// - public string DispName; + /// + /// Name of the setting. + /// + public string DispName; - /// - /// Order of the setting on the settings list relative to other settings in a category. - /// 0 by default, higher number is higher on the list. - /// - public int? Order; + /// + /// Order of the setting on the settings list relative to other settings in a category. + /// 0 by default, higher number is higher on the list. + /// + public int? Order; - /// - /// Only show the value, don't allow editing it. - /// - public bool? ReadOnly; + /// + /// Only show the value, don't allow editing it. + /// + public bool? ReadOnly; - /// - /// If true, don't show the setting by default. User has to turn on showing advanced settings or search for it. - /// - public bool? IsAdvanced; + /// + /// If true, don't show the setting by default. User has to turn on showing advanced settings or search for it. + /// + public bool? IsAdvanced; - /// - /// Custom converter from setting type to string for the built-in editor textboxes. - /// - public System.Func ObjToStr; + /// + /// Custom converter from setting type to string for the built-in editor textboxes. + /// + public System.Func ObjToStr; - /// - /// Custom converter from string to setting type for the built-in editor textboxes. - /// - public System.Func StrToObj; + /// + /// Custom converter from string to setting type for the built-in editor textboxes. + /// + public System.Func StrToObj; } \ No newline at end of file diff --git a/Fika.Core/Console/FikaCommands.cs b/Fika.Core/Console/FikaCommands.cs index db4aba25..5d0e3a83 100644 --- a/Fika.Core/Console/FikaCommands.cs +++ b/Fika.Core/Console/FikaCommands.cs @@ -10,184 +10,184 @@ namespace Fika.Core.Console { - public class FikaCommands - { + public class FikaCommands + { #if DEBUG - [ConsoleCommand("bring", "", null, "Teleports all AI to yourself as the host", [])] - public static void Bring() - { - 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; - } - - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - if (FikaBackendUtils.IsServer) - { - int count = 0; - foreach (CoopPlayer player in coopHandler.Players.Values) - { - BifacialTransform myPosition = coopHandler.MyPlayer.Transform; - if (player.IsAI && player.HealthController.IsAlive) - { - count++; - player.Teleport(myPosition.Original.position + myPosition.Original.forward * 2); - } - } - ConsoleScreen.Log($"Teleported {count} AI to host."); - } - else - { - ConsoleScreen.LogWarning("You are not the host"); - } - } - else - { - ConsoleScreen.LogWarning("Could not find CoopHandler."); - } - } - - [ConsoleCommand("god", "", null, "Set god mode on/off", [])] - public static void God([ConsoleArgument(false, "true or false to toggle god mode")] 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; - } - - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - int value = state ? 0 : 1; - coopHandler.MyPlayer.ActiveHealthController.SetDamageCoeff(value); - if (value == 0) - { - ConsoleScreen.Log("God mode on"); - } - else - { - ConsoleScreen.Log("God mode off"); - } - } - else - { - ConsoleScreen.LogWarning("Could not find CoopHandler."); - } - } - - [ConsoleCommand("extract", "", null, "Extract from raid", [])] - public static void Extract() - { - 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; - } - - CoopPlayer localPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - - coopGame.Extract(localPlayer, null); - } - - [ConsoleCommand("despawnallai", "", null, "Despawns all AI bots", [])] - public static void DespawnAllAI() - { - if (Singleton.Instance is CoopGame game) - { - if (!FikaBackendUtils.IsServer) - { - ConsoleScreen.LogWarning("You are not the host."); - return; - } - - CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler); - - List Bots = new(game.BotsController.Players); - - foreach (Player bot in Bots) - { - if (bot.AIData.BotOwner == null) - { - continue; - } - - ConsoleScreen.Log($"Despawning: {bot.Profile.Nickname}"); - - game.DespawnBot(coopHandler, bot); - } - } - } - - [ConsoleCommand("stoptimer", "", null, "Stops the game timer", [])] - public static void StopTimer() - { - if (Singleton.Instance is CoopGame game) - { - if (game.GameTimer.Status == GameTimerClass.EGameTimerStatus.Stopped) - { - ConsoleScreen.LogError("GameTimer is already stopped at: " + game.GameTimer.PastTime.ToString()); - return; - } - game.GameTimer.TryStop(); - if (game.GameTimer.Status == GameTimerClass.EGameTimerStatus.Stopped) - { - ConsoleScreen.Log("GameTimer stopped at: " + game.GameTimer.PastTime.ToString()); - } - } - } + [ConsoleCommand("bring", "", null, "Teleports all AI to yourself as the host", [])] + public static void Bring() + { + 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; + } + + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + if (FikaBackendUtils.IsServer) + { + int count = 0; + foreach (CoopPlayer player in coopHandler.Players.Values) + { + BifacialTransform myPosition = coopHandler.MyPlayer.Transform; + if (player.IsAI && player.HealthController.IsAlive) + { + count++; + player.Teleport(myPosition.Original.position + myPosition.Original.forward * 2); + } + } + ConsoleScreen.Log($"Teleported {count} AI to host."); + } + else + { + ConsoleScreen.LogWarning("You are not the host"); + } + } + else + { + ConsoleScreen.LogWarning("Could not find CoopHandler."); + } + } + + [ConsoleCommand("god", "", null, "Set god mode on/off", [])] + public static void God([ConsoleArgument(false, "true or false to toggle god mode")] 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; + } + + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + int value = state ? 0 : 1; + coopHandler.MyPlayer.ActiveHealthController.SetDamageCoeff(value); + if (value == 0) + { + ConsoleScreen.Log("God mode on"); + } + else + { + ConsoleScreen.Log("God mode off"); + } + } + else + { + ConsoleScreen.LogWarning("Could not find CoopHandler."); + } + } + + [ConsoleCommand("extract", "", null, "Extract from raid", [])] + public static void Extract() + { + 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; + } + + CoopPlayer localPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + + coopGame.Extract(localPlayer, null); + } + + [ConsoleCommand("despawnallai", "", null, "Despawns all AI bots", [])] + public static void DespawnAllAI() + { + if (Singleton.Instance is CoopGame game) + { + if (!FikaBackendUtils.IsServer) + { + ConsoleScreen.LogWarning("You are not the host."); + return; + } + + CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler); + + List Bots = new(game.BotsController.Players); + + foreach (Player bot in Bots) + { + if (bot.AIData.BotOwner == null) + { + continue; + } + + ConsoleScreen.Log($"Despawning: {bot.Profile.Nickname}"); + + game.DespawnBot(coopHandler, bot); + } + } + } + + [ConsoleCommand("stoptimer", "", null, "Stops the game timer", [])] + public static void StopTimer() + { + if (Singleton.Instance is CoopGame game) + { + if (game.GameTimer.Status == GameTimerClass.EGameTimerStatus.Stopped) + { + ConsoleScreen.LogError("GameTimer is already stopped at: " + game.GameTimer.PastTime.ToString()); + return; + } + game.GameTimer.TryStop(); + if (game.GameTimer.Status == GameTimerClass.EGameTimerStatus.Stopped) + { + ConsoleScreen.Log("GameTimer stopped at: " + game.GameTimer.PastTime.ToString()); + } + } + } #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() - { - Singleton.Instance.Console.Clear(); - } - } + [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() + { + Singleton.Instance.Console.Clear(); + } + } } diff --git a/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs b/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs index cc6a54c9..49fa57c4 100644 --- a/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropPlane.cs @@ -7,160 +7,160 @@ namespace Fika.Core.Coop.Airdrops { - /// - /// 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 - { - private const string PLANE_PATH = "assets/content/location_objects/lootable/prefab/il76md-90.prefab"; - private const float RADIUS_TO_PICK_RANDOM_POINT = 3000f; - - private AirplaneSynchronizableObject airplaneSync; - private float speed; - private float distanceToDrop; - private float flaresCooldown; - private bool flaresDeployed; - private bool headingChanged; - public Vector3 newPosition; - public Vector3 newRotation; - - public static async Task Init(Vector3 airdropPoint, int dropHeight, float planeVolume, float speed, bool isClient = false, Vector3 lookPoint = new Vector3()) - { - FikaAirdropPlane instance = (await LoadPlane()).AddComponent(); - - instance.airplaneSync = instance.GetComponent(); - instance.airplaneSync.SetLogic(new AirplaneLogicClass()); - - if (!isClient) - { - instance.SetPosition(dropHeight, airdropPoint); - } - else - { - instance.SetPositionClient(airdropPoint, lookPoint); - } - - instance.SetAudio(planeVolume); - instance.speed = speed; - instance.gameObject.SetActive(false); - return instance; - } - - private static async Task LoadPlane() - { - IEasyAssets easyAssets = Singleton.Instance.EasyAssets; - await easyAssets.Retain(PLANE_PATH, null, null).LoadingJob; - GameObject plane = Instantiate(easyAssets.GetAsset(PLANE_PATH)); - return plane; - } - - private void SetAudio(float planeVolume) - { - AudioSource airplaneAudio = gameObject.AddComponent(); - airplaneAudio.clip = airplaneSync.soundClip.Clip; - - airplaneAudio.dopplerLevel = 1f; - airplaneAudio.outputAudioMixerGroup = Singleton.Instance.VeryStandartMixerGroup; - airplaneAudio.loop = true; - airplaneAudio.maxDistance = 2000; - airplaneAudio.minDistance = 1; - airplaneAudio.pitch = 0.5f; - airplaneAudio.priority = 128; - airplaneAudio.reverbZoneMix = 1; - airplaneAudio.rolloffMode = AudioRolloffMode.Custom; - airplaneAudio.spatialBlend = 1; - airplaneAudio.spread = 60; - airplaneAudio.volume = planeVolume; - - airplaneAudio.Play(); - } - - private void SetPosition(int dropHeight, Vector3 airdropPoint) - { - Vector2 pointOnCircle = Random.insideUnitCircle.normalized * RADIUS_TO_PICK_RANDOM_POINT; - - transform.position = new Vector3(pointOnCircle.x, dropHeight, pointOnCircle.y); - newPosition = transform.position; - Vector3 lookPoint = new(airdropPoint.x, dropHeight, airdropPoint.z); - transform.LookAt(lookPoint); - newRotation = lookPoint; - } - - private void SetPositionClient(Vector3 position, Vector3 lookPoint) - { - transform.position = position; - transform.LookAt(lookPoint); - } - - public void ManualUpdate(float distance) - { - transform.Translate(Vector3.forward * (Time.deltaTime * speed)); - distanceToDrop = distance; - UpdateFlaresLogic(); - - if (distance - 200f > 0f || headingChanged) return; - - StartCoroutine(ChangeHeading()); - headingChanged = true; - } - - private void UpdateFlaresLogic() - { - if (flaresDeployed) return; - - if (distanceToDrop > 0f && flaresCooldown <= Time.unscaledTime) - { - flaresCooldown = Time.unscaledTime + 4f; - StartCoroutine(DeployFlares(Random.Range(0.2f, 0.4f))); - } - - if (distanceToDrop > 0f) return; - - flaresDeployed = true; - StartCoroutine(DeployFlares(5f)); - } - - private IEnumerator DeployFlares(float emissionTime) - { - GameObject projectile = Instantiate(airplaneSync.infraredCountermeasureParticles, transform); - projectile.transform.localPosition = new Vector3(0f, -5f, 0f); - ParticleSystem[] flares = projectile.GetComponentsInChildren(); - float endTime = Time.unscaledTime + emissionTime; - - GameWorld gameWorld = Singleton.Instance; - - if (gameWorld.SynchronizableObjectLogicProcessor.AirdropManager != null) - { - gameWorld.SynchronizableObjectLogicProcessor.AirdropManager.AddProjectile(projectile, - endTime + flares[0].main.duration + flares[0].main.startLifetime.Evaluate(1f)); - } - - while (endTime > Time.unscaledTime) - yield return null; - - projectile.transform.parent = null; - foreach (ParticleSystem particleSystem in flares) - particleSystem.Stop(); - } - - private IEnumerator ChangeHeading() - { - Vector3 startingRotation = transform.eulerAngles; - Vector3 middleRotation = startingRotation + new Vector3(0f, 40f, -200f); - Vector3 endRotation = middleRotation + new Vector3(0f, 40f, 200f); - - for (float i = 0; i < 1; i += Time.deltaTime / 25f) - { - Vector3 finalRotation = Vector3.Lerp(middleRotation, endRotation, EasingSmoothSquared(i)); - transform.eulerAngles = Vector3.Lerp(startingRotation, finalRotation, EasingSmoothSquared(i)); - yield return null; - } - } - - private float EasingSmoothSquared(float x) - { - return x < 0.5 ? x * x * 2 : 1 - (1 - x) * (1 - x) * 2; - } - } + /// + /// 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 + { + private const string PLANE_PATH = "assets/content/location_objects/lootable/prefab/il76md-90.prefab"; + private const float RADIUS_TO_PICK_RANDOM_POINT = 3000f; + + private AirplaneSynchronizableObject airplaneSync; + private float speed; + private float distanceToDrop; + private float flaresCooldown; + private bool flaresDeployed; + private bool headingChanged; + public Vector3 newPosition; + public Vector3 newRotation; + + public static async Task Init(Vector3 airdropPoint, int dropHeight, float planeVolume, float speed, bool isClient = false, Vector3 lookPoint = new Vector3()) + { + FikaAirdropPlane instance = (await LoadPlane()).AddComponent(); + + instance.airplaneSync = instance.GetComponent(); + instance.airplaneSync.SetLogic(new AirplaneLogicClass()); + + if (!isClient) + { + instance.SetPosition(dropHeight, airdropPoint); + } + else + { + instance.SetPositionClient(airdropPoint, lookPoint); + } + + instance.SetAudio(planeVolume); + instance.speed = speed; + instance.gameObject.SetActive(false); + return instance; + } + + private static async Task LoadPlane() + { + IEasyAssets easyAssets = Singleton.Instance.EasyAssets; + await easyAssets.Retain(PLANE_PATH, null, null).LoadingJob; + GameObject plane = Instantiate(easyAssets.GetAsset(PLANE_PATH)); + return plane; + } + + private void SetAudio(float planeVolume) + { + AudioSource airplaneAudio = gameObject.AddComponent(); + airplaneAudio.clip = airplaneSync.soundClip.Clip; + + airplaneAudio.dopplerLevel = 1f; + airplaneAudio.outputAudioMixerGroup = Singleton.Instance.VeryStandartMixerGroup; + airplaneAudio.loop = true; + airplaneAudio.maxDistance = 2000; + airplaneAudio.minDistance = 1; + airplaneAudio.pitch = 0.5f; + airplaneAudio.priority = 128; + airplaneAudio.reverbZoneMix = 1; + airplaneAudio.rolloffMode = AudioRolloffMode.Custom; + airplaneAudio.spatialBlend = 1; + airplaneAudio.spread = 60; + airplaneAudio.volume = planeVolume; + + airplaneAudio.Play(); + } + + private void SetPosition(int dropHeight, Vector3 airdropPoint) + { + Vector2 pointOnCircle = Random.insideUnitCircle.normalized * RADIUS_TO_PICK_RANDOM_POINT; + + transform.position = new Vector3(pointOnCircle.x, dropHeight, pointOnCircle.y); + newPosition = transform.position; + Vector3 lookPoint = new(airdropPoint.x, dropHeight, airdropPoint.z); + transform.LookAt(lookPoint); + newRotation = lookPoint; + } + + private void SetPositionClient(Vector3 position, Vector3 lookPoint) + { + transform.position = position; + transform.LookAt(lookPoint); + } + + public void ManualUpdate(float distance) + { + transform.Translate(Vector3.forward * (Time.deltaTime * speed)); + distanceToDrop = distance; + UpdateFlaresLogic(); + + if (distance - 200f > 0f || headingChanged) return; + + StartCoroutine(ChangeHeading()); + headingChanged = true; + } + + private void UpdateFlaresLogic() + { + if (flaresDeployed) return; + + if (distanceToDrop > 0f && flaresCooldown <= Time.unscaledTime) + { + flaresCooldown = Time.unscaledTime + 4f; + StartCoroutine(DeployFlares(Random.Range(0.2f, 0.4f))); + } + + if (distanceToDrop > 0f) return; + + flaresDeployed = true; + StartCoroutine(DeployFlares(5f)); + } + + private IEnumerator DeployFlares(float emissionTime) + { + GameObject projectile = Instantiate(airplaneSync.infraredCountermeasureParticles, transform); + projectile.transform.localPosition = new Vector3(0f, -5f, 0f); + ParticleSystem[] flares = projectile.GetComponentsInChildren(); + float endTime = Time.unscaledTime + emissionTime; + + GameWorld gameWorld = Singleton.Instance; + + if (gameWorld.SynchronizableObjectLogicProcessor.AirdropManager != null) + { + gameWorld.SynchronizableObjectLogicProcessor.AirdropManager.AddProjectile(projectile, + endTime + flares[0].main.duration + flares[0].main.startLifetime.Evaluate(1f)); + } + + while (endTime > Time.unscaledTime) + yield return null; + + projectile.transform.parent = null; + foreach (ParticleSystem particleSystem in flares) + particleSystem.Stop(); + } + + private IEnumerator ChangeHeading() + { + Vector3 startingRotation = transform.eulerAngles; + Vector3 middleRotation = startingRotation + new Vector3(0f, 40f, -200f); + Vector3 endRotation = middleRotation + new Vector3(0f, 40f, 200f); + + for (float i = 0; i < 1; i += Time.deltaTime / 25f) + { + Vector3 finalRotation = Vector3.Lerp(middleRotation, endRotation, EasingSmoothSquared(i)); + transform.eulerAngles = Vector3.Lerp(startingRotation, finalRotation, EasingSmoothSquared(i)); + yield return null; + } + } + + private float EasingSmoothSquared(float x) + { + 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 d77fa54f..affbc8c8 100644 --- a/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs +++ b/Fika.Core/Coop/Airdrops/FikaAirdropsManager.cs @@ -17,345 +17,345 @@ namespace Coop.Airdrops { - /// - /// 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 - { - private FikaAirdropPlane airdropPlane; - private AirdropBox airdropBox; - private FikaItemFactoryUtil factory; - public bool isFlareDrop; - public FikaAirdropParametersModel AirdropParameters { get; set; } - private ManualLogSource Logger { get; set; } - float distanceTravelled = 0; - public bool ClientPlaneSpawned; - public bool ClientLootBuilt = false; - public static int ContainerCount = 0; - - // Client fields - private string containerId; - private int containerNetId; - private Item rootItem; - - protected void Awake() - { - Logger = BepInEx.Logging.Logger.CreateLogSource("FikaAirdropsManager"); - Logger.LogInfo(isFlareDrop ? "Initializing from flare..." : "Initializing..."); - if (Singleton.Instance != null) - { - Logger.LogWarning("Another manager already exists, destroying old..."); - if (airdropPlane != null) - { - Destroy(airdropPlane.gameObject); - } - if (airdropBox != null) - { - Destroy(airdropBox.gameObject); - } - Destroy(Singleton.Instance); - } - Singleton.Create(this); - } - - protected void OnDestroy() - { - Logger.LogWarning("Destroying AirdropsManager"); - } - - protected async void Start() - { - GameWorld gameWorld = Singleton.Instance; - - if (gameWorld == null) - { - Logger.LogError("gameWorld is NULL"); - Destroy(this); - } - - string location = gameWorld.MainPlayer.Location; - if (location.Contains("factory") || location.Contains("laboratory") || location.Contains("Sandbox")) - { - Destroy(this); - return; - } - - // If this is not the server, then this manager will have to wait for the packet to initialize stuff. - if (FikaBackendUtils.IsClient) - { - return; - } - - // The server will generate stuff ready for the packet - AirdropParameters = FikaAirdropUtil.InitAirdropParams(gameWorld, isFlareDrop); - - if (!AirdropParameters.AirdropAvailable) - { - Logger.LogInfo("Airdrop is not available, destroying manager..."); - - GenericPacket packet = new() - { - NetId = 0, - PacketType = EPackageType.RemoveAirdropManager - }; - - Singleton.Instance.SendDataToAll(new(), ref packet, DeliveryMethod.ReliableOrdered); - - Destroy(this); - return; - } - - try - { - airdropPlane = await FikaAirdropPlane.Init(AirdropParameters.RandomAirdropPoint, - AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, - AirdropParameters.Config.PlaneSpeed); - airdropBox = await AirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); - airdropBox.container.Id = "FikaAirdropContainer"; - factory = new FikaItemFactoryUtil(); - } - catch - { - Logger.LogError("[SPT-AIRDROPS]: Unable to create plane or crate, airdrop won't occur"); - Destroy(this); - throw; - } - - SetDistanceToDrop(); - - BuildLootContainer(AirdropParameters.Config); - } - - public void SendParamsToClients() - { - if (!FikaBackendUtils.IsServer) - { - return; - } - - Logger.LogInfo("Sending Airdrop Params"); - AirdropPacket airdropPacket = new() - { - Config = AirdropParameters.Config, - AirdropAvailable = AirdropParameters.AirdropAvailable, - PlaneSpawned = AirdropParameters.PlaneSpawned, - BoxSpawned = AirdropParameters.BoxSpawned, - DistanceTraveled = AirdropParameters.DistanceTraveled, - DistanceToTravel = AirdropParameters.DistanceToTravel, - DistanceToDrop = AirdropParameters.DistanceToDrop, - Timer = AirdropParameters.Timer, - DropHeight = AirdropParameters.DropHeight, - TimeToStart = AirdropParameters.TimeToStart, - BoxPoint = AirdropParameters.RandomAirdropPoint, - SpawnPoint = airdropPlane.newPosition, - LookPoint = airdropPlane.newRotation - }; - NetDataWriter writer = new(); - Singleton.Instance.SendDataToAll(writer, ref airdropPacket, DeliveryMethod.ReliableOrdered); - } - - protected async void FixedUpdate() - { - if (AirdropParameters == null || AirdropParameters.Config == null) - { - return; - } - - // If we are a client. Wait until the server has sent all the data. - 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 (FikaBackendUtils.IsClient && !ClientLootBuilt) - { - ClientLootBuilt = true; - - Logger.LogInfo("Client::Building Plane, Box, Factory and Loot."); - - airdropPlane = await FikaAirdropPlane.Init(AirdropParameters.SpawnPoint, AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, - AirdropParameters.Config.PlaneSpeed, true, AirdropParameters.LookPoint); - - airdropBox = await AirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); - factory = new FikaItemFactoryUtil(); - - factory.BuildClientContainer(airdropBox.container, rootItem); - - if (airdropBox.container != null) - { - if (containerNetId > 0) - { - airdropBox.container.NetId = containerNetId; - airdropBox.container.Id = containerId; - Singleton.Instance.RegisterWorldInteractionObject(airdropBox.container); - Logger.LogInfo($"Adding AirdropBox {airdropBox.container.Id} to interactive objects."); - } - else - { - Logger.LogError("ContainerId received from server was empty."); - } - } - - } - - if (!ClientLootBuilt) - { - return; - } - - if (airdropPlane == null || airdropBox == null || factory == null) - { - return; - } - - if (FikaBackendUtils.IsServer || FikaBackendUtils.IsSinglePlayer) - { - AirdropParameters.Timer += 0.02f; - - if (AirdropParameters.Timer >= AirdropParameters.TimeToStart && !AirdropParameters.PlaneSpawned) - { - SendParamsToClients(); - StartPlane(); - } - - if (!AirdropParameters.PlaneSpawned) - { - return; - } - } - else - { - AirdropParameters.Timer += 0.02f; - - if (!ClientPlaneSpawned) - { - ClientPlaneSpawned = true; - StartPlane(); - } - } - - if (distanceTravelled >= AirdropParameters.DistanceToDrop && !AirdropParameters.BoxSpawned) - { - StartBox(); - } - - if (distanceTravelled < AirdropParameters.DistanceToTravel) - { - distanceTravelled += Time.deltaTime * AirdropParameters.Config.PlaneSpeed; - float distanceToDrop = AirdropParameters.DistanceToDrop - distanceTravelled; - airdropPlane.ManualUpdate(distanceToDrop); - } - else - { - Destroy(airdropPlane.gameObject); - Destroy(this); - } - } - - private void StartPlane() - { - airdropPlane.gameObject.SetActive(true); - AirdropParameters.PlaneSpawned = true; - } - - private void StartBox() - { - AirdropParameters.BoxSpawned = true; - Vector3 pointPos = AirdropParameters.RandomAirdropPoint; - Vector3 dropPos = new(pointPos.x, AirdropParameters.DropHeight, pointPos.z); - airdropBox.gameObject.SetActive(true); - airdropBox.StartCoroutine(airdropBox.DropCrate(dropPos)); - } - - private void BuildLootContainer(FikaAirdropConfigModel config) - { - if (FikaBackendUtils.IsClient) - { - return; - } - - FikaAirdropLootResultModel lootData = factory.GetLoot(); - - if (lootData == null) - { - throw new Exception("Airdrops. Tried to BuildLootContainer without any Loot."); - } - - factory.BuildContainer(airdropBox.container, config, lootData.DropType); - factory.AddLoot(airdropBox.container, lootData); - - if (airdropBox.container != null) - { - ContainerCount++; - airdropBox.container.Id = $"Airdrop{ContainerCount}"; - Singleton.Instance.RegisterWorldInteractionObject(airdropBox.container); - Logger.LogInfo($"Adding AirdropBox {airdropBox.container.Id} to interactive objects."); - } - - // Get the lootData. Send to clients. - if (FikaBackendUtils.IsServer) - { - StartCoroutine(SendLootToClients(isFlareDrop)); - } - } - - public void ReceiveBuildLootContainer(AirdropLootPacket packet) - { - Logger.LogInfo("Received loot container parameters"); - rootItem = packet.RootItem; - containerId = packet.ContainerId; - containerNetId = packet.ContainerNetId; - } - - private void SetDistanceToDrop() - { - AirdropParameters.DistanceToDrop = Vector3.Distance(new Vector3(AirdropParameters.RandomAirdropPoint.x, AirdropParameters.DropHeight, AirdropParameters.RandomAirdropPoint.z), - airdropPlane.transform.position); - } - - private IEnumerator SendLootToClients(bool isFlare = false) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - if (!isFlare) - { - while (!ClientLootBuilt) - { - yield return null; - } - yield return new WaitForSeconds(5); - } - else - { - while (!AirdropParameters.PlaneSpawned && !ClientLootBuilt) - { - yield return null; - } - } - - Logger.LogInfo("Sending Airdrop Loot to clients."); - - Item rootItem = airdropBox.container.ItemOwner.RootItem; - - AirdropLootPacket lootPacket = new() - { - RootItem = rootItem, - ContainerId = airdropBox.container.Id, - ContainerNetId = airdropBox.container.NetId, - }; - - NetDataWriter writer = new(); - Singleton.Instance.SendDataToAll(writer, ref lootPacket, DeliveryMethod.ReliableOrdered); - - yield break; - } - } + /// + /// 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 + { + private FikaAirdropPlane airdropPlane; + private AirdropBox airdropBox; + private FikaItemFactoryUtil factory; + public bool isFlareDrop; + public FikaAirdropParametersModel AirdropParameters { get; set; } + private ManualLogSource Logger { get; set; } + float distanceTravelled = 0; + public bool ClientPlaneSpawned; + public bool ClientLootBuilt = false; + public static int ContainerCount = 0; + + // Client fields + private string containerId; + private int containerNetId; + private Item rootItem; + + protected void Awake() + { + Logger = BepInEx.Logging.Logger.CreateLogSource("FikaAirdropsManager"); + Logger.LogInfo(isFlareDrop ? "Initializing from flare..." : "Initializing..."); + if (Singleton.Instance != null) + { + Logger.LogWarning("Another manager already exists, destroying old..."); + if (airdropPlane != null) + { + Destroy(airdropPlane.gameObject); + } + if (airdropBox != null) + { + Destroy(airdropBox.gameObject); + } + Destroy(Singleton.Instance); + } + Singleton.Create(this); + } + + protected void OnDestroy() + { + Logger.LogWarning("Destroying AirdropsManager"); + } + + protected async void Start() + { + GameWorld gameWorld = Singleton.Instance; + + if (gameWorld == null) + { + Logger.LogError("gameWorld is NULL"); + Destroy(this); + } + + string location = gameWorld.MainPlayer.Location; + if (location.Contains("factory") || location.Contains("laboratory") || location.Contains("Sandbox")) + { + Destroy(this); + return; + } + + // If this is not the server, then this manager will have to wait for the packet to initialize stuff. + if (FikaBackendUtils.IsClient) + { + return; + } + + // The server will generate stuff ready for the packet + AirdropParameters = FikaAirdropUtil.InitAirdropParams(gameWorld, isFlareDrop); + + if (!AirdropParameters.AirdropAvailable) + { + Logger.LogInfo("Airdrop is not available, destroying manager..."); + + GenericPacket packet = new() + { + NetId = 0, + PacketType = EPackageType.RemoveAirdropManager + }; + + Singleton.Instance.SendDataToAll(new(), ref packet, DeliveryMethod.ReliableOrdered); + + Destroy(this); + return; + } + + try + { + airdropPlane = await FikaAirdropPlane.Init(AirdropParameters.RandomAirdropPoint, + AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, + AirdropParameters.Config.PlaneSpeed); + airdropBox = await AirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); + airdropBox.container.Id = "FikaAirdropContainer"; + factory = new FikaItemFactoryUtil(); + } + catch + { + Logger.LogError("[SPT-AIRDROPS]: Unable to create plane or crate, airdrop won't occur"); + Destroy(this); + throw; + } + + SetDistanceToDrop(); + + BuildLootContainer(AirdropParameters.Config); + } + + public void SendParamsToClients() + { + if (!FikaBackendUtils.IsServer) + { + return; + } + + Logger.LogInfo("Sending Airdrop Params"); + AirdropPacket airdropPacket = new() + { + Config = AirdropParameters.Config, + AirdropAvailable = AirdropParameters.AirdropAvailable, + PlaneSpawned = AirdropParameters.PlaneSpawned, + BoxSpawned = AirdropParameters.BoxSpawned, + DistanceTraveled = AirdropParameters.DistanceTraveled, + DistanceToTravel = AirdropParameters.DistanceToTravel, + DistanceToDrop = AirdropParameters.DistanceToDrop, + Timer = AirdropParameters.Timer, + DropHeight = AirdropParameters.DropHeight, + TimeToStart = AirdropParameters.TimeToStart, + BoxPoint = AirdropParameters.RandomAirdropPoint, + SpawnPoint = airdropPlane.newPosition, + LookPoint = airdropPlane.newRotation + }; + NetDataWriter writer = new(); + Singleton.Instance.SendDataToAll(writer, ref airdropPacket, DeliveryMethod.ReliableOrdered); + } + + protected async void FixedUpdate() + { + if (AirdropParameters == null || AirdropParameters.Config == null) + { + return; + } + + // If we are a client. Wait until the server has sent all the data. + 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 (FikaBackendUtils.IsClient && !ClientLootBuilt) + { + ClientLootBuilt = true; + + Logger.LogInfo("Client::Building Plane, Box, Factory and Loot."); + + airdropPlane = await FikaAirdropPlane.Init(AirdropParameters.SpawnPoint, AirdropParameters.DropHeight, AirdropParameters.Config.PlaneVolume, + AirdropParameters.Config.PlaneSpeed, true, AirdropParameters.LookPoint); + + airdropBox = await AirdropBox.Init(AirdropParameters.Config.CrateFallSpeed); + factory = new FikaItemFactoryUtil(); + + factory.BuildClientContainer(airdropBox.container, rootItem); + + if (airdropBox.container != null) + { + if (containerNetId > 0) + { + airdropBox.container.NetId = containerNetId; + airdropBox.container.Id = containerId; + Singleton.Instance.RegisterWorldInteractionObject(airdropBox.container); + Logger.LogInfo($"Adding AirdropBox {airdropBox.container.Id} to interactive objects."); + } + else + { + Logger.LogError("ContainerId received from server was empty."); + } + } + + } + + if (!ClientLootBuilt) + { + return; + } + + if (airdropPlane == null || airdropBox == null || factory == null) + { + return; + } + + if (FikaBackendUtils.IsServer || FikaBackendUtils.IsSinglePlayer) + { + AirdropParameters.Timer += 0.02f; + + if (AirdropParameters.Timer >= AirdropParameters.TimeToStart && !AirdropParameters.PlaneSpawned) + { + SendParamsToClients(); + StartPlane(); + } + + if (!AirdropParameters.PlaneSpawned) + { + return; + } + } + else + { + AirdropParameters.Timer += 0.02f; + + if (!ClientPlaneSpawned) + { + ClientPlaneSpawned = true; + StartPlane(); + } + } + + if (distanceTravelled >= AirdropParameters.DistanceToDrop && !AirdropParameters.BoxSpawned) + { + StartBox(); + } + + if (distanceTravelled < AirdropParameters.DistanceToTravel) + { + distanceTravelled += Time.deltaTime * AirdropParameters.Config.PlaneSpeed; + float distanceToDrop = AirdropParameters.DistanceToDrop - distanceTravelled; + airdropPlane.ManualUpdate(distanceToDrop); + } + else + { + Destroy(airdropPlane.gameObject); + Destroy(this); + } + } + + private void StartPlane() + { + airdropPlane.gameObject.SetActive(true); + AirdropParameters.PlaneSpawned = true; + } + + private void StartBox() + { + AirdropParameters.BoxSpawned = true; + Vector3 pointPos = AirdropParameters.RandomAirdropPoint; + Vector3 dropPos = new(pointPos.x, AirdropParameters.DropHeight, pointPos.z); + airdropBox.gameObject.SetActive(true); + airdropBox.StartCoroutine(airdropBox.DropCrate(dropPos)); + } + + private void BuildLootContainer(FikaAirdropConfigModel config) + { + if (FikaBackendUtils.IsClient) + { + return; + } + + FikaAirdropLootResultModel lootData = factory.GetLoot(); + + if (lootData == null) + { + throw new Exception("Airdrops. Tried to BuildLootContainer without any Loot."); + } + + factory.BuildContainer(airdropBox.container, config, lootData.DropType); + factory.AddLoot(airdropBox.container, lootData); + + if (airdropBox.container != null) + { + ContainerCount++; + airdropBox.container.Id = $"Airdrop{ContainerCount}"; + Singleton.Instance.RegisterWorldInteractionObject(airdropBox.container); + Logger.LogInfo($"Adding AirdropBox {airdropBox.container.Id} to interactive objects."); + } + + // Get the lootData. Send to clients. + if (FikaBackendUtils.IsServer) + { + StartCoroutine(SendLootToClients(isFlareDrop)); + } + } + + public void ReceiveBuildLootContainer(AirdropLootPacket packet) + { + Logger.LogInfo("Received loot container parameters"); + rootItem = packet.RootItem; + containerId = packet.ContainerId; + containerNetId = packet.ContainerNetId; + } + + private void SetDistanceToDrop() + { + AirdropParameters.DistanceToDrop = Vector3.Distance(new Vector3(AirdropParameters.RandomAirdropPoint.x, AirdropParameters.DropHeight, AirdropParameters.RandomAirdropPoint.z), + airdropPlane.transform.position); + } + + private IEnumerator SendLootToClients(bool isFlare = false) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + if (!isFlare) + { + while (!ClientLootBuilt) + { + yield return null; + } + yield return new WaitForSeconds(5); + } + else + { + while (!AirdropParameters.PlaneSpawned && !ClientLootBuilt) + { + yield return null; + } + } + + Logger.LogInfo("Sending Airdrop Loot to clients."); + + Item rootItem = airdropBox.container.ItemOwner.RootItem; + + AirdropLootPacket lootPacket = new() + { + RootItem = rootItem, + ContainerId = airdropBox.container.Id, + ContainerNetId = airdropBox.container.NetId, + }; + + NetDataWriter writer = new(); + Singleton.Instance.SendDataToAll(writer, ref lootPacket, DeliveryMethod.ReliableOrdered); + + yield break; + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs index db7c677b..8d641799 100644 --- a/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropConfigModel.cs @@ -3,61 +3,61 @@ 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 FikaAirdropConfigModel - { - [JsonProperty("airdropChancePercent")] - public FikaAirdropChancePercent AirdropChancePercent { get; set; } + /// + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/Models + /// + public class FikaAirdropConfigModel + { + [JsonProperty("airdropChancePercent")] + public FikaAirdropChancePercent AirdropChancePercent { get; set; } - [JsonProperty("airdropMinStartTimeSeconds")] - public int AirdropMinStartTimeSeconds { get; set; } + [JsonProperty("airdropMinStartTimeSeconds")] + public int AirdropMinStartTimeSeconds { get; set; } - [JsonProperty("airdropMaxStartTimeSeconds")] - public int AirdropMaxStartTimeSeconds { get; set; } + [JsonProperty("airdropMaxStartTimeSeconds")] + public int AirdropMaxStartTimeSeconds { get; set; } - [JsonProperty("planeMinFlyHeight")] - public int PlaneMinFlyHeight { get; set; } + [JsonProperty("planeMinFlyHeight")] + public int PlaneMinFlyHeight { get; set; } - [JsonProperty("planeMaxFlyHeight")] - public int PlaneMaxFlyHeight { get; set; } + [JsonProperty("planeMaxFlyHeight")] + public int PlaneMaxFlyHeight { get; set; } - [JsonProperty("planeVolume")] - public float PlaneVolume { get; set; } + [JsonProperty("planeVolume")] + public float PlaneVolume { get; set; } - [JsonProperty("planeSpeed")] - public float PlaneSpeed { get; set; } + [JsonProperty("planeSpeed")] + public float PlaneSpeed { get; set; } - [JsonProperty("crateFallSpeed")] - public float CrateFallSpeed { get; set; } + [JsonProperty("crateFallSpeed")] + public float CrateFallSpeed { get; set; } - [JsonProperty("containerIds")] - public Dictionary ContainerIds { get; set; } - } + [JsonProperty("containerIds")] + public Dictionary ContainerIds { get; set; } + } - public class FikaAirdropChancePercent - { - [JsonProperty("bigmap")] - public int Bigmap { get; set; } + public class FikaAirdropChancePercent + { + [JsonProperty("bigmap")] + public int Bigmap { get; set; } - [JsonProperty("woods")] - public int Woods { get; set; } + [JsonProperty("woods")] + public int Woods { get; set; } - [JsonProperty("lighthouse")] - public int Lighthouse { get; set; } + [JsonProperty("lighthouse")] + public int Lighthouse { get; set; } - [JsonProperty("shoreline")] - public int Shoreline { get; set; } + [JsonProperty("shoreline")] + public int Shoreline { get; set; } - [JsonProperty("interchange")] - public int Interchange { get; set; } + [JsonProperty("interchange")] + public int Interchange { get; set; } - [JsonProperty("reserve")] - public int Reserve { get; set; } + [JsonProperty("reserve")] + public int Reserve { get; set; } - [JsonProperty("tarkovStreets")] - public int TarkovStreets { get; set; } - } + [JsonProperty("tarkovStreets")] + public int TarkovStreets { get; set; } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs index fa7ae4f1..20582879 100644 --- a/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropLootModel.cs @@ -3,32 +3,32 @@ 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 FikaAirdropLootResultModel - { - [JsonProperty("dropType")] - public string DropType { get; set; } + /// + /// Created by: SPT team + /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/Models + /// + public class FikaAirdropLootResultModel + { + [JsonProperty("dropType")] + public string DropType { get; set; } - [JsonProperty("loot")] - public IEnumerable Loot { get; set; } - } + [JsonProperty("loot")] + public IEnumerable Loot { get; set; } + } - public class FikaAirdropLootModel - { - [JsonProperty("tpl")] - public string Tpl { get; set; } + public class FikaAirdropLootModel + { + [JsonProperty("tpl")] + public string Tpl { get; set; } - [JsonProperty("isPreset")] - public bool IsPreset { get; set; } + [JsonProperty("isPreset")] + public bool IsPreset { get; set; } - [JsonProperty("stackCount")] - public int StackCount { get; set; } + [JsonProperty("stackCount")] + public int StackCount { get; set; } - [JsonProperty("id")] - public string ID { get; set; } - } + [JsonProperty("id")] + public string ID { get; set; } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs b/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs index f5f444da..11e7eedc 100644 --- a/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs +++ b/Fika.Core/Coop/Airdrops/Models/FikaAirdropParametersModel.cs @@ -3,27 +3,27 @@ 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; + /// + /// 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; - } + [JsonIgnore] + public Vector3 RandomAirdropPoint = Vector3.zero; + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs b/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs index acef3c5b..64f7ec87 100644 --- a/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs +++ b/Fika.Core/Coop/Airdrops/Utils/FikaAirdropUtil.cs @@ -11,140 +11,140 @@ namespace Fika.Core.Coop.Airdrops.Utils { - /// - /// Originally developed by SPT - /// - public static class FikaAirdropUtil - { - public static FikaAirdropConfigModel AirdropConfigModel { get; private set; } - - public static void GetConfigFromServer() - { - string json = RequestHandler.GetJson("/singleplayer/airdrop/config"); - AirdropConfigModel = JsonConvert.DeserializeObject(json); - } - - public static int ChanceToSpawn(GameWorld gameWorld, FikaAirdropConfigModel config, bool isFlare) - { - // Flare summoned airdrops are guaranteed - if (isFlare) - { - return 100; - } - - string location = gameWorld.MainPlayer.Location; - - int result = 25; - switch (location.ToLower()) - { - case "bigmap": - { - result = config.AirdropChancePercent.Bigmap; - break; - } - case "interchange": - { - result = config.AirdropChancePercent.Interchange; - break; - } - case "rezervbase": - { - result = config.AirdropChancePercent.Reserve; - break; - } - case "shoreline": - { - result = config.AirdropChancePercent.Shoreline; - break; - } - case "woods": - { - result = config.AirdropChancePercent.Woods; - break; - } - case "lighthouse": - { - result = config.AirdropChancePercent.Lighthouse; - break; - } - case "tarkovstreets": - { - result = config.AirdropChancePercent.TarkovStreets; - break; - } - } - - return result; - } - - public static bool ShouldAirdropOccur(int dropChance, List airdropPoints) - { - return airdropPoints.Count > 0 && Random.Range(0, 100) <= dropChance; - } - - public static FikaAirdropParametersModel InitAirdropParams(GameWorld gameWorld, bool isFlare) - { - if (AirdropConfigModel == null) - { - return new FikaAirdropParametersModel() - { - Config = new FikaAirdropConfigModel(), - AirdropAvailable = false - }; - } - - if (AirdropConfigModel.AirdropChancePercent == null) - { - return new FikaAirdropParametersModel() - { - Config = new FikaAirdropConfigModel(), - AirdropAvailable = false - }; - } - - List allAirdropPoints = LocationScene.GetAll().ToList(); - Vector3 playerPosition = gameWorld.MainPlayer.Position; - List flareAirdropPoints = new(); - int dropChance = ChanceToSpawn(gameWorld, AirdropConfigModel, isFlare); - float flareSpawnRadiusDistance = 100f; - - if (isFlare && allAirdropPoints.Count > 0) - { - foreach (AirdropPoint point in allAirdropPoints) - { - if (Vector3.Distance(playerPosition, point.transform.position) <= flareSpawnRadiusDistance) - { - flareAirdropPoints.Add(point); - } - } - } - - if (flareAirdropPoints.Count == 0 && isFlare) - { - 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()); - } - - return new FikaAirdropParametersModel() - { - Config = AirdropConfigModel, - AirdropAvailable = ShouldAirdropOccur(dropChance, allAirdropPoints), - - DistanceTraveled = 0f, - DistanceToTravel = 8000f, - Timer = 0, - PlaneSpawned = false, - BoxSpawned = false, - - DropHeight = Random.Range(AirdropConfigModel.PlaneMinFlyHeight, AirdropConfigModel.PlaneMaxFlyHeight), - TimeToStart = isFlare - ? 5 - : Random.Range(AirdropConfigModel.AirdropMinStartTimeSeconds, AirdropConfigModel.AirdropMaxStartTimeSeconds), - - RandomAirdropPoint = isFlare && allAirdropPoints.Count > 0 - ? flareAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position - : allAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position - }; - } - } + /// + /// Originally developed by SPT + /// + public static class FikaAirdropUtil + { + public static FikaAirdropConfigModel AirdropConfigModel { get; private set; } + + public static void GetConfigFromServer() + { + string json = RequestHandler.GetJson("/singleplayer/airdrop/config"); + AirdropConfigModel = JsonConvert.DeserializeObject(json); + } + + public static int ChanceToSpawn(GameWorld gameWorld, FikaAirdropConfigModel config, bool isFlare) + { + // Flare summoned airdrops are guaranteed + if (isFlare) + { + return 100; + } + + string location = gameWorld.MainPlayer.Location; + + int result = 25; + switch (location.ToLower()) + { + case "bigmap": + { + result = config.AirdropChancePercent.Bigmap; + break; + } + case "interchange": + { + result = config.AirdropChancePercent.Interchange; + break; + } + case "rezervbase": + { + result = config.AirdropChancePercent.Reserve; + break; + } + case "shoreline": + { + result = config.AirdropChancePercent.Shoreline; + break; + } + case "woods": + { + result = config.AirdropChancePercent.Woods; + break; + } + case "lighthouse": + { + result = config.AirdropChancePercent.Lighthouse; + break; + } + case "tarkovstreets": + { + result = config.AirdropChancePercent.TarkovStreets; + break; + } + } + + return result; + } + + public static bool ShouldAirdropOccur(int dropChance, List airdropPoints) + { + return airdropPoints.Count > 0 && Random.Range(0, 100) <= dropChance; + } + + public static FikaAirdropParametersModel InitAirdropParams(GameWorld gameWorld, bool isFlare) + { + if (AirdropConfigModel == null) + { + return new FikaAirdropParametersModel() + { + Config = new FikaAirdropConfigModel(), + AirdropAvailable = false + }; + } + + if (AirdropConfigModel.AirdropChancePercent == null) + { + return new FikaAirdropParametersModel() + { + Config = new FikaAirdropConfigModel(), + AirdropAvailable = false + }; + } + + List allAirdropPoints = LocationScene.GetAll().ToList(); + Vector3 playerPosition = gameWorld.MainPlayer.Position; + List flareAirdropPoints = new(); + int dropChance = ChanceToSpawn(gameWorld, AirdropConfigModel, isFlare); + float flareSpawnRadiusDistance = 100f; + + if (isFlare && allAirdropPoints.Count > 0) + { + foreach (AirdropPoint point in allAirdropPoints) + { + if (Vector3.Distance(playerPosition, point.transform.position) <= flareSpawnRadiusDistance) + { + flareAirdropPoints.Add(point); + } + } + } + + if (flareAirdropPoints.Count == 0 && isFlare) + { + 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()); + } + + return new FikaAirdropParametersModel() + { + Config = AirdropConfigModel, + AirdropAvailable = ShouldAirdropOccur(dropChance, allAirdropPoints), + + DistanceTraveled = 0f, + DistanceToTravel = 8000f, + Timer = 0, + PlaneSpawned = false, + BoxSpawned = false, + + DropHeight = Random.Range(AirdropConfigModel.PlaneMinFlyHeight, AirdropConfigModel.PlaneMaxFlyHeight), + TimeToStart = isFlare + ? 5 + : Random.Range(AirdropConfigModel.AirdropMinStartTimeSeconds, AirdropConfigModel.AirdropMaxStartTimeSeconds), + + RandomAirdropPoint = isFlare && allAirdropPoints.Count > 0 + ? flareAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position + : allAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position + }; + } + } } diff --git a/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs b/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs index b913db85..46561369 100644 --- a/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs +++ b/Fika.Core/Coop/Airdrops/Utils/FikaItemFactoryUtil.cs @@ -11,80 +11,80 @@ namespace Fika.Core.Coop.Airdrops.Utils { - /// - /// Originally developed by SPT - /// - public class FikaItemFactoryUtil - { - private readonly ItemFactory itemFactory; - ManualLogSource logSource; + /// + /// Originally developed by SPT + /// + public class FikaItemFactoryUtil + { + private readonly ItemFactory itemFactory; + ManualLogSource logSource; - public FikaItemFactoryUtil() - { - itemFactory = Singleton.Instance; - logSource = Logger.CreateLogSource("ItemFactoryUtil"); - } + public FikaItemFactoryUtil() + { + itemFactory = Singleton.Instance; + logSource = Logger.CreateLogSource("ItemFactoryUtil"); + } - public void BuildContainer(LootableContainer container, FikaAirdropConfigModel config, string dropType) - { - string containerId = config.ContainerIds[dropType]; - if (itemFactory.ItemTemplates.TryGetValue(containerId, out ItemTemplate template)) - { - Item item = itemFactory.CreateItem(containerId, template._id, null); - item.Id = Singleton.Instance.MainPlayer.InventoryControllerClass.NextId; - LootItem.CreateLootContainer(container, item, "CRATE", Singleton.Instance); - } - else - { - logSource.LogError($"[SPT-AIRDROPS]: unable to find template: {containerId}"); - } - } + public void BuildContainer(LootableContainer container, FikaAirdropConfigModel config, string dropType) + { + string containerId = config.ContainerIds[dropType]; + if (itemFactory.ItemTemplates.TryGetValue(containerId, out ItemTemplate template)) + { + Item item = itemFactory.CreateItem(containerId, template._id, null); + item.Id = Singleton.Instance.MainPlayer.InventoryControllerClass.NextId; + LootItem.CreateLootContainer(container, item, "CRATE", Singleton.Instance); + } + else + { + logSource.LogError($"[SPT-AIRDROPS]: unable to find template: {containerId}"); + } + } - public void BuildClientContainer(LootableContainer container, Item rootItem) - { - LootItem.CreateLootContainer(container, rootItem, "CRATE", Singleton.Instance); - } + public void BuildClientContainer(LootableContainer container, Item rootItem) + { + LootItem.CreateLootContainer(container, rootItem, "CRATE", Singleton.Instance); + } - public async void AddLoot(LootableContainer container, FikaAirdropLootResultModel lootToAdd) - { - FikaPlugin.Instance.FikaLogger.LogInfo($"ItemFactory::AirDropLoot: Reading loot from server. LootToAdd: {lootToAdd.Loot.Count()}"); + public async void AddLoot(LootableContainer container, FikaAirdropLootResultModel lootToAdd) + { + FikaPlugin.Instance.FikaLogger.LogInfo($"ItemFactory::AirDropLoot: Reading loot from server. LootToAdd: {lootToAdd.Loot.Count()}"); - Item actualItem; - foreach (FikaAirdropLootModel item in lootToAdd.Loot) - { - ResourceKey[] resources; - if (item.IsPreset) - { - actualItem = itemFactory.GetPresetItem(item.Tpl); - actualItem.SpawnedInSession = true; - actualItem.GetAllItems().ExecuteForEach(x => x.SpawnedInSession = true); - resources = actualItem.GetAllItems().Select(x => x.Template).SelectMany(x => x.AllResources).ToArray(); - } - else - { - actualItem = itemFactory.CreateItem(item.ID, item.Tpl, null); - actualItem.StackObjectsCount = item.StackCount; - actualItem.SpawnedInSession = true; + Item actualItem; + foreach (FikaAirdropLootModel item in lootToAdd.Loot) + { + ResourceKey[] resources; + if (item.IsPreset) + { + actualItem = itemFactory.GetPresetItem(item.Tpl); + actualItem.SpawnedInSession = true; + actualItem.GetAllItems().ExecuteForEach(x => x.SpawnedInSession = true); + resources = actualItem.GetAllItems().Select(x => x.Template).SelectMany(x => x.AllResources).ToArray(); + } + else + { + actualItem = itemFactory.CreateItem(item.ID, item.Tpl, null); + actualItem.StackObjectsCount = item.StackCount; + actualItem.SpawnedInSession = true; - resources = actualItem.Template.AllResources.ToArray(); - } + resources = actualItem.Template.AllResources.ToArray(); + } - container.ItemOwner.MainStorage[0].Add(actualItem); - await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Local, resources, JobPriority.Immediate, null, PoolManager.DefaultCancellationToken); - } + container.ItemOwner.MainStorage[0].Add(actualItem); + await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Local, resources, JobPriority.Immediate, null, PoolManager.DefaultCancellationToken); + } - if (Singleton.Instance != null) - { - Singleton.Instance.ClientLootBuilt = true; - } - } + if (Singleton.Instance != null) + { + Singleton.Instance.ClientLootBuilt = true; + } + } - public FikaAirdropLootResultModel GetLoot() - { - string json = RequestHandler.GetJson("/client/location/getAirdropLoot"); - FikaAirdropLootResultModel result = JsonConvert.DeserializeObject(json); + public FikaAirdropLootResultModel GetLoot() + { + string json = RequestHandler.GetJson("/client/location/getAirdropLoot"); + FikaAirdropLootResultModel result = JsonConvert.DeserializeObject(json); - return result; - } - } + return result; + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs b/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs index 9a543e1b..7750342e 100644 --- a/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs +++ b/Fika.Core/Coop/BTR/FikaBTRManager_Client.cs @@ -27,234 +27,234 @@ namespace Fika.Core.Coop.BTR { - /// - /// Based on - /// - internal class FikaBTRManager_Client : MonoBehaviour - { - private GameWorld gameWorld; - private BotEventHandler botEventHandler; - - private BotBTRService btrBotService; - private BTRControllerClass btrController; - private BTRVehicle btrServerSide; - public BTRView btrClientSide; - private BTRDataPacket btrDataPacket = default; + /// + /// Based on + /// + internal class FikaBTRManager_Client : MonoBehaviour + { + private GameWorld gameWorld; + private BotEventHandler botEventHandler; + + private BotBTRService btrBotService; + private BTRControllerClass btrController; + private BTRVehicle btrServerSide; + public BTRView btrClientSide; + private BTRDataPacket btrDataPacket = default; #pragma warning disable CS0414 // The field 'FikaBTRManager_Client.btrInitialized' is assigned but its value is never used - private bool btrInitialized = false; + private bool btrInitialized = false; #pragma warning restore CS0414 // The field 'FikaBTRManager_Client.btrInitialized' is assigned but its value is never used - private bool btrBotShooterInitialized = false; - - private BTRTurretServer btrTurretServer; - private bool isTurretInDefaultRotation; - private BulletClass btrMachineGunAmmo; - private Item btrMachineGunWeapon; - private Player.FirearmController firearmController; - private WeaponSoundPlayer weaponSoundPlayer; - - private MethodInfo _updateTaxiPriceMethod; - - private float originalDamageCoeff; - - private FikaClient client; - public Queue btrPackets = new(60); - private string botShooterId = string.Empty; - private ManualLogSource btrLogger; - - FikaBTRManager_Client() - { - Type btrControllerType = typeof(BTRControllerClass); - _updateTaxiPriceMethod = AccessTools.GetDeclaredMethods(btrControllerType).Single(IsUpdateTaxiPriceMethod); - client = Singleton.Instance; - btrLogger = BepInEx.Logging.Logger.CreateLogSource("BTR Client"); - } - - private async void Awake() - { - try - { - gameWorld = Singleton.Instance; - if (gameWorld == null) - { - Destroy(this); - return; - } - - if (gameWorld.BtrController == null) - { - gameWorld.BtrController = new BTRControllerClass(); - } - - btrController = gameWorld.BtrController; - - await InitBtr(); - } - catch - { - btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); - Destroy(this); - throw; - } - } - - public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPacket) - { - // If the player is going into the BTR, store their damage coefficient - // and set it to 0, so they don't die while inside the BTR - if (interactPacket.InteractionType == EInteractionType.GoIn && player.IsYourPlayer) - { - originalDamageCoeff = player.ActiveHealthController.DamageCoeff; - player.ActiveHealthController.SetDamageCoeff(0f); - - } - // Otherwise restore the damage coefficient - else if (interactPacket.InteractionType == EInteractionType.GoOut && player.IsYourPlayer) - { - player.ActiveHealthController.SetDamageCoeff(originalDamageCoeff); - } - } - - // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` - private bool IsUpdateTaxiPriceMethod(MethodInfo method) - { - return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); - } - - private void Update() - { - if (btrPackets.Count > 0) - { - BTRPacket packet = btrPackets.Dequeue(); - if (packet.HasBotProfileId) - { - AttachBot(packet.BotNetId); - } - if (packet.HasShot) - { - ObservedShot(packet.ShotPosition, packet.ShotDirection); - } - btrDataPacket = packet.BTRDataPacket; - } - - if (!btrBotShooterInitialized) - { - InitBtrBotService(); - btrBotShooterInitialized = true; - } - - btrController.SyncBTRVehicleFromServer(btrDataPacket); - - if (!isTurretInDefaultRotation) - { - btrTurretServer.DisableAiming(); - } - } - - private void ObservedShot(Vector3 position, Vector3 direction) - { - gameWorld.SharedBallisticsCalculator.Shoot(btrMachineGunAmmo, position, direction, botShooterId, btrMachineGunWeapon, 1f, 0); - firearmController.method_54(weaponSoundPlayer, btrMachineGunAmmo, position, direction, false); - } - - public void AttachBot(int netId) - { - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - if (coopHandler.Players.TryGetValue(netId, out CoopPlayer player)) - { - BTRTurretView turretView = btrClientSide.turret; - - player.Transform.Original.position = turretView.BotRoot.position; - player.PlayerBones.Weapon_Root_Anim.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); - - WeaponPrefab weaponPrefab = player.HandsController.ControllerGameObject.GetComponent(); - if (weaponPrefab != null) - { - weaponSoundPlayer = weaponPrefab.GetComponent(); - - Transform weaponTransform = weaponPrefab.Hierarchy.GetTransform(ECharacterWeaponBones.weapon); - if (weaponTransform != null) - { - weaponPrefab.transform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); - weaponTransform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); - - string[] gunModsToDisable = Traverse.Create(turretView).Field("_gunModsToDisable").Value; - if (gunModsToDisable != null) - { - foreach (Transform child in weaponTransform) - { - if (gunModsToDisable.Contains(child.name)) - { - child.gameObject.SetActive(false); - } - } - } - } - else - { - btrLogger.LogError("AttachBot: WeaponTransform was null!"); - } - } - else - { - btrLogger.LogError("AttachBot: WeaponPrefab was null!"); - } - - if (player.HealthController.IsAlive) - { - player.BodyAnimatorCommon.enabled = false; - if (player.HandsController.FirearmsAnimator != null) - { - player.HandsController.FirearmsAnimator.Animator.enabled = false; - } - - PlayerPoolObject component = player.gameObject.GetComponent(); - foreach (Collider collider in component.Colliders) - { - collider.enabled = false; - } - - List rendererList = new(256); - player.PlayerBody.GetRenderersNonAlloc(rendererList); - if (weaponPrefab != null) - { - rendererList.AddRange(weaponPrefab.Renderers); - } - rendererList.ForEach(renderer => renderer.forceRenderingOff = true); - } - - firearmController = (Player.FirearmController)player.HandsController; - - btrBotShooterInitialized = true; - botShooterId = player.ProfileId; - } - } - else - { - btrLogger.LogError("AttachBot: CoopHandler was null!"); - } - } - - private async Task InitBtr() - { - // Initial setup - await btrController.InitBtrController(); - - botEventHandler = Singleton.Instance; - 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 - - // Initial BTR configuration - btrServerSide = btrController.BtrVehicle; - btrClientSide = btrController.BtrView; - btrServerSide.transform.Find("KillBox").gameObject.AddComponent(); - - // Get config from server and initialise respective settings - ConfigureSettingsFromServer(); - - /*var btrMapConfig = btrController.MapPathsConfiguration; + private bool btrBotShooterInitialized = false; + + private BTRTurretServer btrTurretServer; + private bool isTurretInDefaultRotation; + private BulletClass btrMachineGunAmmo; + private Item btrMachineGunWeapon; + private Player.FirearmController firearmController; + private WeaponSoundPlayer weaponSoundPlayer; + + private MethodInfo _updateTaxiPriceMethod; + + private float originalDamageCoeff; + + private FikaClient client; + public Queue btrPackets = new(60); + private string botShooterId = string.Empty; + private ManualLogSource btrLogger; + + FikaBTRManager_Client() + { + Type btrControllerType = typeof(BTRControllerClass); + _updateTaxiPriceMethod = AccessTools.GetDeclaredMethods(btrControllerType).Single(IsUpdateTaxiPriceMethod); + client = Singleton.Instance; + btrLogger = BepInEx.Logging.Logger.CreateLogSource("BTR Client"); + } + + private async void Awake() + { + try + { + gameWorld = Singleton.Instance; + if (gameWorld == null) + { + Destroy(this); + return; + } + + if (gameWorld.BtrController == null) + { + gameWorld.BtrController = new BTRControllerClass(); + } + + btrController = gameWorld.BtrController; + + await InitBtr(); + } + catch + { + btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); + Destroy(this); + throw; + } + } + + public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPacket) + { + // If the player is going into the BTR, store their damage coefficient + // and set it to 0, so they don't die while inside the BTR + if (interactPacket.InteractionType == EInteractionType.GoIn && player.IsYourPlayer) + { + originalDamageCoeff = player.ActiveHealthController.DamageCoeff; + player.ActiveHealthController.SetDamageCoeff(0f); + + } + // Otherwise restore the damage coefficient + else if (interactPacket.InteractionType == EInteractionType.GoOut && player.IsYourPlayer) + { + player.ActiveHealthController.SetDamageCoeff(originalDamageCoeff); + } + } + + // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` + private bool IsUpdateTaxiPriceMethod(MethodInfo method) + { + return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); + } + + private void Update() + { + if (btrPackets.Count > 0) + { + BTRPacket packet = btrPackets.Dequeue(); + if (packet.HasBotProfileId) + { + AttachBot(packet.BotNetId); + } + if (packet.HasShot) + { + ObservedShot(packet.ShotPosition, packet.ShotDirection); + } + btrDataPacket = packet.BTRDataPacket; + } + + if (!btrBotShooterInitialized) + { + InitBtrBotService(); + btrBotShooterInitialized = true; + } + + btrController.SyncBTRVehicleFromServer(btrDataPacket); + + if (!isTurretInDefaultRotation) + { + btrTurretServer.DisableAiming(); + } + } + + private void ObservedShot(Vector3 position, Vector3 direction) + { + gameWorld.SharedBallisticsCalculator.Shoot(btrMachineGunAmmo, position, direction, botShooterId, btrMachineGunWeapon, 1f, 0); + firearmController.method_54(weaponSoundPlayer, btrMachineGunAmmo, position, direction, false); + } + + public void AttachBot(int netId) + { + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + if (coopHandler.Players.TryGetValue(netId, out CoopPlayer player)) + { + BTRTurretView turretView = btrClientSide.turret; + + player.Transform.Original.position = turretView.BotRoot.position; + player.PlayerBones.Weapon_Root_Anim.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); + + WeaponPrefab weaponPrefab = player.HandsController.ControllerGameObject.GetComponent(); + if (weaponPrefab != null) + { + weaponSoundPlayer = weaponPrefab.GetComponent(); + + Transform weaponTransform = weaponPrefab.Hierarchy.GetTransform(ECharacterWeaponBones.weapon); + if (weaponTransform != null) + { + weaponPrefab.transform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); + weaponTransform.SetPositionAndRotation(turretView.GunRoot.position, turretView.GunRoot.rotation); + + string[] gunModsToDisable = Traverse.Create(turretView).Field("_gunModsToDisable").Value; + if (gunModsToDisable != null) + { + foreach (Transform child in weaponTransform) + { + if (gunModsToDisable.Contains(child.name)) + { + child.gameObject.SetActive(false); + } + } + } + } + else + { + btrLogger.LogError("AttachBot: WeaponTransform was null!"); + } + } + else + { + btrLogger.LogError("AttachBot: WeaponPrefab was null!"); + } + + if (player.HealthController.IsAlive) + { + player.BodyAnimatorCommon.enabled = false; + if (player.HandsController.FirearmsAnimator != null) + { + player.HandsController.FirearmsAnimator.Animator.enabled = false; + } + + PlayerPoolObject component = player.gameObject.GetComponent(); + foreach (Collider collider in component.Colliders) + { + collider.enabled = false; + } + + List rendererList = new(256); + player.PlayerBody.GetRenderersNonAlloc(rendererList); + if (weaponPrefab != null) + { + rendererList.AddRange(weaponPrefab.Renderers); + } + rendererList.ForEach(renderer => renderer.forceRenderingOff = true); + } + + firearmController = (Player.FirearmController)player.HandsController; + + btrBotShooterInitialized = true; + botShooterId = player.ProfileId; + } + } + else + { + btrLogger.LogError("AttachBot: CoopHandler was null!"); + } + } + + private async Task InitBtr() + { + // Initial setup + await btrController.InitBtrController(); + + botEventHandler = Singleton.Instance; + 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 + + // Initial BTR configuration + btrServerSide = btrController.BtrVehicle; + btrClientSide = btrController.BtrView; + btrServerSide.transform.Find("KillBox").gameObject.AddComponent(); + + // Get config from server and initialise respective settings + ConfigureSettingsFromServer(); + + /*var btrMapConfig = btrController.MapPathsConfiguration; if (btrMapConfig == null) { ConsoleScreen.LogError($"{nameof(btrController.MapPathsConfiguration)}"); @@ -262,291 +262,291 @@ private async Task InitBtr() } btrServerSide.CurrentPathConfig = btrMapConfig.PathsConfiguration.pathsConfigurations.RandomElement(); btrServerSide.Initialization(btrMapConfig);*/ - btrController.method_14(); // creates and assigns the BTR a fake stash + btrController.method_14(); // creates and assigns the BTR a fake stash - DisableServerSideObjects(); + DisableServerSideObjects(); - /*gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState;*/ + /*gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState;*/ - /*btrServerSide.MoveEnable();*/ - btrServerSide.IncomingToDestinationEvent += ToDestinationEvent; + /*btrServerSide.MoveEnable();*/ + btrServerSide.IncomingToDestinationEvent += ToDestinationEvent; - // Sync initial position and rotation - UpdateDataPacket(); - btrClientSide.transform.position = btrDataPacket.position; - btrClientSide.transform.rotation = btrDataPacket.rotation; + // Sync initial position and rotation + UpdateDataPacket(); + btrClientSide.transform.position = btrDataPacket.position; + btrClientSide.transform.rotation = btrDataPacket.rotation; - // Initialise turret variables - btrTurretServer = btrServerSide.BTRTurret; - Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); - isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform - && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; - btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); - btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId); + // Initialise turret variables + btrTurretServer = btrServerSide.BTRTurret; + Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); + isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform + && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; + btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); + btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId); - // Pull services data for the BTR from the server - TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); + // Pull services data for the BTR from the server + TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); - btrInitialized = true; - } + btrInitialized = true; + } - private void ConfigureSettingsFromServer() - { - SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); + private void ConfigureSettingsFromServer() + { + SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); - btrServerSide.moveSpeed = serverConfig.MoveSpeed; - btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; - btrServerSide.pauseDurationRange.y = serverConfig.PointWaitTime.Max; - btrServerSide.readyToDeparture = serverConfig.TaxiWaitTime; - } + btrServerSide.moveSpeed = serverConfig.MoveSpeed; + btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; + btrServerSide.pauseDurationRange.y = serverConfig.PointWaitTime.Max; + btrServerSide.readyToDeparture = serverConfig.TaxiWaitTime; + } - private void InitBtrBotService() - { - //btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list - TraderServicesManager.Instance.OnTraderServicePurchased += BtrTraderServicePurchased; - } + private void InitBtrBotService() + { + //btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list + TraderServicesManager.Instance.OnTraderServicePurchased += BtrTraderServicePurchased; + } - /** + /** * BTR has arrived at a destination, re-calculate taxi prices and remove purchased taxi service */ - private void ToDestinationEvent(PathDestination destinationPoint, bool isFirst, bool isFinal, bool isLastRoutePoint) - { - // Remove purchased taxi service - TraderServicesManager.Instance.RemovePurchasedService(ETraderServiceType.PlayerTaxi, BTRUtil.BTRTraderId); - - // Update the prices for the taxi service - _updateTaxiPriceMethod.Invoke(btrController, [destinationPoint, isFinal]); - - // Update the UI - TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); - } - - public void DisplayNetworkNotification(ETraderServiceType serviceType) - { - if (gameWorld.MainPlayer.BtrState != EPlayerBtrState.Inside) - { - return; - } - - int[] playerArray = [gameWorld.MainPlayer.Id]; - GlobalEventHandlerClass.CreateEvent().Invoke(playerArray, serviceType); - } - - private bool IsBtrService(ETraderServiceType serviceType) - { - if (serviceType == ETraderServiceType.BtrItemsDelivery || serviceType == ETraderServiceType.PlayerTaxi || serviceType == ETraderServiceType.BtrBotCover) - { - return true; - } - - return false; - } - - private void BtrTraderServicePurchased(ETraderServiceType serviceType, string subserviceId) - { - if (!IsBtrService(serviceType)) - { - return; - } - - BTRServicePacket packet = new(gameWorld.MainPlayer.ProfileId) - { - TraderServiceType = serviceType - }; - - if (!string.IsNullOrEmpty(subserviceId)) - { - packet.HasSubservice = true; - packet.SubserviceId = subserviceId; - } - - NetDataWriter writer = new(); - writer.Reset(); - client.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - - private BTRDataPacket UpdateDataPacket() - { - btrDataPacket.position = btrServerSide.transform.position; - btrDataPacket.rotation = btrServerSide.transform.rotation; - if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) - { - btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; - btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; - } - btrDataPacket.State = (byte)btrServerSide.BtrState; - btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; - btrDataPacket.LeftSideState = btrServerSide.LeftSideState; - btrDataPacket.LeftSlot0State = btrServerSide.LeftSlot0State; - btrDataPacket.LeftSlot1State = btrServerSide.LeftSlot1State; - btrDataPacket.RightSideState = btrServerSide.RightSideState; - btrDataPacket.RightSlot0State = btrServerSide.RightSlot0State; - btrDataPacket.RightSlot1State = btrServerSide.RightSlot1State; - btrDataPacket.currentSpeed = btrServerSide.currentSpeed; - btrDataPacket.timeToEndPause = btrServerSide.timeToEndPause; - btrDataPacket.moveDirection = (byte)btrServerSide.VehicleMoveDirection; - btrDataPacket.MoveSpeed = btrServerSide.moveSpeed; - if (btrController != null && btrController.BotShooterBtr != null) - { - btrDataPacket.BtrBotId = btrController.BotShooterBtr.Id; - } - - return btrDataPacket; - } - - private void DisableServerSideObjects() - { - 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. - // We also need to change the client collider's layer to HighPolyCollider due to unknown collisions that occur - // when going down a steep slope. - - // Add collision debugger component to log collisions in the EFT Console - Collider[] clientColliders = btrClientSide.GetComponentsInChildren(true); - //foreach (var collider in clientColliders) - //{ - // collider.gameObject.AddComponent(); - //} - - Collider[] serverColliders = btrServerSide.GetComponentsInChildren(true); - //foreach (var collider in serverColliders) - //{ - // collider.gameObject.AddComponent(); - //} - - Collider clientRootCollider = clientColliders.First(x => x.gameObject.name == "Root"); - - // Retrieve all TerrainColliders - List terrainColliders = new(); - - foreach (GPUInstancerManager manager in GPUInstancerManager.activeManagerList) - { - if (manager.GetType() != typeof(GPUInstancerDetailManager)) - { - continue; - } - - GPUInstancerDetailManager detailManager = (GPUInstancerDetailManager)manager; - if (detailManager.terrain == null) - { - continue; - } - - terrainColliders.Add(detailManager.terrain.GetComponent()); - } - - // Make the Root collider ignore collisions with TerrainColliders - foreach (TerrainCollider collider in terrainColliders) - { - Physics.IgnoreCollision(clientRootCollider, collider); - } - - // Retrieve all wheel colliders on the serverside BTR - const string wheelColliderParentName = "BTR_82_wheel"; - const string wheelColliderName = "Cylinder"; - - Collider[] serverWheelColliders = serverColliders - .Where(x => x.transform.parent.name.StartsWith(wheelColliderParentName) && x.gameObject.name.StartsWith(wheelColliderName)) - .ToArray(); - - // Make the Root collider ignore collisions with the serverside BTR wheels - foreach (Collider collider in serverWheelColliders) - { - Physics.IgnoreCollision(clientRootCollider, collider); - } - - // Enable clientside BTR collider and disable serverside BTR collider - const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; - Collider serverExteriorCollider = serverColliders - .First(x => x.gameObject.name == exteriorColliderName); - Collider clientExteriorCollider = clientColliders - .First(x => x.gameObject.name == exteriorColliderName); - - serverExteriorCollider.gameObject.SetActive(false); - clientExteriorCollider.gameObject.SetActive(true); - clientExteriorCollider.gameObject.layer = LayerMask.NameToLayer("HighPolyCollider"); - } - - public void ClientInteraction(Player player, PlayerInteractPacket packet) - { - BTRView btrView = gameWorld.BtrController.BtrView; - if (btrView == null) - { - btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); - return; - } - - if (player.IsYourPlayer) - { - btrView.Interaction(player, packet); - } - else - { - ObservedBTRInteraction(player, packet); - } - OnPlayerInteractDoor(player, packet); - } - - private void ObservedBTRInteraction(Player player, PlayerInteractPacket packet) - { - BTRSide side = btrClientSide.method_9(packet.SideId); - - if (packet.InteractionType == EInteractionType.GoIn) - { - player.BtrState = EPlayerBtrState.Approach; - btrClientSide.method_18(player); - player.BtrState = EPlayerBtrState.GoIn; - //side.AddPassenger(player, packet.SlotId); - player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(true); - player.MovementContext.PlayerAnimator.SetBtrGoIn(packet.Fast); - player.BtrState = EPlayerBtrState.Inside; - } - else if (packet.InteractionType == EInteractionType.GoOut) - { - player.BtrState = EPlayerBtrState.GoOut; - player.MovementContext.PlayerAnimator.SetBtrGoOut(packet.Fast); - player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(false); - (Vector3 start, Vector3 target) points = side.GoOutPoints(); - side.ApplyPlayerRotation(player.MovementContext, points.start, points.target + Vector3.up * 1.9f); - player.BtrState = EPlayerBtrState.Outside; - //side.RemovePassenger(player); - btrClientSide.method_19(player); - } - } - - private void OnDestroy() - { - if (gameWorld == null) - { - return; - } - - if (TraderServicesManager.Instance != null) - { - TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased; - } - - if (btrClientSide != null) - { - Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); - Destroy(btrClientSide.gameObject); - } - - if (btrServerSide != null) - { - Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); - Destroy(btrServerSide.gameObject); - } - } - } + private void ToDestinationEvent(PathDestination destinationPoint, bool isFirst, bool isFinal, bool isLastRoutePoint) + { + // Remove purchased taxi service + TraderServicesManager.Instance.RemovePurchasedService(ETraderServiceType.PlayerTaxi, BTRUtil.BTRTraderId); + + // Update the prices for the taxi service + _updateTaxiPriceMethod.Invoke(btrController, [destinationPoint, isFinal]); + + // Update the UI + TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); + } + + public void DisplayNetworkNotification(ETraderServiceType serviceType) + { + if (gameWorld.MainPlayer.BtrState != EPlayerBtrState.Inside) + { + return; + } + + int[] playerArray = [gameWorld.MainPlayer.Id]; + GlobalEventHandlerClass.CreateEvent().Invoke(playerArray, serviceType); + } + + private bool IsBtrService(ETraderServiceType serviceType) + { + if (serviceType == ETraderServiceType.BtrItemsDelivery || serviceType == ETraderServiceType.PlayerTaxi || serviceType == ETraderServiceType.BtrBotCover) + { + return true; + } + + return false; + } + + private void BtrTraderServicePurchased(ETraderServiceType serviceType, string subserviceId) + { + if (!IsBtrService(serviceType)) + { + return; + } + + BTRServicePacket packet = new(gameWorld.MainPlayer.ProfileId) + { + TraderServiceType = serviceType + }; + + if (!string.IsNullOrEmpty(subserviceId)) + { + packet.HasSubservice = true; + packet.SubserviceId = subserviceId; + } + + NetDataWriter writer = new(); + writer.Reset(); + client.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + + private BTRDataPacket UpdateDataPacket() + { + btrDataPacket.position = btrServerSide.transform.position; + btrDataPacket.rotation = btrServerSide.transform.rotation; + if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) + { + btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; + btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; + } + btrDataPacket.State = (byte)btrServerSide.BtrState; + btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; + btrDataPacket.LeftSideState = btrServerSide.LeftSideState; + btrDataPacket.LeftSlot0State = btrServerSide.LeftSlot0State; + btrDataPacket.LeftSlot1State = btrServerSide.LeftSlot1State; + btrDataPacket.RightSideState = btrServerSide.RightSideState; + btrDataPacket.RightSlot0State = btrServerSide.RightSlot0State; + btrDataPacket.RightSlot1State = btrServerSide.RightSlot1State; + btrDataPacket.currentSpeed = btrServerSide.currentSpeed; + btrDataPacket.timeToEndPause = btrServerSide.timeToEndPause; + btrDataPacket.moveDirection = (byte)btrServerSide.VehicleMoveDirection; + btrDataPacket.MoveSpeed = btrServerSide.moveSpeed; + if (btrController != null && btrController.BotShooterBtr != null) + { + btrDataPacket.BtrBotId = btrController.BotShooterBtr.Id; + } + + return btrDataPacket; + } + + private void DisableServerSideObjects() + { + 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. + // We also need to change the client collider's layer to HighPolyCollider due to unknown collisions that occur + // when going down a steep slope. + + // Add collision debugger component to log collisions in the EFT Console + Collider[] clientColliders = btrClientSide.GetComponentsInChildren(true); + //foreach (var collider in clientColliders) + //{ + // collider.gameObject.AddComponent(); + //} + + Collider[] serverColliders = btrServerSide.GetComponentsInChildren(true); + //foreach (var collider in serverColliders) + //{ + // collider.gameObject.AddComponent(); + //} + + Collider clientRootCollider = clientColliders.First(x => x.gameObject.name == "Root"); + + // Retrieve all TerrainColliders + List terrainColliders = new(); + + foreach (GPUInstancerManager manager in GPUInstancerManager.activeManagerList) + { + if (manager.GetType() != typeof(GPUInstancerDetailManager)) + { + continue; + } + + GPUInstancerDetailManager detailManager = (GPUInstancerDetailManager)manager; + if (detailManager.terrain == null) + { + continue; + } + + terrainColliders.Add(detailManager.terrain.GetComponent()); + } + + // Make the Root collider ignore collisions with TerrainColliders + foreach (TerrainCollider collider in terrainColliders) + { + Physics.IgnoreCollision(clientRootCollider, collider); + } + + // Retrieve all wheel colliders on the serverside BTR + const string wheelColliderParentName = "BTR_82_wheel"; + const string wheelColliderName = "Cylinder"; + + Collider[] serverWheelColliders = serverColliders + .Where(x => x.transform.parent.name.StartsWith(wheelColliderParentName) && x.gameObject.name.StartsWith(wheelColliderName)) + .ToArray(); + + // Make the Root collider ignore collisions with the serverside BTR wheels + foreach (Collider collider in serverWheelColliders) + { + Physics.IgnoreCollision(clientRootCollider, collider); + } + + // Enable clientside BTR collider and disable serverside BTR collider + const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; + Collider serverExteriorCollider = serverColliders + .First(x => x.gameObject.name == exteriorColliderName); + Collider clientExteriorCollider = clientColliders + .First(x => x.gameObject.name == exteriorColliderName); + + serverExteriorCollider.gameObject.SetActive(false); + clientExteriorCollider.gameObject.SetActive(true); + clientExteriorCollider.gameObject.layer = LayerMask.NameToLayer("HighPolyCollider"); + } + + public void ClientInteraction(Player player, PlayerInteractPacket packet) + { + BTRView btrView = gameWorld.BtrController.BtrView; + if (btrView == null) + { + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); + return; + } + + if (player.IsYourPlayer) + { + btrView.Interaction(player, packet); + } + else + { + ObservedBTRInteraction(player, packet); + } + OnPlayerInteractDoor(player, packet); + } + + private void ObservedBTRInteraction(Player player, PlayerInteractPacket packet) + { + BTRSide side = btrClientSide.method_9(packet.SideId); + + if (packet.InteractionType == EInteractionType.GoIn) + { + player.BtrState = EPlayerBtrState.Approach; + btrClientSide.method_18(player); + player.BtrState = EPlayerBtrState.GoIn; + //side.AddPassenger(player, packet.SlotId); + player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(true); + player.MovementContext.PlayerAnimator.SetBtrGoIn(packet.Fast); + player.BtrState = EPlayerBtrState.Inside; + } + else if (packet.InteractionType == EInteractionType.GoOut) + { + player.BtrState = EPlayerBtrState.GoOut; + player.MovementContext.PlayerAnimator.SetBtrGoOut(packet.Fast); + player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(false); + (Vector3 start, Vector3 target) points = side.GoOutPoints(); + side.ApplyPlayerRotation(player.MovementContext, points.start, points.target + Vector3.up * 1.9f); + player.BtrState = EPlayerBtrState.Outside; + //side.RemovePassenger(player); + btrClientSide.method_19(player); + } + } + + private void OnDestroy() + { + if (gameWorld == null) + { + return; + } + + if (TraderServicesManager.Instance != null) + { + TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased; + } + + if (btrClientSide != null) + { + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); + Destroy(btrClientSide.gameObject); + } + + if (btrServerSide != null) + { + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); + Destroy(btrServerSide.gameObject); + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs b/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs index 0cc810f4..12ed7b65 100644 --- a/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs +++ b/Fika.Core/Coop/BTR/FikaBTRManager_Host.cs @@ -26,810 +26,810 @@ namespace Fika.Core.Coop.BTR { - /// - /// Based on - /// - internal class FikaBTRManager_Host : MonoBehaviour - { - private GameWorld gameWorld; - private BotEventHandler botEventHandler; - - private BotBTRService btrBotService; - private BTRControllerClass btrController; - private BTRVehicle btrServerSide; - private BTRView btrClientSide; - private BotOwner btrBotShooter; - private BTRDataPacket btrDataPacket = default; + /// + /// Based on + /// + internal class FikaBTRManager_Host : MonoBehaviour + { + private GameWorld gameWorld; + private BotEventHandler botEventHandler; + + private BotBTRService btrBotService; + private BTRControllerClass btrController; + private BTRVehicle btrServerSide; + private BTRView btrClientSide; + private BotOwner btrBotShooter; + private BTRDataPacket btrDataPacket = default; #pragma warning disable CS0414 // The field 'FikaBTRManager_Host.btrInitialized' is assigned but its value is never used - private bool btrInitialized = false; + private bool btrInitialized = false; #pragma warning restore CS0414 // The field 'FikaBTRManager_Host.btrInitialized' is assigned but its value is never used - private bool btrBotShooterInitialized = false; - - private float coverFireTime = 90f; - private Coroutine _coverFireTimerCoroutine; - - private BTRSide lastInteractedBtrSide; - public BTRSide LastInteractedBtrSide => lastInteractedBtrSide; - - private Coroutine _shootingTargetCoroutine; - private BTRTurretServer btrTurretServer; - private bool isTurretInDefaultRotation; - private EnemyInfo currentTarget = null; - private bool isShooting = false; - private float machineGunAimDelay = 0.4f; - private Vector2 machineGunBurstCount; - private Vector2 machineGunRecoveryTime; - private BulletClass btrMachineGunAmmo; - private Item btrMachineGunWeapon; - private Player.FirearmController firearmController; - private WeaponSoundPlayer weaponSoundPlayer; - - private MethodInfo _updateTaxiPriceMethod; - - private float originalDamageCoeff; - - private FikaServer server; - private NetDataWriter writer = new(); - Queue> shotQueue = new(20); - private Player lastInteractPlayer = null; - private ManualLogSource btrLogger; - - FikaBTRManager_Host() - { - Type btrControllerType = typeof(BTRControllerClass); - _updateTaxiPriceMethod = AccessTools.GetDeclaredMethods(btrControllerType).Single(IsUpdateTaxiPriceMethod); - server = Singleton.Instance; - btrLogger = BepInEx.Logging.Logger.CreateLogSource("BTR Host"); - } - - public bool CanPlayerEnter(IPlayer player) - { - if (btrBotShooter.BotsGroup.Enemies.ContainsKey(player)) - { - return false; - } - else - { - return true; - } - } - - private async void Awake() - { - try - { - gameWorld = Singleton.Instance; - if (gameWorld == null) - { - Destroy(this); - return; - } - - if (gameWorld.BtrController == null) - { - gameWorld.BtrController = new BTRControllerClass(); - } - - btrController = gameWorld.BtrController; - - await InitBtr(); - } - catch - { - btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); - Destroy(this); - throw; - } - } - - private IEnumerator SendBotProfileId() - { - while (!Singleton.Instantiated) - { - yield return null; - } - - while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) - { - yield return null; - } - - CoopGame coopGame = (CoopGame)Singleton.Instance; - - while (coopGame.Status != GameStatus.Started && btrController.BotShooterBtr == null) - { - yield return null; - } - - yield return new WaitForSeconds(20); - - BTRPacket packet = new() - { - BTRDataPacket = btrDataPacket, - HasBotProfileId = true, - BotNetId = ((CoopPlayer)btrBotShooter.GetPlayer).NetId - }; - - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPacket) - { - bool playerGoIn = interactPacket.InteractionType == EInteractionType.GoIn; - bool playerGoOut = interactPacket.InteractionType == EInteractionType.GoOut; - - lastInteractPlayer = player; - player.BtrInteractionSide = btrClientSide.method_9(interactPacket.SideId); - lastInteractedBtrSide = player.BtrInteractionSide; - - if (!player.IsYourPlayer) - { - HandleBtrDoorState(player.BtrState); - } - - if (interactPacket.SideId == 0 && playerGoIn) - { - if (interactPacket.SlotId == 0) - { - btrServerSide.LeftSlot0State = 1; - } - else if (interactPacket.SlotId == 1) - { - btrServerSide.LeftSlot1State = 1; - } - } - else if (interactPacket.SideId == 0 && playerGoOut) - { - if (interactPacket.SlotId == 0) - { - btrServerSide.LeftSlot0State = 0; - } - else if (interactPacket.SlotId == 1) - { - btrServerSide.LeftSlot1State = 0; - } - } - else if (interactPacket.SideId == 1 && playerGoIn) - { - if (interactPacket.SlotId == 0) - { - btrServerSide.RightSlot0State = 1; - } - else if (interactPacket.SlotId == 1) - { - btrServerSide.RightSlot1State = 1; - } - } - else if (interactPacket.SideId == 1 && playerGoOut) - { - if (interactPacket.SlotId == 0) - { - btrServerSide.RightSlot0State = 0; - } - else if (interactPacket.SlotId == 1) - { - btrServerSide.RightSlot1State = 0; - } - } - - // If the player is going into the BTR, store their damage coefficient - // and set it to 0, so they don't die while inside the BTR - if (interactPacket.InteractionType == EInteractionType.GoIn && player.IsYourPlayer) - { - originalDamageCoeff = player.ActiveHealthController.DamageCoeff; - player.ActiveHealthController.SetDamageCoeff(0f); - - } - // Otherwise restore the damage coefficient - else if (interactPacket.InteractionType == EInteractionType.GoOut && player.IsYourPlayer) - { - player.ActiveHealthController.SetDamageCoeff(originalDamageCoeff); - } - } - - // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` - private bool IsUpdateTaxiPriceMethod(MethodInfo method) - { - return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); - } - - private void Update() - { - btrController.SyncBTRVehicleFromServer(UpdateDataPacket()); - - if (btrController.BotShooterBtr == null) return; - - // BotShooterBtr doesn't get assigned to BtrController immediately so we check this in Update - if (!btrBotShooterInitialized) - { - InitBtrBotService(); - btrBotShooterInitialized = true; - } - - UpdateTarget(); - - if (HasTarget()) - { - SetAim(); - - if (!isShooting && CanShoot()) - { - StartShooting(); - } - } - else if (!isTurretInDefaultRotation) - { - btrTurretServer.DisableAiming(); - } - } - - private async Task InitBtr() - { - // Initial setup - await btrController.InitBtrController(); - - botEventHandler = Singleton.Instance; - 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 - - // Initial BTR configuration - btrServerSide = btrController.BtrVehicle; - btrClientSide = btrController.BtrView; - btrServerSide.transform.Find("KillBox").gameObject.AddComponent(); - - // Get config from server and initialise respective settings - ConfigureSettingsFromServer(); - - MapPathConfig btrMapConfig = btrController.MapPathsConfiguration; - if (btrMapConfig == null) - { - ConsoleScreen.LogError($"{nameof(btrController.MapPathsConfiguration)}"); - return; - } - btrServerSide.CurrentPathConfig = btrMapConfig.PathsConfiguration.pathsConfigurations.RandomElement(); - btrServerSide.Initialization(btrMapConfig); - btrController.method_14(); // creates and assigns the BTR a fake stash - - DisableServerSideObjects(); - - gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState; - - btrServerSide.MoveEnable(); - btrServerSide.IncomingToDestinationEvent += ToDestinationEvent; - - // Sync initial position and rotation - UpdateDataPacket(); - btrClientSide.transform.position = btrDataPacket.position; - btrClientSide.transform.rotation = btrDataPacket.rotation; - - // Initialise turret variables - btrTurretServer = btrServerSide.BTRTurret; - Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); - isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform - && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; - btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); - btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId); - - // Pull services data for the BTR from the server - TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); - - btrInitialized = true; - StartCoroutine(SendBotProfileId()); - } - - - private void ConfigureSettingsFromServer() - { - SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); - - btrServerSide.moveSpeed = serverConfig.MoveSpeed; - btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; - btrServerSide.pauseDurationRange.y = serverConfig.PointWaitTime.Max; - btrServerSide.readyToDeparture = serverConfig.TaxiWaitTime; - coverFireTime = serverConfig.CoverFireTime; - machineGunAimDelay = serverConfig.MachineGunAimDelay; - machineGunBurstCount = new Vector2(serverConfig.MachineGunBurstCount.Min, serverConfig.MachineGunBurstCount.Max); - machineGunRecoveryTime = new Vector2(serverConfig.MachineGunRecoveryTime.Min, serverConfig.MachineGunRecoveryTime.Max); - } - - private void InitBtrBotService() - { - btrBotShooter = btrController.BotShooterBtr; - btrBotShooter.GetPlayer.GetComponent().detectCollisions = false; // disable rigidbody collisions with BTR bot - firearmController = btrBotShooter.GetComponent(); - 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 - TraderServicesManager.Instance.OnTraderServicePurchased += BtrTraderServicePurchased; - } - - /** + private bool btrBotShooterInitialized = false; + + private float coverFireTime = 90f; + private Coroutine _coverFireTimerCoroutine; + + private BTRSide lastInteractedBtrSide; + public BTRSide LastInteractedBtrSide => lastInteractedBtrSide; + + private Coroutine _shootingTargetCoroutine; + private BTRTurretServer btrTurretServer; + private bool isTurretInDefaultRotation; + private EnemyInfo currentTarget = null; + private bool isShooting = false; + private float machineGunAimDelay = 0.4f; + private Vector2 machineGunBurstCount; + private Vector2 machineGunRecoveryTime; + private BulletClass btrMachineGunAmmo; + private Item btrMachineGunWeapon; + private Player.FirearmController firearmController; + private WeaponSoundPlayer weaponSoundPlayer; + + private MethodInfo _updateTaxiPriceMethod; + + private float originalDamageCoeff; + + private FikaServer server; + private NetDataWriter writer = new(); + Queue> shotQueue = new(20); + private Player lastInteractPlayer = null; + private ManualLogSource btrLogger; + + FikaBTRManager_Host() + { + Type btrControllerType = typeof(BTRControllerClass); + _updateTaxiPriceMethod = AccessTools.GetDeclaredMethods(btrControllerType).Single(IsUpdateTaxiPriceMethod); + server = Singleton.Instance; + btrLogger = BepInEx.Logging.Logger.CreateLogSource("BTR Host"); + } + + public bool CanPlayerEnter(IPlayer player) + { + if (btrBotShooter.BotsGroup.Enemies.ContainsKey(player)) + { + return false; + } + else + { + return true; + } + } + + private async void Awake() + { + try + { + gameWorld = Singleton.Instance; + if (gameWorld == null) + { + Destroy(this); + return; + } + + if (gameWorld.BtrController == null) + { + gameWorld.BtrController = new BTRControllerClass(); + } + + btrController = gameWorld.BtrController; + + await InitBtr(); + } + catch + { + btrLogger.LogError("[SPT-BTR] Unable to spawn BTR. Check logs."); + Destroy(this); + throw; + } + } + + private IEnumerator SendBotProfileId() + { + while (!Singleton.Instantiated) + { + yield return null; + } + + while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) + { + yield return null; + } + + CoopGame coopGame = (CoopGame)Singleton.Instance; + + while (coopGame.Status != GameStatus.Started && btrController.BotShooterBtr == null) + { + yield return null; + } + + yield return new WaitForSeconds(20); + + BTRPacket packet = new() + { + BTRDataPacket = btrDataPacket, + HasBotProfileId = true, + BotNetId = ((CoopPlayer)btrBotShooter.GetPlayer).NetId + }; + + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + public void OnPlayerInteractDoor(Player player, PlayerInteractPacket interactPacket) + { + bool playerGoIn = interactPacket.InteractionType == EInteractionType.GoIn; + bool playerGoOut = interactPacket.InteractionType == EInteractionType.GoOut; + + lastInteractPlayer = player; + player.BtrInteractionSide = btrClientSide.method_9(interactPacket.SideId); + lastInteractedBtrSide = player.BtrInteractionSide; + + if (!player.IsYourPlayer) + { + HandleBtrDoorState(player.BtrState); + } + + if (interactPacket.SideId == 0 && playerGoIn) + { + if (interactPacket.SlotId == 0) + { + btrServerSide.LeftSlot0State = 1; + } + else if (interactPacket.SlotId == 1) + { + btrServerSide.LeftSlot1State = 1; + } + } + else if (interactPacket.SideId == 0 && playerGoOut) + { + if (interactPacket.SlotId == 0) + { + btrServerSide.LeftSlot0State = 0; + } + else if (interactPacket.SlotId == 1) + { + btrServerSide.LeftSlot1State = 0; + } + } + else if (interactPacket.SideId == 1 && playerGoIn) + { + if (interactPacket.SlotId == 0) + { + btrServerSide.RightSlot0State = 1; + } + else if (interactPacket.SlotId == 1) + { + btrServerSide.RightSlot1State = 1; + } + } + else if (interactPacket.SideId == 1 && playerGoOut) + { + if (interactPacket.SlotId == 0) + { + btrServerSide.RightSlot0State = 0; + } + else if (interactPacket.SlotId == 1) + { + btrServerSide.RightSlot1State = 0; + } + } + + // If the player is going into the BTR, store their damage coefficient + // and set it to 0, so they don't die while inside the BTR + if (interactPacket.InteractionType == EInteractionType.GoIn && player.IsYourPlayer) + { + originalDamageCoeff = player.ActiveHealthController.DamageCoeff; + player.ActiveHealthController.SetDamageCoeff(0f); + + } + // Otherwise restore the damage coefficient + else if (interactPacket.InteractionType == EInteractionType.GoOut && player.IsYourPlayer) + { + player.ActiveHealthController.SetDamageCoeff(originalDamageCoeff); + } + } + + // Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)` + private bool IsUpdateTaxiPriceMethod(MethodInfo method) + { + return method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(PathDestination); + } + + private void Update() + { + btrController.SyncBTRVehicleFromServer(UpdateDataPacket()); + + if (btrController.BotShooterBtr == null) return; + + // BotShooterBtr doesn't get assigned to BtrController immediately so we check this in Update + if (!btrBotShooterInitialized) + { + InitBtrBotService(); + btrBotShooterInitialized = true; + } + + UpdateTarget(); + + if (HasTarget()) + { + SetAim(); + + if (!isShooting && CanShoot()) + { + StartShooting(); + } + } + else if (!isTurretInDefaultRotation) + { + btrTurretServer.DisableAiming(); + } + } + + private async Task InitBtr() + { + // Initial setup + await btrController.InitBtrController(); + + botEventHandler = Singleton.Instance; + 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 + + // Initial BTR configuration + btrServerSide = btrController.BtrVehicle; + btrClientSide = btrController.BtrView; + btrServerSide.transform.Find("KillBox").gameObject.AddComponent(); + + // Get config from server and initialise respective settings + ConfigureSettingsFromServer(); + + MapPathConfig btrMapConfig = btrController.MapPathsConfiguration; + if (btrMapConfig == null) + { + ConsoleScreen.LogError($"{nameof(btrController.MapPathsConfiguration)}"); + return; + } + btrServerSide.CurrentPathConfig = btrMapConfig.PathsConfiguration.pathsConfigurations.RandomElement(); + btrServerSide.Initialization(btrMapConfig); + btrController.method_14(); // creates and assigns the BTR a fake stash + + DisableServerSideObjects(); + + gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState; + + btrServerSide.MoveEnable(); + btrServerSide.IncomingToDestinationEvent += ToDestinationEvent; + + // Sync initial position and rotation + UpdateDataPacket(); + btrClientSide.transform.position = btrDataPacket.position; + btrClientSide.transform.rotation = btrDataPacket.rotation; + + // Initialise turret variables + btrTurretServer = btrServerSide.BTRTurret; + Transform btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer); + isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform + && btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition; + btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId); + btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId); + + // Pull services data for the BTR from the server + TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); + + btrInitialized = true; + StartCoroutine(SendBotProfileId()); + } + + + private void ConfigureSettingsFromServer() + { + SPT.Custom.BTR.Models.BTRConfigModel serverConfig = BTRUtil.GetConfigFromServer(); + + btrServerSide.moveSpeed = serverConfig.MoveSpeed; + btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min; + btrServerSide.pauseDurationRange.y = serverConfig.PointWaitTime.Max; + btrServerSide.readyToDeparture = serverConfig.TaxiWaitTime; + coverFireTime = serverConfig.CoverFireTime; + machineGunAimDelay = serverConfig.MachineGunAimDelay; + machineGunBurstCount = new Vector2(serverConfig.MachineGunBurstCount.Min, serverConfig.MachineGunBurstCount.Max); + machineGunRecoveryTime = new Vector2(serverConfig.MachineGunRecoveryTime.Min, serverConfig.MachineGunRecoveryTime.Max); + } + + private void InitBtrBotService() + { + btrBotShooter = btrController.BotShooterBtr; + btrBotShooter.GetPlayer.GetComponent().detectCollisions = false; // disable rigidbody collisions with BTR bot + firearmController = btrBotShooter.GetComponent(); + 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 + TraderServicesManager.Instance.OnTraderServicePurchased += BtrTraderServicePurchased; + } + + /** * BTR has arrived at a destination, re-calculate taxi prices and remove purchased taxi service */ - private void ToDestinationEvent(PathDestination destinationPoint, bool isFirst, bool isFinal, bool isLastRoutePoint) - { - // Remove purchased taxi service - TraderServicesManager.Instance.RemovePurchasedService(ETraderServiceType.PlayerTaxi, BTRUtil.BTRTraderId); - - // Update the prices for the taxi service - _updateTaxiPriceMethod.Invoke(btrController, [destinationPoint, isFinal]); - - // Update the UI - TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); - } - - private bool IsBtrService(ETraderServiceType serviceType) - { - if (serviceType == ETraderServiceType.BtrItemsDelivery || serviceType == ETraderServiceType.PlayerTaxi || serviceType == ETraderServiceType.BtrBotCover) - { - return true; - } - - return false; - } - - private void BtrTraderServicePurchased(ETraderServiceType serviceType, string subserviceId) - { - if (!IsBtrService(serviceType)) - { - return; - } - - List passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList(); - List playersToNotify = passengers.Select(x => x.Id).ToList(); - btrController.method_6(playersToNotify, serviceType); // notify BTR passengers that a service has been purchased - - switch (serviceType) - { - case ETraderServiceType.BtrBotCover: - botEventHandler.ApplyTraderServiceBtrSupport(passengers); - StartCoverFireTimer(coverFireTime); - break; - case ETraderServiceType.PlayerTaxi: - btrController.BtrVehicle.IsPaid = true; - btrController.BtrVehicle.MoveToDestination(subserviceId); - break; - } - - GenericPacket responsePacket = new(EPackageType.TraderServiceNotification) - { - NetId = ((CoopPlayer)gameWorld.MainPlayer).NetId, - TraderServiceType = serviceType - }; - - NetDataWriter writer = new(); - writer.Reset(); - server.SendDataToAll(writer, ref responsePacket, DeliveryMethod.ReliableUnordered); - } - - public void NetworkBtrTraderServicePurchased(BTRServicePacket packet) - { - if (!IsBtrService(packet.TraderServiceType)) - { - return; - } - - List passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList(); - List playersToNotify = passengers.Select(x => x.Id).ToList(); - btrController.method_6(playersToNotify, packet.TraderServiceType); // notify BTR passengers that a service has been purchased - - switch (packet.TraderServiceType) - { - case ETraderServiceType.BtrBotCover: - botEventHandler.ApplyTraderServiceBtrSupport(passengers); - StartCoverFireTimer(coverFireTime); - break; - case ETraderServiceType.PlayerTaxi: - btrController.BtrVehicle.IsPaid = true; - btrController.BtrVehicle.MoveToDestination(packet.SubserviceId); - break; - } - - GenericPacket responsePacket = new(EPackageType.TraderServiceNotification) - { - NetId = ((CoopPlayer)gameWorld.MainPlayer).NetId, - TraderServiceType = packet.TraderServiceType - }; - - NetDataWriter writer = new(); - writer.Reset(); - server.SendDataToAll(writer, ref responsePacket, DeliveryMethod.ReliableUnordered); - } - - private void StartCoverFireTimer(float time) - { - _coverFireTimerCoroutine = StaticManager.BeginCoroutine(CoverFireTimer(time)); - } - - private IEnumerator CoverFireTimer(float time) - { - yield return new WaitForSeconds(time); - botEventHandler.StopTraderServiceBtrSupport(); - } - - private void HandleBtrDoorState(EPlayerBtrState playerBtrState) - { - if (playerBtrState == EPlayerBtrState.GoIn || playerBtrState == EPlayerBtrState.GoOut) - { - // Open Door - UpdateBTRSideDoorState(1); - } - else if (playerBtrState == EPlayerBtrState.Inside || playerBtrState == EPlayerBtrState.Outside) - { - // Close Door - UpdateBTRSideDoorState(0); - } - } - - private void UpdateBTRSideDoorState(byte state) - { - try - { - BTRSide btrSide = lastInteractPlayer.BtrInteractionSide != null ? lastInteractPlayer.BtrInteractionSide : lastInteractedBtrSide; - byte sideId = btrClientSide.GetSideId(btrSide); - switch (sideId) - { - case 0: - btrServerSide.LeftSideState = state; - break; - case 1: - btrServerSide.RightSideState = state; - break; - } - } - catch - { - btrLogger.LogError("[SPT-BTR] lastInteractedBtrSide is null when it shouldn't be. Check logs."); - throw; - } - } - - private BTRDataPacket UpdateDataPacket() - { - btrDataPacket.position = btrServerSide.transform.position; - btrDataPacket.rotation = btrServerSide.transform.rotation; - if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) - { - btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; - btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; - } - btrDataPacket.State = (byte)btrServerSide.BtrState; - btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; - btrDataPacket.LeftSideState = btrServerSide.LeftSideState; - btrDataPacket.LeftSlot0State = btrServerSide.LeftSlot0State; - btrDataPacket.LeftSlot1State = btrServerSide.LeftSlot1State; - btrDataPacket.RightSideState = btrServerSide.RightSideState; - btrDataPacket.RightSlot0State = btrServerSide.RightSlot0State; - btrDataPacket.RightSlot1State = btrServerSide.RightSlot1State; - btrDataPacket.currentSpeed = btrServerSide.currentSpeed; - btrDataPacket.timeToEndPause = btrServerSide.timeToEndPause; - btrDataPacket.moveDirection = (byte)btrServerSide.VehicleMoveDirection; - btrDataPacket.MoveSpeed = btrServerSide.moveSpeed; - if (btrController != null && btrController.BotShooterBtr != null) - { - btrDataPacket.BtrBotId = btrController.BotShooterBtr.Id; - } - - BTRPacket packet = new() - { - BTRDataPacket = btrDataPacket, - }; - - if (shotQueue.Count > 0) - { - packet.HasShot = true; - KeyValuePair shotInfo = shotQueue.Dequeue(); - packet.ShotPosition = shotInfo.Key; - packet.ShotDirection = shotInfo.Value; - } - - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.Unreliable); - - return btrDataPacket; - } - - private void DisableServerSideObjects() - { - 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. - // We also need to change the client collider's layer to HighPolyCollider due to unknown collisions that occur - // when going down a steep slope. - - // Add collision debugger component to log collisions in the EFT Console - Collider[] clientColliders = btrClientSide.GetComponentsInChildren(true); - //foreach (var collider in clientColliders) - //{ - // collider.gameObject.AddComponent(); - //} - - Collider[] serverColliders = btrServerSide.GetComponentsInChildren(true); - //foreach (var collider in serverColliders) - //{ - // collider.gameObject.AddComponent(); - //} - - Collider clientRootCollider = clientColliders.First(x => x.gameObject.name == "Root"); - - // Retrieve all TerrainColliders - List terrainColliders = new(); - - foreach (GPUInstancerManager manager in GPUInstancerManager.activeManagerList) - { - if (manager.GetType() != typeof(GPUInstancerDetailManager)) - { - continue; - } - - GPUInstancerDetailManager detailManager = (GPUInstancerDetailManager)manager; - if (detailManager.terrain == null) - { - continue; - } - - terrainColliders.Add(detailManager.terrain.GetComponent()); - } - - // Make the Root collider ignore collisions with TerrainColliders - foreach (Collider collider in terrainColliders) - { - Physics.IgnoreCollision(clientRootCollider, collider); - } - - // Retrieve all wheel colliders on the serverside BTR - const string wheelColliderParentName = "BTR_82_wheel"; - const string wheelColliderName = "Cylinder"; - - Collider[] serverWheelColliders = serverColliders - .Where(x => x.transform.parent.name.StartsWith(wheelColliderParentName) && x.gameObject.name.StartsWith(wheelColliderName)) - .ToArray(); - - // Make the Root collider ignore collisions with the serverside BTR wheels - foreach (Collider collider in serverWheelColliders) - { - Physics.IgnoreCollision(clientRootCollider, collider); - } - - // Enable clientside BTR collider and disable serverside BTR collider - const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; - Collider serverExteriorCollider = serverColliders - .First(x => x.gameObject.name == exteriorColliderName); - Collider clientExteriorCollider = clientColliders - .First(x => x.gameObject.name == exteriorColliderName); - - serverExteriorCollider.gameObject.SetActive(false); - clientExteriorCollider.gameObject.SetActive(true); - clientExteriorCollider.gameObject.layer = LayerMask.NameToLayer("HighPolyCollider"); - } - - private void UpdateTarget() - { - currentTarget = btrBotShooter.Memory.GoalEnemy; - } - - private bool HasTarget() - { - if (currentTarget != null) - { - return true; - } - - return false; - } - - private void SetAim() - { - if (currentTarget.IsVisible) - { - Vector3 targetPos = currentTarget.CurrPosition; - Transform targetTransform = currentTarget.Person.Transform.Original; - if (btrTurretServer.CheckPositionInAimingZone(targetPos) && btrTurretServer.targetTransform != targetTransform) - { - btrTurretServer.EnableAimingObject(targetTransform); - } - } - else - { - Vector3 targetLastPos = currentTarget.EnemyLastPositionReal; - if (btrTurretServer.CheckPositionInAimingZone(targetLastPos) - && Time.time - currentTarget.PersonalLastSeenTime < 3f - && btrTurretServer.targetPosition != targetLastPos) - { - btrTurretServer.EnableAimingPosition(targetLastPos); - - } - else if (Time.time - currentTarget.PersonalLastSeenTime >= 3f && !isTurretInDefaultRotation) - { - btrTurretServer.DisableAiming(); - } - } - } - - private bool CanShoot() - { - if (currentTarget.IsVisible && btrBotShooter.BotBtrData.CanShoot()) - { - return true; - } - - return false; - } - - private void StartShooting() - { - _shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootMachineGun()); - } - - /// - /// Custom method to make the BTR coaxial machine gun shoot. - /// - private IEnumerator ShootMachineGun() - { - isShooting = true; - - yield return new WaitForSeconds(machineGunAimDelay); - if (currentTarget?.Person == null || currentTarget?.IsVisible == false || !btrBotShooter.BotBtrData.CanShoot()) - { - isShooting = false; - yield break; - } - - Transform machineGunMuzzle = btrTurretServer.machineGunLaunchPoint; - ISharedBallisticsCalculator ballisticCalculator = gameWorld.SharedBallisticsCalculator; - - int burstMin = Mathf.FloorToInt(machineGunBurstCount.x); - int burstMax = Mathf.FloorToInt(machineGunBurstCount.y); - int burstCount = Random.Range(burstMin, burstMax + 1); - Vector3 targetHeadPos = currentTarget.Person.PlayerBones.Head.position; - while (burstCount > 0) - { - // Only update shooting position if the target isn't null - if (currentTarget?.Person != null) - { - targetHeadPos = currentTarget.Person.PlayerBones.Head.position; - } - Vector3 aimDirection = Vector3.Normalize(targetHeadPos - machineGunMuzzle.position); - ballisticCalculator.Shoot(btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, btrBotShooter.ProfileId, btrMachineGunWeapon, 1f, 0); - firearmController.method_54(weaponSoundPlayer, btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, false); - burstCount--; - shotQueue.Enqueue(new(machineGunMuzzle.position, aimDirection)); - yield return new WaitForSeconds(0.092308f); // 650 RPM - } - - float waitTime = Random.Range(machineGunRecoveryTime.x, machineGunRecoveryTime.y); - yield return new WaitForSeconds(waitTime); - - isShooting = false; - } - - public bool HostInteraction(Player player, PlayerInteractPacket packet) - { - GameWorld gameWorld = Singleton.Instance; - - // Prevent player from entering BTR when blacklisted - BotOwner btrBot = gameWorld.BtrController.BotShooterBtr; - if (btrBot.BotsGroup.Enemies.ContainsKey(player)) - { - // Notify player they are blacklisted from entering BTR - GlobalEventHandlerClass.CreateEvent().Invoke(player.Id, EBtrInteractionStatus.Blacklisted); - return false; - } - - if (packet.HasInteraction) - { - BTRView btrView = gameWorld.BtrController.BtrView; - if (btrView == null) - { - btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); - return false; - } - - OnPlayerInteractDoor(player, packet); - btrView.Interaction(player, packet); - - return true; - } - return false; - } - - public void HostObservedInteraction(Player player, PlayerInteractPacket packet) - { - if (packet.HasInteraction) - { - BTRView btrView = gameWorld.BtrController.BtrView; - if (btrView == null) - { - btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); - return; - } - - OnPlayerInteractDoor(player, packet); - ObservedBTRInteraction(player, packet); - } - } - - private async void ObservedBTRInteraction(Player player, PlayerInteractPacket packet) - { - BTRSide side = btrClientSide.method_9(packet.SideId); - - if (packet.InteractionType == EInteractionType.GoIn) - { - lastInteractedBtrSide = side; - player.BtrInteractionSide = side; - UpdateBTRSideDoorState(1); - player.BtrState = EPlayerBtrState.Approach; - btrClientSide.method_18(player); - player.BtrState = EPlayerBtrState.GoIn; - //side.AddPassenger(player, packet.SlotId); - player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(true); - player.MovementContext.PlayerAnimator.SetBtrGoIn(packet.Fast); - player.BtrState = EPlayerBtrState.Inside; - await Task.Delay(2200); - UpdateBTRSideDoorState(0); - } - else if (packet.InteractionType == EInteractionType.GoOut) - { - lastInteractedBtrSide = side; - player.BtrInteractionSide = side; - UpdateBTRSideDoorState(1); - player.BtrState = EPlayerBtrState.GoOut; - player.MovementContext.PlayerAnimator.SetBtrGoOut(packet.Fast); - player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(false); - (Vector3 start, Vector3 target) points = side.GoOutPoints(); - side.ApplyPlayerRotation(player.MovementContext, points.start, points.target + Vector3.up * 1.9f); - player.BtrState = EPlayerBtrState.Outside; - //side.RemovePassenger(player); - btrClientSide.method_19(player); - await Task.Delay(2200); - UpdateBTRSideDoorState(0); - } - } - - private void OnDestroy() - { - if (gameWorld == null) - { - return; - } - - StaticManager.KillCoroutine(ref _shootingTargetCoroutine); - StaticManager.KillCoroutine(ref _coverFireTimerCoroutine); - - if (TraderServicesManager.Instance != null) - { - TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased; - } - - if (gameWorld.MainPlayer != null) - { - gameWorld.MainPlayer.OnBtrStateChanged -= HandleBtrDoorState; - } - - if (btrClientSide != null) - { - Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); - Destroy(btrClientSide.gameObject); - } - - if (btrServerSide != null) - { - Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); - Destroy(btrServerSide.gameObject); - } - } - } + private void ToDestinationEvent(PathDestination destinationPoint, bool isFirst, bool isFinal, bool isLastRoutePoint) + { + // Remove purchased taxi service + TraderServicesManager.Instance.RemovePurchasedService(ETraderServiceType.PlayerTaxi, BTRUtil.BTRTraderId); + + // Update the prices for the taxi service + _updateTaxiPriceMethod.Invoke(btrController, [destinationPoint, isFinal]); + + // Update the UI + TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId); + } + + private bool IsBtrService(ETraderServiceType serviceType) + { + if (serviceType == ETraderServiceType.BtrItemsDelivery || serviceType == ETraderServiceType.PlayerTaxi || serviceType == ETraderServiceType.BtrBotCover) + { + return true; + } + + return false; + } + + private void BtrTraderServicePurchased(ETraderServiceType serviceType, string subserviceId) + { + if (!IsBtrService(serviceType)) + { + return; + } + + List passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList(); + List playersToNotify = passengers.Select(x => x.Id).ToList(); + btrController.method_6(playersToNotify, serviceType); // notify BTR passengers that a service has been purchased + + switch (serviceType) + { + case ETraderServiceType.BtrBotCover: + botEventHandler.ApplyTraderServiceBtrSupport(passengers); + StartCoverFireTimer(coverFireTime); + break; + case ETraderServiceType.PlayerTaxi: + btrController.BtrVehicle.IsPaid = true; + btrController.BtrVehicle.MoveToDestination(subserviceId); + break; + } + + GenericPacket responsePacket = new(EPackageType.TraderServiceNotification) + { + NetId = ((CoopPlayer)gameWorld.MainPlayer).NetId, + TraderServiceType = serviceType + }; + + NetDataWriter writer = new(); + writer.Reset(); + server.SendDataToAll(writer, ref responsePacket, DeliveryMethod.ReliableUnordered); + } + + public void NetworkBtrTraderServicePurchased(BTRServicePacket packet) + { + if (!IsBtrService(packet.TraderServiceType)) + { + return; + } + + List passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList(); + List playersToNotify = passengers.Select(x => x.Id).ToList(); + btrController.method_6(playersToNotify, packet.TraderServiceType); // notify BTR passengers that a service has been purchased + + switch (packet.TraderServiceType) + { + case ETraderServiceType.BtrBotCover: + botEventHandler.ApplyTraderServiceBtrSupport(passengers); + StartCoverFireTimer(coverFireTime); + break; + case ETraderServiceType.PlayerTaxi: + btrController.BtrVehicle.IsPaid = true; + btrController.BtrVehicle.MoveToDestination(packet.SubserviceId); + break; + } + + GenericPacket responsePacket = new(EPackageType.TraderServiceNotification) + { + NetId = ((CoopPlayer)gameWorld.MainPlayer).NetId, + TraderServiceType = packet.TraderServiceType + }; + + NetDataWriter writer = new(); + writer.Reset(); + server.SendDataToAll(writer, ref responsePacket, DeliveryMethod.ReliableUnordered); + } + + private void StartCoverFireTimer(float time) + { + _coverFireTimerCoroutine = StaticManager.BeginCoroutine(CoverFireTimer(time)); + } + + private IEnumerator CoverFireTimer(float time) + { + yield return new WaitForSeconds(time); + botEventHandler.StopTraderServiceBtrSupport(); + } + + private void HandleBtrDoorState(EPlayerBtrState playerBtrState) + { + if (playerBtrState == EPlayerBtrState.GoIn || playerBtrState == EPlayerBtrState.GoOut) + { + // Open Door + UpdateBTRSideDoorState(1); + } + else if (playerBtrState == EPlayerBtrState.Inside || playerBtrState == EPlayerBtrState.Outside) + { + // Close Door + UpdateBTRSideDoorState(0); + } + } + + private void UpdateBTRSideDoorState(byte state) + { + try + { + BTRSide btrSide = lastInteractPlayer.BtrInteractionSide != null ? lastInteractPlayer.BtrInteractionSide : lastInteractedBtrSide; + byte sideId = btrClientSide.GetSideId(btrSide); + switch (sideId) + { + case 0: + btrServerSide.LeftSideState = state; + break; + case 1: + btrServerSide.RightSideState = state; + break; + } + } + catch + { + btrLogger.LogError("[SPT-BTR] lastInteractedBtrSide is null when it shouldn't be. Check logs."); + throw; + } + } + + private BTRDataPacket UpdateDataPacket() + { + btrDataPacket.position = btrServerSide.transform.position; + btrDataPacket.rotation = btrServerSide.transform.rotation; + if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null) + { + btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y; + btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x; + } + btrDataPacket.State = (byte)btrServerSide.BtrState; + btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState; + btrDataPacket.LeftSideState = btrServerSide.LeftSideState; + btrDataPacket.LeftSlot0State = btrServerSide.LeftSlot0State; + btrDataPacket.LeftSlot1State = btrServerSide.LeftSlot1State; + btrDataPacket.RightSideState = btrServerSide.RightSideState; + btrDataPacket.RightSlot0State = btrServerSide.RightSlot0State; + btrDataPacket.RightSlot1State = btrServerSide.RightSlot1State; + btrDataPacket.currentSpeed = btrServerSide.currentSpeed; + btrDataPacket.timeToEndPause = btrServerSide.timeToEndPause; + btrDataPacket.moveDirection = (byte)btrServerSide.VehicleMoveDirection; + btrDataPacket.MoveSpeed = btrServerSide.moveSpeed; + if (btrController != null && btrController.BotShooterBtr != null) + { + btrDataPacket.BtrBotId = btrController.BotShooterBtr.Id; + } + + BTRPacket packet = new() + { + BTRDataPacket = btrDataPacket, + }; + + if (shotQueue.Count > 0) + { + packet.HasShot = true; + KeyValuePair shotInfo = shotQueue.Dequeue(); + packet.ShotPosition = shotInfo.Key; + packet.ShotDirection = shotInfo.Value; + } + + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.Unreliable); + + return btrDataPacket; + } + + private void DisableServerSideObjects() + { + 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. + // We also need to change the client collider's layer to HighPolyCollider due to unknown collisions that occur + // when going down a steep slope. + + // Add collision debugger component to log collisions in the EFT Console + Collider[] clientColliders = btrClientSide.GetComponentsInChildren(true); + //foreach (var collider in clientColliders) + //{ + // collider.gameObject.AddComponent(); + //} + + Collider[] serverColliders = btrServerSide.GetComponentsInChildren(true); + //foreach (var collider in serverColliders) + //{ + // collider.gameObject.AddComponent(); + //} + + Collider clientRootCollider = clientColliders.First(x => x.gameObject.name == "Root"); + + // Retrieve all TerrainColliders + List terrainColliders = new(); + + foreach (GPUInstancerManager manager in GPUInstancerManager.activeManagerList) + { + if (manager.GetType() != typeof(GPUInstancerDetailManager)) + { + continue; + } + + GPUInstancerDetailManager detailManager = (GPUInstancerDetailManager)manager; + if (detailManager.terrain == null) + { + continue; + } + + terrainColliders.Add(detailManager.terrain.GetComponent()); + } + + // Make the Root collider ignore collisions with TerrainColliders + foreach (Collider collider in terrainColliders) + { + Physics.IgnoreCollision(clientRootCollider, collider); + } + + // Retrieve all wheel colliders on the serverside BTR + const string wheelColliderParentName = "BTR_82_wheel"; + const string wheelColliderName = "Cylinder"; + + Collider[] serverWheelColliders = serverColliders + .Where(x => x.transform.parent.name.StartsWith(wheelColliderParentName) && x.gameObject.name.StartsWith(wheelColliderName)) + .ToArray(); + + // Make the Root collider ignore collisions with the serverside BTR wheels + foreach (Collider collider in serverWheelColliders) + { + Physics.IgnoreCollision(clientRootCollider, collider); + } + + // Enable clientside BTR collider and disable serverside BTR collider + const string exteriorColliderName = "BTR_82_exterior_COLLIDER"; + Collider serverExteriorCollider = serverColliders + .First(x => x.gameObject.name == exteriorColliderName); + Collider clientExteriorCollider = clientColliders + .First(x => x.gameObject.name == exteriorColliderName); + + serverExteriorCollider.gameObject.SetActive(false); + clientExteriorCollider.gameObject.SetActive(true); + clientExteriorCollider.gameObject.layer = LayerMask.NameToLayer("HighPolyCollider"); + } + + private void UpdateTarget() + { + currentTarget = btrBotShooter.Memory.GoalEnemy; + } + + private bool HasTarget() + { + if (currentTarget != null) + { + return true; + } + + return false; + } + + private void SetAim() + { + if (currentTarget.IsVisible) + { + Vector3 targetPos = currentTarget.CurrPosition; + Transform targetTransform = currentTarget.Person.Transform.Original; + if (btrTurretServer.CheckPositionInAimingZone(targetPos) && btrTurretServer.targetTransform != targetTransform) + { + btrTurretServer.EnableAimingObject(targetTransform); + } + } + else + { + Vector3 targetLastPos = currentTarget.EnemyLastPositionReal; + if (btrTurretServer.CheckPositionInAimingZone(targetLastPos) + && Time.time - currentTarget.PersonalLastSeenTime < 3f + && btrTurretServer.targetPosition != targetLastPos) + { + btrTurretServer.EnableAimingPosition(targetLastPos); + + } + else if (Time.time - currentTarget.PersonalLastSeenTime >= 3f && !isTurretInDefaultRotation) + { + btrTurretServer.DisableAiming(); + } + } + } + + private bool CanShoot() + { + if (currentTarget.IsVisible && btrBotShooter.BotBtrData.CanShoot()) + { + return true; + } + + return false; + } + + private void StartShooting() + { + _shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootMachineGun()); + } + + /// + /// Custom method to make the BTR coaxial machine gun shoot. + /// + private IEnumerator ShootMachineGun() + { + isShooting = true; + + yield return new WaitForSeconds(machineGunAimDelay); + if (currentTarget?.Person == null || currentTarget?.IsVisible == false || !btrBotShooter.BotBtrData.CanShoot()) + { + isShooting = false; + yield break; + } + + Transform machineGunMuzzle = btrTurretServer.machineGunLaunchPoint; + ISharedBallisticsCalculator ballisticCalculator = gameWorld.SharedBallisticsCalculator; + + int burstMin = Mathf.FloorToInt(machineGunBurstCount.x); + int burstMax = Mathf.FloorToInt(machineGunBurstCount.y); + int burstCount = Random.Range(burstMin, burstMax + 1); + Vector3 targetHeadPos = currentTarget.Person.PlayerBones.Head.position; + while (burstCount > 0) + { + // Only update shooting position if the target isn't null + if (currentTarget?.Person != null) + { + targetHeadPos = currentTarget.Person.PlayerBones.Head.position; + } + Vector3 aimDirection = Vector3.Normalize(targetHeadPos - machineGunMuzzle.position); + ballisticCalculator.Shoot(btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, btrBotShooter.ProfileId, btrMachineGunWeapon, 1f, 0); + firearmController.method_54(weaponSoundPlayer, btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, false); + burstCount--; + shotQueue.Enqueue(new(machineGunMuzzle.position, aimDirection)); + yield return new WaitForSeconds(0.092308f); // 650 RPM + } + + float waitTime = Random.Range(machineGunRecoveryTime.x, machineGunRecoveryTime.y); + yield return new WaitForSeconds(waitTime); + + isShooting = false; + } + + public bool HostInteraction(Player player, PlayerInteractPacket packet) + { + GameWorld gameWorld = Singleton.Instance; + + // Prevent player from entering BTR when blacklisted + BotOwner btrBot = gameWorld.BtrController.BotShooterBtr; + if (btrBot.BotsGroup.Enemies.ContainsKey(player)) + { + // Notify player they are blacklisted from entering BTR + GlobalEventHandlerClass.CreateEvent().Invoke(player.Id, EBtrInteractionStatus.Blacklisted); + return false; + } + + if (packet.HasInteraction) + { + BTRView btrView = gameWorld.BtrController.BtrView; + if (btrView == null) + { + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); + return false; + } + + OnPlayerInteractDoor(player, packet); + btrView.Interaction(player, packet); + + return true; + } + return false; + } + + public void HostObservedInteraction(Player player, PlayerInteractPacket packet) + { + if (packet.HasInteraction) + { + BTRView btrView = gameWorld.BtrController.BtrView; + if (btrView == null) + { + btrLogger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null"); + return; + } + + OnPlayerInteractDoor(player, packet); + ObservedBTRInteraction(player, packet); + } + } + + private async void ObservedBTRInteraction(Player player, PlayerInteractPacket packet) + { + BTRSide side = btrClientSide.method_9(packet.SideId); + + if (packet.InteractionType == EInteractionType.GoIn) + { + lastInteractedBtrSide = side; + player.BtrInteractionSide = side; + UpdateBTRSideDoorState(1); + player.BtrState = EPlayerBtrState.Approach; + btrClientSide.method_18(player); + player.BtrState = EPlayerBtrState.GoIn; + //side.AddPassenger(player, packet.SlotId); + player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(true); + player.MovementContext.PlayerAnimator.SetBtrGoIn(packet.Fast); + player.BtrState = EPlayerBtrState.Inside; + await Task.Delay(2200); + UpdateBTRSideDoorState(0); + } + else if (packet.InteractionType == EInteractionType.GoOut) + { + lastInteractedBtrSide = side; + player.BtrInteractionSide = side; + UpdateBTRSideDoorState(1); + player.BtrState = EPlayerBtrState.GoOut; + player.MovementContext.PlayerAnimator.SetBtrGoOut(packet.Fast); + player.MovementContext.PlayerAnimator.SetBtrLayerEnabled(false); + (Vector3 start, Vector3 target) points = side.GoOutPoints(); + side.ApplyPlayerRotation(player.MovementContext, points.start, points.target + Vector3.up * 1.9f); + player.BtrState = EPlayerBtrState.Outside; + //side.RemovePassenger(player); + btrClientSide.method_19(player); + await Task.Delay(2200); + UpdateBTRSideDoorState(0); + } + } + + private void OnDestroy() + { + if (gameWorld == null) + { + return; + } + + StaticManager.KillCoroutine(ref _shootingTargetCoroutine); + StaticManager.KillCoroutine(ref _coverFireTimerCoroutine); + + if (TraderServicesManager.Instance != null) + { + TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased; + } + + if (gameWorld.MainPlayer != null) + { + gameWorld.MainPlayer.OnBtrStateChanged -= HandleBtrDoorState; + } + + if (btrClientSide != null) + { + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrClientSide"); + Destroy(btrClientSide.gameObject); + } + + if (btrServerSide != null) + { + Debug.LogWarning("[SPT-BTR] BTRManager - Destroying btrServerSide"); + Destroy(btrServerSide.gameObject); + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/BotClasses/BotFirearmController.cs b/Fika.Core/Coop/BotClasses/BotFirearmController.cs index 5da320dc..ac8ed574 100644 --- a/Fika.Core/Coop/BotClasses/BotFirearmController.cs +++ b/Fika.Core/Coop/BotClasses/BotFirearmController.cs @@ -5,19 +5,19 @@ namespace Fika.Core.Coop.BotClasses { - public class BotFirearmController : CoopClientFirearmController - { - public override Vector3 WeaponDirection - { - get - { - return _player.LookDirection; - } - } + public class BotFirearmController : CoopClientFirearmController + { + public override Vector3 WeaponDirection + { + get + { + return _player.LookDirection; + } + } - public static BotFirearmController Create(CoopBot player, Weapon weapon) - { - return smethod_5(player, weapon); - } - } + public static BotFirearmController Create(CoopBot player, Weapon weapon) + { + return smethod_5(player, weapon); + } + } } diff --git a/Fika.Core/Coop/BotClasses/BotMovementContext.cs b/Fika.Core/Coop/BotClasses/BotMovementContext.cs index 0230ef76..5799adeb 100644 --- a/Fika.Core/Coop/BotClasses/BotMovementContext.cs +++ b/Fika.Core/Coop/BotClasses/BotMovementContext.cs @@ -5,25 +5,25 @@ namespace Fika.Core.Coop.BotClasses { - public sealed class BotMovementContext : MovementContext - { - private CoopBot Bot; + public sealed class BotMovementContext : MovementContext + { + private CoopBot Bot; - public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) - { - if (Bot.AIData.BotOwner.BotState == EBotState.NonActive) - { - return; - } + public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) + { + if (Bot.AIData.BotOwner.BotState == EBotState.NonActive) + { + return; + } - base.ApplyGravity(ref motion, deltaTime, stickToGround); - } + base.ApplyGravity(ref motion, deltaTime, stickToGround); + } - public new static BotMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) - { - BotMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); - movementContext.Bot = (CoopBot)player; - return movementContext; - } - } + public new static BotMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) + { + BotMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); + movementContext.Bot = (CoopBot)player; + return movementContext; + } + } } diff --git a/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs index 4f105cb4..caf5cd56 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotHealthController.cs @@ -6,30 +6,30 @@ namespace Fika.Core.Coop.ClientClasses { - 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; - public override bool _sendNetworkSyncPackets - { - get - { - return true; - } - } + 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; + public override bool _sendNetworkSyncPackets + { + get + { + return true; + } + } - public override void SendNetworkSyncPacket(GStruct346 packet) - { - if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) - { - coopBot.PacketSender.HealthSyncPackets.Enqueue(coopBot.SetupDeathPacket(packet)); - return; - } + public override void SendNetworkSyncPacket(GStruct346 packet) + { + if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) + { + coopBot.PacketSender.HealthSyncPackets.Enqueue(coopBot.SetupDeathPacket(packet)); + return; + } - coopBot.PacketSender.HealthSyncPackets.Enqueue(new(coopBot.NetId) - { - Packet = packet - }); - } - } + coopBot.PacketSender.HealthSyncPackets.Enqueue(new(coopBot.NetId) + { + Packet = packet + }); + } + } } diff --git a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs index 8afa6310..48d1a413 100644 --- a/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs +++ b/Fika.Core/Coop/BotClasses/CoopBotInventoryController.cs @@ -10,30 +10,30 @@ namespace Fika.Core.Coop.BotClasses { - public class CoopBotInventoryController(Player player, Profile profile, bool examined) : PlayerInventoryController(player, profile, examined) - { - private readonly CoopBot CoopBot = (CoopBot)player; + public class CoopBotInventoryController(Player player, Profile profile, bool examined) : PlayerInventoryController(player, profile, examined) + { + private readonly CoopBot CoopBot = (CoopBot)player; - public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) - { - base.Execute(operation, callback); + public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) + { + base.Execute(operation, callback); - InventoryPacket packet = new() - { - HasItemControllerExecutePacket = true - }; + InventoryPacket packet = new() + { + HasItemControllerExecutePacket = true + }; - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operation.Id, - OperationBytes = opBytes - }; + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operation.Id, + OperationBytes = opBytes + }; - CoopBot.PacketSender.InventoryPackets.Enqueue(packet); - } - } + CoopBot.PacketSender.InventoryPackets.Enqueue(packet); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs b/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs index 5e6a8973..23f34daf 100644 --- a/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs +++ b/Fika.Core/Coop/ClientClasses/ClientMovementContext.cs @@ -4,35 +4,35 @@ namespace Fika.Core.Coop.ClientClasses { - public class ClientMovementContext : MovementContext - { - private bool doGravity; + public class ClientMovementContext : MovementContext + { + private bool doGravity; - public new static ClientMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) - { - ClientMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); - return movementContext; - } + public new static ClientMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) + { + ClientMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); + return movementContext; + } - public override void Init() - { - doGravity = true; - base.Init(); - } + public override void Init() + { + doGravity = true; + base.Init(); + } - public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) - { - if (!doGravity) - { - return; - } + public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) + { + if (!doGravity) + { + return; + } - base.ApplyGravity(ref motion, deltaTime, stickToGround); - } + base.ApplyGravity(ref motion, deltaTime, stickToGround); + } - public void SetGravity(bool enabled) - { - doGravity = enabled; - } - } + public void SetGravity(bool enabled) + { + doGravity = enabled; + } + } } diff --git a/Fika.Core/Coop/ClientClasses/CoopClientGameWorld.cs b/Fika.Core/Coop/ClientClasses/CoopClientGameWorld.cs index d2e1c773..b590b8a9 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientGameWorld.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientGameWorld.cs @@ -5,31 +5,31 @@ namespace Fika.Core.Coop.ClientClasses { - public class CoopClientGameWorld : ClientLocalGameWorld - { - public static CoopClientGameWorld Create(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) - { - CoopClientGameWorld gameWorld = gameObject.AddComponent(); - gameWorld.ObjectsFactory = objectsFactory; - Traverse.Create(gameWorld).Field("eupdateQueue_0").Value = updateQueue; - gameWorld.SpeakerManager = gameObject.AddComponent(); - gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); - gameWorld.BufferZoneController = new BufferZoneControllerClass(); - gameWorld.CurrentProfileId = currentProfileId; - gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); - gameObject.AddComponent(); - return gameWorld; - } + public class CoopClientGameWorld : ClientLocalGameWorld + { + public static CoopClientGameWorld Create(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) + { + CoopClientGameWorld gameWorld = gameObject.AddComponent(); + gameWorld.ObjectsFactory = objectsFactory; + Traverse.Create(gameWorld).Field("eupdateQueue_0").Value = updateQueue; + gameWorld.SpeakerManager = gameObject.AddComponent(); + gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); + gameWorld.BufferZoneController = new BufferZoneControllerClass(); + gameWorld.CurrentProfileId = currentProfileId; + gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); + gameObject.AddComponent(); + return gameWorld; + } - public override GClass676 CreateGrenadeFactory() - { - return new GClass677(); - } + public override GClass676 CreateGrenadeFactory() + { + return new GClass677(); + } - public override void Start() - { - base.Start(); - RegisterBorderZones(); - } - } + public override void Start() + { + base.Start(); + RegisterBorderZones(); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs index 849dcce2..60b1eb66 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientHealthController.cs @@ -6,30 +6,30 @@ namespace Fika.Core.Coop.ClientClasses { - 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; - public override bool _sendNetworkSyncPackets - { - get - { - return true; - } - } + 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; + public override bool _sendNetworkSyncPackets + { + get + { + return true; + } + } - public override void SendNetworkSyncPacket(GStruct346 packet) - { - if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) - { - coopPlayer.PacketSender.HealthSyncPackets.Enqueue(coopPlayer.SetupDeathPacket(packet)); - return; - } + public override void SendNetworkSyncPacket(GStruct346 packet) + { + if (packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Data.IsAlive.IsAlive) + { + coopPlayer.PacketSender.HealthSyncPackets.Enqueue(coopPlayer.SetupDeathPacket(packet)); + return; + } - coopPlayer.PacketSender.HealthSyncPackets.Enqueue(new(coopPlayer.NetId) - { - Packet = packet - }); - } - } + coopPlayer.PacketSender.HealthSyncPackets.Enqueue(new(coopPlayer.NetId) + { + Packet = packet + }); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs index 0943f111..53a652ae 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientInventoryController.cs @@ -12,219 +12,219 @@ 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; - - public override void CallMalfunctionRepaired(Weapon weapon) - { - base.CallMalfunctionRepaired(weapon); - if (!Player.IsAI && (bool)Singleton.Instance.Game.Settings.MalfunctionVisability) - { - MonoBehaviourSingleton.Instance.MalfunctionGlow.ShowGlow(BattleUIMalfunctionGlow.GlowType.Repaired, true, method_44()); - } - } - - public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) - { + 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; + + public override void CallMalfunctionRepaired(Weapon weapon) + { + base.CallMalfunctionRepaired(weapon); + if (!Player.IsAI && (bool)Singleton.Instance.Game.Settings.MalfunctionVisability) + { + MonoBehaviourSingleton.Instance.MalfunctionGlow.ShowGlow(BattleUIMalfunctionGlow.GlowType.Repaired, true, method_44()); + } + } + + public override void Execute(GClass2854 operation, [CanBeNull] Callback callback) + { #if DEBUG - ConsoleScreen.Log("InvOperation: " + operation.GetType().Name); + 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) - { - Item lootedItem = moveOperation.Item; - if (lootedItem.Template.QuestItem) - { - if (CoopPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController && sharedQuestController.ContainsAcceptedType("PlaceBeacon")) - { - 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.SendPacket(ref packet); - } - } - base.Execute(operation, callback); - return; - } - } - - // Do not replicate quest operations - // Check for GClass increments - if (operation is GClass2897 or GClass2898 or QuestHandoverOperationClass) - { - base.Execute(operation, callback); - return; - } - - if (FikaBackendUtils.IsServer) - { - HostInventoryOperationManager operationManager = new(this, operation, callback); - if (vmethod_0(operationManager.operation)) - { - operationManager.operation.vmethod_0(operationManager.HandleResult); - - InventoryPacket packet = new() - { - HasItemControllerExecutePacket = true - }; - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operation.Id, - OperationBytes = opBytes - }; - - CoopPlayer.PacketSender.InventoryPackets.Enqueue(packet); - - return; - } - operationManager.operation.Dispose(); - operationManager.callback?.Fail($"Can't execute {operationManager.operation}", 1); - } - else if (FikaBackendUtils.IsClient) - { - InventoryPacket packet = new() - { - HasItemControllerExecutePacket = true - }; - - ClientInventoryOperationManager clientOperationManager = new() - { - operation = operation, - callback = callback, - inventoryController = this - }; - - 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(FromObjectAbstractClass.FromInventoryOperation(operation, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operationNum, - OperationBytes = opBytes - }; - - CoopPlayer.PacketSender.InventoryPackets.Enqueue(packet); - } - } - - private uint AddOperationCallback(GClass2854 operation, Callback callback) - { - ushort id = operation.Id; - CoopPlayer.OperationCallbacks.Add(id, callback); - return id; - } - - private class HostInventoryOperationManager(CoopClientInventoryController inventoryController, GClass2854 operation, Callback callback) - { - public readonly CoopClientInventoryController inventoryController = inventoryController; - public GClass2854 operation = operation; - public readonly Callback callback = callback; - - public void HandleResult(IResult result) - { - if (!result.Succeed) - { - FikaPlugin.Instance.FikaLogger.LogError($"[{Time.frameCount}][{inventoryController.Name}] {inventoryController.ID} - Local operation failed: {operation.Id} - {operation}\r\nError: {result.Error}"); - } - callback?.Invoke(result); - } - } - - private class ClientInventoryOperationManager - { - public EOperationStatus? serverOperationStatus; - public EOperationStatus? localOperationStatus; - public GClass2854 operation; - public Callback callback; - public CoopClientInventoryController inventoryController; - - public void HandleResult(Result result) - { - ClientInventoryCallbackManager callbackManager = new() - { - clientOperationManager = this, - result = result - }; - - if (callbackManager.result.Succeed) - { - EOperationStatus value = callbackManager.result.Value; - if (value == EOperationStatus.Started) - { - localOperationStatus = EOperationStatus.Started; - serverOperationStatus = EOperationStatus.Started; - operation.vmethod_0(new Callback(callbackManager.HandleResult), true); - return; - } - if (value == EOperationStatus.Finished) - { - serverOperationStatus = EOperationStatus.Finished; - if (localOperationStatus == serverOperationStatus) - { - operation.Dispose(); - callback.Succeed(); - return; - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"{inventoryController.ID} - Client operation rejected by server: {operation.Id} - {operation}\r\nReason: {callbackManager.result.Error}"); - serverOperationStatus = EOperationStatus.Failed; - localOperationStatus = EOperationStatus.Failed; - operation.Dispose(); - callback.Invoke(callbackManager.result); - } - } - } - - private class ClientInventoryCallbackManager - { - public Result result; - public ClientInventoryOperationManager clientOperationManager; - - public void HandleResult(IResult executeResult) - { - if (!executeResult.Succeed && (executeResult.Error is not "skipped skippable" or "skipped _completed")) - { - FikaPlugin.Instance.FikaLogger.LogError($"{clientOperationManager.inventoryController.ID} - Client operation critical failure: {clientOperationManager.inventoryController.ID} - {clientOperationManager.operation}\r\nError: {executeResult.Error}"); - } - - clientOperationManager.localOperationStatus = EOperationStatus.Finished; - - if (clientOperationManager.localOperationStatus == clientOperationManager.serverOperationStatus) - { - clientOperationManager.operation.Dispose(); - clientOperationManager.callback.Invoke(result); - return; - } - - if (clientOperationManager.serverOperationStatus != null) - { - if (clientOperationManager.serverOperationStatus == EOperationStatus.Failed) - { - clientOperationManager.operation.Dispose(); - clientOperationManager.callback.Invoke(result); - } - } - } - } - } + // Do not replicate picking up quest items, throws an error on the other clients + if (operation is InventoryItemFromToClass moveOperation) + { + Item lootedItem = moveOperation.Item; + if (lootedItem.Template.QuestItem) + { + if (CoopPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController && sharedQuestController.ContainsAcceptedType("PlaceBeacon")) + { + 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.SendPacket(ref packet); + } + } + base.Execute(operation, callback); + return; + } + } + + // Do not replicate quest operations + // Check for GClass increments + if (operation is GClass2897 or GClass2898 or QuestHandoverOperationClass) + { + base.Execute(operation, callback); + return; + } + + if (FikaBackendUtils.IsServer) + { + HostInventoryOperationManager operationManager = new(this, operation, callback); + if (vmethod_0(operationManager.operation)) + { + operationManager.operation.vmethod_0(operationManager.HandleResult); + + InventoryPacket packet = new() + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(operation, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operation.Id, + OperationBytes = opBytes + }; + + CoopPlayer.PacketSender.InventoryPackets.Enqueue(packet); + + return; + } + operationManager.operation.Dispose(); + operationManager.callback?.Fail($"Can't execute {operationManager.operation}", 1); + } + else if (FikaBackendUtils.IsClient) + { + InventoryPacket packet = new() + { + HasItemControllerExecutePacket = true + }; + + ClientInventoryOperationManager clientOperationManager = new() + { + operation = operation, + callback = callback, + inventoryController = this + }; + + 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(FromObjectAbstractClass.FromInventoryOperation(operation, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationNum, + OperationBytes = opBytes + }; + + CoopPlayer.PacketSender.InventoryPackets.Enqueue(packet); + } + } + + private uint AddOperationCallback(GClass2854 operation, Callback callback) + { + ushort id = operation.Id; + CoopPlayer.OperationCallbacks.Add(id, callback); + return id; + } + + private class HostInventoryOperationManager(CoopClientInventoryController inventoryController, GClass2854 operation, Callback callback) + { + public readonly CoopClientInventoryController inventoryController = inventoryController; + public GClass2854 operation = operation; + public readonly Callback callback = callback; + + public void HandleResult(IResult result) + { + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"[{Time.frameCount}][{inventoryController.Name}] {inventoryController.ID} - Local operation failed: {operation.Id} - {operation}\r\nError: {result.Error}"); + } + callback?.Invoke(result); + } + } + + private class ClientInventoryOperationManager + { + public EOperationStatus? serverOperationStatus; + public EOperationStatus? localOperationStatus; + public GClass2854 operation; + public Callback callback; + public CoopClientInventoryController inventoryController; + + public void HandleResult(Result result) + { + ClientInventoryCallbackManager callbackManager = new() + { + clientOperationManager = this, + result = result + }; + + if (callbackManager.result.Succeed) + { + EOperationStatus value = callbackManager.result.Value; + if (value == EOperationStatus.Started) + { + localOperationStatus = EOperationStatus.Started; + serverOperationStatus = EOperationStatus.Started; + operation.vmethod_0(new Callback(callbackManager.HandleResult), true); + return; + } + if (value == EOperationStatus.Finished) + { + serverOperationStatus = EOperationStatus.Finished; + if (localOperationStatus == serverOperationStatus) + { + operation.Dispose(); + callback.Succeed(); + return; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"{inventoryController.ID} - Client operation rejected by server: {operation.Id} - {operation}\r\nReason: {callbackManager.result.Error}"); + serverOperationStatus = EOperationStatus.Failed; + localOperationStatus = EOperationStatus.Failed; + operation.Dispose(); + callback.Invoke(callbackManager.result); + } + } + } + + private class ClientInventoryCallbackManager + { + public Result result; + public ClientInventoryOperationManager clientOperationManager; + + public void HandleResult(IResult executeResult) + { + if (!executeResult.Succeed && (executeResult.Error is not "skipped skippable" or "skipped _completed")) + { + FikaPlugin.Instance.FikaLogger.LogError($"{clientOperationManager.inventoryController.ID} - Client operation critical failure: {clientOperationManager.inventoryController.ID} - {clientOperationManager.operation}\r\nError: {executeResult.Error}"); + } + + clientOperationManager.localOperationStatus = EOperationStatus.Finished; + + if (clientOperationManager.localOperationStatus == clientOperationManager.serverOperationStatus) + { + clientOperationManager.operation.Dispose(); + clientOperationManager.callback.Invoke(result); + return; + } + + if (clientOperationManager.serverOperationStatus != null) + { + if (clientOperationManager.serverOperationStatus == EOperationStatus.Failed) + { + clientOperationManager.operation.Dispose(); + clientOperationManager.callback.Invoke(result); + } + } + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs index b113251d..3f207bb0 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientSharedQuestController.cs @@ -11,328 +11,328 @@ 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 = []; - private bool canSendAndReceive = true; - private bool isItemBeingDropped = false; - - 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.Kills: - if (!FikaPlugin.EasyKillConditions.Value) - { - 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; - } - } - } - } - - /// - /// Used to prevent errors when subscribing to the event - /// - public void LateInit() - { - if (acceptedTypes.Contains("PlaceBeacon")) - { - player.Profile.OnItemZoneDropped += Profile_OnItemZoneDropped; - } - } - - private void Profile_OnItemZoneDropped(string itemId, string zoneId) - { - if (isItemBeingDropped) - { - return; - } - - QuestDropItemPacket packet = new(player.Profile.Info.MainProfileNickname, itemId, zoneId); + 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 = []; + private bool canSendAndReceive = true; + private bool isItemBeingDropped = false; + + 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.Kills: + if (!FikaPlugin.EasyKillConditions.Value) + { + 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; + } + } + } + } + + /// + /// Used to prevent errors when subscribing to the event + /// + public void LateInit() + { + if (acceptedTypes.Contains("PlaceBeacon")) + { + player.Profile.OnItemZoneDropped += Profile_OnItemZoneDropped; + } + } + + private void Profile_OnItemZoneDropped(string itemId, string zoneId) + { + if (isItemBeingDropped) + { + return; + } + + QuestDropItemPacket packet = new(player.Profile.Info.MainProfileNickname, itemId, zoneId); #if DEBUG - FikaPlugin.Instance.FikaLogger.LogInfo($"Profile_OnItemZoneDropped: Sending quest progress itemId:{itemId} zoneId:{zoneId}"); + FikaPlugin.Instance.FikaLogger.LogInfo($"Profile_OnItemZoneDropped: Sending quest progress itemId:{itemId} zoneId:{zoneId}"); #endif - player.PacketSender.SendPacket(ref packet); - } - - public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify = true) - { - base.OnConditionValueChanged(conditional, status, condition, notify); - if (!canSendAndReceive) - { - return; - } - - if (lastFromNetwork.Contains(condition.id)) - { - lastFromNetwork.Remove(condition.id); - return; - } - SendQuestPacket(conditional, condition); - } - - public bool ContainsAcceptedType(string type) - { - return acceptedTypes.Contains(type); - } - - 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); - } - - public void ToggleQuestSharing(bool state) - { - canSendAndReceive = state; - } - - private void SendQuestPacket(IConditionCounter conditional, Condition condition) - { - if (!canSendAndReceive) - { - return; - } - - 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); + player.PacketSender.SendPacket(ref packet); + } + + public override void OnConditionValueChanged(IConditionCounter conditional, EQuestStatus status, Condition condition, bool notify = true) + { + base.OnConditionValueChanged(conditional, status, condition, notify); + if (!canSendAndReceive) + { + return; + } + + if (lastFromNetwork.Contains(condition.id)) + { + lastFromNetwork.Remove(condition.id); + return; + } + SendQuestPacket(conditional, condition); + } + + public bool ContainsAcceptedType(string type) + { + return acceptedTypes.Contains(type); + } + + 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); + } + + public void ToggleQuestSharing(bool state) + { + canSendAndReceive = state; + } + + private void SendQuestPacket(IConditionCounter conditional, Condition condition) + { + if (!canSendAndReceive) + { + return; + } + + 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"); + FikaPlugin.Instance.FikaLogger.LogInfo("SendQuestPacket: Sending quest progress"); #endif - player.PacketSender.SendPacket(ref packet); - } - } - } - - internal void ReceiveQuestPacket(ref QuestConditionPacket packet) - { - if (!canSendAndReceive) - { - return; - } - - 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++; - if (FikaPlugin.QuestSharingNotifications.Value) - { - NotificationManagerClass.DisplayMessageNotification( - $"Received shared quest progression from {ColorizeText(Colors.GREEN, packet.Nickname)} for the quest {ColorizeText(Colors.BROWN, quest.Template.Name)}", - iconType: EFT.Communications.ENotificationIconType.Quest); - } - } - } - } - } - - internal void ReceiveQuestItemPacket(ref QuestItemPacket packet) - { - if (!canSendAndReceive) - { - return; - } - - 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); - if (FikaPlugin.QuestSharingNotifications.Value) - { - NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, packet.Nickname)} picked up {ColorizeText(Colors.BLUE, item.Name.Localized())}", - iconType: EFT.Communications.ENotificationIconType.Quest); - } - } - } - } - } - - internal void ReceiveQuestDropItemPacket(ref QuestDropItemPacket packet) - { - if (!canSendAndReceive) - { - return; - } - - if (!acceptedTypes.Contains("PlaceBeacon")) - { - return; - } - - isItemBeingDropped = true; - string itemId = packet.ItemId; - string zoneId = packet.ZoneId; - - if (!HasQuestForItem(itemId, zoneId, out string questName)) - { - return; - } - - if (FikaPlugin.QuestSharingNotifications.Value) - { - NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, packet.Nickname)} planted an item for {ColorizeText(Colors.BROWN, questName)}", - iconType: EFT.Communications.ENotificationIconType.Quest); - } - - Item item = player.Inventory.QuestRaidItems.GetAllItems().FirstOrDefault(x => x.TemplateId == itemId); - if (item != null) - { - GStruct414 removeResult = InteractionsHandlerClass.Remove(item, player.InventoryControllerClass, true, false); - player.InventoryControllerClass.TryRunNetworkTransaction(removeResult); - } - player.Profile.ItemDroppedAtPlace(itemId, zoneId); - isItemBeingDropped = false; - } - - private bool HasQuestForItem(string itemId, string zoneId, out string questName) - { - foreach (QuestClass quest in Quests) - { - foreach (ConditionPlaceBeacon conditionPlaceBeacon in quest.GetConditions(EQuestStatus.AvailableForFinish)) - { - if (conditionPlaceBeacon.target.Contains(itemId) && conditionPlaceBeacon.zoneId == zoneId) - { - if (!quest.CompletedConditions.Contains(conditionPlaceBeacon.id)) - { + player.PacketSender.SendPacket(ref packet); + } + } + } + + internal void ReceiveQuestPacket(ref QuestConditionPacket packet) + { + if (!canSendAndReceive) + { + return; + } + + 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++; + if (FikaPlugin.QuestSharingNotifications.Value) + { + NotificationManagerClass.DisplayMessageNotification( + $"Received shared quest progression from {ColorizeText(Colors.GREEN, packet.Nickname)} for the quest {ColorizeText(Colors.BROWN, quest.Template.Name)}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + } + } + } + } + + internal void ReceiveQuestItemPacket(ref QuestItemPacket packet) + { + if (!canSendAndReceive) + { + return; + } + + 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); + if (FikaPlugin.QuestSharingNotifications.Value) + { + NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, packet.Nickname)} picked up {ColorizeText(Colors.BLUE, item.Name.Localized())}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + } + } + } + } + + internal void ReceiveQuestDropItemPacket(ref QuestDropItemPacket packet) + { + if (!canSendAndReceive) + { + return; + } + + if (!acceptedTypes.Contains("PlaceBeacon")) + { + return; + } + + isItemBeingDropped = true; + string itemId = packet.ItemId; + string zoneId = packet.ZoneId; + + if (!HasQuestForItem(itemId, zoneId, out string questName)) + { + return; + } + + if (FikaPlugin.QuestSharingNotifications.Value) + { + NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, packet.Nickname)} planted an item for {ColorizeText(Colors.BROWN, questName)}", + iconType: EFT.Communications.ENotificationIconType.Quest); + } + + Item item = player.Inventory.QuestRaidItems.GetAllItems().FirstOrDefault(x => x.TemplateId == itemId); + if (item != null) + { + GStruct414 removeResult = InteractionsHandlerClass.Remove(item, player.InventoryControllerClass, true, false); + player.InventoryControllerClass.TryRunNetworkTransaction(removeResult); + } + player.Profile.ItemDroppedAtPlace(itemId, zoneId); + isItemBeingDropped = false; + } + + private bool HasQuestForItem(string itemId, string zoneId, out string questName) + { + foreach (QuestClass quest in Quests) + { + foreach (ConditionPlaceBeacon conditionPlaceBeacon in quest.GetConditions(EQuestStatus.AvailableForFinish)) + { + if (conditionPlaceBeacon.target.Contains(itemId) && conditionPlaceBeacon.zoneId == zoneId) + { + if (!quest.CompletedConditions.Contains(conditionPlaceBeacon.id)) + { #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Beacon, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}"); + FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Beacon, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}"); #endif - questName = quest.Template.Name; - return true; - } + questName = quest.Template.Name; + return true; + } #if DEBUG - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Beacon, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}, but it was COMPLETED"); - } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Beacon, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}, but it was COMPLETED"); + } #endif - } - } - - foreach (ConditionLeaveItemAtLocation conditionLeaveItemAtLocation in quest.GetConditions(EQuestStatus.AvailableForFinish)) - { - if (conditionLeaveItemAtLocation.target.Contains(itemId) && conditionLeaveItemAtLocation.zoneId == zoneId) - { - if (!quest.CompletedConditions.Contains(conditionLeaveItemAtLocation.id)) - { + } + } + + foreach (ConditionLeaveItemAtLocation conditionLeaveItemAtLocation in quest.GetConditions(EQuestStatus.AvailableForFinish)) + { + if (conditionLeaveItemAtLocation.target.Contains(itemId) && conditionLeaveItemAtLocation.zoneId == zoneId) + { + if (!quest.CompletedConditions.Contains(conditionLeaveItemAtLocation.id)) + { #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Item, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}"); + FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Item, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}"); #endif - questName = quest.Template.Name; - return true; - } + questName = quest.Template.Name; + return true; + } #if DEBUG - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Item, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}, but it was COMPLETED"); - } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Found quest for Placed Item, itemId: {itemId}, zoneId: {zoneId}, quest: {quest.Template.Name}, but it was COMPLETED"); + } #endif - } - } - } + } + } + } #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning($"Did not have quest for Place Beacon/Item, itemId: {itemId}, zoneId: {zoneId}"); + FikaPlugin.Instance.FikaLogger.LogWarning($"Did not have quest for Place Beacon/Item, itemId: {itemId}, zoneId: {zoneId}"); #endif - questName = null; - return false; - } - - /// - /// 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; + questName = null; + return false; + } + + /// + /// 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}"); + FikaPlugin.Instance.FikaLogger.LogInfo($"CoopClientSharedQuestController::ValidateQuestType: CounterCreator Type {CounterCreator.type}"); #endif - if (acceptedTypes.Contains(CounterCreator.type.ToString())) - { - return true; - } - } + if (acceptedTypes.Contains(CounterCreator.type.ToString())) + { + return true; + } + } - return false; - } - } + return false; + } + } } diff --git a/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs b/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs index 3aedb6c2..c8e6bc6d 100644 --- a/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs +++ b/Fika.Core/Coop/ClientClasses/CoopClientStatisticsManager.cs @@ -3,22 +3,22 @@ namespace Fika.Core.Coop.ClientClasses { - public sealed class CoopClientStatisticsManager(Profile profile) : LocationStatisticsCollectorAbstractClass() - { - public Profile Profile = profile; + public sealed class CoopClientStatisticsManager(Profile profile) : LocationStatisticsCollectorAbstractClass() + { + public Profile Profile = profile; - public new void Init(Profile profile, IHealthController healthController) - { - Profile_0 = Profile; - IHealthController_0 = healthController; - } + public new void Init(Profile profile, IHealthController healthController) + { + Profile_0 = Profile; + IHealthController_0 = healthController; + } - public override void ShowStatNotification(LocalizationKey localizationKey1, LocalizationKey localizationKey2, int value) - { - if (value > 0) - { - NotificationManagerClass.DisplayNotification(new StatNotificationClass(localizationKey1, localizationKey2, value)); - } - } - } + public override void ShowStatNotification(LocalizationKey localizationKey1, LocalizationKey localizationKey2, int value) + { + if (value > 0) + { + 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 fd29d6a0..b44773c0 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientFirearmController.cs @@ -12,799 +12,799 @@ namespace Fika.Core.Coop.ClientClasses { - public class CoopClientFirearmController : Player.FirearmController - { - public CoopPlayer coopPlayer; - - private void Awake() - { - coopPlayer = GetComponent(); - } - - public static CoopClientFirearmController Create(CoopPlayer player, Weapon weapon) - { - return smethod_5(player, weapon); - } - - public override void SetWeaponOverlapValue(float overlap) - { - base.SetWeaponOverlapValue(overlap); - coopPlayer.observedOverlap = overlap; - } - - public override void WeaponOverlapping() - { - base.WeaponOverlapping(); - coopPlayer.leftStanceDisabled = DisableLeftStanceByOverlap; - } - - public override Dictionary GetOperationFactoryDelegates() - { - // Check for GClass increments - Dictionary operationFactoryDelegates = base.GetOperationFactoryDelegates(); - operationFactoryDelegates[typeof(GClass1599)] = new OperationFactoryDelegate(Weapon1); - operationFactoryDelegates[typeof(GClass1600)] = new OperationFactoryDelegate(Weapon2); - operationFactoryDelegates[typeof(GClass1612)] = new OperationFactoryDelegate(Weapon3); - return operationFactoryDelegates; - } - - public Player.GClass1594 Weapon1() - { - if (Item.ReloadMode == Weapon.EReloadMode.InternalMagazine && Item.Chambers.Length == 0) - { - return new FirearmClass2(this); - } - if (Item.MustBoltBeOpennedForInternalReload) - { - return new FirearmClass3(this); - } - return new FirearmClass2(this); - } - - public Player.GClass1594 Weapon2() - { - return new FirearmClass1(this); - } - - public Player.GClass1594 Weapon3() - { - if (Item.IsFlareGun) - { - return new GClass1616(this); - } - if (Item.IsOneOff) - { - return new GClass1618(this); - } - if (Item.ReloadMode == Weapon.EReloadMode.OnlyBarrel) - { - return new GClass1615(this); - } - if (Item is GClass2711) - { - return new GClass1614(this); - } - if (!Item.BoltAction) - { - return new GClass1612(this); - } - return new FirearmClass4(this); - } - - public override bool CheckChamber() - { - bool flag = base.CheckChamber(); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - CheckChamber = true - }); - } - return flag; - } - - public override bool CheckAmmo() - { - bool flag = base.CheckAmmo(); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - CheckAmmo = true - }); - } - return flag; - } - - public override bool ChangeFireMode(Weapon.EFireMode fireMode) - { - bool flag = base.ChangeFireMode(fireMode); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ChangeFireMode = true, - FireMode = fireMode - }); - } - return flag; - } - - public override void ChangeAimingMode() - { - base.ChangeAimingMode(); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ToggleAim = true, - AimingIndex = IsAiming ? Item.AimIndex.Value : -1 - }); - } - - public override void SetAim(bool value) - { - bool isAiming = IsAiming; - bool aimingInterruptedByOverlap = AimingInterruptedByOverlap; - base.SetAim(value); - if (IsAiming != isAiming || aimingInterruptedByOverlap) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ToggleAim = true, - AimingIndex = IsAiming ? Item.AimIndex.Value : -1 - }); - } - } - - public override bool CheckFireMode() - { - bool flag = base.CheckFireMode(); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - CheckFireMode = true - }); - } - return flag; - } - - public override void DryShot(int chamberIndex = 0, bool underbarrelShot = false) - { - base.DryShot(chamberIndex, underbarrelShot); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasShotInfo = true, - ShotInfoPacket = new() - { - ShotType = EShotType.DryFire, - AmmoAfterShot = underbarrelShot ? 0 : Item.GetCurrentMagazineCount(), - ChamberIndex = chamberIndex, - UnderbarrelShot = underbarrelShot - } - }); - } - - public override bool ExamineWeapon() - { - bool flag = base.ExamineWeapon(); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ExamineWeapon = true - }); - } - return flag; - } - - public override void InitiateShot(IWeapon weapon, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, Vector3 fireportPosition, int chamberIndex, float overheat) - { - EShotType shotType = default; - - switch (weapon.MalfState.State) - { - case Weapon.EMalfunctionState.None: - shotType = EShotType.RegularShot; - break; - case Weapon.EMalfunctionState.Misfire: - shotType = EShotType.Misfire; - break; - case Weapon.EMalfunctionState.Jam: - shotType = EShotType.JamedShot; - break; - case Weapon.EMalfunctionState.HardSlide: - shotType = EShotType.HardSlidedShot; - break; - case Weapon.EMalfunctionState.SoftSlide: - shotType = EShotType.SoftSlidedShot; - break; - case Weapon.EMalfunctionState.Feed: - shotType = EShotType.Feed; - break; - } - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasShotInfo = true, - ShotInfoPacket = new() - { - ShotType = shotType, - AmmoAfterShot = weapon.GetCurrentMagazineCount(), - ShotPosition = shotPosition, - ShotDirection = shotDirection, - FireportPosition = fireportPosition, - ChamberIndex = chamberIndex, - Overheat = overheat, - UnderbarrelShot = Weapon.IsUnderBarrelDeviceActive, - AmmoTemplate = ammo.AmmoTemplate._id, - LastShotOverheat = weapon.MalfState.LastShotOverheat, - LastShotTime = weapon.MalfState.LastShotTime, - SlideOnOverheatReached = weapon.MalfState.SlideOnOverheatReached - } - }); - - coopPlayer.StatisticsManager.OnShot(Weapon, ammo); - - base.InitiateShot(weapon, ammo, shotPosition, shotDirection, fireportPosition, chamberIndex, overheat); - } - - public override void QuickReloadMag(MagazineClass magazine, Callback callback) - { - if (!CanStartReload()) - { - return; - } - - base.QuickReloadMag(magazine, callback); - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasQuickReloadMagPacket = true, - QuickReloadMagPacket = new() - { - Reload = true, - MagId = magazine.Id - } - }); - } - - public override void ReloadBarrels(AmmoPackReloadingClass ammoPack, ItemAddressClass placeToPutContainedAmmoMagazine, Callback callback) - { - if (!CanStartReload() && ammoPack.AmmoCount < 1) - { - return; - } - - ReloadBarrelsHandler handler = new(coopPlayer, placeToPutContainedAmmoMagazine, ammoPack); - CurrentOperation.ReloadBarrels(ammoPack, placeToPutContainedAmmoMagazine, callback, new Callback(handler.Process)); - } - - public override void ReloadCylinderMagazine(AmmoPackReloadingClass ammoPack, Callback callback, bool quickReload = false) - { - if (Blindfire) - { - return; - } - if (Item.GetCurrentMagazine() == null) - { - return; - } - if (!CanStartReload()) - { - return; - } - - ReloadCylinderMagazineHandler handler = new(coopPlayer, this, quickReload, ammoPack.GetReloadingAmmoIds(), [], (CylinderMagazineClass)Item.GetCurrentMagazine()); - Weapon.GetShellsIndexes(handler.shellsIndexes); - CurrentOperation.ReloadCylinderMagazine(ammoPack, callback, new Callback(handler.Process), handler.quickReload); - } - - public override void ReloadGrenadeLauncher(AmmoPackReloadingClass ammoPack, Callback callback) - { - if (!CanStartReload()) - { - return; - } - - CurrentOperation.ReloadGrenadeLauncher(ammoPack, callback); - - string[] reloadingAmmoIds = ammoPack.GetReloadingAmmoIds(); - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ReloadLauncher = new() - { - Reload = true, - AmmoIds = reloadingAmmoIds - } - }); - } - - public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback callback) - { - if (!CanStartReload() || Blindfire) - { - return; - } - - ReloadMagHandler handler = new(coopPlayer, gridItemAddress, magazine); - CurrentOperation.ReloadMag(magazine, gridItemAddress, callback, new Callback(handler.Process)); - } - - public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback callback) - { - if (Item.GetCurrentMagazine() == null) - { - return; - } - if (!CanStartReload()) - { - return; - } - - ReloadWithAmmoHandler handler = new(coopPlayer, ammoPack.GetReloadingAmmoIds()); - CurrentOperation.ReloadWithAmmo(ammoPack, callback, new Callback(handler.Process)); - } - - public override void SetLightsState(FirearmLightStateStruct[] lightsStates, bool force = false) - { - if (force || CurrentOperation.CanChangeLightState(lightsStates)) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ToggleTacticalCombo = true, - LightStatesPacket = new() - { - Amount = lightsStates.Length, - LightStates = lightsStates - } - }); - } - base.SetLightsState(lightsStates, force); - } - - 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) - { - if (!CurrentOperation.CanChangeScopeStates(scopeStates)) - { - return; - } - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ChangeSightMode = true, - ScopeStatesPacket = new() - { - Amount = scopeStates.Length, - FirearmScopeStateStruct = scopeStates - } - }); - } - - public override void ShotMisfired(BulletClass ammo, Weapon.EMalfunctionState malfunctionState, float overheat) - { - EShotType shotType = new(); - - switch (malfunctionState) - { - case Weapon.EMalfunctionState.Misfire: - shotType = EShotType.Misfire; - break; - case Weapon.EMalfunctionState.Jam: - shotType = EShotType.JamedShot; - break; - case Weapon.EMalfunctionState.HardSlide: - shotType = EShotType.HardSlidedShot; - break; - case Weapon.EMalfunctionState.SoftSlide: - shotType = EShotType.SoftSlidedShot; - break; - case Weapon.EMalfunctionState.Feed: - shotType = EShotType.Feed; - break; - } - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasShotInfo = true, - ShotInfoPacket = new() - { - ShotType = shotType, - AmmoAfterShot = Item.GetCurrentMagazineCount(), - Overheat = overheat, - AmmoTemplate = ammo.TemplateId - } - }); - - base.ShotMisfired(ammo, malfunctionState, overheat); - } - - public override bool ToggleLauncher() - { - bool flag = base.ToggleLauncher(); - if (flag) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ToggleLauncher = true - }); - } - return flag; - } - - public override void Loot(bool p) - { - base.Loot(p); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - Loot = p - }); - } - - public override void SetInventoryOpened(bool opened) - { - base.SetInventoryOpened(opened); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - EnableInventory = true, - InventoryStatus = opened - }); - } - - public override void ChangeLeftStance() - { - base.ChangeLeftStance(); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasStanceChange = true, - LeftStanceState = coopPlayer.MovementContext.LeftStanceEnabled - }); - } - - public override void SendStartOneShotFire() - { - base.SendStartOneShotFire(); - } - - public override void CreateFlareShot(BulletClass flareItem, Vector3 shotPosition, Vector3 forward) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasFlareShot = true, - FlareShotPacket = new() - { - ShotPosition = shotPosition, - ShotForward = forward, - AmmoTemplateId = flareItem.TemplateId - } - }); - base.CreateFlareShot(flareItem, shotPosition, forward); - } - - private void SendAbortReloadPacket(int amount) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadWithAmmoPacket = true, - ReloadWithAmmo = new() - { - Reload = true, - Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.AbortReload, - AmmoLoadedToMag = amount - } - }); - } - - public override void RollCylinder(bool rollToZeroCamora) - { - if (Blindfire || IsAiming) - { - return; - } - - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasRollCylinder = true, - RollToZeroCamora = rollToZeroCamora - }); - - CurrentOperation.RollCylinder(null, rollToZeroCamora); - } - - private void SendEndReloadPacket(int amount) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadWithAmmoPacket = true, - ReloadWithAmmo = new() - { - Reload = true, - Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.EndReload, - AmmoLoadedToMag = amount - } - }); - } - - private void SendBoltActionReloadPacket() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - ReloadBoltAction = true - }); - } - - private class FirearmClass1(Player.FirearmController controller) : GClass1600(controller) - { - public override void SetTriggerPressed(bool pressed) - { - bool bool_ = bool_1; - base.SetTriggerPressed(pressed); - if (bool_1 && !bool_) - { - coopClientFirearmController.SendAbortReloadPacket(int_0); - } - } - - public override void SwitchToIdle() - { - coopClientFirearmController.SendEndReloadPacket(int_0); - method_13(); - base.SwitchToIdle(); - } - - private CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; - } - - private class FirearmClass2(Player.FirearmController controller) : GClass1601(controller) - { - public override void SetTriggerPressed(bool pressed) - { - bool bool_ = bool_1; - base.SetTriggerPressed(pressed); - if (bool_1 && !bool_) - { - coopClientFirearmController.SendAbortReloadPacket(int_0); - } - } - - public override void SwitchToIdle() - { - coopClientFirearmController.SendEndReloadPacket(int_0); - base.SwitchToIdle(); - } - - private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; - } - - private class FirearmClass3(Player.FirearmController controller) : GClass1602(controller) - { - public override void SetTriggerPressed(bool pressed) - { - bool bool_ = bool_1; - base.SetTriggerPressed(pressed); - if (bool_1 && !bool_) - { - coopClientFirearmController.SendAbortReloadPacket(int_0); - } - } - - public override void SwitchToIdle() - { - coopClientFirearmController.SendEndReloadPacket(int_0); - base.SwitchToIdle(); - } - - private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; - } - - // Check for GClass increments - private class FirearmClass4(Player.FirearmController controller) : GClass1613(controller) - { - public override void Start() - { - base.Start(); - SendBoltActionReloadPacket(!firearmController_0.IsTriggerPressed); - } - - public override void SetTriggerPressed(bool pressed) - { - base.SetTriggerPressed(pressed); - SendBoltActionReloadPacket(!firearmController_0.IsTriggerPressed); - } - - public override void SetInventoryOpened(bool opened) - { - base.SetInventoryOpened(opened); - SendBoltActionReloadPacket(true); - } - - public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback finishCallback, Callback startCallback) - { - base.ReloadMag(magazine, gridItemAddress, finishCallback, startCallback); - SendBoltActionReloadPacket(true); - } - - public override void QuickReloadMag(MagazineClass magazine, Callback finishCallback, Callback startCallback) - { - base.QuickReloadMag(magazine, finishCallback, startCallback); - SendBoltActionReloadPacket(true); - } - - public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback finishCallback, Callback startCallback) - { - base.ReloadWithAmmo(ammoPack, finishCallback, startCallback); - SendBoltActionReloadPacket(true); - } - - private void SendBoltActionReloadPacket(bool value) - { - if (!hasSent && value) - { - hasSent = true; - coopClientFirearmController.SendBoltActionReloadPacket(); - } - } - - public override void Reset() - { - base.Reset(); - hasSent = false; - } - - private CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; - private bool hasSent; - } - - private class ReloadMagHandler(CoopPlayer coopPlayer, ItemAddressClass gridItemAddress, MagazineClass magazine) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly ItemAddressClass gridItemAddress = gridItemAddress; - private readonly MagazineClass magazine = magazine; - - public void Process(IResult error) - { - GridItemAddressDescriptorClass gridItemAddressDescriptor = (gridItemAddress == null) ? null : FromObjectAbstractClass.FromGridItemAddress(gridItemAddress); - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - byte[] locationDescription; - if (gridItemAddressDescriptor != null) - { - binaryWriter.Write(gridItemAddressDescriptor); - locationDescription = memoryStream.ToArray(); - } - else - { - locationDescription = Array.Empty(); - } - - if (error.Succeed) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadMagPacket = true, - ReloadMagPacket = new() - { - Reload = true, - MagId = magazine.Id, - LocationDescription = locationDescription, - } - }); - } - } - } - - private class ReloadCylinderMagazineHandler(CoopPlayer coopPlayer, CoopClientFirearmController coopClientFirearmController, bool quickReload, string[] ammoIds, List shellsIndexes, CylinderMagazineClass cylinderMagazine) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly CoopClientFirearmController coopClientFirearmController = coopClientFirearmController; - public readonly bool quickReload = quickReload; - private readonly string[] ammoIds = ammoIds; - public readonly List shellsIndexes = shellsIndexes; - private readonly CylinderMagazineClass cylinderMagazine = cylinderMagazine; - - public void Process(IResult error) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadWithAmmoPacket = true, - ReloadWithAmmo = new() - { - Reload = true, - Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload, - AmmoIds = ammoIds - }, - HasCylinderMagPacket = true, - CylinderMag = new() - { - Changed = true, - CamoraIndex = cylinderMagazine.CurrentCamoraIndex, - HammerClosed = coopClientFirearmController.Item.CylinderHammerClosed - } - }); - } - } - - private class ReloadBarrelsHandler(CoopPlayer coopPlayer, ItemAddressClass placeToPutContainedAmmoMagazine, AmmoPackReloadingClass ammoPack) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly ItemAddressClass placeToPutContainedAmmoMagazine = placeToPutContainedAmmoMagazine; - private readonly AmmoPackReloadingClass ammoPack = ammoPack; - - public void Process(IResult error) - { - GridItemAddressDescriptorClass gridItemAddressDescriptor = (placeToPutContainedAmmoMagazine == null) ? null : FromObjectAbstractClass.FromGridItemAddress(placeToPutContainedAmmoMagazine); - - string[] ammoIds = ammoPack.GetReloadingAmmoIds(); - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - byte[] locationDescription; - if (gridItemAddressDescriptor != null) - { - binaryWriter.Write(gridItemAddressDescriptor); - locationDescription = memoryStream.ToArray(); - } - else - { - locationDescription = Array.Empty(); - } - - if (coopPlayer.HealthController.IsAlive) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadBarrelsPacket = true, - ReloadBarrels = new() - { - Reload = true, - AmmoIds = ammoIds, - LocationDescription = locationDescription, - } - }); - } - } - } - - private class ReloadWithAmmoHandler(CoopPlayer coopPlayer, string[] ammoIds) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly string[] ammoIds = ammoIds; - - public void Process(IResult error) - { - if (error.Succeed) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasReloadWithAmmoPacket = true, - ReloadWithAmmo = new() - { - Reload = true, - Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload, - AmmoIds = ammoIds - } - }); - } - } - } - } + public class CoopClientFirearmController : Player.FirearmController + { + public CoopPlayer coopPlayer; + + private void Awake() + { + coopPlayer = GetComponent(); + } + + public static CoopClientFirearmController Create(CoopPlayer player, Weapon weapon) + { + return smethod_5(player, weapon); + } + + public override void SetWeaponOverlapValue(float overlap) + { + base.SetWeaponOverlapValue(overlap); + coopPlayer.observedOverlap = overlap; + } + + public override void WeaponOverlapping() + { + base.WeaponOverlapping(); + coopPlayer.leftStanceDisabled = DisableLeftStanceByOverlap; + } + + public override Dictionary GetOperationFactoryDelegates() + { + // Check for GClass increments + Dictionary operationFactoryDelegates = base.GetOperationFactoryDelegates(); + operationFactoryDelegates[typeof(GClass1599)] = new OperationFactoryDelegate(Weapon1); + operationFactoryDelegates[typeof(GClass1600)] = new OperationFactoryDelegate(Weapon2); + operationFactoryDelegates[typeof(GClass1612)] = new OperationFactoryDelegate(Weapon3); + return operationFactoryDelegates; + } + + public Player.GClass1594 Weapon1() + { + if (Item.ReloadMode == Weapon.EReloadMode.InternalMagazine && Item.Chambers.Length == 0) + { + return new FirearmClass2(this); + } + if (Item.MustBoltBeOpennedForInternalReload) + { + return new FirearmClass3(this); + } + return new FirearmClass2(this); + } + + public Player.GClass1594 Weapon2() + { + return new FirearmClass1(this); + } + + public Player.GClass1594 Weapon3() + { + if (Item.IsFlareGun) + { + return new GClass1616(this); + } + if (Item.IsOneOff) + { + return new GClass1618(this); + } + if (Item.ReloadMode == Weapon.EReloadMode.OnlyBarrel) + { + return new GClass1615(this); + } + if (Item is GClass2711) + { + return new GClass1614(this); + } + if (!Item.BoltAction) + { + return new GClass1612(this); + } + return new FirearmClass4(this); + } + + public override bool CheckChamber() + { + bool flag = base.CheckChamber(); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + CheckChamber = true + }); + } + return flag; + } + + public override bool CheckAmmo() + { + bool flag = base.CheckAmmo(); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + CheckAmmo = true + }); + } + return flag; + } + + public override bool ChangeFireMode(Weapon.EFireMode fireMode) + { + bool flag = base.ChangeFireMode(fireMode); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ChangeFireMode = true, + FireMode = fireMode + }); + } + return flag; + } + + public override void ChangeAimingMode() + { + base.ChangeAimingMode(); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ToggleAim = true, + AimingIndex = IsAiming ? Item.AimIndex.Value : -1 + }); + } + + public override void SetAim(bool value) + { + bool isAiming = IsAiming; + bool aimingInterruptedByOverlap = AimingInterruptedByOverlap; + base.SetAim(value); + if (IsAiming != isAiming || aimingInterruptedByOverlap) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ToggleAim = true, + AimingIndex = IsAiming ? Item.AimIndex.Value : -1 + }); + } + } + + public override bool CheckFireMode() + { + bool flag = base.CheckFireMode(); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + CheckFireMode = true + }); + } + return flag; + } + + public override void DryShot(int chamberIndex = 0, bool underbarrelShot = false) + { + base.DryShot(chamberIndex, underbarrelShot); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasShotInfo = true, + ShotInfoPacket = new() + { + ShotType = EShotType.DryFire, + AmmoAfterShot = underbarrelShot ? 0 : Item.GetCurrentMagazineCount(), + ChamberIndex = chamberIndex, + UnderbarrelShot = underbarrelShot + } + }); + } + + public override bool ExamineWeapon() + { + bool flag = base.ExamineWeapon(); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ExamineWeapon = true + }); + } + return flag; + } + + public override void InitiateShot(IWeapon weapon, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, Vector3 fireportPosition, int chamberIndex, float overheat) + { + EShotType shotType = default; + + switch (weapon.MalfState.State) + { + case Weapon.EMalfunctionState.None: + shotType = EShotType.RegularShot; + break; + case Weapon.EMalfunctionState.Misfire: + shotType = EShotType.Misfire; + break; + case Weapon.EMalfunctionState.Jam: + shotType = EShotType.JamedShot; + break; + case Weapon.EMalfunctionState.HardSlide: + shotType = EShotType.HardSlidedShot; + break; + case Weapon.EMalfunctionState.SoftSlide: + shotType = EShotType.SoftSlidedShot; + break; + case Weapon.EMalfunctionState.Feed: + shotType = EShotType.Feed; + break; + } + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasShotInfo = true, + ShotInfoPacket = new() + { + ShotType = shotType, + AmmoAfterShot = weapon.GetCurrentMagazineCount(), + ShotPosition = shotPosition, + ShotDirection = shotDirection, + FireportPosition = fireportPosition, + ChamberIndex = chamberIndex, + Overheat = overheat, + UnderbarrelShot = Weapon.IsUnderBarrelDeviceActive, + AmmoTemplate = ammo.AmmoTemplate._id, + LastShotOverheat = weapon.MalfState.LastShotOverheat, + LastShotTime = weapon.MalfState.LastShotTime, + SlideOnOverheatReached = weapon.MalfState.SlideOnOverheatReached + } + }); + + coopPlayer.StatisticsManager.OnShot(Weapon, ammo); + + base.InitiateShot(weapon, ammo, shotPosition, shotDirection, fireportPosition, chamberIndex, overheat); + } + + public override void QuickReloadMag(MagazineClass magazine, Callback callback) + { + if (!CanStartReload()) + { + return; + } + + base.QuickReloadMag(magazine, callback); + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasQuickReloadMagPacket = true, + QuickReloadMagPacket = new() + { + Reload = true, + MagId = magazine.Id + } + }); + } + + public override void ReloadBarrels(AmmoPackReloadingClass ammoPack, ItemAddressClass placeToPutContainedAmmoMagazine, Callback callback) + { + if (!CanStartReload() && ammoPack.AmmoCount < 1) + { + return; + } + + ReloadBarrelsHandler handler = new(coopPlayer, placeToPutContainedAmmoMagazine, ammoPack); + CurrentOperation.ReloadBarrels(ammoPack, placeToPutContainedAmmoMagazine, callback, new Callback(handler.Process)); + } + + public override void ReloadCylinderMagazine(AmmoPackReloadingClass ammoPack, Callback callback, bool quickReload = false) + { + if (Blindfire) + { + return; + } + if (Item.GetCurrentMagazine() == null) + { + return; + } + if (!CanStartReload()) + { + return; + } + + ReloadCylinderMagazineHandler handler = new(coopPlayer, this, quickReload, ammoPack.GetReloadingAmmoIds(), [], (CylinderMagazineClass)Item.GetCurrentMagazine()); + Weapon.GetShellsIndexes(handler.shellsIndexes); + CurrentOperation.ReloadCylinderMagazine(ammoPack, callback, new Callback(handler.Process), handler.quickReload); + } + + public override void ReloadGrenadeLauncher(AmmoPackReloadingClass ammoPack, Callback callback) + { + if (!CanStartReload()) + { + return; + } + + CurrentOperation.ReloadGrenadeLauncher(ammoPack, callback); + + string[] reloadingAmmoIds = ammoPack.GetReloadingAmmoIds(); + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ReloadLauncher = new() + { + Reload = true, + AmmoIds = reloadingAmmoIds + } + }); + } + + public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback callback) + { + if (!CanStartReload() || Blindfire) + { + return; + } + + ReloadMagHandler handler = new(coopPlayer, gridItemAddress, magazine); + CurrentOperation.ReloadMag(magazine, gridItemAddress, callback, new Callback(handler.Process)); + } + + public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback callback) + { + if (Item.GetCurrentMagazine() == null) + { + return; + } + if (!CanStartReload()) + { + return; + } + + ReloadWithAmmoHandler handler = new(coopPlayer, ammoPack.GetReloadingAmmoIds()); + CurrentOperation.ReloadWithAmmo(ammoPack, callback, new Callback(handler.Process)); + } + + public override void SetLightsState(FirearmLightStateStruct[] lightsStates, bool force = false) + { + if (force || CurrentOperation.CanChangeLightState(lightsStates)) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ToggleTacticalCombo = true, + LightStatesPacket = new() + { + Amount = lightsStates.Length, + LightStates = lightsStates + } + }); + } + base.SetLightsState(lightsStates, force); + } + + 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) + { + if (!CurrentOperation.CanChangeScopeStates(scopeStates)) + { + return; + } + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ChangeSightMode = true, + ScopeStatesPacket = new() + { + Amount = scopeStates.Length, + FirearmScopeStateStruct = scopeStates + } + }); + } + + public override void ShotMisfired(BulletClass ammo, Weapon.EMalfunctionState malfunctionState, float overheat) + { + EShotType shotType = new(); + + switch (malfunctionState) + { + case Weapon.EMalfunctionState.Misfire: + shotType = EShotType.Misfire; + break; + case Weapon.EMalfunctionState.Jam: + shotType = EShotType.JamedShot; + break; + case Weapon.EMalfunctionState.HardSlide: + shotType = EShotType.HardSlidedShot; + break; + case Weapon.EMalfunctionState.SoftSlide: + shotType = EShotType.SoftSlidedShot; + break; + case Weapon.EMalfunctionState.Feed: + shotType = EShotType.Feed; + break; + } + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasShotInfo = true, + ShotInfoPacket = new() + { + ShotType = shotType, + AmmoAfterShot = Item.GetCurrentMagazineCount(), + Overheat = overheat, + AmmoTemplate = ammo.TemplateId + } + }); + + base.ShotMisfired(ammo, malfunctionState, overheat); + } + + public override bool ToggleLauncher() + { + bool flag = base.ToggleLauncher(); + if (flag) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ToggleLauncher = true + }); + } + return flag; + } + + public override void Loot(bool p) + { + base.Loot(p); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + Loot = p + }); + } + + public override void SetInventoryOpened(bool opened) + { + base.SetInventoryOpened(opened); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + EnableInventory = true, + InventoryStatus = opened + }); + } + + public override void ChangeLeftStance() + { + base.ChangeLeftStance(); + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasStanceChange = true, + LeftStanceState = coopPlayer.MovementContext.LeftStanceEnabled + }); + } + + public override void SendStartOneShotFire() + { + base.SendStartOneShotFire(); + } + + public override void CreateFlareShot(BulletClass flareItem, Vector3 shotPosition, Vector3 forward) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasFlareShot = true, + FlareShotPacket = new() + { + ShotPosition = shotPosition, + ShotForward = forward, + AmmoTemplateId = flareItem.TemplateId + } + }); + base.CreateFlareShot(flareItem, shotPosition, forward); + } + + private void SendAbortReloadPacket(int amount) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadWithAmmoPacket = true, + ReloadWithAmmo = new() + { + Reload = true, + Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.AbortReload, + AmmoLoadedToMag = amount + } + }); + } + + public override void RollCylinder(bool rollToZeroCamora) + { + if (Blindfire || IsAiming) + { + return; + } + + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasRollCylinder = true, + RollToZeroCamora = rollToZeroCamora + }); + + CurrentOperation.RollCylinder(null, rollToZeroCamora); + } + + private void SendEndReloadPacket(int amount) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadWithAmmoPacket = true, + ReloadWithAmmo = new() + { + Reload = true, + Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.EndReload, + AmmoLoadedToMag = amount + } + }); + } + + private void SendBoltActionReloadPacket() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + ReloadBoltAction = true + }); + } + + private class FirearmClass1(Player.FirearmController controller) : GClass1600(controller) + { + public override void SetTriggerPressed(bool pressed) + { + bool bool_ = bool_1; + base.SetTriggerPressed(pressed); + if (bool_1 && !bool_) + { + coopClientFirearmController.SendAbortReloadPacket(int_0); + } + } + + public override void SwitchToIdle() + { + coopClientFirearmController.SendEndReloadPacket(int_0); + method_13(); + base.SwitchToIdle(); + } + + private CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; + } + + private class FirearmClass2(Player.FirearmController controller) : GClass1601(controller) + { + public override void SetTriggerPressed(bool pressed) + { + bool bool_ = bool_1; + base.SetTriggerPressed(pressed); + if (bool_1 && !bool_) + { + coopClientFirearmController.SendAbortReloadPacket(int_0); + } + } + + public override void SwitchToIdle() + { + coopClientFirearmController.SendEndReloadPacket(int_0); + base.SwitchToIdle(); + } + + private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; + } + + private class FirearmClass3(Player.FirearmController controller) : GClass1602(controller) + { + public override void SetTriggerPressed(bool pressed) + { + bool bool_ = bool_1; + base.SetTriggerPressed(pressed); + if (bool_1 && !bool_) + { + coopClientFirearmController.SendAbortReloadPacket(int_0); + } + } + + public override void SwitchToIdle() + { + coopClientFirearmController.SendEndReloadPacket(int_0); + base.SwitchToIdle(); + } + + private readonly CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; + } + + // Check for GClass increments + private class FirearmClass4(Player.FirearmController controller) : GClass1613(controller) + { + public override void Start() + { + base.Start(); + SendBoltActionReloadPacket(!firearmController_0.IsTriggerPressed); + } + + public override void SetTriggerPressed(bool pressed) + { + base.SetTriggerPressed(pressed); + SendBoltActionReloadPacket(!firearmController_0.IsTriggerPressed); + } + + public override void SetInventoryOpened(bool opened) + { + base.SetInventoryOpened(opened); + SendBoltActionReloadPacket(true); + } + + public override void ReloadMag(MagazineClass magazine, ItemAddressClass gridItemAddress, Callback finishCallback, Callback startCallback) + { + base.ReloadMag(magazine, gridItemAddress, finishCallback, startCallback); + SendBoltActionReloadPacket(true); + } + + public override void QuickReloadMag(MagazineClass magazine, Callback finishCallback, Callback startCallback) + { + base.QuickReloadMag(magazine, finishCallback, startCallback); + SendBoltActionReloadPacket(true); + } + + public override void ReloadWithAmmo(AmmoPackReloadingClass ammoPack, Callback finishCallback, Callback startCallback) + { + base.ReloadWithAmmo(ammoPack, finishCallback, startCallback); + SendBoltActionReloadPacket(true); + } + + private void SendBoltActionReloadPacket(bool value) + { + if (!hasSent && value) + { + hasSent = true; + coopClientFirearmController.SendBoltActionReloadPacket(); + } + } + + public override void Reset() + { + base.Reset(); + hasSent = false; + } + + private CoopClientFirearmController coopClientFirearmController = (CoopClientFirearmController)controller; + private bool hasSent; + } + + private class ReloadMagHandler(CoopPlayer coopPlayer, ItemAddressClass gridItemAddress, MagazineClass magazine) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly ItemAddressClass gridItemAddress = gridItemAddress; + private readonly MagazineClass magazine = magazine; + + public void Process(IResult error) + { + GridItemAddressDescriptorClass gridItemAddressDescriptor = (gridItemAddress == null) ? null : FromObjectAbstractClass.FromGridItemAddress(gridItemAddress); + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + byte[] locationDescription; + if (gridItemAddressDescriptor != null) + { + binaryWriter.Write(gridItemAddressDescriptor); + locationDescription = memoryStream.ToArray(); + } + else + { + locationDescription = Array.Empty(); + } + + if (error.Succeed) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadMagPacket = true, + ReloadMagPacket = new() + { + Reload = true, + MagId = magazine.Id, + LocationDescription = locationDescription, + } + }); + } + } + } + + private class ReloadCylinderMagazineHandler(CoopPlayer coopPlayer, CoopClientFirearmController coopClientFirearmController, bool quickReload, string[] ammoIds, List shellsIndexes, CylinderMagazineClass cylinderMagazine) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly CoopClientFirearmController coopClientFirearmController = coopClientFirearmController; + public readonly bool quickReload = quickReload; + private readonly string[] ammoIds = ammoIds; + public readonly List shellsIndexes = shellsIndexes; + private readonly CylinderMagazineClass cylinderMagazine = cylinderMagazine; + + public void Process(IResult error) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadWithAmmoPacket = true, + ReloadWithAmmo = new() + { + Reload = true, + Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload, + AmmoIds = ammoIds + }, + HasCylinderMagPacket = true, + CylinderMag = new() + { + Changed = true, + CamoraIndex = cylinderMagazine.CurrentCamoraIndex, + HammerClosed = coopClientFirearmController.Item.CylinderHammerClosed + } + }); + } + } + + private class ReloadBarrelsHandler(CoopPlayer coopPlayer, ItemAddressClass placeToPutContainedAmmoMagazine, AmmoPackReloadingClass ammoPack) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly ItemAddressClass placeToPutContainedAmmoMagazine = placeToPutContainedAmmoMagazine; + private readonly AmmoPackReloadingClass ammoPack = ammoPack; + + public void Process(IResult error) + { + GridItemAddressDescriptorClass gridItemAddressDescriptor = (placeToPutContainedAmmoMagazine == null) ? null : FromObjectAbstractClass.FromGridItemAddress(placeToPutContainedAmmoMagazine); + + string[] ammoIds = ammoPack.GetReloadingAmmoIds(); + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + byte[] locationDescription; + if (gridItemAddressDescriptor != null) + { + binaryWriter.Write(gridItemAddressDescriptor); + locationDescription = memoryStream.ToArray(); + } + else + { + locationDescription = Array.Empty(); + } + + if (coopPlayer.HealthController.IsAlive) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadBarrelsPacket = true, + ReloadBarrels = new() + { + Reload = true, + AmmoIds = ammoIds, + LocationDescription = locationDescription, + } + }); + } + } + } + + private class ReloadWithAmmoHandler(CoopPlayer coopPlayer, string[] ammoIds) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly string[] ammoIds = ammoIds; + + public void Process(IResult error) + { + if (error.Succeed) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasReloadWithAmmoPacket = true, + ReloadWithAmmo = new() + { + Reload = true, + Status = FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload, + AmmoIds = ammoIds + } + }); + } + } + } + } } diff --git a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientGrenadeController.cs b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientGrenadeController.cs index a34d3617..43e12b3f 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientGrenadeController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientGrenadeController.cs @@ -8,112 +8,112 @@ namespace Fika.Core.Coop.ClientClasses { - internal class CoopClientGrenadeController : EFT.Player.GrenadeController - { - public CoopPlayer coopPlayer; + internal class CoopClientGrenadeController : EFT.Player.GrenadeController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopClientGrenadeController Create(CoopPlayer player, GrenadeClass item) - { - return smethod_8(player, item); - } + public static CoopClientGrenadeController Create(CoopPlayer player, GrenadeClass item) + { + return smethod_8(player, item); + } - public override void ExamineWeapon() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.ExamineWeapon - } - }); - base.ExamineWeapon(); - } + public override void ExamineWeapon() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.ExamineWeapon + } + }); + base.ExamineWeapon(); + } - public override void HighThrow() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.HighThrow - } - }); - base.HighThrow(); - } + public override void HighThrow() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.HighThrow + } + }); + base.HighThrow(); + } - public override void LowThrow() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.LowThrow - } - }); - base.LowThrow(); - } + public override void LowThrow() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.LowThrow + } + }); + base.LowThrow(); + } - public override void PullRingForHighThrow() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.PullRingForHighThrow - } - }); - base.PullRingForHighThrow(); - } + public override void PullRingForHighThrow() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.PullRingForHighThrow + } + }); + base.PullRingForHighThrow(); + } - public override void PullRingForLowThrow() - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.PullRingForLowThrow - } - }); - base.PullRingForLowThrow(); - } + public override void PullRingForLowThrow() + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.PullRingForLowThrow + } + }); + base.PullRingForLowThrow(); + } - public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.None, - HasGrenade = true, - GrenadeRotation = rotation, - GrenadePosition = position, - ThrowForce = force, - LowThrow = lowThrow - } - }); - base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); - } + public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.None, + HasGrenade = true, + GrenadeRotation = rotation, + GrenadePosition = position, + ThrowForce = force, + LowThrow = lowThrow + } + }); + base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); + } - public override void ActualDrop(Result controller, float animationSpeed, Action callback, bool fastDrop) - { - // TODO: Override Class1025 + public override void ActualDrop(Result controller, float animationSpeed, Action callback, bool fastDrop) + { + // TODO: Override Class1025 - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - CancelGrenade = true - }); - base.ActualDrop(controller, animationSpeed, callback, fastDrop); - } - } + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + CancelGrenade = true + }); + base.ActualDrop(controller, animationSpeed, callback, fastDrop); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientKnifeController.cs b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientKnifeController.cs index 273560a8..01a27a66 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientKnifeController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientKnifeController.cs @@ -5,84 +5,84 @@ namespace Fika.Core.Coop.ClientClasses { - internal class CoopClientKnifeController : EFT.Player.KnifeController - { - public CoopPlayer coopPlayer; + internal class CoopClientKnifeController : EFT.Player.KnifeController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopClientKnifeController Create(CoopPlayer player, KnifeComponent item) - { - return smethod_8(player, item); - } + public static CoopClientKnifeController Create(CoopPlayer player, KnifeComponent item) + { + return smethod_8(player, item); + } - public override void ExamineWeapon() - { - base.ExamineWeapon(); + public override void ExamineWeapon() + { + base.ExamineWeapon(); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasKnifePacket = true, - KnifePacket = new() - { - Examine = true - } - }); - } + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasKnifePacket = true, + KnifePacket = new() + { + Examine = true + } + }); + } - public override bool MakeKnifeKick() - { - bool knifeKick = base.MakeKnifeKick(); + public override bool MakeKnifeKick() + { + bool knifeKick = base.MakeKnifeKick(); - if (knifeKick) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasKnifePacket = true, - KnifePacket = new() - { - Kick = knifeKick - } - }); - } + if (knifeKick) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasKnifePacket = true, + KnifePacket = new() + { + Kick = knifeKick + } + }); + } - return knifeKick; - } + return knifeKick; + } - public override bool MakeAlternativeKick() - { - bool alternateKnifeKick = base.MakeAlternativeKick(); + public override bool MakeAlternativeKick() + { + bool alternateKnifeKick = base.MakeAlternativeKick(); - if (alternateKnifeKick) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasKnifePacket = true, - KnifePacket = new() - { - AltKick = alternateKnifeKick - } - }); - } + if (alternateKnifeKick) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasKnifePacket = true, + KnifePacket = new() + { + AltKick = alternateKnifeKick + } + }); + } - return alternateKnifeKick; - } + return alternateKnifeKick; + } - public override void BrakeCombo() - { - base.BrakeCombo(); + public override void BrakeCombo() + { + base.BrakeCombo(); - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasKnifePacket = true, - KnifePacket = new() - { - BreakCombo = true - } - }); - } - } + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasKnifePacket = true, + KnifePacket = new() + { + BreakCombo = true + } + }); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientQuickGrenadeController.cs b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientQuickGrenadeController.cs index aa1d8316..19865e7c 100644 --- a/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientQuickGrenadeController.cs +++ b/Fika.Core/Coop/ClientClasses/HandsControllers/CoopClientQuickGrenadeController.cs @@ -6,40 +6,40 @@ namespace Fika.Core.Coop.ClientClasses { - /// - /// This is only used by AI - /// - internal class CoopClientQuickGrenadeController : EFT.Player.QuickGrenadeThrowController - { - public CoopPlayer coopPlayer; + /// + /// This is only used by AI + /// + internal class CoopClientQuickGrenadeController : EFT.Player.QuickGrenadeThrowController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopClientQuickGrenadeController Create(CoopPlayer player, GrenadeClass item) - { - return smethod_8(player, item); - } + public static CoopClientQuickGrenadeController Create(CoopPlayer player, GrenadeClass item) + { + return smethod_8(player, item); + } - public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - coopPlayer.PacketSender.FirearmPackets.Enqueue(new() - { - HasGrenadePacket = true, - GrenadePacket = new() - { - PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.None, - HasGrenade = true, - GrenadeRotation = rotation, - GrenadePosition = position, - ThrowForce = force, - LowThrow = lowThrow - } - }); + public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + coopPlayer.PacketSender.FirearmPackets.Enqueue(new() + { + HasGrenadePacket = true, + GrenadePacket = new() + { + PacketType = FikaSerialization.GrenadePacket.GrenadePacketType.None, + HasGrenade = true, + GrenadeRotation = rotation, + GrenadePosition = position, + ThrowForce = force, + LowThrow = lowThrow + } + }); - base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); - } - } + base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs b/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs index edaa4a4c..5885ba84 100644 --- a/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs +++ b/Fika.Core/Coop/ClientClasses/NoInertiaMovementContext.cs @@ -5,53 +5,53 @@ namespace Fika.Core.Coop.ClientClasses { - /// - /// Used to simulate having near no inertia - /// - public class NoInertiaMovementContext : ClientMovementContext - { - public new static NoInertiaMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) - { - NoInertiaMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); - return movementContext; - } + /// + /// Used to simulate having near no inertia + /// + public class NoInertiaMovementContext : ClientMovementContext + { + public new static NoInertiaMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) + { + NoInertiaMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); + return movementContext; + } - public override void Init() - { - base.Init(); - TiltInertia = 0.22f; - WalkInertia = 0.005f; - SprintBrakeInertia = 0f; - } + public override void Init() + { + base.Init(); + TiltInertia = 0.22f; + WalkInertia = 0.005f; + SprintBrakeInertia = 0f; + } - public override void WeightRelatedValuesUpdated() - { - if (_player.ProceduralWeaponAnimation != null) - { - _player.ProceduralWeaponAnimation.Overweight = _player.Physical.Overweight; - _player.ProceduralWeaponAnimation.UpdateSwayFactors(); - _player.ProceduralWeaponAnimation.UpdateSwaySettings(); - _player.ProceduralWeaponAnimation.WeaponFlipSpeed = InertiaSettings.WeaponFlipSpeed.Evaluate(_player.Physical.Inertia); - } - UpdateCovertEfficiency(_player.MovementContext.ClampedSpeed, true); - _player.UpdateStepSoundRolloff(); - _player.HealthController.FallSafeHeight = Mathf.Lerp(Singleton.Instance.Health.Falling.SafeHeight, Singleton.Instance.Stamina.SafeHeightOverweight, _player.Physical.Overweight); - PlayerAnimatorTransitionSpeed = TransitionSpeed; - if (PoseLevel > _player.Physical.MaxPoseLevel && CurrentState is MovementState movementState) - { - movementState.ChangePose(_player.Physical.MaxPoseLevel - PoseLevel); - } - if (_player.PoseMemo > _player.Physical.MaxPoseLevel) - { - _player.PoseMemo = _player.Physical.MaxPoseLevel; - } - float walkSpeedLimit = _player.Physical.WalkSpeedLimit; - RemoveStateSpeedLimit(Player.ESpeedLimit.Weight); - if (walkSpeedLimit < 1f) - { - AddStateSpeedLimit(walkSpeedLimit * MaxSpeed, Player.ESpeedLimit.Weight); - } - UpdateCharacterControllerSpeedLimit(); - } - } + public override void WeightRelatedValuesUpdated() + { + if (_player.ProceduralWeaponAnimation != null) + { + _player.ProceduralWeaponAnimation.Overweight = _player.Physical.Overweight; + _player.ProceduralWeaponAnimation.UpdateSwayFactors(); + _player.ProceduralWeaponAnimation.UpdateSwaySettings(); + _player.ProceduralWeaponAnimation.WeaponFlipSpeed = InertiaSettings.WeaponFlipSpeed.Evaluate(_player.Physical.Inertia); + } + UpdateCovertEfficiency(_player.MovementContext.ClampedSpeed, true); + _player.UpdateStepSoundRolloff(); + _player.HealthController.FallSafeHeight = Mathf.Lerp(Singleton.Instance.Health.Falling.SafeHeight, Singleton.Instance.Stamina.SafeHeightOverweight, _player.Physical.Overweight); + PlayerAnimatorTransitionSpeed = TransitionSpeed; + if (PoseLevel > _player.Physical.MaxPoseLevel && CurrentState is MovementState movementState) + { + movementState.ChangePose(_player.Physical.MaxPoseLevel - PoseLevel); + } + if (_player.PoseMemo > _player.Physical.MaxPoseLevel) + { + _player.PoseMemo = _player.Physical.MaxPoseLevel; + } + float walkSpeedLimit = _player.Physical.WalkSpeedLimit; + RemoveStateSpeedLimit(Player.ESpeedLimit.Weight); + if (walkSpeedLimit < 1f) + { + AddStateSpeedLimit(walkSpeedLimit * MaxSpeed, Player.ESpeedLimit.Weight); + } + UpdateCharacterControllerSpeedLimit(); + } + } } diff --git a/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs b/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs index 41718af3..2acf01d3 100644 --- a/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs +++ b/Fika.Core/Coop/ClientClasses/NoInertiaPhysical.cs @@ -5,58 +5,58 @@ namespace Fika.Core.Coop.ClientClasses { - /// - /// Currently unused - /// - public class NoInertiaPhysical : PlayerPhysicalClass - { - private CoopPlayer coopPlayer; + /// + /// Currently unused + /// + public class NoInertiaPhysical : PlayerPhysicalClass + { + private CoopPlayer coopPlayer; - public override void Init(IPlayer player) - { - base.Init(player); - coopPlayer = (CoopPlayer)player; - } + 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); - } - } + 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 6abd8740..73ca28af 100644 --- a/Fika.Core/Coop/Components/CoopExfilManager.cs +++ b/Fika.Core/Coop/Components/CoopExfilManager.cs @@ -13,215 +13,215 @@ namespace Fika.Core.Coop.Components { - internal class CoopExfilManager : MonoBehaviour - { - private CoopGame game; - private List playerHandlers; - private List countdownPoints; - private ExfiltrationPoint[] exfiltrationPoints; - private CarExtraction carExfil = null; - - protected void Awake() - { - game = gameObject.GetComponent(); - playerHandlers = []; - countdownPoints = []; - exfiltrationPoints = []; - carExfil = FindObjectOfType(); - } - - protected void Update() - { - if (exfiltrationPoints == null) - { - return; - } - - for (int i = 0; i < playerHandlers.Count; i++) - { - ExtractionPlayerHandler playerHandler = playerHandlers[i]; - if (playerHandler.startTime + playerHandler.point.Settings.ExfiltrationTime - game.PastTime <= 0) - { - playerHandlers.Remove(playerHandler); - game.MyExitLocation = playerHandler.point.Settings.Name; - game.Extract(playerHandler.player, playerHandler.point); - } - } - - for (int i = 0; i < countdownPoints.Count; i++) - { - ExfiltrationPoint exfiltrationPoint = countdownPoints[i]; - if (game.PastTime - exfiltrationPoint.ExfiltrationStartTime > exfiltrationPoint.Settings.ExfiltrationTime) - { - foreach (Player player in exfiltrationPoint.Entered.ToArray()) - { - if (player == null) - { - continue; - } - - if (!player.IsYourPlayer) - { - continue; - } - - if (!player.HealthController.IsAlive) - { - continue; - } - - if (!exfiltrationPoint.UnmetRequirements(player).Any()) - { - game.MyExitLocation = exfiltrationPoint.Settings.Name; - game.Extract((CoopPlayer)player, exfiltrationPoint); - } - } - - if (carExfil != null) - { - if (carExfil.Subscribee == exfiltrationPoint) - { - carExfil.Play(); - } - } - - exfiltrationPoint.ExternalSetStatus(EExfiltrationStatus.NotPresent); - countdownPoints.Remove(exfiltrationPoint); - } - } - } - - public void Run(ExfiltrationPoint[] exfilPoints) - { - foreach (ExfiltrationPoint exfiltrationPoint in exfilPoints) - { - exfiltrationPoint.OnStartExtraction += ExfiltrationPoint_OnStartExtraction; - exfiltrationPoint.OnCancelExtraction += ExfiltrationPoint_OnCancelExtraction; - exfiltrationPoint.OnStatusChanged += ExfiltrationPoint_OnStatusChanged; - game.UpdateExfiltrationUi(exfiltrationPoint, false, true); - if (FikaPlugin.Instance.DynamicVExfils && exfiltrationPoint.Settings.PlayersCount > 0 && exfiltrationPoint.Settings.PlayersCount < FikaBackendUtils.HostExpectedNumberOfPlayers) - { - exfiltrationPoint.Settings.PlayersCount = FikaBackendUtils.HostExpectedNumberOfPlayers; - } - } - - exfiltrationPoints = exfilPoints; - } - - public void Stop() - { - playerHandlers.Clear(); - countdownPoints.Clear(); - - if (exfiltrationPoints == null) - { - return; - } - - foreach (ExfiltrationPoint exfiltrationPoint in exfiltrationPoints) - { - exfiltrationPoint.OnStartExtraction -= ExfiltrationPoint_OnStartExtraction; - exfiltrationPoint.OnCancelExtraction -= ExfiltrationPoint_OnCancelExtraction; - exfiltrationPoint.OnStatusChanged -= ExfiltrationPoint_OnStatusChanged; - exfiltrationPoint.Disable(); - } - } - - public void UpdateExfilPointFromServer(ExfiltrationPoint point, bool enable) - { - if (enable) - { - point.OnStartExtraction += ExfiltrationPoint_OnStartExtraction; - point.OnCancelExtraction += ExfiltrationPoint_OnCancelExtraction; - point.OnStatusChanged += ExfiltrationPoint_OnStatusChanged; - } - else - { - point.OnStartExtraction -= ExfiltrationPoint_OnStartExtraction; - point.OnCancelExtraction -= ExfiltrationPoint_OnCancelExtraction; - point.OnStatusChanged -= ExfiltrationPoint_OnStatusChanged; - } - } - - private void ExfiltrationPoint_OnCancelExtraction(ExfiltrationPoint point, Player player) - { - if (!player.IsYourPlayer) - { - return; - } - - ExtractionPlayerHandler extractionPlayerHandler = playerHandlers.FirstOrDefault(x => x.player == player); - if (extractionPlayerHandler != null) - { - playerHandlers.Remove(extractionPlayerHandler); - } - } - - private void ExfiltrationPoint_OnStartExtraction(ExfiltrationPoint point, Player player) - { - if (!player.IsYourPlayer) - { - return; - } - - if (playerHandlers.All(x => x.player != player)) - { - playerHandlers.Add(new(player, point, game.PastTime)); - } - } - - private void ExfiltrationPoint_OnStatusChanged(ExfiltrationPoint point, EExfiltrationStatus prevStatus) - { - bool isCounting = countdownPoints.Contains(point); - if (isCounting && point.Status != EExfiltrationStatus.Countdown) - { - point.ExfiltrationStartTime = -100; - countdownPoints.Remove(point); - - if (carExfil != null) - { - if (carExfil.Subscribee == point) - { - carExfil.Play(); - } - } - } - - if (!isCounting && point.Status == EExfiltrationStatus.Countdown) - { - if (point.ExfiltrationStartTime is <= 0 and > -90) - { - point.ExfiltrationStartTime = game.PastTime; - - CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - GenericPacket packet = new(EPackageType.ExfilCountdown) - { - NetId = mainPlayer.NetId, - ExfilName = point.Settings.Name, - ExfilStartTime = point.ExfiltrationStartTime - }; - - NetDataWriter writer = mainPlayer.PacketSender.Writer; - writer.Reset(); - - if (FikaBackendUtils.IsServer) - { - mainPlayer.PacketSender.Server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - else if (FikaBackendUtils.IsClient) - { - mainPlayer.PacketSender.Client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - } - countdownPoints.Add(point); - } - } - - private class ExtractionPlayerHandler(Player player, ExfiltrationPoint point, float startTime) - { - public CoopPlayer player = (CoopPlayer)player; - public ExfiltrationPoint point = point; - public float startTime = startTime; - } - } + internal class CoopExfilManager : MonoBehaviour + { + private CoopGame game; + private List playerHandlers; + private List countdownPoints; + private ExfiltrationPoint[] exfiltrationPoints; + private CarExtraction carExfil = null; + + protected void Awake() + { + game = gameObject.GetComponent(); + playerHandlers = []; + countdownPoints = []; + exfiltrationPoints = []; + carExfil = FindObjectOfType(); + } + + protected void Update() + { + if (exfiltrationPoints == null) + { + return; + } + + for (int i = 0; i < playerHandlers.Count; i++) + { + ExtractionPlayerHandler playerHandler = playerHandlers[i]; + if (playerHandler.startTime + playerHandler.point.Settings.ExfiltrationTime - game.PastTime <= 0) + { + playerHandlers.Remove(playerHandler); + game.MyExitLocation = playerHandler.point.Settings.Name; + game.Extract(playerHandler.player, playerHandler.point); + } + } + + for (int i = 0; i < countdownPoints.Count; i++) + { + ExfiltrationPoint exfiltrationPoint = countdownPoints[i]; + if (game.PastTime - exfiltrationPoint.ExfiltrationStartTime > exfiltrationPoint.Settings.ExfiltrationTime) + { + foreach (Player player in exfiltrationPoint.Entered.ToArray()) + { + if (player == null) + { + continue; + } + + if (!player.IsYourPlayer) + { + continue; + } + + if (!player.HealthController.IsAlive) + { + continue; + } + + if (!exfiltrationPoint.UnmetRequirements(player).Any()) + { + game.MyExitLocation = exfiltrationPoint.Settings.Name; + game.Extract((CoopPlayer)player, exfiltrationPoint); + } + } + + if (carExfil != null) + { + if (carExfil.Subscribee == exfiltrationPoint) + { + carExfil.Play(); + } + } + + exfiltrationPoint.ExternalSetStatus(EExfiltrationStatus.NotPresent); + countdownPoints.Remove(exfiltrationPoint); + } + } + } + + public void Run(ExfiltrationPoint[] exfilPoints) + { + foreach (ExfiltrationPoint exfiltrationPoint in exfilPoints) + { + exfiltrationPoint.OnStartExtraction += ExfiltrationPoint_OnStartExtraction; + exfiltrationPoint.OnCancelExtraction += ExfiltrationPoint_OnCancelExtraction; + exfiltrationPoint.OnStatusChanged += ExfiltrationPoint_OnStatusChanged; + game.UpdateExfiltrationUi(exfiltrationPoint, false, true); + if (FikaPlugin.Instance.DynamicVExfils && exfiltrationPoint.Settings.PlayersCount > 0 && exfiltrationPoint.Settings.PlayersCount < FikaBackendUtils.HostExpectedNumberOfPlayers) + { + exfiltrationPoint.Settings.PlayersCount = FikaBackendUtils.HostExpectedNumberOfPlayers; + } + } + + exfiltrationPoints = exfilPoints; + } + + public void Stop() + { + playerHandlers.Clear(); + countdownPoints.Clear(); + + if (exfiltrationPoints == null) + { + return; + } + + foreach (ExfiltrationPoint exfiltrationPoint in exfiltrationPoints) + { + exfiltrationPoint.OnStartExtraction -= ExfiltrationPoint_OnStartExtraction; + exfiltrationPoint.OnCancelExtraction -= ExfiltrationPoint_OnCancelExtraction; + exfiltrationPoint.OnStatusChanged -= ExfiltrationPoint_OnStatusChanged; + exfiltrationPoint.Disable(); + } + } + + public void UpdateExfilPointFromServer(ExfiltrationPoint point, bool enable) + { + if (enable) + { + point.OnStartExtraction += ExfiltrationPoint_OnStartExtraction; + point.OnCancelExtraction += ExfiltrationPoint_OnCancelExtraction; + point.OnStatusChanged += ExfiltrationPoint_OnStatusChanged; + } + else + { + point.OnStartExtraction -= ExfiltrationPoint_OnStartExtraction; + point.OnCancelExtraction -= ExfiltrationPoint_OnCancelExtraction; + point.OnStatusChanged -= ExfiltrationPoint_OnStatusChanged; + } + } + + private void ExfiltrationPoint_OnCancelExtraction(ExfiltrationPoint point, Player player) + { + if (!player.IsYourPlayer) + { + return; + } + + ExtractionPlayerHandler extractionPlayerHandler = playerHandlers.FirstOrDefault(x => x.player == player); + if (extractionPlayerHandler != null) + { + playerHandlers.Remove(extractionPlayerHandler); + } + } + + private void ExfiltrationPoint_OnStartExtraction(ExfiltrationPoint point, Player player) + { + if (!player.IsYourPlayer) + { + return; + } + + if (playerHandlers.All(x => x.player != player)) + { + playerHandlers.Add(new(player, point, game.PastTime)); + } + } + + private void ExfiltrationPoint_OnStatusChanged(ExfiltrationPoint point, EExfiltrationStatus prevStatus) + { + bool isCounting = countdownPoints.Contains(point); + if (isCounting && point.Status != EExfiltrationStatus.Countdown) + { + point.ExfiltrationStartTime = -100; + countdownPoints.Remove(point); + + if (carExfil != null) + { + if (carExfil.Subscribee == point) + { + carExfil.Play(); + } + } + } + + if (!isCounting && point.Status == EExfiltrationStatus.Countdown) + { + if (point.ExfiltrationStartTime is <= 0 and > -90) + { + point.ExfiltrationStartTime = game.PastTime; + + CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + GenericPacket packet = new(EPackageType.ExfilCountdown) + { + NetId = mainPlayer.NetId, + ExfilName = point.Settings.Name, + ExfilStartTime = point.ExfiltrationStartTime + }; + + NetDataWriter writer = mainPlayer.PacketSender.Writer; + writer.Reset(); + + if (FikaBackendUtils.IsServer) + { + mainPlayer.PacketSender.Server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + else if (FikaBackendUtils.IsClient) + { + mainPlayer.PacketSender.Client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + } + countdownPoints.Add(point); + } + } + + private class ExtractionPlayerHandler(Player player, ExfiltrationPoint point, float startTime) + { + public CoopPlayer player = (CoopPlayer)player; + public ExfiltrationPoint point = point; + public float startTime = startTime; + } + } } diff --git a/Fika.Core/Coop/Components/CoopHalloweenEventManager.cs b/Fika.Core/Coop/Components/CoopHalloweenEventManager.cs index 46b5a0c4..58c5d7ab 100644 --- a/Fika.Core/Coop/Components/CoopHalloweenEventManager.cs +++ b/Fika.Core/Coop/Components/CoopHalloweenEventManager.cs @@ -10,80 +10,80 @@ namespace Fika.Core.Coop.Components { - internal class CoopHalloweenEventManager : MonoBehaviour - { - ManualLogSource Logger; - - private Action summonStartedAction; - private Action syncStateEvent; - private Action syncExitsEvent; - - private FikaServer server; - private NetDataWriter writer = new(); - - protected void Awake() - { - Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHalloweenEventManager"); - } - - protected void Start() - { - Logger.LogInfo("Initializing"); - - //Destroy on client as we dont need to listen to these events, and receive them from the host. - if (FikaBackendUtils.IsClient) - { - Logger.LogInfo("Running on client, destroying"); - - Destroy(this); - - return; - } - - server = Singleton.Instance; - - summonStartedAction = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSummonStarted)); - syncStateEvent = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSyncStateEvent)); - syncExitsEvent = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSyncExitsEvent)); - } - - private void OnHalloweenSummonStarted(HalloweenSummonStartedEvent summonStartedEvent) - { - Logger.LogDebug("OnHalloweenSummonStarted"); - - HalloweenEventPacket packet = new(EHalloweenPacketType.Summon) - { - SummonPosition = summonStartedEvent.PointPosition - }; - - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - - private void OnHalloweenSyncStateEvent(HalloweenSyncStateEvent syncStateEvent) - { - Logger.LogDebug("OnHalloweenSyncStateEvent"); - - HalloweenEventPacket packet = new(EHalloweenPacketType.Sync) - { - EventState = syncStateEvent.EventState - }; - - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - - private void OnHalloweenSyncExitsEvent(HalloweenSyncExitsEvent syncStateEvent) - { - Logger.LogDebug("OnHalloweenSyncExitsEvent"); - - HalloweenEventPacket packet = new(EHalloweenPacketType.Exit) - { - Exit = syncStateEvent.ExitName - }; - - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - } + internal class CoopHalloweenEventManager : MonoBehaviour + { + ManualLogSource Logger; + + private Action summonStartedAction; + private Action syncStateEvent; + private Action syncExitsEvent; + + private FikaServer server; + private NetDataWriter writer = new(); + + protected void Awake() + { + Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHalloweenEventManager"); + } + + protected void Start() + { + Logger.LogInfo("Initializing"); + + //Destroy on client as we dont need to listen to these events, and receive them from the host. + if (FikaBackendUtils.IsClient) + { + Logger.LogInfo("Running on client, destroying"); + + Destroy(this); + + return; + } + + server = Singleton.Instance; + + summonStartedAction = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSummonStarted)); + syncStateEvent = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSyncStateEvent)); + syncExitsEvent = GlobalEventHandlerClass.Instance.SubscribeOnEvent(new Action(this.OnHalloweenSyncExitsEvent)); + } + + private void OnHalloweenSummonStarted(HalloweenSummonStartedEvent summonStartedEvent) + { + Logger.LogDebug("OnHalloweenSummonStarted"); + + HalloweenEventPacket packet = new(EHalloweenPacketType.Summon) + { + SummonPosition = summonStartedEvent.PointPosition + }; + + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + + private void OnHalloweenSyncStateEvent(HalloweenSyncStateEvent syncStateEvent) + { + Logger.LogDebug("OnHalloweenSyncStateEvent"); + + HalloweenEventPacket packet = new(EHalloweenPacketType.Sync) + { + EventState = syncStateEvent.EventState + }; + + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + + private void OnHalloweenSyncExitsEvent(HalloweenSyncExitsEvent syncStateEvent) + { + Logger.LogDebug("OnHalloweenSyncExitsEvent"); + + HalloweenEventPacket packet = new(EHalloweenPacketType.Exit) + { + Exit = syncStateEvent.ExitName + }; + + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + } } diff --git a/Fika.Core/Coop/Components/CoopHandler.cs b/Fika.Core/Coop/Components/CoopHandler.cs index c4a6d7c4..f933d96f 100644 --- a/Fika.Core/Coop/Components/CoopHandler.cs +++ b/Fika.Core/Coop/Components/CoopHandler.cs @@ -18,586 +18,586 @@ namespace Fika.Core.Coop.Components { - /// - /// CoopHandler is the User 1-2-1 communication to the Server. This can be seen as an extension component to CoopGame. - /// - public class CoopHandler : MonoBehaviour - { - #region Fields/Properties - public CoopGame LocalGameInstance { get; internal set; } - public string ServerId { get; set; } = null; - public Dictionary Players = []; - public List HumanPlayers = []; - public int AmountOfHumans = 1; - public List ExtractedPlayers = []; - public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - public List queuedProfileIds = []; - - private ManualLogSource Logger; - private Queue spawnQueue = new(50); - private bool ready; - private bool isClient; - private float charSyncCounter; - - public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool isAI, int netId) - { - public Profile Profile { get; set; } = profile; - public Vector3 Position { get; set; } = position; - public bool IsAlive { get; set; } = isAlive; - public bool IsAI { get; set; } = isAI; - public int NetId { get; set; } = netId; - } - - internal FikaBTRManager_Client clientBTR = null; - internal FikaBTRManager_Host serverBTR = null; - internal static GameObject CoopHandlerParent; - - #endregion - - #region Public Voids - - public static CoopHandler GetCoopHandler() - { - if (CoopHandlerParent == null) - { - return null; - } - - CoopHandler coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - if (coopHandler != null) - { - return coopHandler; - } - - return null; - } - - public static bool TryGetCoopHandler(out CoopHandler coopHandler) - { - coopHandler = GetCoopHandler(); - return coopHandler != null; - } - - public static string GetServerId() - { - CoopHandler coopGC = GetCoopHandler(); - if (coopGC == null) - { - return FikaBackendUtils.GetGroupId(); - } - - return coopGC.ServerId; - } - #endregion - - #region Unity Component Methods - - protected void Awake() - { - Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHandler"); - } - - protected void Start() - { - if (FikaBackendUtils.IsClient) - { - //_ = Task.Run(ReadFromServerCharactersLoop); - isClient = true; - charSyncCounter = 0f; - } - - if (FikaBackendUtils.IsServer) - { - isClient = false; - ready = true; - Singleton.Instance.World_0.RegisterNetworkInteractionObjects(null); - } - } - - protected void OnDestroy() - { - Players.Clear(); - HumanPlayers.Clear(); - } - - private bool requestQuitGame = false; - - /// - /// The state your character or game is in to Quit. - /// - public enum EQuitState - { - NONE = -1, - YouAreDead, - YouHaveExtracted - } - - public EQuitState GetQuitState() - { - EQuitState quitState = EQuitState.NONE; - - if (!Singleton.Instantiated) - { - return quitState; - } - - IFikaGame coopGame = Singleton.Instance; - if (coopGame == null) - { - return quitState; - } - - if (Players == null) - { - return quitState; - } - - if (coopGame.ExtractedPlayers == null) - { - return quitState; - } - - if (MyPlayer == null) - { - return quitState; - } - - // Check alive status - if (!MyPlayer.HealthController.IsAlive) - { - quitState = EQuitState.YouAreDead; - } - - // Extractions - if (coopGame.ExtractedPlayers.Contains(MyPlayer.NetId)) - { - quitState = EQuitState.YouHaveExtracted; - } - - return quitState; - } - - /// - /// This handles the ways of exiting the active game session - /// - private void ProcessQuitting() - { - EQuitState quitState = GetQuitState(); - - if (FikaPlugin.ExtractKey.Value.IsDown() && quitState != EQuitState.NONE && !requestQuitGame) - { - //Log to both the in-game console as well as into the BepInEx logfile - ConsoleScreen.Log($"{FikaPlugin.ExtractKey.Value} pressed, attempting to extract!"); - Logger.LogInfo($"{FikaPlugin.ExtractKey.Value} pressed, attempting to extract!"); - - requestQuitGame = true; - CoopGame coopGame = (CoopGame)Singleton.Instance; - - // If you are the server / host - if (FikaBackendUtils.IsServer) - { - // A host needs to wait for the team to extract or die! - if ((Singleton.Instance.NetServer.ConnectedPeersCount > 0) && quitState != EQuitState.NONE) - { - NotificationManagerClass.DisplayWarningNotification("HOSTING: You cannot exit the game until all clients have disconnected."); - requestQuitGame = false; - return; - } - 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; - return; - } - else - { - coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); - } - } - else - { - coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); - } - return; - } - } - - public void SetReady(bool state) - { - ready = state; - } - - protected private void Update() - { - if (LocalGameInstance == null) - { - return; - } - - if (!ready) - { - return; - } - - if (spawnQueue.Count > 0) - { - SpawnPlayer(spawnQueue.Dequeue()); - } - - ProcessQuitting(); - - if (isClient) - { - charSyncCounter += Time.deltaTime; - int waitTime = LocalGameInstance.Status == GameStatus.Started ? 15 : 2; - - if (charSyncCounter > waitTime) - { - charSyncCounter = 0f; - - if (Players == null) - { - return; - } - - SyncPlayersWithServer(); - } - } - } - - #endregion - - private void SyncPlayersWithServer() - { - AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId); - - if (Players.Count > 0) - { - requestPacket.HasCharacters = true; - requestPacket.Characters = [.. Players.Values.Select(p => p.ProfileId), .. queuedProfileIds]; - } - - NetDataWriter writer = Singleton.Instance.Writer; - if (writer != null) - { - writer.Reset(); - Singleton.Instance.SendData(writer, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - - private async void SpawnPlayer(SpawnObject spawnObject) - { - if (spawnObject.Profile == null) - { - Logger.LogError("SpawnPlayer: Profile was null!"); - queuedProfileIds.Remove(spawnObject.Profile.ProfileId); - return; - } - - foreach (IPlayer player in Singleton.Instance.RegisteredPlayers) - { - if (player.ProfileId == spawnObject.Profile.ProfileId) - { - return; - } - } - - foreach (IPlayer player in Singleton.Instance.AllAlivePlayersList) - { - if (player.ProfileId == spawnObject.Profile.ProfileId) - { - return; - } - } - - int playerId = LocalGameInstance.method_12(); - - IEnumerable allPrefabPaths = spawnObject.Profile.GetAllPrefabPaths(); - if (allPrefabPaths.Count() == 0) - { - Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::PrefabPaths are empty!"); - return; - } - - await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, - PoolManager.AssemblyType.Local, allPrefabPaths.ToArray(), JobPriority.General).ContinueWith(x => - { - if (x.IsCompleted) - { + /// + /// CoopHandler is the User 1-2-1 communication to the Server. This can be seen as an extension component to CoopGame. + /// + public class CoopHandler : MonoBehaviour + { + #region Fields/Properties + public CoopGame LocalGameInstance { get; internal set; } + public string ServerId { get; set; } = null; + public Dictionary Players = []; + public List HumanPlayers = []; + public int AmountOfHumans = 1; + public List ExtractedPlayers = []; + public CoopPlayer MyPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; + public List queuedProfileIds = []; + + private ManualLogSource Logger; + private Queue spawnQueue = new(50); + private bool ready; + private bool isClient; + private float charSyncCounter; + + public class SpawnObject(Profile profile, Vector3 position, bool isAlive, bool isAI, int netId) + { + public Profile Profile { get; set; } = profile; + public Vector3 Position { get; set; } = position; + public bool IsAlive { get; set; } = isAlive; + public bool IsAI { get; set; } = isAI; + public int NetId { get; set; } = netId; + } + + internal FikaBTRManager_Client clientBTR = null; + internal FikaBTRManager_Host serverBTR = null; + internal static GameObject CoopHandlerParent; + + #endregion + + #region Public Voids + + public static CoopHandler GetCoopHandler() + { + if (CoopHandlerParent == null) + { + return null; + } + + CoopHandler coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + if (coopHandler != null) + { + return coopHandler; + } + + return null; + } + + public static bool TryGetCoopHandler(out CoopHandler coopHandler) + { + coopHandler = GetCoopHandler(); + return coopHandler != null; + } + + public static string GetServerId() + { + CoopHandler coopGC = GetCoopHandler(); + if (coopGC == null) + { + return FikaBackendUtils.GetGroupId(); + } + + return coopGC.ServerId; + } + #endregion + + #region Unity Component Methods + + protected void Awake() + { + Logger = BepInEx.Logging.Logger.CreateLogSource("CoopHandler"); + } + + protected void Start() + { + if (FikaBackendUtils.IsClient) + { + //_ = Task.Run(ReadFromServerCharactersLoop); + isClient = true; + charSyncCounter = 0f; + } + + if (FikaBackendUtils.IsServer) + { + isClient = false; + ready = true; + Singleton.Instance.World_0.RegisterNetworkInteractionObjects(null); + } + } + + protected void OnDestroy() + { + Players.Clear(); + HumanPlayers.Clear(); + } + + private bool requestQuitGame = false; + + /// + /// The state your character or game is in to Quit. + /// + public enum EQuitState + { + NONE = -1, + YouAreDead, + YouHaveExtracted + } + + public EQuitState GetQuitState() + { + EQuitState quitState = EQuitState.NONE; + + if (!Singleton.Instantiated) + { + return quitState; + } + + IFikaGame coopGame = Singleton.Instance; + if (coopGame == null) + { + return quitState; + } + + if (Players == null) + { + return quitState; + } + + if (coopGame.ExtractedPlayers == null) + { + return quitState; + } + + if (MyPlayer == null) + { + return quitState; + } + + // Check alive status + if (!MyPlayer.HealthController.IsAlive) + { + quitState = EQuitState.YouAreDead; + } + + // Extractions + if (coopGame.ExtractedPlayers.Contains(MyPlayer.NetId)) + { + quitState = EQuitState.YouHaveExtracted; + } + + return quitState; + } + + /// + /// This handles the ways of exiting the active game session + /// + private void ProcessQuitting() + { + EQuitState quitState = GetQuitState(); + + if (FikaPlugin.ExtractKey.Value.IsDown() && quitState != EQuitState.NONE && !requestQuitGame) + { + //Log to both the in-game console as well as into the BepInEx logfile + ConsoleScreen.Log($"{FikaPlugin.ExtractKey.Value} pressed, attempting to extract!"); + Logger.LogInfo($"{FikaPlugin.ExtractKey.Value} pressed, attempting to extract!"); + + requestQuitGame = true; + CoopGame coopGame = (CoopGame)Singleton.Instance; + + // If you are the server / host + if (FikaBackendUtils.IsServer) + { + // A host needs to wait for the team to extract or die! + if ((Singleton.Instance.NetServer.ConnectedPeersCount > 0) && quitState != EQuitState.NONE) + { + NotificationManagerClass.DisplayWarningNotification("HOSTING: You cannot exit the game until all clients have disconnected."); + requestQuitGame = false; + return; + } + 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; + return; + } + else + { + coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); + } + } + else + { + coopGame.Stop(Singleton.Instance.MainPlayer.ProfileId, coopGame.MyExitStatus, MyPlayer.ActiveHealthController.IsAlive ? coopGame.MyExitLocation : null, 0); + } + return; + } + } + + public void SetReady(bool state) + { + ready = state; + } + + protected private void Update() + { + if (LocalGameInstance == null) + { + return; + } + + if (!ready) + { + return; + } + + if (spawnQueue.Count > 0) + { + SpawnPlayer(spawnQueue.Dequeue()); + } + + ProcessQuitting(); + + if (isClient) + { + charSyncCounter += Time.deltaTime; + int waitTime = LocalGameInstance.Status == GameStatus.Started ? 15 : 2; + + if (charSyncCounter > waitTime) + { + charSyncCounter = 0f; + + if (Players == null) + { + return; + } + + SyncPlayersWithServer(); + } + } + } + + #endregion + + private void SyncPlayersWithServer() + { + AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId); + + if (Players.Count > 0) + { + requestPacket.HasCharacters = true; + requestPacket.Characters = [.. Players.Values.Select(p => p.ProfileId), .. queuedProfileIds]; + } + + NetDataWriter writer = Singleton.Instance.Writer; + if (writer != null) + { + writer.Reset(); + Singleton.Instance.SendData(writer, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + + private async void SpawnPlayer(SpawnObject spawnObject) + { + if (spawnObject.Profile == null) + { + Logger.LogError("SpawnPlayer: Profile was null!"); + queuedProfileIds.Remove(spawnObject.Profile.ProfileId); + return; + } + + foreach (IPlayer player in Singleton.Instance.RegisteredPlayers) + { + if (player.ProfileId == spawnObject.Profile.ProfileId) + { + return; + } + } + + foreach (IPlayer player in Singleton.Instance.AllAlivePlayersList) + { + if (player.ProfileId == spawnObject.Profile.ProfileId) + { + return; + } + } + + int playerId = LocalGameInstance.method_12(); + + IEnumerable allPrefabPaths = spawnObject.Profile.GetAllPrefabPaths(); + if (allPrefabPaths.Count() == 0) + { + Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::PrefabPaths are empty!"); + return; + } + + await Singleton.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, + PoolManager.AssemblyType.Local, allPrefabPaths.ToArray(), JobPriority.General).ContinueWith(x => + { + if (x.IsCompleted) + { #if DEBUG - Logger.LogInfo($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Complete"); + Logger.LogInfo($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Complete"); #endif - } - else if (x.IsFaulted) - { - Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Failed"); - } - else if (x.IsCanceled) - { - Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Cancelled"); - } - }); - - ObservedCoopPlayer otherPlayer = SpawnObservedPlayer(spawnObject.Profile, spawnObject.Position, playerId, spawnObject.IsAI, spawnObject.NetId); - - if (!spawnObject.IsAlive) - { - // TODO: Spawn them as corpses? - // Run 'OnDead' - otherPlayer.OnDead(EDamageType.Undefined); - otherPlayer.NetworkHealthController.IsAlive = false; - } - - if (FikaBackendUtils.IsServer) - { - if (LocalGameInstance != null) - { - BotsController botController = LocalGameInstance.BotsController; - if (botController != null) - { - // Start Coroutine as botController might need a while to start sometimes... + } + else if (x.IsFaulted) + { + Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Failed"); + } + else if (x.IsCanceled) + { + Logger.LogError($"SpawnPlayer::{spawnObject.Profile.Info.Nickname}::Load Cancelled"); + } + }); + + ObservedCoopPlayer otherPlayer = SpawnObservedPlayer(spawnObject.Profile, spawnObject.Position, playerId, spawnObject.IsAI, spawnObject.NetId); + + if (!spawnObject.IsAlive) + { + // TODO: Spawn them as corpses? + // Run 'OnDead' + otherPlayer.OnDead(EDamageType.Undefined); + otherPlayer.NetworkHealthController.IsAlive = false; + } + + if (FikaBackendUtils.IsServer) + { + if (LocalGameInstance != null) + { + BotsController botController = LocalGameInstance.BotsController; + if (botController != null) + { + // Start Coroutine as botController might need a while to start sometimes... #if DEBUG - Logger.LogInfo("Starting AddClientToBotEnemies routine."); + Logger.LogInfo("Starting AddClientToBotEnemies routine."); #endif - StartCoroutine(AddClientToBotEnemies(botController, otherPlayer)); - } - else - { - Logger.LogError("botController was null when trying to add player to enemies!"); - } - } - else - { - Logger.LogError("LocalGameInstance was null when trying to add player to enemies!"); - } - } - - queuedProfileIds.Remove(spawnObject.Profile.ProfileId); - } - - public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAlive = true, bool isAI = false) - { - GameWorld gameWorld = Singleton.Instance; - if (gameWorld == null) - { - return; - } - - foreach (IPlayer player in gameWorld.RegisteredPlayers) - { - if (player.ProfileId == profile.ProfileId) - { - return; - } - } - - foreach (IPlayer player in gameWorld.AllAlivePlayersList) - { - if (player.ProfileId == profile.ProfileId) - { - return; - } - } - - if (queuedProfileIds.Contains(profile.ProfileId)) - { - return; - } - - queuedProfileIds.Add(profile.ProfileId); + StartCoroutine(AddClientToBotEnemies(botController, otherPlayer)); + } + else + { + Logger.LogError("botController was null when trying to add player to enemies!"); + } + } + else + { + Logger.LogError("LocalGameInstance was null when trying to add player to enemies!"); + } + } + + queuedProfileIds.Remove(spawnObject.Profile.ProfileId); + } + + public void QueueProfile(Profile profile, Vector3 position, int netId, bool isAlive = true, bool isAI = false) + { + GameWorld gameWorld = Singleton.Instance; + if (gameWorld == null) + { + return; + } + + foreach (IPlayer player in gameWorld.RegisteredPlayers) + { + if (player.ProfileId == profile.ProfileId) + { + return; + } + } + + foreach (IPlayer player in gameWorld.AllAlivePlayersList) + { + if (player.ProfileId == profile.ProfileId) + { + return; + } + } + + if (queuedProfileIds.Contains(profile.ProfileId)) + { + return; + } + + queuedProfileIds.Add(profile.ProfileId); #if DEBUG - Logger.LogInfo($"Queueing profile: {profile.Nickname}, {profile.ProfileId}"); + Logger.LogInfo($"Queueing profile: {profile.Nickname}, {profile.ProfileId}"); #endif - spawnQueue.Enqueue(new SpawnObject(profile, position, isAlive, isAI, netId)); - } - - private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position, int playerId, bool isAI, int netId) - { - bool isDedicatedProfile = !isAI && profile.Info.MainProfileNickname.Contains("dedicated_"); - - ObservedCoopPlayer otherPlayer = ObservedCoopPlayer.CreateObservedPlayer(netId, 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, - GClass1457.Default).Result; - - if (otherPlayer == null) - { - return null; - } - - otherPlayer.NetId = netId; + spawnQueue.Enqueue(new SpawnObject(profile, position, isAlive, isAI, netId)); + } + + private ObservedCoopPlayer SpawnObservedPlayer(Profile profile, Vector3 position, int playerId, bool isAI, int netId) + { + bool isDedicatedProfile = !isAI && profile.Info.MainProfileNickname.Contains("dedicated_"); + + ObservedCoopPlayer otherPlayer = ObservedCoopPlayer.CreateObservedPlayer(netId, 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, + GClass1457.Default).Result; + + if (otherPlayer == null) + { + return null; + } + + otherPlayer.NetId = netId; #if DEBUG - Logger.LogInfo($"SpawnObservedPlayer: {profile.Nickname} spawning with NetId {netId}"); + Logger.LogInfo($"SpawnObservedPlayer: {profile.Nickname} spawning with NetId {netId}"); #endif - if (!isAI) - { - AmountOfHumans++; - } - - if (!Players.ContainsKey(netId)) - { - Players.Add(netId, otherPlayer); - } - else - { - Logger.LogError($"Trying to add {otherPlayer.Profile.Nickname} to list of players but it was already there!"); - } - - if (!isAI && !isDedicatedProfile && !HumanPlayers.Contains(otherPlayer)) - { - HumanPlayers.Add(otherPlayer); - } - - if (!Singleton.Instance.RegisteredPlayers.Any(x => x.Profile.ProfileId == profile.ProfileId)) - { - Singleton.Instance.RegisteredPlayers.Add(otherPlayer); - } - - foreach (CoopPlayer player in Players.Values) - { - if (player is not ObservedCoopPlayer) - { - continue; - } - - Collider playerCollider = otherPlayer.GetCharacterControllerCommon().GetCollider(); - Collider otherCollider = player.GetCharacterControllerCommon().GetCollider(); - - if (playerCollider != null && otherCollider != null) - { - EFTPhysicsClass.IgnoreCollision(playerCollider, otherCollider); - } - } - - if (isAI) - { - 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); - - // We still want DogTags to be 'FiR' - Item item = otherPlayer.Inventory.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; - if (item != null) - { - item.SpawnedInSession = true; - } - } - } - else if (profile.Info.Side != EPlayerSide.Savage)// Make Player PMC items are all not 'FiR' - { - profile.SetSpawnedInSession(false); - - // We still want DogTags to be 'FiR' - Item item = otherPlayer.Inventory.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; - if (item != null) - { - item.SpawnedInSession = true; - } - } - - otherPlayer.InitObservedPlayer(isDedicatedProfile); + if (!isAI) + { + AmountOfHumans++; + } + + if (!Players.ContainsKey(netId)) + { + Players.Add(netId, otherPlayer); + } + else + { + Logger.LogError($"Trying to add {otherPlayer.Profile.Nickname} to list of players but it was already there!"); + } + + if (!isAI && !isDedicatedProfile && !HumanPlayers.Contains(otherPlayer)) + { + HumanPlayers.Add(otherPlayer); + } + + if (!Singleton.Instance.RegisteredPlayers.Any(x => x.Profile.ProfileId == profile.ProfileId)) + { + Singleton.Instance.RegisteredPlayers.Add(otherPlayer); + } + + foreach (CoopPlayer player in Players.Values) + { + if (player is not ObservedCoopPlayer) + { + continue; + } + + Collider playerCollider = otherPlayer.GetCharacterControllerCommon().GetCollider(); + Collider otherCollider = player.GetCharacterControllerCommon().GetCollider(); + + if (playerCollider != null && otherCollider != null) + { + EFTPhysicsClass.IgnoreCollision(playerCollider, otherCollider); + } + } + + if (isAI) + { + 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); + + // We still want DogTags to be 'FiR' + Item item = otherPlayer.Inventory.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; + if (item != null) + { + item.SpawnedInSession = true; + } + } + } + else if (profile.Info.Side != EPlayerSide.Savage)// Make Player PMC items are all not 'FiR' + { + profile.SetSpawnedInSession(false); + + // We still want DogTags to be 'FiR' + Item item = otherPlayer.Inventory.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; + if (item != null) + { + item.SpawnedInSession = true; + } + } + + otherPlayer.InitObservedPlayer(isDedicatedProfile); #if DEBUG - Logger.LogInfo($"CreateLocalPlayer::{profile.Info.Nickname}::Spawned."); + Logger.LogInfo($"CreateLocalPlayer::{profile.Info.Nickname}::Spawned."); #endif - SetWeaponInHandsOfNewPlayer(otherPlayer); + SetWeaponInHandsOfNewPlayer(otherPlayer); - return otherPlayer; - } + return otherPlayer; + } - private IEnumerator AddClientToBotEnemies(BotsController botController, LocalPlayer playerToAdd) - { - CoopGame coopGame = LocalGameInstance; + private IEnumerator AddClientToBotEnemies(BotsController botController, LocalPlayer playerToAdd) + { + CoopGame coopGame = LocalGameInstance; - Logger.LogInfo($"AddClientToBotEnemies: " + playerToAdd.Profile.Nickname); + Logger.LogInfo($"AddClientToBotEnemies: " + playerToAdd.Profile.Nickname); - while (coopGame.Status != GameStatus.Running && !botController.IsEnable) - { - yield return null; - } + while (coopGame.Status != GameStatus.Running && !botController.IsEnable) + { + yield return null; + } - while (coopGame.BotsController.BotSpawner == null) - { - yield return null; - } + while (coopGame.BotsController.BotSpawner == null) + { + yield return null; + } #if DEBUG - Logger.LogInfo($"Adding Client {playerToAdd.Profile.Nickname} to enemy list"); + Logger.LogInfo($"Adding Client {playerToAdd.Profile.Nickname} to enemy list"); #endif - botController.AddActivePLayer(playerToAdd); + botController.AddActivePLayer(playerToAdd); - bool found = false; + bool found = false; - for (int i = 0; i < botController.BotSpawner.PlayersCount; i++) - { - if (botController.BotSpawner.GetPlayer(i) == playerToAdd) - { - found = true; - break; - } - } + for (int i = 0; i < botController.BotSpawner.PlayersCount; i++) + { + if (botController.BotSpawner.GetPlayer(i) == playerToAdd) + { + found = true; + break; + } + } - if (found) - { + if (found) + { #if DEBUG - Logger.LogInfo($"Verified that {playerToAdd.Profile.Nickname} was added to the enemy list."); + Logger.LogInfo($"Verified that {playerToAdd.Profile.Nickname} was added to the enemy list."); #endif - } - else - { - Logger.LogError($"Failed to add {playerToAdd.Profile.Nickname} to the enemy list."); - } - } - - /// - /// Attempts to set up the New Player with the current weapon after spawning - /// - /// The player to set the item on - public void SetWeaponInHandsOfNewPlayer(Player player) - { - EquipmentClass equipment = player.Profile.Inventory.Equipment; - if (equipment == null) - { - Logger.LogError($"SetWeaponInHandsOfNewPlayer: {player.Profile.Nickname}, {player.Profile.ProfileId} has no Equipment!"); - } - Item item = null; - - if (equipment.GetSlot(EquipmentSlot.FirstPrimaryWeapon).ContainedItem != null) - { - item = equipment.GetSlot(EquipmentSlot.FirstPrimaryWeapon).ContainedItem; - } - - if (item == null && equipment.GetSlot(EquipmentSlot.SecondPrimaryWeapon).ContainedItem != null) - { - item = equipment.GetSlot(EquipmentSlot.SecondPrimaryWeapon).ContainedItem; - } - - if (item == null && equipment.GetSlot(EquipmentSlot.Holster).ContainedItem != null) - { - item = equipment.GetSlot(EquipmentSlot.Holster).ContainedItem; - } - - if (item == null && equipment.GetSlot(EquipmentSlot.Scabbard).ContainedItem != null) - { - item = equipment.GetSlot(EquipmentSlot.Scabbard).ContainedItem; - } - - player.SetItemInHands(item, (IResult) => - { - if (IResult.Failed == true) - { - Logger.LogError($"SetWeaponInHandsOfNewPlayer: Unable to set item {item} in hands for {player.Profile.Nickname}, {player.Profile.ProfileId}"); - } - }); - } - } + } + else + { + Logger.LogError($"Failed to add {playerToAdd.Profile.Nickname} to the enemy list."); + } + } + + /// + /// Attempts to set up the New Player with the current weapon after spawning + /// + /// The player to set the item on + public void SetWeaponInHandsOfNewPlayer(Player player) + { + EquipmentClass equipment = player.Profile.Inventory.Equipment; + if (equipment == null) + { + Logger.LogError($"SetWeaponInHandsOfNewPlayer: {player.Profile.Nickname}, {player.Profile.ProfileId} has no Equipment!"); + } + Item item = null; + + if (equipment.GetSlot(EquipmentSlot.FirstPrimaryWeapon).ContainedItem != null) + { + item = equipment.GetSlot(EquipmentSlot.FirstPrimaryWeapon).ContainedItem; + } + + if (item == null && equipment.GetSlot(EquipmentSlot.SecondPrimaryWeapon).ContainedItem != null) + { + item = equipment.GetSlot(EquipmentSlot.SecondPrimaryWeapon).ContainedItem; + } + + if (item == null && equipment.GetSlot(EquipmentSlot.Holster).ContainedItem != null) + { + item = equipment.GetSlot(EquipmentSlot.Holster).ContainedItem; + } + + if (item == null && equipment.GetSlot(EquipmentSlot.Scabbard).ContainedItem != null) + { + item = equipment.GetSlot(EquipmentSlot.Scabbard).ContainedItem; + } + + player.SetItemInHands(item, (IResult) => + { + if (IResult.Failed == true) + { + Logger.LogError($"SetWeaponInHandsOfNewPlayer: Unable to set item {item} in hands for {player.Profile.Nickname}, {player.Profile.ProfileId}"); + } + }); + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Components/CoopTimeManager.cs b/Fika.Core/Coop/Components/CoopTimeManager.cs index 1dfb9f95..c10e2d05 100644 --- a/Fika.Core/Coop/Components/CoopTimeManager.cs +++ b/Fika.Core/Coop/Components/CoopTimeManager.cs @@ -6,28 +6,28 @@ namespace Fika.Core.Coop.Components { - internal class CoopTimeManager : MonoBehaviour - { - public CoopGame CoopGame; - public GameTimerClass GameTimer; + 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; - } + 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; - } - } - } + 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 index aa32d7e4..0800325e 100644 --- a/Fika.Core/Coop/Components/FikaPinger.cs +++ b/Fika.Core/Coop/Components/FikaPinger.cs @@ -6,45 +6,45 @@ 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; + /// + /// 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 StartPingRoutine() + { + pingRoutine = StartCoroutine(PingServer()); + } - public void StopPingRoutine() - { - if (pingRoutine != null) - { - StopCoroutine(pingRoutine); - pingRoutine = null; - } - } + public void StopPingRoutine() + { + if (pingRoutine != null) + { + StopCoroutine(pingRoutine); + pingRoutine = null; + } + } - private IEnumerator PingServer() - { - PingRequest pingRequest = new(); + private IEnumerator PingServer() + { + PingRequest pingRequest = new(); - while (true) - { - Task pingTask = FikaRequestHandler.UpdatePing(pingRequest); - while (!pingTask.IsCompleted) - { - yield return null; - } - yield return new WaitForSeconds(30); - } - } + while (true) + { + Task pingTask = FikaRequestHandler.UpdatePing(pingRequest); + while (!pingTask.IsCompleted) + { + yield return null; + } + yield return new WaitForSeconds(30); + } + } - private void OnDestroy() - { - StopPingRoutine(); - } - } + private void OnDestroy() + { + StopPingRoutine(); + } + } } diff --git a/Fika.Core/Coop/Custom/FikaChat.cs b/Fika.Core/Coop/Custom/FikaChat.cs index 1acb6322..82409498 100644 --- a/Fika.Core/Coop/Custom/FikaChat.cs +++ b/Fika.Core/Coop/Custom/FikaChat.cs @@ -8,130 +8,130 @@ 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; + 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 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 Update() + { + if (Input.GetKeyDown(FikaPlugin.ChatKey.Value.MainKey)) + { + ToggleVisibility(); + } + } - protected void OnGUI() - { - if (!show) - { - return; - } + 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"); + 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); - } - } - } + 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 ToggleVisibility() + { + show = !show; + } - private void SendMessage() - { - if (!show) - { - return; - } + private void SendMessage() + { + if (!show) + { + return; + } - if (!string.IsNullOrEmpty(textField)) - { - string message = textField; - textField = string.Empty; + if (!string.IsNullOrEmpty(textField)) + { + string message = textField; + textField = string.Empty; - if (message.Length > 100) - { - message = message.Substring(0, 100); - } + if (message.Length > 100) + { + message = message.Substring(0, 100); + } - TextMessagePacket packet = new(nickname, message); - writer.Reset(); + 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); - } - } + 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); - } + 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 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); - } - } - } + 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 index 90fc8a25..11e7ed5e 100644 --- a/Fika.Core/Coop/Custom/FikaDebug.cs +++ b/Fika.Core/Coop/Custom/FikaDebug.cs @@ -8,167 +8,167 @@ 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 ServerFPS - { - get - { - return Singleton.Instance.ServerFPS; - } - } - - 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.HumanPlayers) - { - if (!alivePlayers.Contains(player) && player.HealthController.IsAlive) - { - AddPlayer(player); - } - } - - foreach (CoopPlayer player in coopHandler.Players.Values) - { - if (!player.gameObject.name.StartsWith("Player_") && !player.IsYourPlayer) - { - 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; - - windowRect = 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.Label($"Server FPS: {ServerFPS}"); - } - - GUILayout.EndVertical(); - GUILayout.EndArea(); - GUI.DragWindow(); - } - } + 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 ServerFPS + { + get + { + return Singleton.Instance.ServerFPS; + } + } + + 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.HumanPlayers) + { + if (!alivePlayers.Contains(player) && player.HealthController.IsAlive) + { + AddPlayer(player); + } + } + + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (!player.gameObject.name.StartsWith("Player_") && !player.IsYourPlayer) + { + 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; + + windowRect = 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.Label($"Server FPS: {ServerFPS}"); + } + + GUILayout.EndVertical(); + GUILayout.EndArea(); + GUI.DragWindow(); + } + } } diff --git a/Fika.Core/Coop/Custom/FikaDynamicAI.cs b/Fika.Core/Coop/Custom/FikaDynamicAI.cs index de6e16cd..af12c1e0 100644 --- a/Fika.Core/Coop/Custom/FikaDynamicAI.cs +++ b/Fika.Core/Coop/Custom/FikaDynamicAI.cs @@ -10,227 +10,227 @@ namespace Fika.Core.Coop.Custom { - public class FikaDynamicAI : MonoBehaviour - { - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("DynamicAI"); - private CoopHandler coopHandler; - private int frameCounter; - private int resetCounter; - private readonly List humanPlayers = []; - private readonly List bots = []; - private readonly HashSet disabledBots = []; - private BotSpawner spawner; - - protected void Awake() - { - if (FikaPlugin.Instance.ModHandler.QuestingBotsLoaded) - { - logger.LogWarning("QuestingBots detected, destroying DynamicAI component. Use QuestingBots AI limiter instead!"); - Destroy(this); - } - - if (!CoopHandler.TryGetCoopHandler(out coopHandler)) - { - logger.LogError("Could not find CoopHandler! Destroying self"); - Destroy(this); - return; - } - - resetCounter = FikaPlugin.DynamicAIRate.Value switch - { - FikaPlugin.EDynamicAIRates.Low => 600, - FikaPlugin.EDynamicAIRates.Medium => 300, - FikaPlugin.EDynamicAIRates.High => 120, - _ => 300, - }; - - spawner = Singleton.Instance.BotsController.BotSpawner; - if (spawner == null) - { - logger.LogError("Could not find BotSpawner! Destroying self"); - Destroy(this); - return; - } - - spawner.OnBotCreated += Spawner_OnBotCreated; - spawner.OnBotRemoved += Spawner_OnBotRemoved; - } - - private void Spawner_OnBotRemoved(BotOwner botOwner) - { - CoopBot bot = (CoopBot)botOwner.GetPlayer; - if (!bots.Remove(bot)) - { - logger.LogWarning($"Could not remove {botOwner.gameObject.name} from bots list."); - } - - if (disabledBots.Contains(bot)) - { - disabledBots.Remove(bot); - } - } - - private void Spawner_OnBotCreated(BotOwner botOwner) - { - if (botOwner.IsYourPlayer || !botOwner.IsAI) - { - return; - } - - if (FikaPlugin.DynamicAIIgnoreSnipers.Value) - { - if (botOwner.IsRole(WildSpawnType.marksman)) - { - return; - } - } - - bots.Add((CoopBot)botOwner.GetPlayer); - } - - protected void Update() - { - if (!FikaPlugin.DynamicAI.Value) - { - return; - } - - frameCounter++; - - if (frameCounter % resetCounter == 0) - { - frameCounter = 0; - foreach (CoopBot bot in bots) - { - CheckForPlayers(bot); - } - } - } - - public void AddHumans() - { - foreach (CoopPlayer player in coopHandler.HumanPlayers) - { - // Do not count the dedicated profile as an active player - if (player.Profile.Nickname.Contains("dedicated_")) - { - continue; - } - humanPlayers.Add(player); - } - } - - private void DeactivateBot(CoopBot bot) - { + public class FikaDynamicAI : MonoBehaviour + { + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("DynamicAI"); + private CoopHandler coopHandler; + private int frameCounter; + private int resetCounter; + private readonly List humanPlayers = []; + private readonly List bots = []; + private readonly HashSet disabledBots = []; + private BotSpawner spawner; + + protected void Awake() + { + if (FikaPlugin.Instance.ModHandler.QuestingBotsLoaded) + { + logger.LogWarning("QuestingBots detected, destroying DynamicAI component. Use QuestingBots AI limiter instead!"); + Destroy(this); + } + + if (!CoopHandler.TryGetCoopHandler(out coopHandler)) + { + logger.LogError("Could not find CoopHandler! Destroying self"); + Destroy(this); + return; + } + + resetCounter = FikaPlugin.DynamicAIRate.Value switch + { + FikaPlugin.EDynamicAIRates.Low => 600, + FikaPlugin.EDynamicAIRates.Medium => 300, + FikaPlugin.EDynamicAIRates.High => 120, + _ => 300, + }; + + spawner = Singleton.Instance.BotsController.BotSpawner; + if (spawner == null) + { + logger.LogError("Could not find BotSpawner! Destroying self"); + Destroy(this); + return; + } + + spawner.OnBotCreated += Spawner_OnBotCreated; + spawner.OnBotRemoved += Spawner_OnBotRemoved; + } + + private void Spawner_OnBotRemoved(BotOwner botOwner) + { + CoopBot bot = (CoopBot)botOwner.GetPlayer; + if (!bots.Remove(bot)) + { + logger.LogWarning($"Could not remove {botOwner.gameObject.name} from bots list."); + } + + if (disabledBots.Contains(bot)) + { + disabledBots.Remove(bot); + } + } + + private void Spawner_OnBotCreated(BotOwner botOwner) + { + if (botOwner.IsYourPlayer || !botOwner.IsAI) + { + return; + } + + if (FikaPlugin.DynamicAIIgnoreSnipers.Value) + { + if (botOwner.IsRole(WildSpawnType.marksman)) + { + return; + } + } + + bots.Add((CoopBot)botOwner.GetPlayer); + } + + protected void Update() + { + if (!FikaPlugin.DynamicAI.Value) + { + return; + } + + frameCounter++; + + if (frameCounter % resetCounter == 0) + { + frameCounter = 0; + foreach (CoopBot bot in bots) + { + CheckForPlayers(bot); + } + } + } + + public void AddHumans() + { + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + // Do not count the dedicated profile as an active player + if (player.Profile.Nickname.Contains("dedicated_")) + { + continue; + } + humanPlayers.Add(player); + } + } + + private void DeactivateBot(CoopBot bot) + { #if DEBUG - logger.LogWarning($"Disabling {bot.gameObject.name}"); + logger.LogWarning($"Disabling {bot.gameObject.name}"); #endif - bot.AIData.BotOwner.DecisionQueue.Clear(); - bot.AIData.BotOwner.Memory.GoalEnemy = null; - bot.AIData.BotOwner.PatrollingData.Pause(); - ShootData shootData = bot.AIData.BotOwner.ShootData; - shootData.method_5(); - shootData.Shooting = false; - if (shootData.ShootController != null) - { - shootData.ShootController.SetTriggerPressed(false); - } - bot.ActiveHealthController.PauseAllEffects(); - bot.gameObject.SetActive(false); - - if (!disabledBots.Contains(bot)) - { - disabledBots.Add(bot); - } - else - { - logger.LogError($"{bot.gameObject.name} was already in the disabled bots list when adding!"); - } - } - - private void ActivateBot(CoopBot bot) - { + bot.AIData.BotOwner.DecisionQueue.Clear(); + bot.AIData.BotOwner.Memory.GoalEnemy = null; + bot.AIData.BotOwner.PatrollingData.Pause(); + ShootData shootData = bot.AIData.BotOwner.ShootData; + shootData.method_5(); + shootData.Shooting = false; + if (shootData.ShootController != null) + { + shootData.ShootController.SetTriggerPressed(false); + } + bot.ActiveHealthController.PauseAllEffects(); + bot.gameObject.SetActive(false); + + if (!disabledBots.Contains(bot)) + { + disabledBots.Add(bot); + } + else + { + logger.LogError($"{bot.gameObject.name} was already in the disabled bots list when adding!"); + } + } + + private void ActivateBot(CoopBot bot) + { #if DEBUG - logger.LogWarning($"Enabling {bot.gameObject.name}"); + logger.LogWarning($"Enabling {bot.gameObject.name}"); #endif - bot.gameObject.SetActive(true); - bot.AIData.BotOwner.PatrollingData.Unpause(); - bot.AIData.BotOwner.PostActivate(); - bot.ActiveHealthController.UnpauseAllEffects(); - disabledBots.Remove(bot); - } - - private void CheckForPlayers(CoopBot bot) - { - // Do not run on bots that have no initialized yet - if (bot.AIData.BotOwner.BotState is EBotState.NonActive or EBotState.PreActive) - { - return; - } - - if (!bot.HealthController.IsAlive) - { - return; - } - - int notInRange = 0; - float range = FikaPlugin.DynamicAIRange.Value; - - foreach (CoopPlayer humanPlayer in humanPlayers) - { - if (humanPlayer == null) - { - notInRange++; - continue; - } - - if (!humanPlayer.HealthController.IsAlive) - { - notInRange++; - continue; - } - - float distance = Vector3.SqrMagnitude(bot.Position - humanPlayer.Position); - - if (distance > range * range) - { - notInRange++; - } - } - - if (notInRange >= humanPlayers.Count && bot.gameObject.activeSelf) - { - DeactivateBot(bot); - } - else if (notInRange < humanPlayers.Count && !bot.gameObject.activeSelf) - { - ActivateBot(bot); - } - } - - public void EnabledChange(bool value) - { - if (!value) - { - CoopBot[] disabledBotsArray = [.. disabledBots]; - for (int i = 0; i < disabledBotsArray.Length; i++) - { - ActivateBot(disabledBotsArray[i]); - } - - disabledBots.Clear(); - } - } - - internal void RateChanged(FikaPlugin.EDynamicAIRates value) - { - resetCounter = value switch - { - FikaPlugin.EDynamicAIRates.Low => 600, - FikaPlugin.EDynamicAIRates.Medium => 300, - FikaPlugin.EDynamicAIRates.High => 120, - _ => 300, - }; - } - } + bot.gameObject.SetActive(true); + bot.AIData.BotOwner.PatrollingData.Unpause(); + bot.AIData.BotOwner.PostActivate(); + bot.ActiveHealthController.UnpauseAllEffects(); + disabledBots.Remove(bot); + } + + private void CheckForPlayers(CoopBot bot) + { + // Do not run on bots that have no initialized yet + if (bot.AIData.BotOwner.BotState is EBotState.NonActive or EBotState.PreActive) + { + return; + } + + if (!bot.HealthController.IsAlive) + { + return; + } + + int notInRange = 0; + float range = FikaPlugin.DynamicAIRange.Value; + + foreach (CoopPlayer humanPlayer in humanPlayers) + { + if (humanPlayer == null) + { + notInRange++; + continue; + } + + if (!humanPlayer.HealthController.IsAlive) + { + notInRange++; + continue; + } + + float distance = Vector3.SqrMagnitude(bot.Position - humanPlayer.Position); + + if (distance > range * range) + { + notInRange++; + } + } + + if (notInRange >= humanPlayers.Count && bot.gameObject.activeSelf) + { + DeactivateBot(bot); + } + else if (notInRange < humanPlayers.Count && !bot.gameObject.activeSelf) + { + ActivateBot(bot); + } + } + + public void EnabledChange(bool value) + { + if (!value) + { + CoopBot[] disabledBotsArray = [.. disabledBots]; + for (int i = 0; i < disabledBotsArray.Length; i++) + { + ActivateBot(disabledBotsArray[i]); + } + + disabledBots.Clear(); + } + } + + internal void RateChanged(FikaPlugin.EDynamicAIRates value) + { + resetCounter = value switch + { + 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 1d354aa5..42ec0aba 100644 --- a/Fika.Core/Coop/Custom/FikaHealthBar.cs +++ b/Fika.Core/Coop/Custom/FikaHealthBar.cs @@ -16,534 +16,534 @@ namespace Fika.Core.Coop.Custom { - /// - /// Displays a health bar over another player
- /// Created by: ssh_ - ///
- public class FikaHealthBar : MonoBehaviour - { - private ObservedCoopPlayer currentPlayer; - private CoopPlayer mainPlayer; - private PlayerPlateUI playerPlate; - private float screenScale = 1f; - private Dictionary effectIcons; - private List effects; - private List ignoredTypes; // Check for GClass increments - private float counter = 0; - private bool updatePos = true; - - protected void Awake() - { - currentPlayer = GetComponent(); - mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - effectIcons = EFTHardSettings.Instance.StaticIcons.EffectIcons.EffectIcons; - effects = []; - ignoredTypes = [typeof(GInterface279), typeof(GInterface281), typeof(GInterface282), typeof(GInterface294), typeof(GInterface295), typeof(GInterface296)]; // Wound, Encumbered, OverEncumbered, MusclePain, MildMusclePlain, SevereMusclePain - CreateHealthBar(); - } - - public void ClearEffects() - { - foreach (HealthBarEffect effect in effects) - { - effect.Remove(); - } - effects.Clear(); - } - - protected void Update() - { - if (currentPlayer != null) - { - UpdateScreenSpacePosition(); - if (FikaPlugin.UseOcclusion.Value) - { - counter += Time.deltaTime; - if (counter > 1) - { - counter = 0; - CheckForOcclusion(); - } - } - } - else - { - Destroy(this); - } - } - - private void CheckForOcclusion() - { - Vector3 camPos = CameraClass.Instance.Camera.transform.position; - Vector3 targetPos = currentPlayer.PlayerBones.Neck.position; - int layer = LayerMask.GetMask(["HighPolyCollider", "Terrain", "Player"]); - - if (Physics.Raycast(camPos, targetPos - camPos, out RaycastHit hitinfo, 800f, layer)) - { - if (LayerMask.LayerToName(hitinfo.collider.gameObject.layer) != "Player") - { - playerPlate.ScalarObjectScreen.SetActive(false); - updatePos = false; - } - else - { - if (!playerPlate.ScalarObjectScreen.activeSelf) - { - playerPlate.ScalarObjectScreen.SetActive(true); - updatePos = true; - UpdateScreenSpacePosition(); - } - } - } - } - - private void UpdateScreenSpacePosition() - { - if (!updatePos) - { - return; - } - - // ADS opacity handling - float opacityMultiplier = 1f; - ProceduralWeaponAnimation proceduralWeaponAnimation = mainPlayer.ProceduralWeaponAnimation; - if (mainPlayer.HealthController.IsAlive && proceduralWeaponAnimation.IsAiming) - { - if (proceduralWeaponAnimation.CurrentScope.IsOptic && FikaPlugin.HideNamePlateInOptic.Value) - { - playerPlate.ScalarObjectScreen.SetActive(false); - return; - } - opacityMultiplier = FikaPlugin.OpacityInADS.Value; - } - CameraClass cameraInstance = CameraClass.Instance; - Camera camera = cameraInstance.Camera; - - // Distance check - Vector3 direction = camera.transform.position - currentPlayer.Position; - float sqrDistance = direction.sqrMagnitude; - float maxDistanceToShow = FikaPlugin.MaxDistanceToShow.Value * FikaPlugin.MaxDistanceToShow.Value; - if (sqrDistance > maxDistanceToShow) - { - playerPlate.ScalarObjectScreen.SetActive(false); - return; - } - - // If we're here, we can show the name plate - playerPlate.ScalarObjectScreen.SetActive(true); - - 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, FikaPlugin.NamePlateUseOpticZoom.Value)) - { - UpdateColorTextMeshProUGUI(playerPlate.playerNameScreen, 0); - UpdateColorImage(playerPlate.healthBarScreen, 0); - UpdateColorTextMeshProUGUI(playerPlate.healthNumberScreen, 0); - UpdateColorImage(playerPlate.healthBarBackgroundScreen, 0); - UpdateColorImage(playerPlate.healthNumberBackgroundScreen, 0); - UpdateColorImage(playerPlate.usecPlateScreen, 0); - UpdateColorImage(playerPlate.bearPlateScreen, 0); - return; - } - - SSAA ssaa = cameraInstance.SSAA; - bool isSSAAEnabled = ssaa != null && ssaa.isActiveAndEnabled; - if (isSSAAEnabled) - { - int outputWidth = ssaa.GetOutputWidth(); - float inputWidth = ssaa.GetInputWidth(); - screenScale = outputWidth / inputWidth; - } - - playerPlate.ScalarObjectScreen.transform.position = screenScale < 1 ? screenPoint : screenPoint * screenScale; - - float distFromCenterMultiplier = 1f; - if (FikaPlugin.DecreaseOpacityNotLookingAt.Value) - { - float screenWidth = isSSAAEnabled ? ssaa.GetOutputWidth() : Screen.width; - float screenHeight = isSSAAEnabled ? ssaa.GetOutputHeight() : Screen.height; - Vector3 screenCenter = new(screenWidth / 2, screenHeight / 2, 0); - Vector3 playerPosition = playerPlate.ScalarObjectScreen.transform.position; - float sqrDistFromCenter = (screenCenter - playerPosition).sqrMagnitude; - float minScreenSizeHalf = Mathf.Min(screenWidth, screenHeight) / 2; - float maxSqrDistFromCenter = minScreenSizeHalf * minScreenSizeHalf; - distFromCenterMultiplier = Mathf.Clamp01(1 - (sqrDistFromCenter / maxSqrDistFromCenter)); - } - - float alpha = 1f; - float halfMaxDistanceToShow = maxDistanceToShow / 2; - 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; - - alpha *= opacityMultiplier; - alpha *= distFromCenterMultiplier; - alpha = Mathf.Max(FikaPlugin.MinimumOpacity.Value, alpha); - - float backgroundOpacity = Mathf.Clamp(alpha, 0f, 0.44f); - float healthAlphaMultiplier = FikaPlugin.HideHealthBar.Value ? 0 : 1f; - - UpdateColorTextMeshProUGUI(playerPlate.playerNameScreen, alpha); - UpdateColorImage(playerPlate.healthBarScreen, alpha * healthAlphaMultiplier); - UpdateColorTextMeshProUGUI(playerPlate.healthNumberScreen, alpha * healthAlphaMultiplier); - UpdateColorImage(playerPlate.healthBarBackgroundScreen, backgroundOpacity * healthAlphaMultiplier); - UpdateColorImage(playerPlate.healthNumberBackgroundScreen, backgroundOpacity * healthAlphaMultiplier); - UpdateColorImage(playerPlate.usecPlateScreen, alpha); - UpdateColorImage(playerPlate.bearPlateScreen, alpha); - } - - private void CreateHealthBar() - { - if (currentPlayer != null) - { - GameObject uiPrefab = InternalBundleLoader.Instance.GetAssetBundle("playerui").LoadAsset("PlayerFriendlyUI"); - GameObject uiGameObj = Instantiate(uiPrefab); - playerPlate = uiGameObj.GetComponent(); - playerPlate.SetNameText(currentPlayer.Profile.Info.MainProfileNickname); - if (FikaPlugin.DevelopersList.ContainsKey(currentPlayer.Profile.Nickname.ToLower())) - { - playerPlate.playerNameScreen.color = new Color(0, 0.6091f, 1, 1); - ChatSpecialIconSettings specialIcons = Resources.Load("ChatSpecialIconSettings"); - playerPlate.bearPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[1].IconSprite; - playerPlate.bearPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); - playerPlate.usecPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[1].IconSprite; - playerPlate.usecPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); - } - else if (FikaPlugin.RespectedPlayersList.ContainsKey(currentPlayer.Profile.Nickname.ToLower())) - { - playerPlate.playerNameScreen.color = new Color(1, 0.6f, 0, 1); - ChatSpecialIconSettings specialIcons = Resources.Load("ChatSpecialIconSettings"); - playerPlate.bearPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[2].IconSprite; - playerPlate.bearPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); - playerPlate.usecPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[2].IconSprite; - playerPlate.usecPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); - } - // Start the plates both disabled, the visibility will be set in the update loop - playerPlate.usecPlateScreen.gameObject.SetActive(false); - playerPlate.bearPlateScreen.gameObject.SetActive(false); - } - - SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); - SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); - - playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); - - if (FikaPlugin.ShowEffects.Value) - { - currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; - currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; - AddAllActiveEffects(); - } - - FikaPlugin.UsePlateFactionSide.SettingChanged += UsePlateFactionSide_SettingChanged; - FikaPlugin.HideHealthBar.SettingChanged += HideHealthBar_SettingChanged; - FikaPlugin.UseNamePlates.SettingChanged += UseNamePlates_SettingChanged; - FikaPlugin.UseHealthNumber.SettingChanged += UseHealthNumber_SettingChanged; - FikaPlugin.ShowEffects.SettingChanged += ShowEffects_SettingChanged; - - currentPlayer.HealthController.HealthChangedEvent += HealthController_HealthChangedEvent; - currentPlayer.HealthController.BodyPartDestroyedEvent += HealthController_BodyPartDestroyedEvent; - currentPlayer.HealthController.BodyPartRestoredEvent += HealthController_BodyPartRestoredEvent; - currentPlayer.HealthController.DiedEvent += HealthController_DiedEvent; - - playerPlate.SetHealthNumberText("100%"); - - UpdateHealth(); - } - - #region events - private void UseHealthNumber_SettingChanged(object sender, EventArgs e) - { - UpdateHealth(); - } - - private void HealthController_EffectRemovedEvent(IEffect effect) - { - for (int i = 0; i < effects.Count; i++) - { - HealthBarEffect currentEffect = effects[i]; - if (currentEffect.effectType == effect.Type) - { - currentEffect.DecreaseAmount(); - if (currentEffect.GetAmount() == 0) - { - currentEffect.Remove(); - effects.Remove(currentEffect); - } - break; - } - } - } - - private void HealthController_EffectAddedEvent(IEffect effect) - { - AddEffect(effect); - } - - private void AddEffect(IEffect effect) - { - if (ignoredTypes.Contains(effect.Type)) - { - return; - } - - bool found = false; - foreach (HealthBarEffect currentEffect in effects) - { - if (currentEffect.effectType == effect.Type) - { - currentEffect.IncreaseAmount(); - found = true; - } - } - - if (found) - { - return; - } - - if (effectIcons.TryGetValue(effect.Type, out Sprite effectSprite)) - { - GameObject newEffect = Instantiate(playerPlate.EffectImageTemplate, playerPlate.EffectsBackground.transform); - HealthBarEffect healthBarEffect = new(); - healthBarEffect.Init(newEffect, effect, effectSprite); - effects.Add(healthBarEffect); - } - } - - private void ShowEffects_SettingChanged(object sender, EventArgs e) - { - if (FikaPlugin.ShowEffects.Value) - { - currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; - currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; - AddAllActiveEffects(); - } - else - { - currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; - currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; - - List tempList = new(effects); - foreach (HealthBarEffect effect in tempList) - { - effect.Remove(); - } - effects.Clear(); - tempList.Clear(); - tempList = null; - } - } - - private void AddAllActiveEffects() - { - IEnumerable currentEffects = currentPlayer.HealthController.GetAllActiveEffects(); - foreach (IEffect effect in currentEffects) - { - AddEffect(effect); - } - } - - private void HealthController_DiedEvent(EDamageType obj) - { - Destroy(this); - } - - private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, ValueStruct arg2) - { - UpdateHealth(); - } - - private void HealthController_BodyPartDestroyedEvent(EBodyPart arg1, EDamageType arg2) - { - UpdateHealth(); - } - - private void HealthController_HealthChangedEvent(EBodyPart arg1, float arg2, DamageInfo arg3) - { - 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); - } - #endregion - - /// - /// Updates the health on the HealthBar, this is invoked from events on the healthcontroller - /// - private void UpdateHealth() - { - float currentHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Current; - float maxHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Maximum; - if (FikaPlugin.UseHealthNumber.Value) - { - if (!playerPlate.healthNumberBackgroundScreen.gameObject.activeSelf) - { - SetPlayerPlateHealthVisibility(false); - } - int healthNumberPercentage = (int)Math.Round(currentHealth / maxHealth * 100); - playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); - } - else - { - if (!playerPlate.healthBarBackgroundScreen.gameObject.activeSelf) - { - SetPlayerPlateHealthVisibility(false); - } - - float normalizedHealth = Mathf.Clamp01(currentHealth / maxHealth); - playerPlate.healthBarScreen.fillAmount = normalizedHealth; - UpdateHealthBarColor(normalizedHealth); - } - } - - private void UpdateHealthBarColor(float normalizedHealth) - { - Color color = Color.Lerp(Color.red, Color.green, normalizedHealth); - color.a = playerPlate.healthBarScreen.color.a; // Keep the alpha value unchanged - playerPlate.healthBarScreen.color = color; - } - - private void UpdateColorImage(Image screenObject, float alpha) - { - if (screenObject.gameObject.activeInHierarchy) - { - Color color = screenObject.color; - color.a = alpha; - screenObject.color = color; - } - } - - private void UpdateColorTextMeshProUGUI(TMPro.TextMeshProUGUI screenObject, float alpha) - { - if (screenObject.gameObject.activeInHierarchy) - { - Color color = screenObject.color; - color.a = alpha; - screenObject.color = color; - } - } - - private void SetPlayerPlateHealthVisibility(bool hidden) - { - playerPlate.healthNumberScreen.gameObject.SetActive(!hidden && FikaPlugin.UseHealthNumber.Value); - playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(!hidden && FikaPlugin.UseHealthNumber.Value); - playerPlate.healthBarScreen.gameObject.SetActive(!hidden && !FikaPlugin.UseHealthNumber.Value); - playerPlate.healthBarBackgroundScreen.gameObject.SetActive(!hidden && !FikaPlugin.UseHealthNumber.Value); - } - - - private void SetPlayerPlateFactionVisibility(bool visible) - { - if (currentPlayer.Profile.Side == EPlayerSide.Usec) - { - playerPlate.usecPlateScreen.gameObject.SetActive(visible); - } - else if (currentPlayer.Profile.Side == EPlayerSide.Bear) - { - playerPlate.bearPlateScreen.gameObject.SetActive(visible); - } - } - - protected void OnDestroy() - { - FikaPlugin.UsePlateFactionSide.SettingChanged -= UsePlateFactionSide_SettingChanged; - FikaPlugin.HideHealthBar.SettingChanged -= HideHealthBar_SettingChanged; - FikaPlugin.UseNamePlates.SettingChanged -= UseNamePlates_SettingChanged; - FikaPlugin.UseHealthNumber.SettingChanged -= UseHealthNumber_SettingChanged; - FikaPlugin.ShowEffects.SettingChanged -= ShowEffects_SettingChanged; - - currentPlayer.HealthController.HealthChangedEvent -= HealthController_HealthChangedEvent; - currentPlayer.HealthController.BodyPartDestroyedEvent -= HealthController_BodyPartDestroyedEvent; - currentPlayer.HealthController.BodyPartRestoredEvent -= HealthController_BodyPartRestoredEvent; - currentPlayer.HealthController.DiedEvent -= HealthController_DiedEvent; - currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; - currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; - - playerPlate.gameObject.SetActive(false); - effects.Clear(); - Destroy(this); - } - - private class HealthBarEffect - { - public Type effectType; - private int amount; - private GameObject effectObject; - private Image effectImage; - private TextMeshProUGUI tmpText; - - public void Init(GameObject initObject, IEffect effect, Sprite effectSprite) - { - effectObject = initObject; - effectObject.SetActive(true); - effectImage = effectObject.transform.GetChild(0).GetComponent(); - effectImage.sprite = effectSprite; - tmpText = effectObject.transform.GetChild(1).GetComponent(); - amount = 1; - tmpText.text = amount.ToString(); - tmpText.enabled = false; - effectType = effect.Type; - } - - public void Remove() - { - Destroy(effectImage); - Destroy(tmpText); - Destroy(effectObject); - } - - public int GetAmount() - { - return amount; - } - - public void IncreaseAmount() - { - amount++; - tmpText.text = amount.ToString(); - - if (amount > 1) - { - tmpText.enabled = true; - } - } - - public void DecreaseAmount() - { - amount = Math.Max(0, amount - 1); - - if (amount == 1) - { - tmpText.enabled = false; - } - - if (amount == 0) - { - Remove(); - return; - } - else - { - tmpText.text = amount.ToString(); - } - } - } - } + /// + /// Displays a health bar over another player
+ /// Created by: ssh_ + ///
+ public class FikaHealthBar : MonoBehaviour + { + private ObservedCoopPlayer currentPlayer; + private CoopPlayer mainPlayer; + private PlayerPlateUI playerPlate; + private float screenScale = 1f; + private Dictionary effectIcons; + private List effects; + private List ignoredTypes; // Check for GClass increments + private float counter = 0; + private bool updatePos = true; + + protected void Awake() + { + currentPlayer = GetComponent(); + mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + effectIcons = EFTHardSettings.Instance.StaticIcons.EffectIcons.EffectIcons; + effects = []; + ignoredTypes = [typeof(GInterface279), typeof(GInterface281), typeof(GInterface282), typeof(GInterface294), typeof(GInterface295), typeof(GInterface296)]; // Wound, Encumbered, OverEncumbered, MusclePain, MildMusclePlain, SevereMusclePain + CreateHealthBar(); + } + + public void ClearEffects() + { + foreach (HealthBarEffect effect in effects) + { + effect.Remove(); + } + effects.Clear(); + } + + protected void Update() + { + if (currentPlayer != null) + { + UpdateScreenSpacePosition(); + if (FikaPlugin.UseOcclusion.Value) + { + counter += Time.deltaTime; + if (counter > 1) + { + counter = 0; + CheckForOcclusion(); + } + } + } + else + { + Destroy(this); + } + } + + private void CheckForOcclusion() + { + Vector3 camPos = CameraClass.Instance.Camera.transform.position; + Vector3 targetPos = currentPlayer.PlayerBones.Neck.position; + int layer = LayerMask.GetMask(["HighPolyCollider", "Terrain", "Player"]); + + if (Physics.Raycast(camPos, targetPos - camPos, out RaycastHit hitinfo, 800f, layer)) + { + if (LayerMask.LayerToName(hitinfo.collider.gameObject.layer) != "Player") + { + playerPlate.ScalarObjectScreen.SetActive(false); + updatePos = false; + } + else + { + if (!playerPlate.ScalarObjectScreen.activeSelf) + { + playerPlate.ScalarObjectScreen.SetActive(true); + updatePos = true; + UpdateScreenSpacePosition(); + } + } + } + } + + private void UpdateScreenSpacePosition() + { + if (!updatePos) + { + return; + } + + // ADS opacity handling + float opacityMultiplier = 1f; + ProceduralWeaponAnimation proceduralWeaponAnimation = mainPlayer.ProceduralWeaponAnimation; + if (mainPlayer.HealthController.IsAlive && proceduralWeaponAnimation.IsAiming) + { + if (proceduralWeaponAnimation.CurrentScope.IsOptic && FikaPlugin.HideNamePlateInOptic.Value) + { + playerPlate.ScalarObjectScreen.SetActive(false); + return; + } + opacityMultiplier = FikaPlugin.OpacityInADS.Value; + } + CameraClass cameraInstance = CameraClass.Instance; + Camera camera = cameraInstance.Camera; + + // Distance check + Vector3 direction = camera.transform.position - currentPlayer.Position; + float sqrDistance = direction.sqrMagnitude; + float maxDistanceToShow = FikaPlugin.MaxDistanceToShow.Value * FikaPlugin.MaxDistanceToShow.Value; + if (sqrDistance > maxDistanceToShow) + { + playerPlate.ScalarObjectScreen.SetActive(false); + return; + } + + // If we're here, we can show the name plate + playerPlate.ScalarObjectScreen.SetActive(true); + + 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, FikaPlugin.NamePlateUseOpticZoom.Value)) + { + UpdateColorTextMeshProUGUI(playerPlate.playerNameScreen, 0); + UpdateColorImage(playerPlate.healthBarScreen, 0); + UpdateColorTextMeshProUGUI(playerPlate.healthNumberScreen, 0); + UpdateColorImage(playerPlate.healthBarBackgroundScreen, 0); + UpdateColorImage(playerPlate.healthNumberBackgroundScreen, 0); + UpdateColorImage(playerPlate.usecPlateScreen, 0); + UpdateColorImage(playerPlate.bearPlateScreen, 0); + return; + } + + SSAA ssaa = cameraInstance.SSAA; + bool isSSAAEnabled = ssaa != null && ssaa.isActiveAndEnabled; + if (isSSAAEnabled) + { + int outputWidth = ssaa.GetOutputWidth(); + float inputWidth = ssaa.GetInputWidth(); + screenScale = outputWidth / inputWidth; + } + + playerPlate.ScalarObjectScreen.transform.position = screenScale < 1 ? screenPoint : screenPoint * screenScale; + + float distFromCenterMultiplier = 1f; + if (FikaPlugin.DecreaseOpacityNotLookingAt.Value) + { + float screenWidth = isSSAAEnabled ? ssaa.GetOutputWidth() : Screen.width; + float screenHeight = isSSAAEnabled ? ssaa.GetOutputHeight() : Screen.height; + Vector3 screenCenter = new(screenWidth / 2, screenHeight / 2, 0); + Vector3 playerPosition = playerPlate.ScalarObjectScreen.transform.position; + float sqrDistFromCenter = (screenCenter - playerPosition).sqrMagnitude; + float minScreenSizeHalf = Mathf.Min(screenWidth, screenHeight) / 2; + float maxSqrDistFromCenter = minScreenSizeHalf * minScreenSizeHalf; + distFromCenterMultiplier = Mathf.Clamp01(1 - (sqrDistFromCenter / maxSqrDistFromCenter)); + } + + float alpha = 1f; + float halfMaxDistanceToShow = maxDistanceToShow / 2; + 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; + + alpha *= opacityMultiplier; + alpha *= distFromCenterMultiplier; + alpha = Mathf.Max(FikaPlugin.MinimumOpacity.Value, alpha); + + float backgroundOpacity = Mathf.Clamp(alpha, 0f, 0.44f); + float healthAlphaMultiplier = FikaPlugin.HideHealthBar.Value ? 0 : 1f; + + UpdateColorTextMeshProUGUI(playerPlate.playerNameScreen, alpha); + UpdateColorImage(playerPlate.healthBarScreen, alpha * healthAlphaMultiplier); + UpdateColorTextMeshProUGUI(playerPlate.healthNumberScreen, alpha * healthAlphaMultiplier); + UpdateColorImage(playerPlate.healthBarBackgroundScreen, backgroundOpacity * healthAlphaMultiplier); + UpdateColorImage(playerPlate.healthNumberBackgroundScreen, backgroundOpacity * healthAlphaMultiplier); + UpdateColorImage(playerPlate.usecPlateScreen, alpha); + UpdateColorImage(playerPlate.bearPlateScreen, alpha); + } + + private void CreateHealthBar() + { + if (currentPlayer != null) + { + GameObject uiPrefab = InternalBundleLoader.Instance.GetAssetBundle("playerui").LoadAsset("PlayerFriendlyUI"); + GameObject uiGameObj = Instantiate(uiPrefab); + playerPlate = uiGameObj.GetComponent(); + playerPlate.SetNameText(currentPlayer.Profile.Info.MainProfileNickname); + if (FikaPlugin.DevelopersList.ContainsKey(currentPlayer.Profile.Nickname.ToLower())) + { + playerPlate.playerNameScreen.color = new Color(0, 0.6091f, 1, 1); + ChatSpecialIconSettings specialIcons = Resources.Load("ChatSpecialIconSettings"); + playerPlate.bearPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[1].IconSprite; + playerPlate.bearPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); + playerPlate.usecPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[1].IconSprite; + playerPlate.usecPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); + } + else if (FikaPlugin.RespectedPlayersList.ContainsKey(currentPlayer.Profile.Nickname.ToLower())) + { + playerPlate.playerNameScreen.color = new Color(1, 0.6f, 0, 1); + ChatSpecialIconSettings specialIcons = Resources.Load("ChatSpecialIconSettings"); + playerPlate.bearPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[2].IconSprite; + playerPlate.bearPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); + playerPlate.usecPlateScreen.GetComponent().sprite = specialIcons.IconsSettings[2].IconSprite; + playerPlate.usecPlateScreen.transform.localPosition = new Vector3(0f, 24.9f, 0); + } + // Start the plates both disabled, the visibility will be set in the update loop + playerPlate.usecPlateScreen.gameObject.SetActive(false); + playerPlate.bearPlateScreen.gameObject.SetActive(false); + } + + SetPlayerPlateFactionVisibility(FikaPlugin.UsePlateFactionSide.Value); + SetPlayerPlateHealthVisibility(FikaPlugin.HideHealthBar.Value); + + playerPlate.gameObject.SetActive(FikaPlugin.UseNamePlates.Value); + + if (FikaPlugin.ShowEffects.Value) + { + currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; + AddAllActiveEffects(); + } + + FikaPlugin.UsePlateFactionSide.SettingChanged += UsePlateFactionSide_SettingChanged; + FikaPlugin.HideHealthBar.SettingChanged += HideHealthBar_SettingChanged; + FikaPlugin.UseNamePlates.SettingChanged += UseNamePlates_SettingChanged; + FikaPlugin.UseHealthNumber.SettingChanged += UseHealthNumber_SettingChanged; + FikaPlugin.ShowEffects.SettingChanged += ShowEffects_SettingChanged; + + currentPlayer.HealthController.HealthChangedEvent += HealthController_HealthChangedEvent; + currentPlayer.HealthController.BodyPartDestroyedEvent += HealthController_BodyPartDestroyedEvent; + currentPlayer.HealthController.BodyPartRestoredEvent += HealthController_BodyPartRestoredEvent; + currentPlayer.HealthController.DiedEvent += HealthController_DiedEvent; + + playerPlate.SetHealthNumberText("100%"); + + UpdateHealth(); + } + + #region events + private void UseHealthNumber_SettingChanged(object sender, EventArgs e) + { + UpdateHealth(); + } + + private void HealthController_EffectRemovedEvent(IEffect effect) + { + for (int i = 0; i < effects.Count; i++) + { + HealthBarEffect currentEffect = effects[i]; + if (currentEffect.effectType == effect.Type) + { + currentEffect.DecreaseAmount(); + if (currentEffect.GetAmount() == 0) + { + currentEffect.Remove(); + effects.Remove(currentEffect); + } + break; + } + } + } + + private void HealthController_EffectAddedEvent(IEffect effect) + { + AddEffect(effect); + } + + private void AddEffect(IEffect effect) + { + if (ignoredTypes.Contains(effect.Type)) + { + return; + } + + bool found = false; + foreach (HealthBarEffect currentEffect in effects) + { + if (currentEffect.effectType == effect.Type) + { + currentEffect.IncreaseAmount(); + found = true; + } + } + + if (found) + { + return; + } + + if (effectIcons.TryGetValue(effect.Type, out Sprite effectSprite)) + { + GameObject newEffect = Instantiate(playerPlate.EffectImageTemplate, playerPlate.EffectsBackground.transform); + HealthBarEffect healthBarEffect = new(); + healthBarEffect.Init(newEffect, effect, effectSprite); + effects.Add(healthBarEffect); + } + } + + private void ShowEffects_SettingChanged(object sender, EventArgs e) + { + if (FikaPlugin.ShowEffects.Value) + { + currentPlayer.HealthController.EffectAddedEvent += HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent += HealthController_EffectRemovedEvent; + AddAllActiveEffects(); + } + else + { + currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; + + List tempList = new(effects); + foreach (HealthBarEffect effect in tempList) + { + effect.Remove(); + } + effects.Clear(); + tempList.Clear(); + tempList = null; + } + } + + private void AddAllActiveEffects() + { + IEnumerable currentEffects = currentPlayer.HealthController.GetAllActiveEffects(); + foreach (IEffect effect in currentEffects) + { + AddEffect(effect); + } + } + + private void HealthController_DiedEvent(EDamageType obj) + { + Destroy(this); + } + + private void HealthController_BodyPartRestoredEvent(EBodyPart arg1, ValueStruct arg2) + { + UpdateHealth(); + } + + private void HealthController_BodyPartDestroyedEvent(EBodyPart arg1, EDamageType arg2) + { + UpdateHealth(); + } + + private void HealthController_HealthChangedEvent(EBodyPart arg1, float arg2, DamageInfo arg3) + { + 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); + } + #endregion + + /// + /// Updates the health on the HealthBar, this is invoked from events on the healthcontroller + /// + private void UpdateHealth() + { + float currentHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Current; + float maxHealth = currentPlayer.HealthController.GetBodyPartHealth(EBodyPart.Common, true).Maximum; + if (FikaPlugin.UseHealthNumber.Value) + { + if (!playerPlate.healthNumberBackgroundScreen.gameObject.activeSelf) + { + SetPlayerPlateHealthVisibility(false); + } + int healthNumberPercentage = (int)Math.Round(currentHealth / maxHealth * 100); + playerPlate.SetHealthNumberText($"{healthNumberPercentage}%"); + } + else + { + if (!playerPlate.healthBarBackgroundScreen.gameObject.activeSelf) + { + SetPlayerPlateHealthVisibility(false); + } + + float normalizedHealth = Mathf.Clamp01(currentHealth / maxHealth); + playerPlate.healthBarScreen.fillAmount = normalizedHealth; + UpdateHealthBarColor(normalizedHealth); + } + } + + private void UpdateHealthBarColor(float normalizedHealth) + { + Color color = Color.Lerp(Color.red, Color.green, normalizedHealth); + color.a = playerPlate.healthBarScreen.color.a; // Keep the alpha value unchanged + playerPlate.healthBarScreen.color = color; + } + + private void UpdateColorImage(Image screenObject, float alpha) + { + if (screenObject.gameObject.activeInHierarchy) + { + Color color = screenObject.color; + color.a = alpha; + screenObject.color = color; + } + } + + private void UpdateColorTextMeshProUGUI(TMPro.TextMeshProUGUI screenObject, float alpha) + { + if (screenObject.gameObject.activeInHierarchy) + { + Color color = screenObject.color; + color.a = alpha; + screenObject.color = color; + } + } + + private void SetPlayerPlateHealthVisibility(bool hidden) + { + playerPlate.healthNumberScreen.gameObject.SetActive(!hidden && FikaPlugin.UseHealthNumber.Value); + playerPlate.healthNumberBackgroundScreen.gameObject.SetActive(!hidden && FikaPlugin.UseHealthNumber.Value); + playerPlate.healthBarScreen.gameObject.SetActive(!hidden && !FikaPlugin.UseHealthNumber.Value); + playerPlate.healthBarBackgroundScreen.gameObject.SetActive(!hidden && !FikaPlugin.UseHealthNumber.Value); + } + + + private void SetPlayerPlateFactionVisibility(bool visible) + { + if (currentPlayer.Profile.Side == EPlayerSide.Usec) + { + playerPlate.usecPlateScreen.gameObject.SetActive(visible); + } + else if (currentPlayer.Profile.Side == EPlayerSide.Bear) + { + playerPlate.bearPlateScreen.gameObject.SetActive(visible); + } + } + + protected void OnDestroy() + { + FikaPlugin.UsePlateFactionSide.SettingChanged -= UsePlateFactionSide_SettingChanged; + FikaPlugin.HideHealthBar.SettingChanged -= HideHealthBar_SettingChanged; + FikaPlugin.UseNamePlates.SettingChanged -= UseNamePlates_SettingChanged; + FikaPlugin.UseHealthNumber.SettingChanged -= UseHealthNumber_SettingChanged; + FikaPlugin.ShowEffects.SettingChanged -= ShowEffects_SettingChanged; + + currentPlayer.HealthController.HealthChangedEvent -= HealthController_HealthChangedEvent; + currentPlayer.HealthController.BodyPartDestroyedEvent -= HealthController_BodyPartDestroyedEvent; + currentPlayer.HealthController.BodyPartRestoredEvent -= HealthController_BodyPartRestoredEvent; + currentPlayer.HealthController.DiedEvent -= HealthController_DiedEvent; + currentPlayer.HealthController.EffectAddedEvent -= HealthController_EffectAddedEvent; + currentPlayer.HealthController.EffectRemovedEvent -= HealthController_EffectRemovedEvent; + + playerPlate.gameObject.SetActive(false); + effects.Clear(); + Destroy(this); + } + + private class HealthBarEffect + { + public Type effectType; + private int amount; + private GameObject effectObject; + private Image effectImage; + private TextMeshProUGUI tmpText; + + public void Init(GameObject initObject, IEffect effect, Sprite effectSprite) + { + effectObject = initObject; + effectObject.SetActive(true); + effectImage = effectObject.transform.GetChild(0).GetComponent(); + effectImage.sprite = effectSprite; + tmpText = effectObject.transform.GetChild(1).GetComponent(); + amount = 1; + tmpText.text = amount.ToString(); + tmpText.enabled = false; + effectType = effect.Type; + } + + public void Remove() + { + Destroy(effectImage); + Destroy(tmpText); + Destroy(effectObject); + } + + public int GetAmount() + { + return amount; + } + + public void IncreaseAmount() + { + amount++; + tmpText.text = amount.ToString(); + + if (amount > 1) + { + tmpText.enabled = true; + } + } + + public void DecreaseAmount() + { + amount = Math.Max(0, amount - 1); + + if (amount == 1) + { + tmpText.enabled = false; + } + + if (amount == 0) + { + Remove(); + return; + } + else + { + tmpText.text = amount.ToString(); + } + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Custom/FikaPing.cs b/Fika.Core/Coop/Custom/FikaPing.cs index 994be8e0..2bfd3627 100644 --- a/Fika.Core/Coop/Custom/FikaPing.cs +++ b/Fika.Core/Coop/Custom/FikaPing.cs @@ -5,18 +5,18 @@ namespace Fika.Core.Coop.Custom { - internal class FikaPing : MonoBehaviour - { - Image image; + internal class FikaPing : MonoBehaviour + { + Image image; - private void Awake() - { - image = GetComponent(); - } + private void Awake() + { + image = GetComponent(); + } - private void Update() - { + private void Update() + { - } - } + } + } } diff --git a/Fika.Core/Coop/Custom/PlayerPlateUI.cs b/Fika.Core/Coop/Custom/PlayerPlateUI.cs index bf12ee56..094bd0cd 100644 --- a/Fika.Core/Coop/Custom/PlayerPlateUI.cs +++ b/Fika.Core/Coop/Custom/PlayerPlateUI.cs @@ -10,36 +10,36 @@ ///
public class PlayerPlateUI : MonoBehaviour { - [SerializeField] - public GameObject ScreenSpaceNamePlate; - [SerializeField] - public GameObject ScalarObjectScreen; - [SerializeField] - public TextMeshProUGUI playerNameScreen; - [SerializeField] - public Image healthBarBackgroundScreen; - [SerializeField] - public Image healthBarScreen; - [SerializeField] - public Image healthNumberBackgroundScreen; - [SerializeField] - public TextMeshProUGUI healthNumberScreen; - [SerializeField] - public Image usecPlateScreen; - [SerializeField] - public Image bearPlateScreen; - [SerializeField] - public GameObject EffectsBackground; - [SerializeField] - public GameObject EffectImageTemplate; + [SerializeField] + public GameObject ScreenSpaceNamePlate; + [SerializeField] + public GameObject ScalarObjectScreen; + [SerializeField] + public TextMeshProUGUI playerNameScreen; + [SerializeField] + public Image healthBarBackgroundScreen; + [SerializeField] + public Image healthBarScreen; + [SerializeField] + public Image healthNumberBackgroundScreen; + [SerializeField] + public TextMeshProUGUI healthNumberScreen; + [SerializeField] + public Image usecPlateScreen; + [SerializeField] + public Image bearPlateScreen; + [SerializeField] + public GameObject EffectsBackground; + [SerializeField] + public GameObject EffectImageTemplate; - public void SetNameText(string text) - { - playerNameScreen.SetText(text); - } + public void SetNameText(string text) + { + playerNameScreen.SetText(text); + } - public void SetHealthNumberText(string text) - { - healthNumberScreen.SetText(text); - } + public void SetHealthNumberText(string text) + { + healthNumberScreen.SetText(text); + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Factories/HandsControllerFactory.cs b/Fika.Core/Coop/Factories/HandsControllerFactory.cs index bb82acbc..a9e9b1ec 100644 --- a/Fika.Core/Coop/Factories/HandsControllerFactory.cs +++ b/Fika.Core/Coop/Factories/HandsControllerFactory.cs @@ -6,109 +6,109 @@ namespace Fika.Core.Coop.Factories { - /// - /// Used to create custom s for the client that are used for networking - /// - /// The to initiate the controller on. - /// The to add to the controller. - internal class HandsControllerFactory(CoopPlayer player, Item item = null, KnifeComponent knifeComponent = null) - { - public CoopPlayer player = player; - public Item item = item; - public KnifeComponent knifeComponent = knifeComponent; - public MedsClass meds; - public FoodClass food; - public EBodyPart bodyPart; - public float amount; - public int animationVariant; + /// + /// Used to create custom s for the client that are used for networking + /// + /// The to initiate the controller on. + /// The to add to the controller. + internal class HandsControllerFactory(CoopPlayer player, Item item = null, KnifeComponent knifeComponent = null) + { + public CoopPlayer player = player; + public Item item = item; + public KnifeComponent knifeComponent = knifeComponent; + public MedsClass meds; + public FoodClass food; + public EBodyPart bodyPart; + public float amount; + public int animationVariant; - /// - /// Creates a - /// - /// A new or null if the action failed. - public EFT.Player.FirearmController CreateObservedFirearmController() - { - if (item is Weapon weapon) - { - return CoopObservedFirearmController.Create(player, weapon); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedFirearmController: item was not of type Weapon, was: {item.GetType()}"); - return null; - } - } + /// + /// Creates a + /// + /// A new or null if the action failed. + public EFT.Player.FirearmController CreateObservedFirearmController() + { + if (item is Weapon weapon) + { + return CoopObservedFirearmController.Create(player, weapon); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedFirearmController: item was not of type Weapon, was: {item.GetType()}"); + return null; + } + } - /// - /// Creates a - /// - /// A new or null if the action failed. - public EFT.Player.GrenadeController CreateObservedGrenadeController() - { - if (item is GrenadeClass grenade) - { - return CoopObservedGrenadeController.Create(player, grenade); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CoopObservedGrenadeController: item was not of type GrenadeClass, was: {item.GetType()}"); - return null; - } - } + /// + /// Creates a + /// + /// A new or null if the action failed. + public EFT.Player.GrenadeController CreateObservedGrenadeController() + { + if (item is GrenadeClass grenade) + { + return CoopObservedGrenadeController.Create(player, grenade); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CoopObservedGrenadeController: item was not of type GrenadeClass, was: {item.GetType()}"); + return null; + } + } - /// - /// Creates a - /// - /// A new or null if the action failed. - public EFT.Player.QuickGrenadeThrowController CreateObservedQuickGrenadeController() - { - if (item is GrenadeClass grenade) - { - return CoopObservedQuickGrenadeController.Create(player, grenade); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedQuickGrenadeController: item was not of type GrenadeClass, was: {item.GetType()}"); - return null; - } - } + /// + /// Creates a + /// + /// A new or null if the action failed. + public EFT.Player.QuickGrenadeThrowController CreateObservedQuickGrenadeController() + { + if (item is GrenadeClass grenade) + { + return CoopObservedQuickGrenadeController.Create(player, grenade); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedQuickGrenadeController: item was not of type GrenadeClass, was: {item.GetType()}"); + return null; + } + } - /// - /// Creates a - /// - /// A new or null if the action failed. - public EFT.Player.KnifeController CreateObservedKnifeController() - { - if (knifeComponent != null) - { - return CoopObservedKnifeController.Create(player, knifeComponent); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CoopObservedKnifeController: knifeComponent was null!"); - return null; - } - } + /// + /// Creates a + /// + /// A new or null if the action failed. + public EFT.Player.KnifeController CreateObservedKnifeController() + { + if (knifeComponent != null) + { + return CoopObservedKnifeController.Create(player, knifeComponent); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CoopObservedKnifeController: knifeComponent was null!"); + return null; + } + } - /// - /// Creates a - /// - /// A new or null if the action failed. - public EFT.Player.MedsController CreateObservedMedsController() - { - if (food != null) - { - return CoopObservedMedsController.Create(player, food, EBodyPart.Head, amount, animationVariant); - } - if (meds != null) - { - return CoopObservedMedsController.Create(player, meds, bodyPart, 1f, animationVariant); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedMedsController: meds or food was null!"); - return null; - } - } - } + /// + /// Creates a + /// + /// A new or null if the action failed. + public EFT.Player.MedsController CreateObservedMedsController() + { + if (food != null) + { + return CoopObservedMedsController.Create(player, food, EBodyPart.Head, amount, animationVariant); + } + if (meds != null) + { + return CoopObservedMedsController.Create(player, meds, bodyPart, 1f, animationVariant); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandsControllerFactory::CreateObservedMedsController: meds or food was null!"); + return null; + } + } + } } diff --git a/Fika.Core/Coop/Factories/PingFactory.cs b/Fika.Core/Coop/Factories/PingFactory.cs index d900c9f2..c8f5e915 100644 --- a/Fika.Core/Coop/Factories/PingFactory.cs +++ b/Fika.Core/Coop/Factories/PingFactory.cs @@ -13,225 +13,225 @@ namespace Fika.Core.Coop.Factories; public static class PingFactory { - public enum EPingType : byte - { - Point, - Player, - DeadBody, - LootItem, - LootContainer, - Door, - Interactable - } - - public static void ReceivePing(Vector3 location, EPingType pingType, Color pingColor, string nickname, string localeId) - { - GameObject prefab = AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); - GameObject pingGameObject = UnityEngine.Object.Instantiate(prefab); - AbstractPing abstractPing = FromPingType(pingType, pingGameObject); - if (abstractPing != null) - { - abstractPing.Initialize(ref location, null, pingColor); - Singleton.Instance.PlayUISound(GetPingSound()); - if (string.IsNullOrEmpty(localeId)) - { - NotificationManagerClass.DisplayMessageNotification($"Received a ping from {ColorUtils.ColorizeText(Colors.GREEN, nickname)}", - ENotificationDurationType.Default, ENotificationIconType.Friend); - } - else - { - string localizedName = localeId.Localized(); - NotificationManagerClass.DisplayMessageNotification($"{ColorUtils.ColorizeText(Colors.GREEN, nickname)} has pinged {LocaleUtils.GetPrefix(localizedName)} {ColorUtils.ColorizeText(Colors.BLUE, localizedName)}", - ENotificationDurationType.Default, ENotificationIconType.Friend); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"Received {pingType} from {nickname} but factory failed to handle it"); - } - } - - public static EUISoundType GetPingSound() - { - return FikaPlugin.PingSound.Value switch - { - FikaPlugin.EPingSound.InsuranceInsured => EUISoundType.InsuranceInsured, - FikaPlugin.EPingSound.SubQuestComplete => EUISoundType.QuestSubTrackComplete, - FikaPlugin.EPingSound.ButtonClick => EUISoundType.ButtonClick, - FikaPlugin.EPingSound.ButtonHover => EUISoundType.ButtonOver, - FikaPlugin.EPingSound.InsuranceItemInsured => EUISoundType.InsuranceItemOnInsure, - FikaPlugin.EPingSound.MenuButtonBottom => EUISoundType.ButtonBottomBarClick, - FikaPlugin.EPingSound.ErrorMessage => EUISoundType.ErrorMessage, - FikaPlugin.EPingSound.InspectWindow => EUISoundType.MenuInspectorWindowOpen, - FikaPlugin.EPingSound.InspectWindowClose => EUISoundType.MenuInspectorWindowClose, - FikaPlugin.EPingSound.MenuEscape => EUISoundType.MenuEscape, - _ => EUISoundType.QuestSubTrackComplete, - }; - } - - public static AbstractPing FromPingType(EPingType type, GameObject gameObject) - { - return type switch - { - EPingType.Point => gameObject.AddComponent(), - EPingType.Player => gameObject.AddComponent(), - EPingType.DeadBody => gameObject.AddComponent(), - EPingType.LootItem => gameObject.AddComponent(), - EPingType.LootContainer => gameObject.AddComponent(), - EPingType.Door => gameObject.AddComponent(), - EPingType.Interactable => gameObject.AddComponent(), - _ => null - }; - } - - public abstract class AbstractPing : MonoBehaviour - { - internal static readonly AssetBundle pingBundle; - - protected Image image; - protected Vector3 hitPoint; - private float screenScale = 1f; - private Color _pingColor = Color.white; - private CoopPlayer mainPlayer; - - static AbstractPing() - { - pingBundle = InternalBundleLoader.Instance.GetAssetBundle("ping"); - } - - protected void Awake() - { - image = GetComponentInChildren(); - image.color = Color.clear; - mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - if (mainPlayer == null) - { - Destroy(gameObject); - FikaPlugin.Instance.FikaLogger.LogError("Ping::Awake: Could not find MainPlayer!"); - } - Destroy(gameObject, FikaPlugin.PingTime.Value); - } - - protected void Update() - { - if (mainPlayer.HealthController.IsAlive && mainPlayer.ProceduralWeaponAnimation.IsAiming) - { - if (mainPlayer.ProceduralWeaponAnimation.CurrentScope.IsOptic && !FikaPlugin.ShowPingDuringOptics.Value) - { - image.color = Color.clear; - return; - } - } - - if (CameraClass.Instance.SSAA != null && CameraClass.Instance.SSAA.isActiveAndEnabled) - { - int outputWidth = CameraClass.Instance.SSAA.GetOutputWidth(); - float inputWidth = CameraClass.Instance.SSAA.GetInputWidth(); - screenScale = outputWidth / inputWidth; - } - - 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(FikaPlugin.PingMinimumOpacity.Value, distanceToCenter / 200)); - } - else - { - image.color = _pingColor; - } - - image.transform.position = screenScale < 1 ? screenPoint : screenPoint * screenScale; - } - } - - public virtual void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - hitPoint = point; - transform.position = point; - _pingColor = pingColor; - - float distance = Mathf.Clamp(Vector3.Distance(CameraClass.Instance.Camera.transform.position, transform.position) / 100, 0.4f, 0.6f); - float pingSize = FikaPlugin.PingSize.Value; - Vector3 scaledSize = new(pingSize, pingSize, pingSize); - if (FikaPlugin.PingScaleWithDistance.Value == true) - { - scaledSize *= distance; - } - else - { - scaledSize *= 0.5f; - } - image.rectTransform.localScale = scaledSize; - } - } - - public class InteractablePing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - image.sprite = pingBundle.LoadAsset("PingPoint"); - } - } - - public class PlayerPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - //Player player = (Player)userObject; - image.sprite = pingBundle.LoadAsset("PingPlayer"); - } - } - - public class LootContainerPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - //LootableContainer lootableContainer = userObject as LootableContainer; - image.sprite = pingBundle.LoadAsset("PingLootableContainer"); - } - } - - public class DoorPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - image.sprite = pingBundle.LoadAsset("PingDoor"); - } - } - - public class PointPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - image.sprite = pingBundle.LoadAsset("PingPoint"); - } - } - - public class DeadBodyPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, Color.white); // White since this icon is already red... - transform.localScale *= 0.5f; - image.sprite = pingBundle.LoadAsset("PingDeadBody"); - } - } - - public class LootItemPing : AbstractPing - { - public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) - { - base.Initialize(ref point, userObject, pingColor); - //LootItem lootItem = userObject as LootItem; - image.sprite = pingBundle.LoadAsset("PingLootItem"); - } - } + public enum EPingType : byte + { + Point, + Player, + DeadBody, + LootItem, + LootContainer, + Door, + Interactable + } + + public static void ReceivePing(Vector3 location, EPingType pingType, Color pingColor, string nickname, string localeId) + { + GameObject prefab = AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); + GameObject pingGameObject = UnityEngine.Object.Instantiate(prefab); + AbstractPing abstractPing = FromPingType(pingType, pingGameObject); + if (abstractPing != null) + { + abstractPing.Initialize(ref location, null, pingColor); + Singleton.Instance.PlayUISound(GetPingSound()); + if (string.IsNullOrEmpty(localeId)) + { + NotificationManagerClass.DisplayMessageNotification($"Received a ping from {ColorUtils.ColorizeText(Colors.GREEN, nickname)}", + ENotificationDurationType.Default, ENotificationIconType.Friend); + } + else + { + string localizedName = localeId.Localized(); + NotificationManagerClass.DisplayMessageNotification($"{ColorUtils.ColorizeText(Colors.GREEN, nickname)} has pinged {LocaleUtils.GetPrefix(localizedName)} {ColorUtils.ColorizeText(Colors.BLUE, localizedName)}", + ENotificationDurationType.Default, ENotificationIconType.Friend); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"Received {pingType} from {nickname} but factory failed to handle it"); + } + } + + public static EUISoundType GetPingSound() + { + return FikaPlugin.PingSound.Value switch + { + FikaPlugin.EPingSound.InsuranceInsured => EUISoundType.InsuranceInsured, + FikaPlugin.EPingSound.SubQuestComplete => EUISoundType.QuestSubTrackComplete, + FikaPlugin.EPingSound.ButtonClick => EUISoundType.ButtonClick, + FikaPlugin.EPingSound.ButtonHover => EUISoundType.ButtonOver, + FikaPlugin.EPingSound.InsuranceItemInsured => EUISoundType.InsuranceItemOnInsure, + FikaPlugin.EPingSound.MenuButtonBottom => EUISoundType.ButtonBottomBarClick, + FikaPlugin.EPingSound.ErrorMessage => EUISoundType.ErrorMessage, + FikaPlugin.EPingSound.InspectWindow => EUISoundType.MenuInspectorWindowOpen, + FikaPlugin.EPingSound.InspectWindowClose => EUISoundType.MenuInspectorWindowClose, + FikaPlugin.EPingSound.MenuEscape => EUISoundType.MenuEscape, + _ => EUISoundType.QuestSubTrackComplete, + }; + } + + public static AbstractPing FromPingType(EPingType type, GameObject gameObject) + { + return type switch + { + EPingType.Point => gameObject.AddComponent(), + EPingType.Player => gameObject.AddComponent(), + EPingType.DeadBody => gameObject.AddComponent(), + EPingType.LootItem => gameObject.AddComponent(), + EPingType.LootContainer => gameObject.AddComponent(), + EPingType.Door => gameObject.AddComponent(), + EPingType.Interactable => gameObject.AddComponent(), + _ => null + }; + } + + public abstract class AbstractPing : MonoBehaviour + { + internal static readonly AssetBundle pingBundle; + + protected Image image; + protected Vector3 hitPoint; + private float screenScale = 1f; + private Color _pingColor = Color.white; + private CoopPlayer mainPlayer; + + static AbstractPing() + { + pingBundle = InternalBundleLoader.Instance.GetAssetBundle("ping"); + } + + protected void Awake() + { + image = GetComponentInChildren(); + image.color = Color.clear; + mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + if (mainPlayer == null) + { + Destroy(gameObject); + FikaPlugin.Instance.FikaLogger.LogError("Ping::Awake: Could not find MainPlayer!"); + } + Destroy(gameObject, FikaPlugin.PingTime.Value); + } + + protected void Update() + { + if (mainPlayer.HealthController.IsAlive && mainPlayer.ProceduralWeaponAnimation.IsAiming) + { + if (mainPlayer.ProceduralWeaponAnimation.CurrentScope.IsOptic && !FikaPlugin.ShowPingDuringOptics.Value) + { + image.color = Color.clear; + return; + } + } + + if (CameraClass.Instance.SSAA != null && CameraClass.Instance.SSAA.isActiveAndEnabled) + { + int outputWidth = CameraClass.Instance.SSAA.GetOutputWidth(); + float inputWidth = CameraClass.Instance.SSAA.GetInputWidth(); + screenScale = outputWidth / inputWidth; + } + + 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(FikaPlugin.PingMinimumOpacity.Value, distanceToCenter / 200)); + } + else + { + image.color = _pingColor; + } + + image.transform.position = screenScale < 1 ? screenPoint : screenPoint * screenScale; + } + } + + public virtual void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + hitPoint = point; + transform.position = point; + _pingColor = pingColor; + + float distance = Mathf.Clamp(Vector3.Distance(CameraClass.Instance.Camera.transform.position, transform.position) / 100, 0.4f, 0.6f); + float pingSize = FikaPlugin.PingSize.Value; + Vector3 scaledSize = new(pingSize, pingSize, pingSize); + if (FikaPlugin.PingScaleWithDistance.Value == true) + { + scaledSize *= distance; + } + else + { + scaledSize *= 0.5f; + } + image.rectTransform.localScale = scaledSize; + } + } + + public class InteractablePing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + image.sprite = pingBundle.LoadAsset("PingPoint"); + } + } + + public class PlayerPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + //Player player = (Player)userObject; + image.sprite = pingBundle.LoadAsset("PingPlayer"); + } + } + + public class LootContainerPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + //LootableContainer lootableContainer = userObject as LootableContainer; + image.sprite = pingBundle.LoadAsset("PingLootableContainer"); + } + } + + public class DoorPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + image.sprite = pingBundle.LoadAsset("PingDoor"); + } + } + + public class PointPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + image.sprite = pingBundle.LoadAsset("PingPoint"); + } + } + + public class DeadBodyPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, Color.white); // White since this icon is already red... + transform.localScale *= 0.5f; + image.sprite = pingBundle.LoadAsset("PingDeadBody"); + } + } + + public class LootItemPing : AbstractPing + { + public override void Initialize(ref Vector3 point, Object userObject, Color pingColor) + { + base.Initialize(ref point, userObject, pingColor); + //LootItem lootItem = userObject as LootItem; + image.sprite = pingBundle.LoadAsset("PingLootItem"); + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs index 69c1c5e9..4f07836f 100644 --- a/Fika.Core/Coop/FreeCamera/FreeCameraController.cs +++ b/Fika.Core/Coop/FreeCamera/FreeCameraController.cs @@ -17,508 +17,508 @@ namespace Fika.Core.Coop.FreeCamera { - /// - /// This is HEAVILY based on Terkoiz's work found here. Thanks for your work Terkoiz! - /// https://dev.sp-tarkov.com/Terkoiz/Freecam/raw/branch/master/project/Terkoiz.Freecam/FreecamController.cs - /// - - public class FreeCameraController : MonoBehaviour - { - private FreeCamera freeCamScript; - - private EftBattleUIScreen playerUi; - private bool uiHidden; - - private bool effectsCleared = false; - - private GamePlayerOwner gamePlayerOwner; - private CoopPlayer player => (CoopPlayer)Singleton.Instance.MainPlayer; - private CoopHandler coopHandler; - - public GameObject CameraParent; - public Camera CameraMain { get; private set; } - public bool IsScriptActive - { - get - { - if (freeCamScript != null) - { - return freeCamScript.IsActive; - } - return false; - } - } - - private TextMeshProUGUI extractText = null; - private bool extracted = false; - private DeathFade deathFade; - private bool deathFadeEnabled; - private DisablerCullingObjectBase[] allCullingObjects; - private List previouslyActiveBakeGroups; - private bool hasEnabledCulling = false; - - protected void Awake() - { - CameraParent = new GameObject("CameraParent"); - Camera FCamera = CameraParent.GetOrAddComponent(); - FCamera.enabled = false; - } - - protected void Start() - { - // Find Main Camera - CameraMain = CameraClass.Instance.Camera; - if (CameraMain == null) - { - return; - } - - // Add Freecam script to main camera in scene - freeCamScript = CameraMain.gameObject.AddComponent(); - if (freeCamScript == null) - { - return; - } - - // Get GamePlayerOwner component - gamePlayerOwner = GetLocalPlayerFromWorld().GetComponentInChildren(); - if (gamePlayerOwner == null) - { - return; - } - - deathFade = CameraClass.Instance.Camera.GetComponent(); - deathFade.enabled = true; - - allCullingObjects = FindObjectsOfType(); - previouslyActiveBakeGroups = []; - - player.ActiveHealthController.DiedEvent += MainPlayer_DiedEvent; - - if (CoopHandler.TryGetCoopHandler(out CoopHandler cHandler)) - { - coopHandler = cHandler; - } - } - - private void MainPlayer_DiedEvent(EDamageType obj) - { - player.ActiveHealthController.DiedEvent -= MainPlayer_DiedEvent; - - if (!deathFadeEnabled) - { - deathFade.EnableEffect(); - deathFadeEnabled = true; - } - - StartCoroutine(DeathRoutine()); - } - - protected void Update() - { - if (gamePlayerOwner == null) - { - return; - } - - if (player == null) - { - return; - } - - if (player.PlayerHealthController == null) - { - return; - } - - CoopHandler.EQuitState quitState = coopHandler.GetQuitState(); - - if (extracted && !freeCamScript.IsActive) - { - ToggleCamera(); - } - - if (FikaPlugin.FreeCamButton.Value.IsDown()) - { - if (!FikaPlugin.Instance.AllowFreeCam) - { - return; - } - - if (quitState == CoopHandler.EQuitState.NONE) - { - ToggleCamera(); - ToggleUi(); - return; - } - } - - if (quitState == CoopHandler.EQuitState.YouHaveExtracted && !extracted) - { - CoopGame coopGame = coopHandler.LocalGameInstance; - if (coopGame.ExtractedPlayers.Contains(player.NetId)) - { - extracted = true; - ShowExtractMessage(); - } - - if (!freeCamScript.IsActive) - { - ToggleCamera(); - ToggleUi(); - } - - if (!effectsCleared) - { - if (player != null) - { - player.Muffled = false; - player.HeavyBreath = false; - } - - if (CameraClass.Exist) - { - ClearEffects(); - } - effectsCleared = true; - } - } - } - - private IEnumerator DeathRoutine() - { - yield return new WaitForSeconds(5); - - CameraClass cameraClassInstance = CameraClass.Instance; - if (cameraClassInstance == null) - { - yield break; - } - - if (cameraClassInstance.EffectsController == null) - { - yield break; - } - - if (cameraClassInstance.Camera != null) - { - cameraClassInstance.Camera.fieldOfView = Singleton.Instance.Game.Settings.FieldOfView; - } - - // Disable the DeathFade effect & Toggle the Camera - deathFade.DisableEffect(); - if (!freeCamScript.IsActive) - { - ToggleCamera(); - ToggleUi(); - } - ShowExtractMessage(); - - if (!effectsCleared) - { - if (player != null) - { - player.Muffled = false; - player.HeavyBreath = false; - } - - if (CameraClass.Exist) - { - ClearEffects(); - } - effectsCleared = true; - } - } - - private void ClearEffects() - { - CameraClass cameraClass = CameraClass.Instance; - cameraClass.EffectsController.method_4(false); - - Traverse effectsController = Traverse.Create(cameraClass.EffectsController); - - BloodOnScreen bloodOnScreen = effectsController.Field("bloodOnScreen_0").Value; - if (bloodOnScreen != null) - { - Destroy(bloodOnScreen); - } - - List effectsManagerList = effectsController.Field>("list_0").Value; - if (effectsManagerList != null) - { - foreach (EffectsController.Class576 effectsManager in effectsManagerList) - { - while (effectsManager.ActiveEffects.Count > 0) - { - IEffect effect = effectsManager.ActiveEffects[0]; - effectsManager.DeleteEffect(effect); - } - } - effectsManagerList.Clear(); - } - - CC_Wiggle wiggleEffect = cameraClass.Camera.gameObject.GetComponent(); - if (wiggleEffect != null) - { - wiggleEffect.enabled = false; - } - - CC_Blend[] blendEffects = cameraClass.Camera.gameObject.GetComponents(); - if (blendEffects.Length > 0) - { - foreach (CC_Blend blendEffect in blendEffects) - { - blendEffect.enabled = false; - } - } - - Destroy(cameraClass.EffectsController); - cameraClass.VisorEffect.Clear(); - Destroy(cameraClass.VisorEffect); - cameraClass.VisorSwitcher.Deinit(); - Destroy(cameraClass.VisorSwitcher); - if (cameraClass.NightVision.On) - { - cameraClass.NightVision.method_1(false); - } - if (cameraClass.ThermalVision.On) - { - cameraClass.ThermalVision.method_1(false); - } - } - - private void ShowExtractMessage() - { - if (FikaPlugin.ShowExtractMessage.Value) - { - string text = FikaPlugin.ExtractKey.Value.MainKey.ToString(); - if (FikaPlugin.ExtractKey.Value.Modifiers.Count() > 0) - { - string modifiers = string.Join(" + ", FikaPlugin.ExtractKey.Value.Modifiers); - text = modifiers + " + " + text; - } - extractText = FikaUIUtils.CreateOverlayText($"Press '{text}' to extract"); - } - } - - /// - /// Toggles the Freecam mode - /// - public void ToggleCamera() - { - // Get our own Player instance. Null means we're not in a raid - if (player == null) - { - return; - } - - if (!freeCamScript.IsActive) - { - SetPlayerToFreecamMode(player); - } - else - { - SetPlayerToFirstPersonMode(player); - } - } - - /// - /// Hides the main UI (health, stamina, stance, hotbar, etc.) - /// - private void ToggleUi() - { - // Check if we're currently in a raid - if (player == null) - { - return; - } - - // If we don't have the UI Component cached, go look for it in the scene - if (playerUi == null) - { - GameObject gameObject = GameObject.Find("BattleUIScreen"); - if (gameObject == null) - { - return; - } - - playerUi = gameObject.GetComponent(); - - if (playerUi == null) - { - //FreecamPlugin.Logger.LogError("Failed to locate player UI"); - return; - } - } - - if (playerUi == null || playerUi.gameObject == null) - { - return; - } - - playerUi.gameObject.SetActive(uiHidden); - uiHidden = !uiHidden; - } - - /// - /// A helper method to set the Player into Freecam mode - /// - /// - private void SetPlayerToFreecamMode(Player localPlayer) - { - // We set the player to third person mode - // This means our character will be fully visible, while letting the camera move freely - localPlayer.PointOfView = EPointOfView.ThirdPerson; - - if (localPlayer.PlayerBody != null) - { - localPlayer.PlayerBody.PointOfView.Value = EPointOfView.FreeCamera; - localPlayer.GetComponent().UpdatePointOfView(); - } - - 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) - { - if (cullingObject.HasEntered) - { - continue; - } - count++; - cullingObject.SetComponentsEnabled(true); - } + /// + /// This is HEAVILY based on Terkoiz's work found here. Thanks for your work Terkoiz! + /// https://dev.sp-tarkov.com/Terkoiz/Freecam/raw/branch/master/project/Terkoiz.Freecam/FreecamController.cs + /// + + public class FreeCameraController : MonoBehaviour + { + private FreeCamera freeCamScript; + + private EftBattleUIScreen playerUi; + private bool uiHidden; + + private bool effectsCleared = false; + + private GamePlayerOwner gamePlayerOwner; + private CoopPlayer player => (CoopPlayer)Singleton.Instance.MainPlayer; + private CoopHandler coopHandler; + + public GameObject CameraParent; + public Camera CameraMain { get; private set; } + public bool IsScriptActive + { + get + { + if (freeCamScript != null) + { + return freeCamScript.IsActive; + } + return false; + } + } + + private TextMeshProUGUI extractText = null; + private bool extracted = false; + private DeathFade deathFade; + private bool deathFadeEnabled; + private DisablerCullingObjectBase[] allCullingObjects; + private List previouslyActiveBakeGroups; + private bool hasEnabledCulling = false; + + protected void Awake() + { + CameraParent = new GameObject("CameraParent"); + Camera FCamera = CameraParent.GetOrAddComponent(); + FCamera.enabled = false; + } + + protected void Start() + { + // Find Main Camera + CameraMain = CameraClass.Instance.Camera; + if (CameraMain == null) + { + return; + } + + // Add Freecam script to main camera in scene + freeCamScript = CameraMain.gameObject.AddComponent(); + if (freeCamScript == null) + { + return; + } + + // Get GamePlayerOwner component + gamePlayerOwner = GetLocalPlayerFromWorld().GetComponentInChildren(); + if (gamePlayerOwner == null) + { + return; + } + + deathFade = CameraClass.Instance.Camera.GetComponent(); + deathFade.enabled = true; + + allCullingObjects = FindObjectsOfType(); + previouslyActiveBakeGroups = []; + + player.ActiveHealthController.DiedEvent += MainPlayer_DiedEvent; + + if (CoopHandler.TryGetCoopHandler(out CoopHandler cHandler)) + { + coopHandler = cHandler; + } + } + + private void MainPlayer_DiedEvent(EDamageType obj) + { + player.ActiveHealthController.DiedEvent -= MainPlayer_DiedEvent; + + if (!deathFadeEnabled) + { + deathFade.EnableEffect(); + deathFadeEnabled = true; + } + + StartCoroutine(DeathRoutine()); + } + + protected void Update() + { + if (gamePlayerOwner == null) + { + return; + } + + if (player == null) + { + return; + } + + if (player.PlayerHealthController == null) + { + return; + } + + CoopHandler.EQuitState quitState = coopHandler.GetQuitState(); + + if (extracted && !freeCamScript.IsActive) + { + ToggleCamera(); + } + + if (FikaPlugin.FreeCamButton.Value.IsDown()) + { + if (!FikaPlugin.Instance.AllowFreeCam) + { + return; + } + + if (quitState == CoopHandler.EQuitState.NONE) + { + ToggleCamera(); + ToggleUi(); + return; + } + } + + if (quitState == CoopHandler.EQuitState.YouHaveExtracted && !extracted) + { + CoopGame coopGame = coopHandler.LocalGameInstance; + if (coopGame.ExtractedPlayers.Contains(player.NetId)) + { + extracted = true; + ShowExtractMessage(); + } + + if (!freeCamScript.IsActive) + { + ToggleCamera(); + ToggleUi(); + } + + if (!effectsCleared) + { + if (player != null) + { + player.Muffled = false; + player.HeavyBreath = false; + } + + if (CameraClass.Exist) + { + ClearEffects(); + } + effectsCleared = true; + } + } + } + + private IEnumerator DeathRoutine() + { + yield return new WaitForSeconds(5); + + CameraClass cameraClassInstance = CameraClass.Instance; + if (cameraClassInstance == null) + { + yield break; + } + + if (cameraClassInstance.EffectsController == null) + { + yield break; + } + + if (cameraClassInstance.Camera != null) + { + cameraClassInstance.Camera.fieldOfView = Singleton.Instance.Game.Settings.FieldOfView; + } + + // Disable the DeathFade effect & Toggle the Camera + deathFade.DisableEffect(); + if (!freeCamScript.IsActive) + { + ToggleCamera(); + ToggleUi(); + } + ShowExtractMessage(); + + if (!effectsCleared) + { + if (player != null) + { + player.Muffled = false; + player.HeavyBreath = false; + } + + if (CameraClass.Exist) + { + ClearEffects(); + } + effectsCleared = true; + } + } + + private void ClearEffects() + { + CameraClass cameraClass = CameraClass.Instance; + cameraClass.EffectsController.method_4(false); + + Traverse effectsController = Traverse.Create(cameraClass.EffectsController); + + BloodOnScreen bloodOnScreen = effectsController.Field("bloodOnScreen_0").Value; + if (bloodOnScreen != null) + { + Destroy(bloodOnScreen); + } + + List effectsManagerList = effectsController.Field>("list_0").Value; + if (effectsManagerList != null) + { + foreach (EffectsController.Class576 effectsManager in effectsManagerList) + { + while (effectsManager.ActiveEffects.Count > 0) + { + IEffect effect = effectsManager.ActiveEffects[0]; + effectsManager.DeleteEffect(effect); + } + } + effectsManagerList.Clear(); + } + + CC_Wiggle wiggleEffect = cameraClass.Camera.gameObject.GetComponent(); + if (wiggleEffect != null) + { + wiggleEffect.enabled = false; + } + + CC_Blend[] blendEffects = cameraClass.Camera.gameObject.GetComponents(); + if (blendEffects.Length > 0) + { + foreach (CC_Blend blendEffect in blendEffects) + { + blendEffect.enabled = false; + } + } + + Destroy(cameraClass.EffectsController); + cameraClass.VisorEffect.Clear(); + Destroy(cameraClass.VisorEffect); + cameraClass.VisorSwitcher.Deinit(); + Destroy(cameraClass.VisorSwitcher); + if (cameraClass.NightVision.On) + { + cameraClass.NightVision.method_1(false); + } + if (cameraClass.ThermalVision.On) + { + cameraClass.ThermalVision.method_1(false); + } + } + + private void ShowExtractMessage() + { + if (FikaPlugin.ShowExtractMessage.Value) + { + string text = FikaPlugin.ExtractKey.Value.MainKey.ToString(); + if (FikaPlugin.ExtractKey.Value.Modifiers.Count() > 0) + { + string modifiers = string.Join(" + ", FikaPlugin.ExtractKey.Value.Modifiers); + text = modifiers + " + " + text; + } + extractText = FikaUIUtils.CreateOverlayText($"Press '{text}' to extract"); + } + } + + /// + /// Toggles the Freecam mode + /// + public void ToggleCamera() + { + // Get our own Player instance. Null means we're not in a raid + if (player == null) + { + return; + } + + if (!freeCamScript.IsActive) + { + SetPlayerToFreecamMode(player); + } + else + { + SetPlayerToFirstPersonMode(player); + } + } + + /// + /// Hides the main UI (health, stamina, stance, hotbar, etc.) + /// + private void ToggleUi() + { + // Check if we're currently in a raid + if (player == null) + { + return; + } + + // If we don't have the UI Component cached, go look for it in the scene + if (playerUi == null) + { + GameObject gameObject = GameObject.Find("BattleUIScreen"); + if (gameObject == null) + { + return; + } + + playerUi = gameObject.GetComponent(); + + if (playerUi == null) + { + //FreecamPlugin.Logger.LogError("Failed to locate player UI"); + return; + } + } + + if (playerUi == null || playerUi.gameObject == null) + { + return; + } + + playerUi.gameObject.SetActive(uiHidden); + uiHidden = !uiHidden; + } + + /// + /// A helper method to set the Player into Freecam mode + /// + /// + private void SetPlayerToFreecamMode(Player localPlayer) + { + // We set the player to third person mode + // This means our character will be fully visible, while letting the camera move freely + localPlayer.PointOfView = EPointOfView.ThirdPerson; + + if (localPlayer.PlayerBody != null) + { + localPlayer.PlayerBody.PointOfView.Value = EPointOfView.FreeCamera; + localPlayer.GetComponent().UpdatePointOfView(); + } + + 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) + { + if (cullingObject.HasEntered) + { + continue; + } + count++; + cullingObject.SetComponentsEnabled(true); + } #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning($"Enabled {count} Culling Triggers."); + FikaPlugin.Instance.FikaLogger.LogWarning($"Enabled {count} Culling Triggers."); #endif - PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); - if (perfectCullingAdaptiveGrid != null) - { - if (perfectCullingAdaptiveGrid.RuntimeGroupMapping.Count > 0) - { - foreach (PerfectCullingCrossSceneGroup sceneGroup in perfectCullingAdaptiveGrid.RuntimeGroupMapping) - { - foreach (PerfectCullingBakeGroup bakeGroup in sceneGroup.bakeGroups) - { - if (!bakeGroup.IsEnabled) - { - bakeGroup.IsEnabled = true; - } - else - { - previouslyActiveBakeGroups.Add(bakeGroup); - } - } - - sceneGroup.enabled = false; - } - } - } - - hasEnabledCulling = true; - } - - public void EnableAllCullingObjects() - { - int count = 0; - foreach (DisablerCullingObjectBase cullingObject in allCullingObjects) - { - if (cullingObject.HasEntered) - { - continue; - } - count++; - cullingObject.SetComponentsEnabled(false); - } + PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); + if (perfectCullingAdaptiveGrid != null) + { + if (perfectCullingAdaptiveGrid.RuntimeGroupMapping.Count > 0) + { + foreach (PerfectCullingCrossSceneGroup sceneGroup in perfectCullingAdaptiveGrid.RuntimeGroupMapping) + { + foreach (PerfectCullingBakeGroup bakeGroup in sceneGroup.bakeGroups) + { + if (!bakeGroup.IsEnabled) + { + bakeGroup.IsEnabled = true; + } + else + { + previouslyActiveBakeGroups.Add(bakeGroup); + } + } + + sceneGroup.enabled = false; + } + } + } + + hasEnabledCulling = true; + } + + public void EnableAllCullingObjects() + { + int count = 0; + foreach (DisablerCullingObjectBase cullingObject in allCullingObjects) + { + if (cullingObject.HasEntered) + { + continue; + } + count++; + cullingObject.SetComponentsEnabled(false); + } #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning($"Disabled {count} Culling Triggers."); + FikaPlugin.Instance.FikaLogger.LogWarning($"Disabled {count} Culling Triggers."); #endif - PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); - if (perfectCullingAdaptiveGrid != null) - { - if (perfectCullingAdaptiveGrid.RuntimeGroupMapping.Count > 0) - { - foreach (PerfectCullingCrossSceneGroup sceneGroup in perfectCullingAdaptiveGrid.RuntimeGroupMapping) - { - sceneGroup.enabled = true; - - foreach (PerfectCullingBakeGroup bakeGroup in sceneGroup.bakeGroups) - { - if (bakeGroup.IsEnabled && !previouslyActiveBakeGroups.Contains(bakeGroup)) - { - bakeGroup.IsEnabled = false; - } - else - { - previouslyActiveBakeGroups.Remove(bakeGroup); - } - } - - previouslyActiveBakeGroups.Clear(); - } - } - } - - hasEnabledCulling = false; - } - - /// - /// Gets the current instance if it's available - /// - /// Local instance; returns null if the game is not in raid - private Player GetLocalPlayerFromWorld() - { - // If the GameWorld instance is null or has no RegisteredPlayers, it most likely means we're not in a raid - GameWorld gameWorld = Singleton.Instance; - if (gameWorld == null || gameWorld.MainPlayer == null) - { - return null; - } - - // One of the RegisteredPlayers will have the IsYourPlayer flag set, which will be our own Player instance - return gameWorld.MainPlayer; - } - - public void OnDestroy() - { - if (!Singleton.TryRelease(this)) - { - FikaPlugin.Instance.FikaLogger.LogWarning("Unable to release FreeCameraController singleton"); - } - Destroy(CameraParent); - - // Destroy FreeCamScript before FreeCamController if exists - Destroy(freeCamScript); - if (extractText != null) - { - Destroy(extractText); - } - Destroy(this); - } - } + PerfectCullingAdaptiveGrid perfectCullingAdaptiveGrid = FindObjectOfType(); + if (perfectCullingAdaptiveGrid != null) + { + if (perfectCullingAdaptiveGrid.RuntimeGroupMapping.Count > 0) + { + foreach (PerfectCullingCrossSceneGroup sceneGroup in perfectCullingAdaptiveGrid.RuntimeGroupMapping) + { + sceneGroup.enabled = true; + + foreach (PerfectCullingBakeGroup bakeGroup in sceneGroup.bakeGroups) + { + if (bakeGroup.IsEnabled && !previouslyActiveBakeGroups.Contains(bakeGroup)) + { + bakeGroup.IsEnabled = false; + } + else + { + previouslyActiveBakeGroups.Remove(bakeGroup); + } + } + + previouslyActiveBakeGroups.Clear(); + } + } + } + + hasEnabledCulling = false; + } + + /// + /// Gets the current instance if it's available + /// + /// Local instance; returns null if the game is not in raid + private Player GetLocalPlayerFromWorld() + { + // If the GameWorld instance is null or has no RegisteredPlayers, it most likely means we're not in a raid + GameWorld gameWorld = Singleton.Instance; + if (gameWorld == null || gameWorld.MainPlayer == null) + { + return null; + } + + // One of the RegisteredPlayers will have the IsYourPlayer flag set, which will be our own Player instance + return gameWorld.MainPlayer; + } + + public void OnDestroy() + { + if (!Singleton.TryRelease(this)) + { + FikaPlugin.Instance.FikaLogger.LogWarning("Unable to release FreeCameraController singleton"); + } + Destroy(CameraParent); + + // Destroy FreeCamScript before FreeCamController if exists + Destroy(freeCamScript); + if (extractText != null) + { + Destroy(extractText); + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs b/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs index 3dda5851..92134081 100644 --- a/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs +++ b/Fika.Core/Coop/FreeCamera/Patches/DeathFade_Patch.cs @@ -5,23 +5,23 @@ namespace Fika.Core.Coop.FreeCamera.Patches { - public class DeathFade_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(DeathFade).GetMethod(nameof(DeathFade.DisableEffect)); - } + public class DeathFade_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(DeathFade).GetMethod(nameof(DeathFade.DisableEffect)); + } - [PatchPrefix] - private static bool Prefix(DeathFade __instance) - { - Type deathFadeType = typeof(DeathFade); + [PatchPrefix] + private static bool Prefix(DeathFade __instance) + { + Type deathFadeType = typeof(DeathFade); - deathFadeType.GetField("_float_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, (float)deathFadeType.GetField("_disableTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance)); - deathFadeType.GetField("bool_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, false); - AnimationCurve disableCurveValue = (AnimationCurve)deathFadeType.GetField("_disableCurve", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); - deathFadeType.GetField("animationCurve_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, disableCurveValue); - return false; - } - } + deathFadeType.GetField("_float_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, (float)deathFadeType.GetField("_disableTime", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance)); + deathFadeType.GetField("bool_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, false); + AnimationCurve disableCurveValue = (AnimationCurve)deathFadeType.GetField("_disableCurve", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + deathFadeType.GetField("animationCurve_0", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(__instance, disableCurveValue); + return false; + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs b/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs index d0655da6..32fc01e0 100644 --- a/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs +++ b/Fika.Core/Coop/FreeCamera/Patches/FadeBlackScreen_Patch.cs @@ -7,49 +7,49 @@ namespace Fika.Core.Coop.FreeCamera.Patches { - internal class FadeBlackScreen_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.FadeBlackScreen)); - - [PatchPrefix] - public static bool Prefix() - { - return false; - } - } - - internal class StartBlackScreenShow_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.StartBlackScreenShow)); - - [PatchPrefix] - public static bool Prefix() - { - return false; - } - - [PatchPostfix] - public static void Postfix(Action callback) - { - callback?.Invoke(); - } - } - - internal class SetBlackImageAlpha_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.SetBlackImageAlpha)); - - [PatchPrefix] - public static bool Prefix() - { - return false; - } - - [PatchPostfix] - public static void Postfix(float alpha, Image ____overlapBlackImage) - { - ____overlapBlackImage.gameObject.SetActive(value: true); - ____overlapBlackImage.color = new Color(0f, 0f, 0f, 0f); - } - } + internal class FadeBlackScreen_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.FadeBlackScreen)); + + [PatchPrefix] + public static bool Prefix() + { + return false; + } + } + + internal class StartBlackScreenShow_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.StartBlackScreenShow)); + + [PatchPrefix] + public static bool Prefix() + { + return false; + } + + [PatchPostfix] + public static void Postfix(Action callback) + { + callback?.Invoke(); + } + } + + internal class SetBlackImageAlpha_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(PreloaderUI).GetMethod(nameof(PreloaderUI.SetBlackImageAlpha)); + + [PatchPrefix] + public static bool Prefix() + { + return false; + } + + [PatchPostfix] + public static void Postfix(float alpha, Image ____overlapBlackImage) + { + ____overlapBlackImage.gameObject.SetActive(value: true); + ____overlapBlackImage.color = new Color(0f, 0f, 0f, 0f); + } + } } diff --git a/Fika.Core/Coop/GameMode/CoopGame.cs b/Fika.Core/Coop/GameMode/CoopGame.cs index af20452f..7a356198 100644 --- a/Fika.Core/Coop/GameMode/CoopGame.cs +++ b/Fika.Core/Coop/GameMode/CoopGame.cs @@ -52,1561 +52,1561 @@ namespace Fika.Core.Coop.GameMode { - /// - /// Coop game used in Fika - /// - public sealed class CoopGame : BaseLocalGame, IBotGame, IFikaGame - { - public string InfiltrationPoint; - public ExitStatus MyExitStatus { get; set; } = ExitStatus.Survived; - public string MyExitLocation { get; set; } = null; - public ISpawnSystem SpawnSystem; - public Dictionary Bots = []; - public List ExtractedPlayers { get; } = []; - public string SpawnId; - public bool InteractablesInitialized { get; set; } = false; - public bool HasReceivedLoot { get; set; } = false; - - private readonly Dictionary botQueue = []; - private Coroutine extractRoutine; - private SpawnPointManagerClass spawnPoints = null; - private ISpawnPoint spawnPoint = null; - private BossSpawnWaveManagerClass BossSpawnWaveManagerClass; - private WavesSpawnScenario wavesSpawnScenario_0; - private NonWavesSpawnScenario nonWavesSpawnScenario_0; - private Func func_1; - private bool hasSaved = false; - private CoopExfilManager exfilManager; - private CoopTimeManager timeManager; - private CoopHalloweenEventManager halloweenEventManager; - private FikaDebug fikaDebug; - private bool isServer; - private List localTriggerZones; - - public FikaDynamicAI DynamicAI { get; private set; } - public RaidSettings RaidSettings { get; private set; } - public byte[] HostLootItems { get; private set; } - public GClass1211 LootItems { get; internal set; } = []; - BotsController IBotGame.BotsController - { - get - { - return botsController_0; - } - } - public BotsController BotsController - { - get - { - return botsController_0; - } - } - public IWeatherCurve WeatherCurve - { - get - { - return WeatherController.Instance.WeatherCurve; - } - } - - private static ManualLogSource Logger; - - /// - /// 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("CoopGame"); - - 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); - coopGame.nonWavesSpawnScenario_0.ImplementWaveSettings(wavesSettings); - - // Waves Scenario setup - WildSpawnWave[] waves = LocalGame.smethod_7(wavesSettings, location.waves); - 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.BossSpawnWaveManagerClass = BossSpawnWaveManagerClass.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); - - if (OfflineRaidSettingsMenuPatch_Override.UseCustomWeather && coopGame.isServer) - { - Logger.LogInfo("Custom weather enabled, initializing curves"); - coopGame.SetupCustomWeather(timeAndWeather); - } - - OfflineRaidSettingsMenuPatch_Override.UseCustomWeather = false; - - 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) - { - return; - } - - DateTime dateTime = EFTDateTimeClass.StartOfDay(); - DateTime dateTime2 = dateTime.AddDays(1); - - WeatherClass weather = WeatherClass.CreateDefault(); - WeatherClass weather2 = WeatherClass.CreateDefault(); - weather.Cloudness = weather2.Cloudness = timeAndWeather.CloudinessType.ToValue(); - weather.Rain = weather2.Rain = timeAndWeather.RainType.ToValue(); - weather.Wind = weather2.Wind = timeAndWeather.WindType.ToValue(); - weather.ScaterringFogDensity = weather2.ScaterringFogDensity = timeAndWeather.FogType.ToValue(); - weather.Time = dateTime.Ticks; - weather2.Time = dateTime2.Ticks; - WeatherController.Instance.method_0([weather, weather2]); - } - - public override void SetMatchmakerStatus(string status, float? progress = null) - { - if (CurrentScreenSingleton.Instance.CurrentScreenController is MatchmakerTimeHasCome.TimeHasComeScreenClass gclass) - { - gclass.ChangeStatus(status, progress); - } - } - - #region Bot - /// - /// Returns all human players - /// - /// used to fetch players - /// - private List GetPlayers(CoopHandler coopHandler) - { - List humanPlayers = []; - - // Grab all players - foreach (CoopPlayer player in coopHandler.Players.Values) - { - if ((player.IsYourPlayer || player is ObservedCoopPlayer) && player.HealthController.IsAlive) - { - humanPlayers.Add(player); - } - } - 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; - - foreach (Player player in humanPlayers) - { - float tempDistance = Vector3.SqrMagnitude(position - player.Position); - - if (tempDistance < distance) // Get the closest distance to any player. so we dont despawn bots in a players face. - { - distance = tempDistance; - } - } - return distance; - } - - /// - /// 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; - - foreach (KeyValuePair botKeyValuePair in Bots) - { - if (IsInvalidBotForDespawning(botKeyValuePair)) - { - continue; - } - - float tempDistance = GetDistanceFromPlayers(botKeyValuePair.Value.Position, humanPlayers); - - if (tempDistance > furthestDistance) // We still want the furthest bot. - { - furthestDistance = tempDistance; - furthestBot = botKeyValuePair.Key; - } - } - - 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) - { -#if DEBUG - Logger.LogWarning("Bot is null, skipping"); -#endif - return true; - } - - CoopBot coopBot = (CoopBot)kvp.Value; - - if (coopBot != null) - { -#if DEBUG - Logger.LogWarning("Bot is not started, skipping"); -#endif - return true; - } - - WildSpawnType role = kvp.Value.Profile.Info.Settings.Role; - - if (role is not WildSpawnType.pmcUSEC and not WildSpawnType.pmcBEAR and not WildSpawnType.assault) - { - // 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 (!isServer) - { - return null; - } - - if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - Logger.LogError($"{nameof(CreateBot)}: Unable to find {nameof(CoopHandler)}"); - return null; - } - - while (!Status.IsRunned()) - { - if (Status == GameStatus.Stopped) - { - return null; - } - - await Task.Yield(); - } - - WildSpawnType role = profile.Info.Settings.Role; - bool isSpecial = false; - 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."); -#endif - isSpecial = true; - } - - if (FikaPlugin.EnforcedSpawnLimits.Value && Bots.Count >= botsController_0.BotSpawner.MaxBots) - { - bool despawned = false; - - if (FikaPlugin.DespawnFurthest.Value) - { - 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: {Bots.Count}, Max: {botsController_0.BotSpawner.MaxBots}, Alive & Loading: {botsController_0.BotSpawner.AliveAndLoadingBotsCount}"); -#endif - return null; - } - } - - int netId = 1000; - LocalPlayer localPlayer; - - if (!Status.IsRunned()) - { - localPlayer = null; - } - else if (Bots.ContainsKey(profile.Id)) - { - localPlayer = null; - } - else - { - //int num = method_12(); - profile.SetSpawnedInSession(profile.Info.Side == EPlayerSide.Savage); - - FikaServer server = Singleton.Instance; - netId = server.PopNetId(); - - SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(profile), true, true, position, netId); - Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableUnordered); - - if (server.NetServer.ConnectedPeersCount > 0) - { - await WaitForPlayersToLoadBotProfile(netId); - } - - localPlayer = await CoopBot.CreateBot(netId, position, Quaternion.identity, "Player", - "Bot_", EPointOfView.ThirdPerson, profile, true, UpdateQueue, Player.EUpdateMode.Auto, - Player.EUpdateMode.Auto, BackendConfigAbstractClass.Config.CharacterController.BotPlayerMode, new Func(LocalGame.Class1394.class1394_0.method_4), - new Func(LocalGame.Class1394.class1394_0.method_5), GClass1457.Default); - - localPlayer.Location = Location_0.Id; - - if (Bots.ContainsKey(localPlayer.ProfileId)) - { - Logger.LogError($"{profile.ProfileId} already exists in the bots list, cancelling..."); - Destroy(localPlayer); - return null; - } - else - { -#if DEBUG - Logger.LogInfo($"Bot {profile.Info.Settings.Role} created at {position} SUCCESSFULLY!"); -#endif - Bots.Add(localPlayer.ProfileId, localPlayer); - } - - if (profile.Info.Side is EPlayerSide.Bear or EPlayerSide.Usec) - { - Slot backpackSlot = profile.Inventory.Equipment.GetSlot(EquipmentSlot.Backpack); - Item backpack = backpackSlot.ContainedItem; - if (backpack != null) - { - Item[] items = backpack.GetAllItems()?.ToArray(); - if (items != null) - { - for (int i = 0; i < items.Count(); i++) - { - Item item = items[i]; - if (item == backpack) - { - continue; - } - - item.SpawnedInSession = true; - } - } - } - } - - if (Singleton.Instance != null) - { - if (!Singleton.Instance.RegisteredPlayers.Any(x => x.ProfileId == localPlayer.ProfileId)) - { - Singleton.Instance.RegisterPlayer(localPlayer); - } - } - else - { - Logger.LogError("Cannot add player because GameWorld is NULL"); - } - } - - 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)) - { - botQueue[netId]++; - } - else - { - Logger.LogError($"IncreaseLoadedPlayers: could not find netId {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); - DateTime start = DateTime.Now; - int connectedPeers = Singleton.Instance.NetServer.ConnectedPeersCount; - - while (botQueue[netId] < connectedPeers) - { - if (start.Subtract(DateTime.Now).TotalSeconds >= 30) // ~30 second failsafe - { - Logger.LogWarning("WaitForPlayersToLoadBotProfile: Took too long to receive all packets!"); - botQueue.Remove(netId); - return; - } - - await Task.Delay(250); - } - - botQueue.Remove(netId); - } - - /// - /// Tries to despawn the furthest bot from all players - /// - /// - /// - /// - /// - private bool TryDespawnFurthestBot(Profile profile, Vector3 position, CoopHandler coopHandler) - { - List humanPlayers = GetPlayers(coopHandler); - - string botKey = GetFurthestBot(humanPlayers, out float furthestDistance); - - if (botKey == string.Empty) - { -#if DEBUG - Logger.LogWarning("TryDespawnFurthest: botKey was empty"); -#endif - return false; - } - - 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."); -#endif - return false; - } - - //Dont despawn inside of dynamic AI range - if (furthestDistance < FikaPlugin.DespawnMinimumDistance.Value * FikaPlugin.DespawnMinimumDistance.Value) //Square it because we use sqrMagnitude for distance calculation - { -#if DEBUG - Logger.LogWarning($"We're not despawning anything. Furthest despawnable bot is inside minimum despawn range."); -#endif - return false; - } - Player bot = Bots[botKey]; -#if DEBUG - Logger.LogWarning($"Removing {bot.Profile.Info.Settings.Role} at a distance of {Math.Sqrt(furthestDistance)}m from its nearest player."); -#endif - DespawnBot(coopHandler, bot); -#if DEBUG - Logger.LogWarning($"Bot {bot.Profile.Info.Settings.Role} despawned successfully."); -#endif - return true; - } - - /// - /// Despawns a bot - /// - /// - /// The bot to despawn - internal void DespawnBot(CoopHandler coopHandler, Player bot) - { - BotOwner botOwner = bot.AIData.BotOwner; - - BotsController.Bots.Remove(botOwner); - bot.HealthController.DiedEvent -= botOwner.method_6; // Unsubscribe from the event to prevent errors. - BotUnspawn(botOwner); - if (botOwner != null) - { - botOwner.Dispose(); - } - - CoopPlayer coopPlayer = (CoopPlayer)bot; - coopHandler.Players.Remove(coopPlayer.NetId); - Bots.Remove(bot.ProfileId); - } - #endregion - - /// - /// The countdown deploy screen - /// - /// - public override IEnumerator vmethod_1() - { - if (!isServer) - { - FikaBackendUtils.ScreenController.ChangeStatus("Waiting for host to finish raid initialization..."); - - FikaClient fikaClient = Singleton.Instance; - do - { - yield return new WaitForEndOfFrame(); - } while (!fikaClient.HostReady); - LootItems = null; - } - else - { - FikaServer fikaServer = Singleton.Instance; - InformationPacket packet = new(false) - { - NumberOfPlayers = fikaServer.NetServer.ConnectedPeersCount, - ReadyPlayers = fikaServer.ReadyClients, - HostReady = true - }; - - fikaServer.SendDataToAll(new(), ref packet, DeliveryMethod.ReliableUnordered); - HostLootItems = null; - } - - CoopPlayer coopPlayer = (CoopPlayer)PlayerOwner.Player; - coopPlayer.PacketSender.Init(); - - int timeBeforeDeployLocal = FikaBackendUtils.IsReconnect ? 3 : 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); - } - - /// - /// This task ensures that all players are joined and loaded before continuing - /// - /// - private IEnumerator WaitForOtherPlayers() - { - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - if (isServer && FikaBackendUtils.HostExpectedNumberOfPlayers <= 1) - { - if (DynamicAI != null) - { - DynamicAI.AddHumans(); - } - - Singleton.Instance.ReadyClients++; - yield break; - } - - NetDataWriter writer = new(); - - float expectedPlayers = FikaBackendUtils.HostExpectedNumberOfPlayers; - FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)(1 / expectedPlayers)); - - if (isServer) - { - do - { - yield return null; - } while (coopHandler.AmountOfHumans < expectedPlayers); - - FikaServer server = Singleton.Instance; - server.ReadyClients++; - InformationPacket packet = new() - { - NumberOfPlayers = server.NetServer.ConnectedPeersCount, - ReadyPlayers = server.ReadyClients - }; - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - - do - { - FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)server.ReadyClients / expectedPlayers); - yield return new WaitForEndOfFrame(); - } while (server.ReadyClients < expectedPlayers); - - foreach (CoopPlayer player in coopHandler.Players.Values) - { - SyncNetIdPacket syncPacket = new(player.ProfileId, player.NetId); - - writer.Reset(); - Singleton.Instance.SendDataToAll(writer, ref syncPacket, DeliveryMethod.ReliableUnordered); - } - - if (DynamicAI != null) - { - DynamicAI.AddHumans(); - } - } - else - { - do - { - yield return null; - } while (coopHandler.AmountOfHumans < expectedPlayers); - - FikaClient client = Singleton.Instance; - InformationPacket packet = new(true) - { - ReadyPlayers = 1 - }; - writer.Reset(); - client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); - - do - { - FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)client.ReadyClients / expectedPlayers); - yield return new WaitForEndOfFrame(); - } while (client.ReadyClients < expectedPlayers); - } - } - } - - public string GetSpawnpointName() - { - if (!string.IsNullOrEmpty(SpawnId)) - { - return SpawnId; - } - return string.Empty; - } - - /// - /// Sends or receives the for the game - /// - /// - private async Task SendOrReceiveSpawnPoint() - { - FikaBackendUtils.ScreenController.ChangeStatus($"Retrieving spawn info from server..."); - if (isServer) - { - bool spawnTogether = RaidSettings.PlayersSpawnPlace == EPlayersSpawnPlace.SamePlace; - if (spawnTogether) - { - Logger.LogInfo($"Setting spawn point to name: '{spawnPoint.Name}', id: '{spawnPoint.Id}'"); - SpawnId = spawnPoint.Id; - } - else - { - Logger.LogInfo("Using random spawn points!"); - NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); - SpawnId = "RANDOM"; - } - } - else - { - NetDataWriter writer = new(); - SpawnpointPacket packet = new(true); - FikaClient client = Singleton.Instance; - - do - { - client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); - await Task.Delay(1000); - if (string.IsNullOrEmpty(SpawnId)) - { - await Task.Delay(2000); - } - } while (string.IsNullOrEmpty(SpawnId)); - - Logger.LogInfo($"Retrieved spawn point id '{SpawnId}' from server"); - - if (SpawnId != "RANDOM") - { - Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field>("dictionary_0").Value; - foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys) - { - if (spawnPointObject.Id == SpawnId) - { - spawnPoint = spawnPointObject; - } - } - } - else - { - Logger.LogInfo("Spawn Point was random"); - NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); - spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); - } - } - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - 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) - { - 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); - - myPlayer.Location = Location_0.Id; - - if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - Logger.LogError($"{nameof(vmethod_2)}:Unable to find {nameof(CoopHandler)}"); - throw new MissingComponentException("CoopHandler was missing during CoopGame init"); - } - - if (RaidSettings.MetabolismDisabled) - { - myPlayer.HealthController.DisableMetabolism(); - NotificationManagerClass.DisplayMessageNotification("Metabolism disabled", iconType: EFT.Communications.ENotificationIconType.Alert); - } - - CoopPlayer coopPlayer = (CoopPlayer)myPlayer; - coopHandler.Players.Add(coopPlayer.NetId, coopPlayer); - coopHandler.HumanPlayers.Add(coopPlayer); - coopPlayer.SetupMainPlayer(); - - PlayerSpawnRequest body = new(myPlayer.ProfileId, FikaBackendUtils.GetGroupId()); - await FikaRequestHandler.UpdatePlayerSpawn(body); - - myPlayer.SpawnPoint = spawnPoint; - - GameObject customButton = null; - - await NetManagerUtils.SetupGameVariables(isServer, coopPlayer); - customButton = CreateCancelButton(myPlayer, customButton); - - if (!isServer && !FikaBackendUtils.IsReconnect) - { - SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(coopPlayer.Profile), coopPlayer.HealthController.IsAlive, false, coopPlayer.Transform.position, coopPlayer.NetId); - FikaClient client = Singleton.Instance; - NetDataWriter writer = new(); - - do - { - await Task.Delay(250); - } while (client.NetClient.FirstPeer == null); - - client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - await WaitForPlayers(); - - fikaDebug = gameObject.AddComponent(); - - Destroy(customButton); - - if (FikaBackendUtils.IsReconnect && !FikaBackendUtils.ReconnectPosition.Equals(Vector3.zero)) - { - myPlayer.Teleport(FikaBackendUtils.ReconnectPosition); - } - - return myPlayer; - } - - /// - /// This creates a "custom" Back button so that we can back out if we get stuck - /// - /// - /// - /// - /// - private GameObject CreateCancelButton(LocalPlayer myPlayer, GameObject customButton) - { - if (myPlayer.Side is EPlayerSide.Savage) - { - return null; - } - - if (MenuUI.Instantiated) - { - MenuUI menuUI = MenuUI.Instance; - 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); - customButton.gameObject.SetActive(true); - DefaultUIButton backButtonComponent = customButton.GetComponent(); - backButtonComponent.SetHeaderText("Cancel", 32); - backButtonComponent.SetEnabledTooltip("EXPERIMENTAL: Cancels the matchmaking and returns to the menu."); - 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?", - ErrorScreen.EButtonType.OkButton, 15f, () => - { - StopFromCancel(myPlayer.ProfileId, ExitStatus.Runner); - PlayerLeftRequest playerLeftRequest = new(FikaBackendUtils.Profile.ProfileId); - FikaRequestHandler.RaidLeave(playerLeftRequest); - }, null); - }); - Traverse.Create(backButtonComponent).Field("OnClick").SetValue(newEvent); - } - - return customButton; - } - - /// - /// Initializes the local player - /// - /// - /// - /// - /// - public async Task InitPlayer(BotControllerSettings botsSettings, string backendUrl, Callback runCallback) - { - Status = GameStatus.Running; - UnityEngine.Random.InitState((int)EFTDateTimeClass.Now.Ticks); - - if (isServer) - { - await NetManagerUtils.CreateCoopHandler(); - } - - CoopHandler handler = CoopHandler.GetCoopHandler(); - if (handler != null) - { - handler.LocalGameInstance = this; - } - else - { - throw new NullReferenceException("CoopHandler was missing!"); - } - - LocationSettingsClass.Location location; - if (Location_0.IsHideout) - { - location = Location_0; - } - else - { - using (CounterCreatorAbstractClass.StartWithToken("LoadLocation")) - { - if (isServer) - { - int num = UnityEngine.Random.Range(1, 6); - method_6(backendUrl, Location_0.Id, num); - location = await iSession.LoadLocationLoot(Location_0.Id, num); - HostLootItems = SimpleZlib.CompressToBytes(location.Loot.ToJson([]), 6); - } - else - { - FikaBackendUtils.ScreenController.ChangeStatus("Retrieving loot from server..."); - if (!FikaBackendUtils.IsReconnect) - { - await RetrieveLootFromServer(true); - } - else - { - await RetrieveLootFromServer(false); - } - location = new() - { - Loot = LootItems - }; - } - } - } - - ApplicationConfigClass config = BackendConfigAbstractClass.Config; - if (config.FixedFrameRate > 0f) - { - FixedDeltaTime = 1f / config.FixedFrameRate; - } - - if (FikaBackendUtils.IsReconnect) - { - await GetReconnectProfile(ProfileId); - } - - using (CounterCreatorAbstractClass.StartWithToken("player create")) - { - Player player = await CreateLocalPlayer(); - dictionary_0.Add(player.ProfileId, player); - gparam_0 = func_1(player); - PlayerCameraController.Create(gparam_0.Player); - CameraClass.Instance.SetOcclusionCullingEnabled(Location_0.OcculsionCullingEnabled); - CameraClass.Instance.IsActive = false; - } - - StartHandler startHandler = new(this, botsSettings, SpawnSystem, runCallback); - - if (FikaBackendUtils.IsReconnect) - { - await Reconnect(); - foreach (KeyValuePair.BodyPartState> item in gparam_0.Player.ActiveHealthController.Dictionary_0) - { - if (item.Value.Health.AtMinimum) - { - item.Value.IsDestroyed = true; - } - } - } - - handler.SetReady(true); - - await method_11(location, startHandler.FinishLoading); - } - - private async Task GetReconnectProfile(string profileId) - { - Profile_0 = null; - - ReconnectPacket reconnectPacket = new(true) - { - InitialRequest = true, - ProfileId = profileId - }; - FikaClient client = Singleton.Instance; - client.Writer.Reset(); - client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); - - do - { - await Task.Delay(250); - } while (Profile_0 == null); - } - - private async Task Reconnect() - { - FikaBackendUtils.ScreenController.ChangeStatus($"Reconnecting..."); - - ReconnectPacket reconnectPacket = new(true) - { - ProfileId = ProfileId - }; - FikaClient client = Singleton.Instance; - client.Writer.Reset(); - client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); - - do - { - await Task.Delay(1000); - } while (!client.ReconnectDone); - } - - private async Task RetrieveLootFromServer(bool register) - { - FikaClient client = Singleton.Instance; - WorldLootPacket packet = new(true); - do - { - client.Writer.Reset(); - client.SendData(client.Writer, ref packet, DeliveryMethod.ReliableUnordered); - await Task.Delay(1000); - if (!HasReceivedLoot && LootItems.Count < 1) - { - await Task.Delay(2000); - } - } while (!HasReceivedLoot); - - if (register) - { - RegisterPlayerRequest request = new(0, Location_0.Id, 0); - await FikaRequestHandler.RegisterPlayer(request); - } - } - - /// - /// Handler used to start the game - /// - /// - /// - /// - /// - private class StartHandler(BaseLocalGame localGame, BotControllerSettings botSettings, ISpawnSystem spawnSystem, Callback runCallback) - { - private readonly BaseLocalGame localGame = localGame; - private readonly BotControllerSettings botSettings = botSettings; - private readonly ISpawnSystem spawnSystem = spawnSystem; - private readonly Callback runCallback = runCallback; - - public void FinishLoading() - { - localGame.method_5(botSettings, spawnSystem, runCallback); - } - } - - /// - /// Creates the local player - /// - /// A - private async Task CreateLocalPlayer() - { - int num = 0; - - Player.EUpdateMode eupdateMode = Player.EUpdateMode.Auto; - if (BackendConfigAbstractClass.Config.UseHandsFastAnimator) - { - eupdateMode = Player.EUpdateMode.Manual; - } - - spawnPoints = SpawnPointManagerClass.CreateFromScene(new DateTime?(EFTDateTimeClass.LocalDateTimeFromUnixTime(Location_0.UnixDateTime)), - Location_0.SpawnPointParams); - int spawnSafeDistance = (Location_0.SpawnSafeDistanceMeters > 0) ? Location_0.SpawnSafeDistanceMeters : 100; - 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 (isServer) - { - spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); - await SendOrReceiveSpawnPoint(); - } - - if (!isServer) - { - await SendOrReceiveSpawnPoint(); - if (spawnPoint == null) - { - Logger.LogWarning("SpawnPoint was null after retrieving it from the server!"); - spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); - } - - await InitInteractables(); - } - - 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, BackendConfigAbstractClass.Config.CharacterController.ClientPlayerMode, - new Func(Class1384.class1384_0.method_3), new Func(Class1384.class1384_0.method_4), - statisticsManager, null, null); - - myPlayer.OnEpInteraction += OnEpInteraction; - - return myPlayer; - } - - private async Task InitInteractables() - { - FikaBackendUtils.ScreenController.ChangeStatus($"Retrieving interactable objects from server..."); - NetDataWriter writer = new(); - FikaClient client = Singleton.Instance; - InteractableInitPacket packet = new(true); - - do - { - writer.Reset(); - client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); - await Task.Delay(1000); - if (!InteractablesInitialized) - { - await Task.Delay(2000); - } - } while (!InteractablesInitialized); - } - - /// - /// 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 (FikaBackendUtils.ScreenController != null) - { - FikaBackendUtils.ScreenController.ChangeStatus($"Initializing Coop Game..."); - } - int numbersOfPlayersToWaitFor = 0; - - if (isServer) - { - FikaServer server = Singleton.Instance; - - do - { - numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (server.NetServer.ConnectedPeersCount + 1); - if (FikaBackendUtils.ScreenController != null) - { - if (numbersOfPlayersToWaitFor > 0) - { - FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); - } - else - { - FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); - } - } - else - { - Logger.LogError("WaitForPlayers::GClass3163 was null!"); - } - await Task.Delay(100); - } while (numbersOfPlayersToWaitFor > 0); - } - else - { - FikaClient client = Singleton.Instance; - - while (client.NetClient == null) - { - await Task.Delay(500); - } - - int connectionAttempts = 0; - - while (client.ServerConnection == null && connectionAttempts < 5) - { - // Server retries 10 times with a 500ms interval, we give it 5 seconds to try - 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."); - } - } - - while (client == null) - { - await Task.Delay(500); - } - - InformationPacket packet = new(true); - NetDataWriter writer = new(); - writer.Reset(); - client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); - do - { - numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (client.ConnectedClients + 1); - if (FikaBackendUtils.ScreenController != null) - { - if (numbersOfPlayersToWaitFor > 0) - { - FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); - } - else - { - FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); - } - } - else - { - Logger.LogError("WaitForPlayers::GClass3163 was null!"); - } - packet = new(true); - writer.Reset(); - client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); - await Task.Delay(1000); - } 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); - await FikaRequestHandler.UpdateSetStatus(statusBody); - Logger.LogInfo("Setting game status to: " + status.ToString()); - } - - /// - /// Bot System Starter -> Countdown - /// - /// - /// - /// - /// - public override IEnumerator vmethod_4(BotControllerSettings controllerSettings, ISpawnSystem spawnSystem, Callback runCallback) - { -#if DEBUG - Logger.LogWarning("vmethod_4"); -#endif - if (isServer) - { - BotsPresets botsPresets = new(iSession, wavesSpawnScenario_0.SpawnWaves, - BossSpawnWaveManagerClass.BossSpawnWaves, nonWavesSpawnScenario_0.GClass1478_0, false); - - GClass814 botCreator = new(this, botsPresets, CreateBot); - BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); - - bool useWaveControl = controllerSettings.BotAmount == EBotAmount.Horde; - - botsController_0.Init(this, botCreator, botZones, spawnSystem, wavesSpawnScenario_0.BotLocationModifier, - controllerSettings.IsEnabled, controllerSettings.IsScavWars, useWaveControl, false, - BossSpawnWaveManagerClass.HaveSectants, Singleton.Instance, Location_0.OpenZones); - - Logger.LogInfo($"Location: {Location_0.Name}"); - - int numberOfBots = controllerSettings.BotAmount switch - { - EBotAmount.AsOnline => 20, - EBotAmount.NoBots => 0, - EBotAmount.Low => 15, - EBotAmount.Medium => 20, - EBotAmount.High => 25, - EBotAmount.Horde => 35, - _ => 15, - }; - - botsController_0.SetSettings(numberOfBots, iSession.BackEndConfig.BotPresets, iSession.BackEndConfig.BotWeaponScatterings); - if (!FikaBackendUtils.IsDedicated) - { - botsController_0.AddActivePLayer(PlayerOwner.Player); - } - - if (FikaPlugin.EnforcedSpawnLimits.Value) - { - int limits = Location_0.Id.ToLower() switch - { - "factory4_day" => FikaPlugin.MaxBotsFactory.Value, - "factory4_night" => FikaPlugin.MaxBotsFactory.Value, - "bigmap" => FikaPlugin.MaxBotsCustoms.Value, - "interchange" => FikaPlugin.MaxBotsInterchange.Value, - "rezervbase" => FikaPlugin.MaxBotsReserve.Value, - "woods" => FikaPlugin.MaxBotsWoods.Value, - "shoreline" => FikaPlugin.MaxBotsShoreline.Value, - "tarkovstreets" => FikaPlugin.MaxBotsStreets.Value, - "sandbox" => FikaPlugin.MaxBotsGroundZero.Value, - "laboratory" => FikaPlugin.MaxBotsLabs.Value, - "lighthouse" => FikaPlugin.MaxBotsLighthouse.Value, - _ => 0 - }; - - if (limits > 0) - { - botsController_0.BotSpawner.SetMaxBots(limits); - } - } - - DynamicAI = gameObject.AddComponent(); - } - else - { - controllerSettings.BotAmount = EBotAmount.NoBots; - - BotsPresets profileCreator = new(iSession, [], [], [], false); - - GClass814 botCreator = new(this, profileCreator, CreateBot); - BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); - - // Setting this to an empty array stops the client from downloading bots - typeof(BotsController).GetField("_allTypes", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(botsController_0, new WildSpawnType[0]); - - botsController_0.Init(this, botCreator, botZones, spawnSystem, wavesSpawnScenario_0.BotLocationModifier, - false, false, true, false, false, Singleton.Instance, Location_0.OpenZones); - - botsController_0.SetSettings(0, [], []); - - Logger.LogInfo($"Location: {Location_0.Name}"); - } - - BackendConfigSettingsClass instance = Singleton.Instance; - - if (instance != null && instance.EventSettings.EventActive && !instance.EventSettings.LocationsToIgnore.Contains(Location_0.Id)) - { - Singleton.Instance.HalloweenEventController = new HalloweenEventControllerClass(); - GameObject gameObject = (GameObject)Resources.Load("Prefabs/HALLOWEEN_CONTROLLER"); - if (gameObject != null) - { - transform.InstantiatePrefab(gameObject); - } - - halloweenEventManager = Singleton.Instance.gameObject.GetOrAddComponent(); - } - - LocalGame.Class1391 seasonTaskHandler = new(); - ESeason season = iSession.Season; - Class394 seasonHandler = new(); - Singleton.Instance.GInterface26_0 = seasonHandler; - seasonTaskHandler.task = seasonHandler.Run(season); - yield return new WaitUntil(new Func(seasonTaskHandler.method_0)); - - yield return WaitForOtherPlayers(); - - if (isServer) - { - if (Location_0.OldSpawn && wavesSpawnScenario_0.SpawnWaves != null && wavesSpawnScenario_0.SpawnWaves.Length != 0) - { - Logger.LogInfo("Running old spawn system. Waves: " + wavesSpawnScenario_0.SpawnWaves.Length); - if (wavesSpawnScenario_0 != null) - { - wavesSpawnScenario_0.Run(EBotsSpawnMode.Anyway); - } - } - - if (Location_0.NewSpawn) - { - Logger.LogInfo("Running new spawn system."); - if (nonWavesSpawnScenario_0 != null) - { - nonWavesSpawnScenario_0.Run(); - } - } - - BossSpawnWaveManagerClass.Run(EBotsSpawnMode.Anyway); - - FikaPlugin.DynamicAI.SettingChanged += DynamicAI_SettingChanged; - FikaPlugin.DynamicAIRate.SettingChanged += DynamicAIRate_SettingChanged; - - SetStatusModel status = new(FikaBackendUtils.GetGroupId(), LobbyEntry.ELobbyStatus.IN_GAME); - Task updateStatus = FikaRequestHandler.UpdateSetStatus(status); - - while (!updateStatus.IsCompleted) - { - yield return null; - } - } - else - { - if (wavesSpawnScenario_0 != null) - { - wavesSpawnScenario_0.Stop(); - } - if (nonWavesSpawnScenario_0 != null) - { - nonWavesSpawnScenario_0.Stop(); - } - if (BossSpawnWaveManagerClass != null) - { - BossSpawnWaveManagerClass.Stop(); - } - } - - // Add FreeCamController to GameWorld GameObject - FreeCameraController freeCamController = Singleton.Instance.gameObject.GetOrAddComponent(); - Singleton.Create(freeCamController); - Singleton.Instance.gameObject.GetOrAddComponent(); - FikaAirdropsManager.ContainerCount = 0; - - SetupRaidCode(); - - // Need to move to separate classes later - if (Singleton.Instance.MineManager != null) - { - Singleton.Instance.MineManager.OnExplosion += OnMineExplode; - } - - Singleton.Instance.TimeBeforeDeployLocal = Math.Max(Singleton.Instance.TimeBeforeDeployLocal, 5); - - FikaBackendUtils.ScreenController.ChangeStatus("Finishing raid initialization..."); - - 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) - { - DynamicAI.RateChanged(FikaPlugin.DynamicAIRate.Value); - } - } - - /// - /// Triggers when the setting is changed - /// - /// - /// - private void DynamicAI_SettingChanged(object sender, EventArgs e) - { - if (DynamicAI != null) - { - DynamicAI.EnabledChange(FikaPlugin.DynamicAI.Value); - } - } - - /// - /// Triggers when a explodes - /// - /// - private void OnMineExplode(MineDirectional directional) - { - if (!directional.gameObject.active) - { - return; - } + /// + /// Coop game used in Fika + /// + public sealed class CoopGame : BaseLocalGame, IBotGame, IFikaGame + { + public string InfiltrationPoint; + public ExitStatus MyExitStatus { get; set; } = ExitStatus.Survived; + public string MyExitLocation { get; set; } = null; + public ISpawnSystem SpawnSystem; + public Dictionary Bots = []; + public List ExtractedPlayers { get; } = []; + public string SpawnId; + public bool InteractablesInitialized { get; set; } = false; + public bool HasReceivedLoot { get; set; } = false; + + private readonly Dictionary botQueue = []; + private Coroutine extractRoutine; + private SpawnPointManagerClass spawnPoints = null; + private ISpawnPoint spawnPoint = null; + private BossSpawnWaveManagerClass BossSpawnWaveManagerClass; + private WavesSpawnScenario wavesSpawnScenario_0; + private NonWavesSpawnScenario nonWavesSpawnScenario_0; + private Func func_1; + private bool hasSaved = false; + private CoopExfilManager exfilManager; + private CoopTimeManager timeManager; + private CoopHalloweenEventManager halloweenEventManager; + private FikaDebug fikaDebug; + private bool isServer; + private List localTriggerZones; + + public FikaDynamicAI DynamicAI { get; private set; } + public RaidSettings RaidSettings { get; private set; } + public byte[] HostLootItems { get; private set; } + public GClass1211 LootItems { get; internal set; } = []; + BotsController IBotGame.BotsController + { + get + { + return botsController_0; + } + } + public BotsController BotsController + { + get + { + return botsController_0; + } + } + public IWeatherCurve WeatherCurve + { + get + { + return WeatherController.Instance.WeatherCurve; + } + } + + private static ManualLogSource Logger; + + /// + /// 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("CoopGame"); + + 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); + coopGame.nonWavesSpawnScenario_0.ImplementWaveSettings(wavesSettings); + + // Waves Scenario setup + WildSpawnWave[] waves = LocalGame.smethod_7(wavesSettings, location.waves); + 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.BossSpawnWaveManagerClass = BossSpawnWaveManagerClass.smethod_0(bossSpawns, new Action(coopGame.botsController_0.ActivateBotsByWave)); + + if (OfflineRaidSettingsMenuPatch_Override.UseCustomWeather && coopGame.isServer) + { + Logger.LogInfo("Custom weather enabled, initializing curves"); + coopGame.SetupCustomWeather(timeAndWeather); + } + + OfflineRaidSettingsMenuPatch_Override.UseCustomWeather = false; + + 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) + { + return; + } + + DateTime dateTime = EFTDateTimeClass.StartOfDay(); + DateTime dateTime2 = dateTime.AddDays(1); + + WeatherClass weather = WeatherClass.CreateDefault(); + WeatherClass weather2 = WeatherClass.CreateDefault(); + weather.Cloudness = weather2.Cloudness = timeAndWeather.CloudinessType.ToValue(); + weather.Rain = weather2.Rain = timeAndWeather.RainType.ToValue(); + weather.Wind = weather2.Wind = timeAndWeather.WindType.ToValue(); + weather.ScaterringFogDensity = weather2.ScaterringFogDensity = timeAndWeather.FogType.ToValue(); + weather.Time = dateTime.Ticks; + weather2.Time = dateTime2.Ticks; + WeatherController.Instance.method_0([weather, weather2]); + } + + public override void SetMatchmakerStatus(string status, float? progress = null) + { + if (CurrentScreenSingleton.Instance.CurrentScreenController is MatchmakerTimeHasCome.TimeHasComeScreenClass gclass) + { + gclass.ChangeStatus(status, progress); + } + } + + #region Bot + /// + /// Returns all human players + /// + /// used to fetch players + /// + private List GetPlayers(CoopHandler coopHandler) + { + List humanPlayers = []; + + // Grab all players + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if ((player.IsYourPlayer || player is ObservedCoopPlayer) && player.HealthController.IsAlive) + { + humanPlayers.Add(player); + } + } + 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; + + foreach (Player player in humanPlayers) + { + float tempDistance = Vector3.SqrMagnitude(position - player.Position); + + if (tempDistance < distance) // Get the closest distance to any player. so we dont despawn bots in a players face. + { + distance = tempDistance; + } + } + return distance; + } + + /// + /// 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; + + foreach (KeyValuePair botKeyValuePair in Bots) + { + if (IsInvalidBotForDespawning(botKeyValuePair)) + { + continue; + } + + float tempDistance = GetDistanceFromPlayers(botKeyValuePair.Value.Position, humanPlayers); + + if (tempDistance > furthestDistance) // We still want the furthest bot. + { + furthestDistance = tempDistance; + furthestBot = botKeyValuePair.Key; + } + } + + 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) + { +#if DEBUG + Logger.LogWarning("Bot is null, skipping"); +#endif + return true; + } - MinePacket packet = new() - { - MinePositon = directional.transform.position - }; - if (!isServer) - { - Singleton.Instance.SendData(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); - } - } - - /// - /// Sets up events and all s - /// - public override void vmethod_5() - { - 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); - } + CoopBot coopBot = (CoopBot)kvp.Value; - InfiltrationPoint = spawnPoint.Infiltration; - Profile_0.Info.EntryPoint = InfiltrationPoint; - Logger.LogInfo("SpawnPoint: " + spawnPoint.Id + ", InfiltrationPoint: " + InfiltrationPoint); + if (coopBot != null) + { +#if DEBUG + Logger.LogWarning("Bot is not started, skipping"); +#endif + return true; + } + + WildSpawnType role = kvp.Value.Profile.Info.Settings.Role; + + if (role is not WildSpawnType.pmcUSEC and not WildSpawnType.pmcBEAR and not WildSpawnType.assault) + { + // 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 (!isServer) + { + return null; + } + + if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + Logger.LogError($"{nameof(CreateBot)}: Unable to find {nameof(CoopHandler)}"); + return null; + } + + while (!Status.IsRunned()) + { + if (Status == GameStatus.Stopped) + { + return null; + } + + await Task.Yield(); + } + + WildSpawnType role = profile.Info.Settings.Role; + bool isSpecial = false; + 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."); +#endif + isSpecial = true; + } - if (!isServer) - { - CarExtraction carExtraction = FindObjectOfType(); - if (carExtraction != null) - { - carExtraction.Subscribee.OnStatusChanged -= carExtraction.OnStatusChangedHandler; - } - } + if (FikaPlugin.EnforcedSpawnLimits.Value && Bots.Count >= botsController_0.BotSpawner.MaxBots) + { + bool despawned = false; - ExfiltrationControllerClass.Instance.InitAllExfiltrationPoints(Location_0.exits, !isServer, ""); - ExfiltrationPoint[] exfilPoints = ExfiltrationControllerClass.Instance.EligiblePoints(Profile_0); + if (FikaPlugin.DespawnFurthest.Value) + { + despawned = TryDespawnFurthestBot(profile, position, coopHandler); + } - GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), exfilPoints); + // 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: {Bots.Count}, Max: {botsController_0.BotSpawner.MaxBots}, Alive & Loading: {botsController_0.BotSpawner.AliveAndLoadingBotsCount}"); +#endif + return null; + } + } + + int netId = 1000; + LocalPlayer localPlayer; + + if (!Status.IsRunned()) + { + localPlayer = null; + } + else if (Bots.ContainsKey(profile.Id)) + { + localPlayer = null; + } + else + { + //int num = method_12(); + profile.SetSpawnedInSession(profile.Info.Side == EPlayerSide.Savage); + + FikaServer server = Singleton.Instance; + netId = server.PopNetId(); + + SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(profile), true, true, position, netId); + Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableUnordered); + + if (server.NetServer.ConnectedPeersCount > 0) + { + await WaitForPlayersToLoadBotProfile(netId); + } + + localPlayer = await CoopBot.CreateBot(netId, position, Quaternion.identity, "Player", + "Bot_", EPointOfView.ThirdPerson, profile, true, UpdateQueue, Player.EUpdateMode.Auto, + Player.EUpdateMode.Auto, BackendConfigAbstractClass.Config.CharacterController.BotPlayerMode, new Func(LocalGame.Class1394.class1394_0.method_4), + new Func(LocalGame.Class1394.class1394_0.method_5), GClass1457.Default); + + localPlayer.Location = Location_0.Id; + + if (Bots.ContainsKey(localPlayer.ProfileId)) + { + Logger.LogError($"{profile.ProfileId} already exists in the bots list, cancelling..."); + Destroy(localPlayer); + return null; + } + else + { +#if DEBUG + Logger.LogInfo($"Bot {profile.Info.Settings.Role} created at {position} SUCCESSFULLY!"); +#endif + Bots.Add(localPlayer.ProfileId, localPlayer); + } + + if (profile.Info.Side is EPlayerSide.Bear or EPlayerSide.Usec) + { + Slot backpackSlot = profile.Inventory.Equipment.GetSlot(EquipmentSlot.Backpack); + Item backpack = backpackSlot.ContainedItem; + if (backpack != null) + { + Item[] items = backpack.GetAllItems()?.ToArray(); + if (items != null) + { + for (int i = 0; i < items.Count(); i++) + { + Item item = items[i]; + if (item == backpack) + { + continue; + } + + item.SpawnedInSession = true; + } + } + } + } + + if (Singleton.Instance != null) + { + if (!Singleton.Instance.RegisteredPlayers.Any(x => x.ProfileId == localPlayer.ProfileId)) + { + Singleton.Instance.RegisterPlayer(localPlayer); + } + } + else + { + Logger.LogError("Cannot add player because GameWorld is NULL"); + } + } + + 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)) + { + botQueue[netId]++; + } + else + { + Logger.LogError($"IncreaseLoadedPlayers: could not find netId {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); + DateTime start = DateTime.Now; + int connectedPeers = Singleton.Instance.NetServer.ConnectedPeersCount; + + while (botQueue[netId] < connectedPeers) + { + if (start.Subtract(DateTime.Now).TotalSeconds >= 30) // ~30 second failsafe + { + Logger.LogWarning("WaitForPlayersToLoadBotProfile: Took too long to receive all packets!"); + botQueue.Remove(netId); + return; + } + + await Task.Delay(250); + } + + botQueue.Remove(netId); + } + + /// + /// Tries to despawn the furthest bot from all players + /// + /// + /// + /// + /// + private bool TryDespawnFurthestBot(Profile profile, Vector3 position, CoopHandler coopHandler) + { + List humanPlayers = GetPlayers(coopHandler); + + string botKey = GetFurthestBot(humanPlayers, out float furthestDistance); + + if (botKey == string.Empty) + { +#if DEBUG + Logger.LogWarning("TryDespawnFurthest: botKey was empty"); +#endif + return false; + } - exfilManager = gameObject.AddComponent(); - exfilManager.Run(exfilPoints); + 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."); +#endif + return false; + } - /*if (FikaPlugin.Instance.UseBTR) + //Dont despawn inside of dynamic AI range + if (furthestDistance < FikaPlugin.DespawnMinimumDistance.Value * FikaPlugin.DespawnMinimumDistance.Value) //Square it because we use sqrMagnitude for distance calculation + { +#if DEBUG + Logger.LogWarning($"We're not despawning anything. Furthest despawnable bot is inside minimum despawn range."); +#endif + return false; + } + Player bot = Bots[botKey]; +#if DEBUG + Logger.LogWarning($"Removing {bot.Profile.Info.Settings.Role} at a distance of {Math.Sqrt(furthestDistance)}m from its nearest player."); +#endif + DespawnBot(coopHandler, bot); +#if DEBUG + Logger.LogWarning($"Bot {bot.Profile.Info.Settings.Role} despawned successfully."); +#endif + return true; + } + + /// + /// Despawns a bot + /// + /// + /// The bot to despawn + internal void DespawnBot(CoopHandler coopHandler, Player bot) + { + BotOwner botOwner = bot.AIData.BotOwner; + + BotsController.Bots.Remove(botOwner); + bot.HealthController.DiedEvent -= botOwner.method_6; // Unsubscribe from the event to prevent errors. + BotUnspawn(botOwner); + if (botOwner != null) + { + botOwner.Dispose(); + } + + CoopPlayer coopPlayer = (CoopPlayer)bot; + coopHandler.Players.Remove(coopPlayer.NetId); + Bots.Remove(bot.ProfileId); + } + #endregion + + /// + /// The countdown deploy screen + /// + /// + public override IEnumerator vmethod_1() + { + if (!isServer) + { + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for host to finish raid initialization..."); + + FikaClient fikaClient = Singleton.Instance; + do + { + yield return new WaitForEndOfFrame(); + } while (!fikaClient.HostReady); + LootItems = null; + } + else + { + FikaServer fikaServer = Singleton.Instance; + InformationPacket packet = new(false) + { + NumberOfPlayers = fikaServer.NetServer.ConnectedPeersCount, + ReadyPlayers = fikaServer.ReadyClients, + HostReady = true + }; + + fikaServer.SendDataToAll(new(), ref packet, DeliveryMethod.ReliableUnordered); + HostLootItems = null; + } + + CoopPlayer coopPlayer = (CoopPlayer)PlayerOwner.Player; + coopPlayer.PacketSender.Init(); + + int timeBeforeDeployLocal = FikaBackendUtils.IsReconnect ? 3 : 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); + } + + /// + /// This task ensures that all players are joined and loaded before continuing + /// + /// + private IEnumerator WaitForOtherPlayers() + { + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + if (isServer && FikaBackendUtils.HostExpectedNumberOfPlayers <= 1) + { + if (DynamicAI != null) + { + DynamicAI.AddHumans(); + } + + Singleton.Instance.ReadyClients++; + yield break; + } + + NetDataWriter writer = new(); + + float expectedPlayers = FikaBackendUtils.HostExpectedNumberOfPlayers; + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)(1 / expectedPlayers)); + + if (isServer) + { + do + { + yield return null; + } while (coopHandler.AmountOfHumans < expectedPlayers); + + FikaServer server = Singleton.Instance; + server.ReadyClients++; + InformationPacket packet = new() + { + NumberOfPlayers = server.NetServer.ConnectedPeersCount, + ReadyPlayers = server.ReadyClients + }; + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + + do + { + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)server.ReadyClients / expectedPlayers); + yield return new WaitForEndOfFrame(); + } while (server.ReadyClients < expectedPlayers); + + foreach (CoopPlayer player in coopHandler.Players.Values) + { + SyncNetIdPacket syncPacket = new(player.ProfileId, player.NetId); + + writer.Reset(); + Singleton.Instance.SendDataToAll(writer, ref syncPacket, DeliveryMethod.ReliableUnordered); + } + + if (DynamicAI != null) + { + DynamicAI.AddHumans(); + } + } + else + { + do + { + yield return null; + } while (coopHandler.AmountOfHumans < expectedPlayers); + + FikaClient client = Singleton.Instance; + InformationPacket packet = new(true) + { + ReadyPlayers = 1 + }; + writer.Reset(); + client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); + + do + { + FikaBackendUtils.ScreenController.ChangeStatus("Waiting for other players to finish loading...", (float)client.ReadyClients / expectedPlayers); + yield return new WaitForEndOfFrame(); + } while (client.ReadyClients < expectedPlayers); + } + } + } + + public string GetSpawnpointName() + { + if (!string.IsNullOrEmpty(SpawnId)) + { + return SpawnId; + } + return string.Empty; + } + + /// + /// Sends or receives the for the game + /// + /// + private async Task SendOrReceiveSpawnPoint() + { + FikaBackendUtils.ScreenController.ChangeStatus($"Retrieving spawn info from server..."); + if (isServer) + { + bool spawnTogether = RaidSettings.PlayersSpawnPlace == EPlayersSpawnPlace.SamePlace; + if (spawnTogether) + { + Logger.LogInfo($"Setting spawn point to name: '{spawnPoint.Name}', id: '{spawnPoint.Id}'"); + SpawnId = spawnPoint.Id; + } + else + { + Logger.LogInfo("Using random spawn points!"); + NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); + SpawnId = "RANDOM"; + } + } + else + { + NetDataWriter writer = new(); + SpawnpointPacket packet = new(true); + FikaClient client = Singleton.Instance; + + do + { + client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); + await Task.Delay(1000); + if (string.IsNullOrEmpty(SpawnId)) + { + await Task.Delay(2000); + } + } while (string.IsNullOrEmpty(SpawnId)); + + Logger.LogInfo($"Retrieved spawn point id '{SpawnId}' from server"); + + if (SpawnId != "RANDOM") + { + Dictionary allSpawnPoints = Traverse.Create(spawnPoints).Field>("dictionary_0").Value; + foreach (ISpawnPoint spawnPointObject in allSpawnPoints.Keys) + { + if (spawnPointObject.Id == SpawnId) + { + spawnPoint = spawnPointObject; + } + } + } + else + { + Logger.LogInfo("Spawn Point was random"); + NotificationManagerClass.DisplayMessageNotification("Using random spawn points", iconType: EFT.Communications.ENotificationIconType.Alert); + spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); + } + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + 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) + { + 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); + + myPlayer.Location = Location_0.Id; + + if (!CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + Logger.LogError($"{nameof(vmethod_2)}:Unable to find {nameof(CoopHandler)}"); + throw new MissingComponentException("CoopHandler was missing during CoopGame init"); + } + + if (RaidSettings.MetabolismDisabled) + { + myPlayer.HealthController.DisableMetabolism(); + NotificationManagerClass.DisplayMessageNotification("Metabolism disabled", iconType: EFT.Communications.ENotificationIconType.Alert); + } + + CoopPlayer coopPlayer = (CoopPlayer)myPlayer; + coopHandler.Players.Add(coopPlayer.NetId, coopPlayer); + coopHandler.HumanPlayers.Add(coopPlayer); + coopPlayer.SetupMainPlayer(); + + PlayerSpawnRequest body = new(myPlayer.ProfileId, FikaBackendUtils.GetGroupId()); + await FikaRequestHandler.UpdatePlayerSpawn(body); + + myPlayer.SpawnPoint = spawnPoint; + + GameObject customButton = null; + + await NetManagerUtils.SetupGameVariables(isServer, coopPlayer); + customButton = CreateCancelButton(myPlayer, customButton); + + if (!isServer && !FikaBackendUtils.IsReconnect) + { + SendCharacterPacket packet = new(new FikaSerialization.PlayerInfoPacket(coopPlayer.Profile), coopPlayer.HealthController.IsAlive, false, coopPlayer.Transform.position, coopPlayer.NetId); + FikaClient client = Singleton.Instance; + NetDataWriter writer = new(); + + do + { + await Task.Delay(250); + } while (client.NetClient.FirstPeer == null); + + client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + await WaitForPlayers(); + + fikaDebug = gameObject.AddComponent(); + + Destroy(customButton); + + if (FikaBackendUtils.IsReconnect && !FikaBackendUtils.ReconnectPosition.Equals(Vector3.zero)) + { + myPlayer.Teleport(FikaBackendUtils.ReconnectPosition); + } + + return myPlayer; + } + + /// + /// This creates a "custom" Back button so that we can back out if we get stuck + /// + /// + /// + /// + /// + private GameObject CreateCancelButton(LocalPlayer myPlayer, GameObject customButton) + { + if (myPlayer.Side is EPlayerSide.Savage) + { + return null; + } + + if (MenuUI.Instantiated) + { + MenuUI menuUI = MenuUI.Instance; + 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); + customButton.gameObject.SetActive(true); + DefaultUIButton backButtonComponent = customButton.GetComponent(); + backButtonComponent.SetHeaderText("Cancel", 32); + backButtonComponent.SetEnabledTooltip("EXPERIMENTAL: Cancels the matchmaking and returns to the menu."); + 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?", + ErrorScreen.EButtonType.OkButton, 15f, () => + { + StopFromCancel(myPlayer.ProfileId, ExitStatus.Runner); + PlayerLeftRequest playerLeftRequest = new(FikaBackendUtils.Profile.ProfileId); + FikaRequestHandler.RaidLeave(playerLeftRequest); + }, null); + }); + Traverse.Create(backButtonComponent).Field("OnClick").SetValue(newEvent); + } + + return customButton; + } + + /// + /// Initializes the local player + /// + /// + /// + /// + /// + public async Task InitPlayer(BotControllerSettings botsSettings, string backendUrl, Callback runCallback) + { + Status = GameStatus.Running; + UnityEngine.Random.InitState((int)EFTDateTimeClass.Now.Ticks); + + if (isServer) + { + await NetManagerUtils.CreateCoopHandler(); + } + + CoopHandler handler = CoopHandler.GetCoopHandler(); + if (handler != null) + { + handler.LocalGameInstance = this; + } + else + { + throw new NullReferenceException("CoopHandler was missing!"); + } + + LocationSettingsClass.Location location; + if (Location_0.IsHideout) + { + location = Location_0; + } + else + { + using (CounterCreatorAbstractClass.StartWithToken("LoadLocation")) + { + if (isServer) + { + int num = UnityEngine.Random.Range(1, 6); + method_6(backendUrl, Location_0.Id, num); + location = await iSession.LoadLocationLoot(Location_0.Id, num); + HostLootItems = SimpleZlib.CompressToBytes(location.Loot.ToJson([]), 6); + } + else + { + FikaBackendUtils.ScreenController.ChangeStatus("Retrieving loot from server..."); + if (!FikaBackendUtils.IsReconnect) + { + await RetrieveLootFromServer(true); + } + else + { + await RetrieveLootFromServer(false); + } + location = new() + { + Loot = LootItems + }; + } + } + } + + ApplicationConfigClass config = BackendConfigAbstractClass.Config; + if (config.FixedFrameRate > 0f) + { + FixedDeltaTime = 1f / config.FixedFrameRate; + } + + if (FikaBackendUtils.IsReconnect) + { + await GetReconnectProfile(ProfileId); + } + + using (CounterCreatorAbstractClass.StartWithToken("player create")) + { + Player player = await CreateLocalPlayer(); + dictionary_0.Add(player.ProfileId, player); + gparam_0 = func_1(player); + PlayerCameraController.Create(gparam_0.Player); + CameraClass.Instance.SetOcclusionCullingEnabled(Location_0.OcculsionCullingEnabled); + CameraClass.Instance.IsActive = false; + } + + StartHandler startHandler = new(this, botsSettings, SpawnSystem, runCallback); + + if (FikaBackendUtils.IsReconnect) + { + await Reconnect(); + foreach (KeyValuePair.BodyPartState> item in gparam_0.Player.ActiveHealthController.Dictionary_0) + { + if (item.Value.Health.AtMinimum) + { + item.Value.IsDestroyed = true; + } + } + } + + handler.SetReady(true); + + await method_11(location, startHandler.FinishLoading); + } + + private async Task GetReconnectProfile(string profileId) + { + Profile_0 = null; + + ReconnectPacket reconnectPacket = new(true) + { + InitialRequest = true, + ProfileId = profileId + }; + FikaClient client = Singleton.Instance; + client.Writer.Reset(); + client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); + + do + { + await Task.Delay(250); + } while (Profile_0 == null); + } + + private async Task Reconnect() + { + FikaBackendUtils.ScreenController.ChangeStatus($"Reconnecting..."); + + ReconnectPacket reconnectPacket = new(true) + { + ProfileId = ProfileId + }; + FikaClient client = Singleton.Instance; + client.Writer.Reset(); + client.SendData(client.Writer, ref reconnectPacket, DeliveryMethod.ReliableUnordered); + + do + { + await Task.Delay(1000); + } while (!client.ReconnectDone); + } + + private async Task RetrieveLootFromServer(bool register) + { + FikaClient client = Singleton.Instance; + WorldLootPacket packet = new(true); + do + { + client.Writer.Reset(); + client.SendData(client.Writer, ref packet, DeliveryMethod.ReliableUnordered); + await Task.Delay(1000); + if (!HasReceivedLoot && LootItems.Count < 1) + { + await Task.Delay(2000); + } + } while (!HasReceivedLoot); + + if (register) + { + RegisterPlayerRequest request = new(0, Location_0.Id, 0); + await FikaRequestHandler.RegisterPlayer(request); + } + } + + /// + /// Handler used to start the game + /// + /// + /// + /// + /// + private class StartHandler(BaseLocalGame localGame, BotControllerSettings botSettings, ISpawnSystem spawnSystem, Callback runCallback) + { + private readonly BaseLocalGame localGame = localGame; + private readonly BotControllerSettings botSettings = botSettings; + private readonly ISpawnSystem spawnSystem = spawnSystem; + private readonly Callback runCallback = runCallback; + + public void FinishLoading() + { + localGame.method_5(botSettings, spawnSystem, runCallback); + } + } + + /// + /// Creates the local player + /// + /// A + private async Task CreateLocalPlayer() + { + int num = 0; + + Player.EUpdateMode eupdateMode = Player.EUpdateMode.Auto; + if (BackendConfigAbstractClass.Config.UseHandsFastAnimator) + { + eupdateMode = Player.EUpdateMode.Manual; + } + + spawnPoints = SpawnPointManagerClass.CreateFromScene(new DateTime?(EFTDateTimeClass.LocalDateTimeFromUnixTime(Location_0.UnixDateTime)), + Location_0.SpawnPointParams); + int spawnSafeDistance = (Location_0.SpawnSafeDistanceMeters > 0) ? Location_0.SpawnSafeDistanceMeters : 100; + 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 (isServer) + { + spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); + await SendOrReceiveSpawnPoint(); + } + + if (!isServer) + { + await SendOrReceiveSpawnPoint(); + if (spawnPoint == null) + { + Logger.LogWarning("SpawnPoint was null after retrieving it from the server!"); + spawnPoint = SpawnSystem.SelectSpawnPoint(ESpawnCategory.Player, Profile_0.Info.Side); + } + + await InitInteractables(); + } + + 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, BackendConfigAbstractClass.Config.CharacterController.ClientPlayerMode, + new Func(Class1384.class1384_0.method_3), new Func(Class1384.class1384_0.method_4), + statisticsManager, null, null); + + myPlayer.OnEpInteraction += OnEpInteraction; + + return myPlayer; + } + + private async Task InitInteractables() + { + FikaBackendUtils.ScreenController.ChangeStatus($"Retrieving interactable objects from server..."); + NetDataWriter writer = new(); + FikaClient client = Singleton.Instance; + InteractableInitPacket packet = new(true); + + do + { + writer.Reset(); + client.SendData(writer, ref packet, DeliveryMethod.ReliableUnordered); + await Task.Delay(1000); + if (!InteractablesInitialized) + { + await Task.Delay(2000); + } + } while (!InteractablesInitialized); + } + + /// + /// 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 (FikaBackendUtils.ScreenController != null) + { + FikaBackendUtils.ScreenController.ChangeStatus($"Initializing Coop Game..."); + } + int numbersOfPlayersToWaitFor = 0; + + if (isServer) + { + FikaServer server = Singleton.Instance; + + do + { + numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (server.NetServer.ConnectedPeersCount + 1); + if (FikaBackendUtils.ScreenController != null) + { + if (numbersOfPlayersToWaitFor > 0) + { + FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); + } + else + { + FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); + } + } + else + { + Logger.LogError("WaitForPlayers::GClass3163 was null!"); + } + await Task.Delay(100); + } while (numbersOfPlayersToWaitFor > 0); + } + else + { + FikaClient client = Singleton.Instance; + + while (client.NetClient == null) + { + await Task.Delay(500); + } + + int connectionAttempts = 0; + + while (client.ServerConnection == null && connectionAttempts < 5) + { + // Server retries 10 times with a 500ms interval, we give it 5 seconds to try + 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."); + } + } + + while (client == null) + { + await Task.Delay(500); + } + + InformationPacket packet = new(true); + NetDataWriter writer = new(); + writer.Reset(); + client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); + do + { + numbersOfPlayersToWaitFor = FikaBackendUtils.HostExpectedNumberOfPlayers - (client.ConnectedClients + 1); + if (FikaBackendUtils.ScreenController != null) + { + if (numbersOfPlayersToWaitFor > 0) + { + FikaBackendUtils.ScreenController.ChangeStatus($"Waiting for {numbersOfPlayersToWaitFor} {(numbersOfPlayersToWaitFor > 1 ? "players" : "player")}"); + } + else + { + FikaBackendUtils.ScreenController.ChangeStatus($"All players joined, starting game..."); + } + } + else + { + Logger.LogError("WaitForPlayers::GClass3163 was null!"); + } + packet = new(true); + writer.Reset(); + client.SendData(writer, ref packet, DeliveryMethod.ReliableOrdered); + await Task.Delay(1000); + } 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); + await FikaRequestHandler.UpdateSetStatus(statusBody); + Logger.LogInfo("Setting game status to: " + status.ToString()); + } + + /// + /// Bot System Starter -> Countdown + /// + /// + /// + /// + /// + public override IEnumerator vmethod_4(BotControllerSettings controllerSettings, ISpawnSystem spawnSystem, Callback runCallback) + { +#if DEBUG + Logger.LogWarning("vmethod_4"); +#endif + if (isServer) + { + BotsPresets botsPresets = new(iSession, wavesSpawnScenario_0.SpawnWaves, + BossSpawnWaveManagerClass.BossSpawnWaves, nonWavesSpawnScenario_0.GClass1478_0, false); + + GClass814 botCreator = new(this, botsPresets, CreateBot); + BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); + + bool useWaveControl = controllerSettings.BotAmount == EBotAmount.Horde; + + botsController_0.Init(this, botCreator, botZones, spawnSystem, wavesSpawnScenario_0.BotLocationModifier, + controllerSettings.IsEnabled, controllerSettings.IsScavWars, useWaveControl, false, + BossSpawnWaveManagerClass.HaveSectants, Singleton.Instance, Location_0.OpenZones); + + Logger.LogInfo($"Location: {Location_0.Name}"); + + int numberOfBots = controllerSettings.BotAmount switch + { + EBotAmount.AsOnline => 20, + EBotAmount.NoBots => 0, + EBotAmount.Low => 15, + EBotAmount.Medium => 20, + EBotAmount.High => 25, + EBotAmount.Horde => 35, + _ => 15, + }; + + botsController_0.SetSettings(numberOfBots, iSession.BackEndConfig.BotPresets, iSession.BackEndConfig.BotWeaponScatterings); + if (!FikaBackendUtils.IsDedicated) + { + botsController_0.AddActivePLayer(PlayerOwner.Player); + } + + if (FikaPlugin.EnforcedSpawnLimits.Value) + { + int limits = Location_0.Id.ToLower() switch + { + "factory4_day" => FikaPlugin.MaxBotsFactory.Value, + "factory4_night" => FikaPlugin.MaxBotsFactory.Value, + "bigmap" => FikaPlugin.MaxBotsCustoms.Value, + "interchange" => FikaPlugin.MaxBotsInterchange.Value, + "rezervbase" => FikaPlugin.MaxBotsReserve.Value, + "woods" => FikaPlugin.MaxBotsWoods.Value, + "shoreline" => FikaPlugin.MaxBotsShoreline.Value, + "tarkovstreets" => FikaPlugin.MaxBotsStreets.Value, + "sandbox" => FikaPlugin.MaxBotsGroundZero.Value, + "laboratory" => FikaPlugin.MaxBotsLabs.Value, + "lighthouse" => FikaPlugin.MaxBotsLighthouse.Value, + _ => 0 + }; + + if (limits > 0) + { + botsController_0.BotSpawner.SetMaxBots(limits); + } + } + + DynamicAI = gameObject.AddComponent(); + } + else + { + controllerSettings.BotAmount = EBotAmount.NoBots; + + BotsPresets profileCreator = new(iSession, [], [], [], false); + + GClass814 botCreator = new(this, profileCreator, CreateBot); + BotZone[] botZones = LocationScene.GetAllObjects(false).ToArray(); + + // Setting this to an empty array stops the client from downloading bots + typeof(BotsController).GetField("_allTypes", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(botsController_0, new WildSpawnType[0]); + + botsController_0.Init(this, botCreator, botZones, spawnSystem, wavesSpawnScenario_0.BotLocationModifier, + false, false, true, false, false, Singleton.Instance, Location_0.OpenZones); + + botsController_0.SetSettings(0, [], []); + + Logger.LogInfo($"Location: {Location_0.Name}"); + } + + BackendConfigSettingsClass instance = Singleton.Instance; + + if (instance != null && instance.EventSettings.EventActive && !instance.EventSettings.LocationsToIgnore.Contains(Location_0.Id)) + { + Singleton.Instance.HalloweenEventController = new HalloweenEventControllerClass(); + GameObject gameObject = (GameObject)Resources.Load("Prefabs/HALLOWEEN_CONTROLLER"); + if (gameObject != null) + { + transform.InstantiatePrefab(gameObject); + } + + halloweenEventManager = Singleton.Instance.gameObject.GetOrAddComponent(); + } + + LocalGame.Class1391 seasonTaskHandler = new(); + ESeason season = iSession.Season; + Class394 seasonHandler = new(); + Singleton.Instance.GInterface26_0 = seasonHandler; + seasonTaskHandler.task = seasonHandler.Run(season); + yield return new WaitUntil(new Func(seasonTaskHandler.method_0)); + + yield return WaitForOtherPlayers(); + + if (isServer) + { + if (Location_0.OldSpawn && wavesSpawnScenario_0.SpawnWaves != null && wavesSpawnScenario_0.SpawnWaves.Length != 0) + { + Logger.LogInfo("Running old spawn system. Waves: " + wavesSpawnScenario_0.SpawnWaves.Length); + if (wavesSpawnScenario_0 != null) + { + wavesSpawnScenario_0.Run(EBotsSpawnMode.Anyway); + } + } + + if (Location_0.NewSpawn) + { + Logger.LogInfo("Running new spawn system."); + if (nonWavesSpawnScenario_0 != null) + { + nonWavesSpawnScenario_0.Run(); + } + } + + BossSpawnWaveManagerClass.Run(EBotsSpawnMode.Anyway); + + FikaPlugin.DynamicAI.SettingChanged += DynamicAI_SettingChanged; + FikaPlugin.DynamicAIRate.SettingChanged += DynamicAIRate_SettingChanged; + + SetStatusModel status = new(FikaBackendUtils.GetGroupId(), LobbyEntry.ELobbyStatus.IN_GAME); + Task updateStatus = FikaRequestHandler.UpdateSetStatus(status); + + while (!updateStatus.IsCompleted) + { + yield return null; + } + } + else + { + if (wavesSpawnScenario_0 != null) + { + wavesSpawnScenario_0.Stop(); + } + if (nonWavesSpawnScenario_0 != null) + { + nonWavesSpawnScenario_0.Stop(); + } + if (BossSpawnWaveManagerClass != null) + { + BossSpawnWaveManagerClass.Stop(); + } + } + + // Add FreeCamController to GameWorld GameObject + FreeCameraController freeCamController = Singleton.Instance.gameObject.GetOrAddComponent(); + Singleton.Create(freeCamController); + Singleton.Instance.gameObject.GetOrAddComponent(); + FikaAirdropsManager.ContainerCount = 0; + + SetupRaidCode(); + + // Need to move to separate classes later + if (Singleton.Instance.MineManager != null) + { + Singleton.Instance.MineManager.OnExplosion += OnMineExplode; + } + + Singleton.Instance.TimeBeforeDeployLocal = Math.Max(Singleton.Instance.TimeBeforeDeployLocal, 5); + + FikaBackendUtils.ScreenController.ChangeStatus("Finishing raid initialization..."); + + 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) + { + DynamicAI.RateChanged(FikaPlugin.DynamicAIRate.Value); + } + } + + /// + /// Triggers when the setting is changed + /// + /// + /// + private void DynamicAI_SettingChanged(object sender, EventArgs e) + { + if (DynamicAI != null) + { + DynamicAI.EnabledChange(FikaPlugin.DynamicAI.Value); + } + } + + /// + /// Triggers when a explodes + /// + /// + private void OnMineExplode(MineDirectional directional) + { + if (!directional.gameObject.active) + { + return; + } + + MinePacket packet = new() + { + MinePositon = directional.transform.position + }; + if (!isServer) + { + Singleton.Instance.SendData(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); + } + } + + /// + /// Sets up events and all s + /// + public override void vmethod_5() + { + 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.LogInfo("SpawnPoint: " + spawnPoint.Id + ", InfiltrationPoint: " + InfiltrationPoint); + + if (!isServer) + { + CarExtraction carExtraction = FindObjectOfType(); + if (carExtraction != null) + { + carExtraction.Subscribee.OnStatusChanged -= carExtraction.OnStatusChangedHandler; + } + } + + ExfiltrationControllerClass.Instance.InitAllExfiltrationPoints(Location_0.exits, !isServer, ""); + ExfiltrationPoint[] exfilPoints = ExfiltrationControllerClass.Instance.EligiblePoints(Profile_0); + + GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), exfilPoints); + + exfilManager = gameObject.AddComponent(); + exfilManager.Run(exfilPoints); + + /*if (FikaPlugin.Instance.UseBTR) { try { @@ -1636,180 +1636,180 @@ public override void vmethod_5() } }*/ - 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").Value; - foreach (ExitTimerPanel exitTimerPanel in currentExfils.Values) - { - exitTimerPanel.Close(); - } - currentExfils.Clear(); - - GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), points); - } - - /// - /// When the local player successfully extracts, enable freecam, notify other players about the extract - /// - /// The local player to start the Coroutine on - /// The point that was used to extract - /// - public void Extract(CoopPlayer player, ExfiltrationPoint point) - { - PreloaderUI preloaderUI = Singleton.Instance; - localTriggerZones = new(player.TriggerZones); - - player.ClientMovementContext.SetGravity(false); - Vector3 position = player.Position; - position.y += 500; - player.Teleport(position); - - if (MyExitStatus == ExitStatus.MissingInAction) - { - NotificationManagerClass.DisplayMessageNotification("You have gone missing in action...", iconType: EFT.Communications.ENotificationIconType.Alert, textColor: Color.red); - } - - if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ToggleQuestSharing(false); - } - - 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(); - - if (point.HasRequirements && point.TransferItemRequirement != null) - { - if (point.TransferItemRequirement.Met(player, point) && player.IsYourPlayer) - { - // Seems to already be handled by SPT so we only add it visibly - player.Profile.EftStats.SessionCounters.AddDouble(0.2, [CounterTag.FenceStanding, EFenceStandingSource.ExitStanding]); - } - } - } - - if (player.Side == EPlayerSide.Savage) - { - // Seems to already be handled by SPT so we only add it visibly - player.Profile.EftStats.SessionCounters.AddDouble(0.01, [CounterTag.FenceStanding, EFenceStandingSource.ExitStanding]); - } - - GenericPacket genericPacket = new() - { - NetId = player.NetId, - PacketType = EPackageType.ClientExtract - }; - - try // This is to allow clients to extract if they lose connection - { - if (!isServer) - { - Singleton.Instance.SendData(new NetDataWriter(), ref genericPacket, DeliveryMethod.ReliableOrdered); - } - else - { - Singleton.Instance.SendDataToAll(new NetDataWriter(), ref genericPacket, DeliveryMethod.ReliableOrdered); - ClearHostAI(player); - } - } - catch - { - - } - - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - - CoopPlayer coopPlayer = player; - ExtractedPlayers.Add(coopPlayer.NetId); - coopHandler.ExtractedPlayers.Add(coopPlayer.NetId); - coopHandler.Players.Remove(coopPlayer.NetId); - - preloaderUI.StartBlackScreenShow(2f, 2f, () => { preloaderUI.FadeBlackScreen(2f, -2f); }); - - player.ActiveHealthController.SetDamageCoeff(0); - player.ActiveHealthController.AddDamageMultiplier(0); - player.ActiveHealthController.DisableMetabolism(); - player.ActiveHealthController.PauseAllEffects(); - - extractRoutine = StartCoroutine(ExtractRoutine(player)); - - // Prevents players from looting after extracting - CurrentScreenSingleton.Instance.CloseAllScreensForced(); - - // Detroys session timer - if (timeManager != null) - { - Destroy(timeManager); - } - if (GameUi.TimerPanel.enabled) - { - GameUi.TimerPanel.Close(); - } - - if (FikaPlugin.AutoExtract.Value) - { - if (!isServer) - { - Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); - } - else if (Singleton.Instance.NetServer.ConnectedPeersCount == 0) - { - Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); - } - } - } - - /// - /// Used to make sure no stims or mods reset the DamageCoeff - /// - /// The to run the coroutine on - /// - private IEnumerator ExtractRoutine(CoopPlayer player) - { - while (true) - { - if (player != null && player.ActiveHealthController != null) - { - if (player.ActiveHealthController.DamageCoeff != 0) - { - player.ActiveHealthController.SetDamageCoeff(0); - } - } - else - { - yield break; - } - yield return new WaitForEndOfFrame(); - } - } - - public void ClearHostAI(Player player) - {/* + 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").Value; + foreach (ExitTimerPanel exitTimerPanel in currentExfils.Values) + { + exitTimerPanel.Close(); + } + currentExfils.Clear(); + + GameUi.TimerPanel.SetTime(EFTDateTimeClass.UtcNow, Profile_0.Info.Side, GameTimer.SessionSeconds(), points); + } + + /// + /// When the local player successfully extracts, enable freecam, notify other players about the extract + /// + /// The local player to start the Coroutine on + /// The point that was used to extract + /// + public void Extract(CoopPlayer player, ExfiltrationPoint point) + { + PreloaderUI preloaderUI = Singleton.Instance; + localTriggerZones = new(player.TriggerZones); + + player.ClientMovementContext.SetGravity(false); + Vector3 position = player.Position; + position.y += 500; + player.Teleport(position); + + if (MyExitStatus == ExitStatus.MissingInAction) + { + NotificationManagerClass.DisplayMessageNotification("You have gone missing in action...", iconType: EFT.Communications.ENotificationIconType.Alert, textColor: Color.red); + } + + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ToggleQuestSharing(false); + } + + 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(); + + if (point.HasRequirements && point.TransferItemRequirement != null) + { + if (point.TransferItemRequirement.Met(player, point) && player.IsYourPlayer) + { + // Seems to already be handled by SPT so we only add it visibly + player.Profile.EftStats.SessionCounters.AddDouble(0.2, [CounterTag.FenceStanding, EFenceStandingSource.ExitStanding]); + } + } + } + + if (player.Side == EPlayerSide.Savage) + { + // Seems to already be handled by SPT so we only add it visibly + player.Profile.EftStats.SessionCounters.AddDouble(0.01, [CounterTag.FenceStanding, EFenceStandingSource.ExitStanding]); + } + + GenericPacket genericPacket = new() + { + NetId = player.NetId, + PacketType = EPackageType.ClientExtract + }; + + try // This is to allow clients to extract if they lose connection + { + if (!isServer) + { + Singleton.Instance.SendData(new NetDataWriter(), ref genericPacket, DeliveryMethod.ReliableOrdered); + } + else + { + Singleton.Instance.SendDataToAll(new NetDataWriter(), ref genericPacket, DeliveryMethod.ReliableOrdered); + ClearHostAI(player); + } + } + catch + { + + } + + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + + CoopPlayer coopPlayer = player; + ExtractedPlayers.Add(coopPlayer.NetId); + coopHandler.ExtractedPlayers.Add(coopPlayer.NetId); + coopHandler.Players.Remove(coopPlayer.NetId); + + preloaderUI.StartBlackScreenShow(2f, 2f, () => { preloaderUI.FadeBlackScreen(2f, -2f); }); + + player.ActiveHealthController.SetDamageCoeff(0); + player.ActiveHealthController.AddDamageMultiplier(0); + player.ActiveHealthController.DisableMetabolism(); + player.ActiveHealthController.PauseAllEffects(); + + extractRoutine = StartCoroutine(ExtractRoutine(player)); + + // Prevents players from looting after extracting + CurrentScreenSingleton.Instance.CloseAllScreensForced(); + + // Detroys session timer + if (timeManager != null) + { + Destroy(timeManager); + } + if (GameUi.TimerPanel.enabled) + { + GameUi.TimerPanel.Close(); + } + + if (FikaPlugin.AutoExtract.Value) + { + if (!isServer) + { + Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); + } + else if (Singleton.Instance.NetServer.ConnectedPeersCount == 0) + { + Stop(coopHandler.MyPlayer.ProfileId, MyExitStatus, coopHandler.MyPlayer.ActiveHealthController.IsAlive ? MyExitLocation : null, 0); + } + } + } + + /// + /// Used to make sure no stims or mods reset the DamageCoeff + /// + /// The to run the coroutine on + /// + private IEnumerator ExtractRoutine(CoopPlayer player) + { + while (true) + { + if (player != null && player.ActiveHealthController != null) + { + if (player.ActiveHealthController.DamageCoeff != 0) + { + player.ActiveHealthController.SetDamageCoeff(0); + } + } + else + { + yield break; + } + yield return new WaitForEndOfFrame(); + } + } + + public void ClearHostAI(Player player) + {/* if (player != null) { if (botsController_0 != null) @@ -1832,497 +1832,497 @@ public void ClearHostAI(Player player) } } }*/ - botsController_0.DestroyInfo(player); - } - - /// - /// Triggers when the main player dies - /// - /// - private async void HealthController_DiedEvent(EDamageType damageType) - { - Player player = gparam_0.Player; - if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ToggleQuestSharing(false); - } - if (timeManager != null) - { - Destroy(timeManager); - } - if (GameUi.TimerPanel != null && GameUi.TimerPanel.enabled) - { - GameUi.TimerPanel.Close(); - } - - player.HealthController.DiedEvent -= method_15; - player.HealthController.DiedEvent -= HealthController_DiedEvent; - - PlayerOwner.vmethod_1(); - MyExitStatus = ExitStatus.Killed; - MyExitLocation = string.Empty; - - if (FikaPlugin.Instance.ForceSaveOnDeath) - { - await SavePlayer((CoopPlayer)player, MyExitStatus, string.Empty, true); - } - } - - /// - /// Stops the local - /// - /// - /// - /// - /// - public override void Stop(string profileId, ExitStatus exitStatus, string exitName, float delay = 0f) - { - Logger.LogDebug("Stop"); - - CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - myPlayer.TriggerZones = localTriggerZones; - myPlayer.PacketSender.DestroyThis(); - - if (myPlayer.Side != EPlayerSide.Savage) - { - if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) - { - GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, - myPlayer.InventoryControllerClass, false, true); - if (result.Error != null) - { - Logger.LogError("Stop: Error removing dog tag!"); - } - } - } - - if (!myPlayer.ActiveHealthController.IsAlive && exitStatus == ExitStatus.Survived) - { - exitStatus = ExitStatus.Killed; - } - - if (!ExtractedPlayers.Contains(myPlayer.NetId)) - { - if (GameTimer.SessionTime != null && GameTimer.PastTime >= GameTimer.SessionTime) - { - exitStatus = ExitStatus.MissingInAction; - } - } - - if (isServer) - { - botsController_0.Stop(); - } - - if (BossSpawnWaveManagerClass != null) - { - BossSpawnWaveManagerClass.Stop(); - } - if (nonWavesSpawnScenario_0 != null) - { - nonWavesSpawnScenario_0.Stop(); - } - if (wavesSpawnScenario_0 != null) - { - wavesSpawnScenario_0.Stop(); - } - - try - { - PlayerLeftRequest body = new(FikaBackendUtils.Profile.ProfileId); - FikaRequestHandler.RaidLeave(body); - } - catch (Exception) - { - Logger.LogError("Unable to send RaidLeave request to server."); - } - - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - // Create a copy to prevent errors when the dictionary is being modified (which happens when using spawn mods) - CoopPlayer[] players = [.. coopHandler.Players.Values]; - foreach (CoopPlayer player in players) - { - if (player == null || player.IsYourPlayer) - { - continue; - } - - player.Dispose(); - AssetPoolObject.ReturnToPool(player.gameObject, true); - } - } - else - { - Logger.LogError("Stop: Could not find CoopHandler!"); - } - - Destroy(coopHandler); - - if (CoopHandler.CoopHandlerParent != null) - { - Destroy(CoopHandler.CoopHandlerParent); - } - - ExitManager stopManager = new(this, exitStatus, exitName, delay, myPlayer); - - GameUI gameUI = GameUI.Instance; - - exfilManager.Stop(); - - Status = GameStatus.Stopping; - GameTimer.TryStop(); - if (gameUI.TimerPanel.enabled) - { - gameUI.TimerPanel.Close(); - } - if (EnvironmentManager.Instance != null) - { - EnvironmentManager.Instance.Stop(); - } - MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.HandleExit)); - BackendConfigAbstractClass.Config.UseSpiritPlayer = false; - } - - /// - /// Saves your own to the server - /// - /// - /// - /// - /// - /// - private async Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitName, bool fromDeath) - { - if (hasSaved) - { - return; - } - - if (fromDeath) - { - //Since we're bypassing saving on exiting, run this now. - player.Profile.EftStats.LastPlayerState = null; - player.StatisticsManager.EndStatisticsSession(exitStatus, PastTime); - player.CheckAndResetControllers(exitStatus, PastTime, Location_0.Id, exitName); - } - - //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; - converters = [.. converters, new NotesJsonConverter()]; - - SaveProfileRequest saveRequest = new() - { - Exit = exitStatus.ToString().ToLowerInvariant(), - Profile = player.Profile, - Health = HealthListener.Instance.CurrentHealth, - Insurance = InsuredItemManager.Instance.GetTrackedItems(), - IsPlayerScav = player.Side is EPlayerSide.Savage - }; - - await RequestHandler.PutJsonAsync("/raid/profile/save", saveRequest.ToJson(converters)); - hasSaved = true; - return; - } - - /// - /// Stops the local when waiting for other players - /// - /// - /// - public void StopFromCancel(string profileId, ExitStatus exitStatus) - { - Logger.LogWarning("Game init was cancelled!"); - - CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - myPlayer.PacketSender.DestroyThis(); - - if (myPlayer.Side != EPlayerSide.Savage) - { - if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) - { - GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, - myPlayer.InventoryControllerClass, false, true); - if (result.Error != null) - { - Logger.LogWarning("StopFromError: Error removing dog tag!"); - } - } - } - - string exitName = null; - float delay = 0f; - - PlayerLeftRequest body = new(FikaBackendUtils.Profile.ProfileId); - FikaRequestHandler.RaidLeave(body); - - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - foreach (CoopPlayer player in coopHandler.Players.Values) - { - if (player == null) - { - continue; - } - - player.Dispose(); - AssetPoolObject.ReturnToPool(player.gameObject, true); - } - coopHandler.Players.Clear(); - } - else - { - Logger.LogError("Stop: Could not find CoopHandler!"); - } - - Destroy(coopHandler); - - if (CoopHandler.CoopHandlerParent != null) - { - Destroy(CoopHandler.CoopHandlerParent); - } - - if (BossSpawnWaveManagerClass != null) - { - BossSpawnWaveManagerClass.Stop(); - } - if (nonWavesSpawnScenario_0 != null) - { - nonWavesSpawnScenario_0.Stop(); - } - if (wavesSpawnScenario_0 != null) - { - wavesSpawnScenario_0.Stop(); - } - - CancelExitManager stopManager = new() - { - baseLocalGame_0 = this, - exitStatus = exitStatus, - exitName = exitName, - delay = delay - }; - - GameUI gameUI = GameUI.Instance; - - if (exfilManager != null) - { - exfilManager.Stop(); - } - - Status = GameStatus.Stopping; - if (GameTimer != null) - { - GameTimer.TryStop(); - } - if (gameUI.TimerPanel.enabled) - { - gameUI.TimerPanel.Close(); - } - - if (EnvironmentManager.Instance != null) - { - EnvironmentManager.Instance.Stop(); - } - MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.ExitOverride)); - 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) - { - try - { - if (player != null) - { - player.Dispose(); - AssetPoolObject.ReturnToPool(player.gameObject, true); - } - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - dictionary_0.Clear(); - // Reset MatchingType to Single when the game ends. - FikaBackendUtils.MatchingType = EMatchmakerType.Single; - } - - /// - /// Disposes of the - /// - public override void Dispose() - { - Logger.LogDebug("Dispose()"); - - if (Singleton.Instance.MineManager != null) - { - Singleton.Instance.MineManager.OnExplosion -= OnMineExplode; - } - - if (extractRoutine != null) - { - StopCoroutine(extractRoutine); - } - - if (isServer) - { - CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - coopPlayer.PacketSender.DestroyThis(); - - FikaDynamicAI newDynamicAI = gameObject.GetComponent(); - if (newDynamicAI != null) - { - Destroy(newDynamicAI); - } - - NetManagerUtils.StopPinger(); - - FikaPlugin.DynamicAI.SettingChanged -= DynamicAI_SettingChanged; - FikaPlugin.DynamicAIRate.SettingChanged -= DynamicAIRate_SettingChanged; - } - else - { - // 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); - } - - NetManagerUtils.DestroyNetManager(isServer); - - FikaBackendUtils.Nodes = null; - FikaBackendUtils.HostExpectedNumberOfPlayers = 1; - FikaBackendUtils.RequestFikaWorld = false; - FikaBackendUtils.IsReconnect = false; - FikaBackendUtils.ReconnectPosition = Vector3.zero; - - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - Destroy(coopHandler); - - if (CoopHandler.CoopHandlerParent != null) - { - Destroy(CoopHandler.CoopHandlerParent); - } - } - - if (Singleton.Instance != null) - { - Destroy(Singleton.Instance); - } - - 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; - private readonly ExitStatus exitStatus = exitStatus; - private readonly string exitName = exitName; - private readonly float delay = delay; - private readonly CoopPlayer localPlayer = localPlayer; - private Action EndAction; - - public void HandleExit() - { - CurrentScreenSingleton screenManager = CurrentScreenSingleton.Instance; - if (screenManager.CheckCurrentScreen(EEftScreenType.Reconnect)) - { - screenManager.CloseAllScreensForced(); - } - localGame.gparam_0.Player.OnGameSessionEnd(exitStatus, localGame.PastTime, localGame.Location_0.Id, exitName); - localGame.CleanUp(); - localGame.Status = GameStatus.Stopped; - TimeSpan timeSpan = EFTDateTimeClass.Now - localGame.dateTime_0; - localGame.iSession.OfflineRaidEnded(exitStatus, exitName, timeSpan.TotalSeconds).HandleExceptions(); - MonoBehaviourSingleton.Instance.FadeOutVolumeAfterRaid(); - StaticManager staticManager = StaticManager.Instance; - float num = delay; - EndAction = new Action(FireCallback); - staticManager.WaitSeconds(num, EndAction); - } - - private async void FireCallback() - { - Callback endCallback = Traverse.Create(localGame).Field>("callback_0").Value; - - await localGame.SavePlayer(localPlayer, exitStatus, exitName, false); - - endCallback(new Result(exitStatus, EFTDateTimeClass.Now - localGame.dateTime_0, new MetricsClass())); - UIEventSystem.Instance.Enable(); - } - } - - /// - /// Used to manage the stopping of the gracefully when cancelling - /// - private class CancelExitManager : Class1386 - { - public void ExitOverride() - { - CurrentScreenSingleton instance = CurrentScreenSingleton.Instance; - if (instance != null && instance.CheckCurrentScreen(EEftScreenType.Reconnect)) - { - instance.CloseAllScreensForced(); - } - if (baseLocalGame_0 != null) - { - baseLocalGame_0.CleanUp(); - baseLocalGame_0.Status = GameStatus.Stopped; - } - if (MonoBehaviourSingleton.Instance != null) - { - MonoBehaviourSingleton.Instance.FadeOutVolumeAfterRaid(); - } - MonoBehaviour instance2 = StaticManager.Instance; - float num = delay; - action_0 = new Action(method_1); - instance2.WaitSeconds(num, action_0); - } - } - - public new void method_6(string backendUrl, string locationId, int variantId) - { - Logger.LogDebug("method_6"); - return; - } - - public byte[] GetHostLootItems() - { - if (HostLootItems == null || HostLootItems.Length == 0) - { - GClass1211 lootItems = new(Singleton.Instance.GetJsonLootItems() - .Where(x => x as CorpseLootItemClass is null)); - return SimpleZlib.CompressToBytes(lootItems.ToJson([]), 6); - } - - return HostLootItems; - } - } + botsController_0.DestroyInfo(player); + } + + /// + /// Triggers when the main player dies + /// + /// + private async void HealthController_DiedEvent(EDamageType damageType) + { + Player player = gparam_0.Player; + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ToggleQuestSharing(false); + } + if (timeManager != null) + { + Destroy(timeManager); + } + if (GameUi.TimerPanel != null && GameUi.TimerPanel.enabled) + { + GameUi.TimerPanel.Close(); + } + + player.HealthController.DiedEvent -= method_15; + player.HealthController.DiedEvent -= HealthController_DiedEvent; + + PlayerOwner.vmethod_1(); + MyExitStatus = ExitStatus.Killed; + MyExitLocation = string.Empty; + + if (FikaPlugin.Instance.ForceSaveOnDeath) + { + await SavePlayer((CoopPlayer)player, MyExitStatus, string.Empty, true); + } + } + + /// + /// Stops the local + /// + /// + /// + /// + /// + public override void Stop(string profileId, ExitStatus exitStatus, string exitName, float delay = 0f) + { + Logger.LogDebug("Stop"); + + CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + myPlayer.TriggerZones = localTriggerZones; + myPlayer.PacketSender.DestroyThis(); + + if (myPlayer.Side != EPlayerSide.Savage) + { + if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) + { + GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, + myPlayer.InventoryControllerClass, false, true); + if (result.Error != null) + { + Logger.LogError("Stop: Error removing dog tag!"); + } + } + } + + if (!myPlayer.ActiveHealthController.IsAlive && exitStatus == ExitStatus.Survived) + { + exitStatus = ExitStatus.Killed; + } + + if (!ExtractedPlayers.Contains(myPlayer.NetId)) + { + if (GameTimer.SessionTime != null && GameTimer.PastTime >= GameTimer.SessionTime) + { + exitStatus = ExitStatus.MissingInAction; + } + } + + if (isServer) + { + botsController_0.Stop(); + } + + if (BossSpawnWaveManagerClass != null) + { + BossSpawnWaveManagerClass.Stop(); + } + if (nonWavesSpawnScenario_0 != null) + { + nonWavesSpawnScenario_0.Stop(); + } + if (wavesSpawnScenario_0 != null) + { + wavesSpawnScenario_0.Stop(); + } + + try + { + PlayerLeftRequest body = new(FikaBackendUtils.Profile.ProfileId); + FikaRequestHandler.RaidLeave(body); + } + catch (Exception) + { + Logger.LogError("Unable to send RaidLeave request to server."); + } + + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + // Create a copy to prevent errors when the dictionary is being modified (which happens when using spawn mods) + CoopPlayer[] players = [.. coopHandler.Players.Values]; + foreach (CoopPlayer player in players) + { + if (player == null || player.IsYourPlayer) + { + continue; + } + + player.Dispose(); + AssetPoolObject.ReturnToPool(player.gameObject, true); + } + } + else + { + Logger.LogError("Stop: Could not find CoopHandler!"); + } + + Destroy(coopHandler); + + if (CoopHandler.CoopHandlerParent != null) + { + Destroy(CoopHandler.CoopHandlerParent); + } + + ExitManager stopManager = new(this, exitStatus, exitName, delay, myPlayer); + + GameUI gameUI = GameUI.Instance; + + exfilManager.Stop(); + + Status = GameStatus.Stopping; + GameTimer.TryStop(); + if (gameUI.TimerPanel.enabled) + { + gameUI.TimerPanel.Close(); + } + if (EnvironmentManager.Instance != null) + { + EnvironmentManager.Instance.Stop(); + } + MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.HandleExit)); + BackendConfigAbstractClass.Config.UseSpiritPlayer = false; + } + + /// + /// Saves your own to the server + /// + /// + /// + /// + /// + /// + private async Task SavePlayer(CoopPlayer player, ExitStatus exitStatus, string exitName, bool fromDeath) + { + if (hasSaved) + { + return; + } + + if (fromDeath) + { + //Since we're bypassing saving on exiting, run this now. + player.Profile.EftStats.LastPlayerState = null; + player.StatisticsManager.EndStatisticsSession(exitStatus, PastTime); + player.CheckAndResetControllers(exitStatus, PastTime, Location_0.Id, exitName); + } + + //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; + converters = [.. converters, new NotesJsonConverter()]; + + SaveProfileRequest saveRequest = new() + { + Exit = exitStatus.ToString().ToLowerInvariant(), + Profile = player.Profile, + Health = HealthListener.Instance.CurrentHealth, + Insurance = InsuredItemManager.Instance.GetTrackedItems(), + IsPlayerScav = player.Side is EPlayerSide.Savage + }; + + await RequestHandler.PutJsonAsync("/raid/profile/save", saveRequest.ToJson(converters)); + hasSaved = true; + return; + } + + /// + /// Stops the local when waiting for other players + /// + /// + /// + public void StopFromCancel(string profileId, ExitStatus exitStatus) + { + Logger.LogWarning("Game init was cancelled!"); + + CoopPlayer myPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + myPlayer.PacketSender.DestroyThis(); + + if (myPlayer.Side != EPlayerSide.Savage) + { + if (myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem != null) + { + GStruct414 result = InteractionsHandlerClass.Remove(myPlayer.Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem, + myPlayer.InventoryControllerClass, false, true); + if (result.Error != null) + { + Logger.LogWarning("StopFromError: Error removing dog tag!"); + } + } + } + + string exitName = null; + float delay = 0f; + + PlayerLeftRequest body = new(FikaBackendUtils.Profile.ProfileId); + FikaRequestHandler.RaidLeave(body); + + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (player == null) + { + continue; + } + + player.Dispose(); + AssetPoolObject.ReturnToPool(player.gameObject, true); + } + coopHandler.Players.Clear(); + } + else + { + Logger.LogError("Stop: Could not find CoopHandler!"); + } + + Destroy(coopHandler); + + if (CoopHandler.CoopHandlerParent != null) + { + Destroy(CoopHandler.CoopHandlerParent); + } + + if (BossSpawnWaveManagerClass != null) + { + BossSpawnWaveManagerClass.Stop(); + } + if (nonWavesSpawnScenario_0 != null) + { + nonWavesSpawnScenario_0.Stop(); + } + if (wavesSpawnScenario_0 != null) + { + wavesSpawnScenario_0.Stop(); + } + + CancelExitManager stopManager = new() + { + baseLocalGame_0 = this, + exitStatus = exitStatus, + exitName = exitName, + delay = delay + }; + + GameUI gameUI = GameUI.Instance; + + if (exfilManager != null) + { + exfilManager.Stop(); + } + + Status = GameStatus.Stopping; + if (GameTimer != null) + { + GameTimer.TryStop(); + } + if (gameUI.TimerPanel.enabled) + { + gameUI.TimerPanel.Close(); + } + + if (EnvironmentManager.Instance != null) + { + EnvironmentManager.Instance.Stop(); + } + MonoBehaviourSingleton.Instance.StartBlackScreenShow(1f, 1f, new Action(stopManager.ExitOverride)); + 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) + { + try + { + if (player != null) + { + player.Dispose(); + AssetPoolObject.ReturnToPool(player.gameObject, true); + } + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + dictionary_0.Clear(); + // Reset MatchingType to Single when the game ends. + FikaBackendUtils.MatchingType = EMatchmakerType.Single; + } + + /// + /// Disposes of the + /// + public override void Dispose() + { + Logger.LogDebug("Dispose()"); + + if (Singleton.Instance.MineManager != null) + { + Singleton.Instance.MineManager.OnExplosion -= OnMineExplode; + } + + if (extractRoutine != null) + { + StopCoroutine(extractRoutine); + } + + if (isServer) + { + CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + coopPlayer.PacketSender.DestroyThis(); + + FikaDynamicAI newDynamicAI = gameObject.GetComponent(); + if (newDynamicAI != null) + { + Destroy(newDynamicAI); + } + + NetManagerUtils.StopPinger(); + + FikaPlugin.DynamicAI.SettingChanged -= DynamicAI_SettingChanged; + FikaPlugin.DynamicAIRate.SettingChanged -= DynamicAIRate_SettingChanged; + } + else + { + // 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); + } + + NetManagerUtils.DestroyNetManager(isServer); + + FikaBackendUtils.Nodes = null; + FikaBackendUtils.HostExpectedNumberOfPlayers = 1; + FikaBackendUtils.RequestFikaWorld = false; + FikaBackendUtils.IsReconnect = false; + FikaBackendUtils.ReconnectPosition = Vector3.zero; + + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + Destroy(coopHandler); + + if (CoopHandler.CoopHandlerParent != null) + { + Destroy(CoopHandler.CoopHandlerParent); + } + } + + if (Singleton.Instance != null) + { + Destroy(Singleton.Instance); + } + + 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; + private readonly ExitStatus exitStatus = exitStatus; + private readonly string exitName = exitName; + private readonly float delay = delay; + private readonly CoopPlayer localPlayer = localPlayer; + private Action EndAction; + + public void HandleExit() + { + CurrentScreenSingleton screenManager = CurrentScreenSingleton.Instance; + if (screenManager.CheckCurrentScreen(EEftScreenType.Reconnect)) + { + screenManager.CloseAllScreensForced(); + } + localGame.gparam_0.Player.OnGameSessionEnd(exitStatus, localGame.PastTime, localGame.Location_0.Id, exitName); + localGame.CleanUp(); + localGame.Status = GameStatus.Stopped; + TimeSpan timeSpan = EFTDateTimeClass.Now - localGame.dateTime_0; + localGame.iSession.OfflineRaidEnded(exitStatus, exitName, timeSpan.TotalSeconds).HandleExceptions(); + MonoBehaviourSingleton.Instance.FadeOutVolumeAfterRaid(); + StaticManager staticManager = StaticManager.Instance; + float num = delay; + EndAction = new Action(FireCallback); + staticManager.WaitSeconds(num, EndAction); + } + + private async void FireCallback() + { + Callback endCallback = Traverse.Create(localGame).Field>("callback_0").Value; + + await localGame.SavePlayer(localPlayer, exitStatus, exitName, false); + + endCallback(new Result(exitStatus, EFTDateTimeClass.Now - localGame.dateTime_0, new MetricsClass())); + UIEventSystem.Instance.Enable(); + } + } + + /// + /// Used to manage the stopping of the gracefully when cancelling + /// + private class CancelExitManager : Class1386 + { + public void ExitOverride() + { + CurrentScreenSingleton instance = CurrentScreenSingleton.Instance; + if (instance != null && instance.CheckCurrentScreen(EEftScreenType.Reconnect)) + { + instance.CloseAllScreensForced(); + } + if (baseLocalGame_0 != null) + { + baseLocalGame_0.CleanUp(); + baseLocalGame_0.Status = GameStatus.Stopped; + } + if (MonoBehaviourSingleton.Instance != null) + { + MonoBehaviourSingleton.Instance.FadeOutVolumeAfterRaid(); + } + MonoBehaviour instance2 = StaticManager.Instance; + float num = delay; + action_0 = new Action(method_1); + instance2.WaitSeconds(num, action_0); + } + } + + public new void method_6(string backendUrl, string locationId, int variantId) + { + Logger.LogDebug("method_6"); + return; + } + + public byte[] GetHostLootItems() + { + if (HostLootItems == null || HostLootItems.Length == 0) + { + GClass1211 lootItems = new(Singleton.Instance.GetJsonLootItems() + .Where(x => x as CorpseLootItemClass is null)); + return SimpleZlib.CompressToBytes(lootItems.ToJson([]), 6); + } + + return HostLootItems; + } + } } diff --git a/Fika.Core/Coop/GameMode/FikaHostWorld.cs b/Fika.Core/Coop/GameMode/FikaHostWorld.cs index 2b3ee670..cad85326 100644 --- a/Fika.Core/Coop/GameMode/FikaHostWorld.cs +++ b/Fika.Core/Coop/GameMode/FikaHostWorld.cs @@ -7,76 +7,76 @@ namespace Fika.Core.Coop.GameMode { - /// - /// Currently used to keep track of interactable objects, in the future this will be used to sync reconnects - /// - public class FikaHostWorld : World - { - private FikaServer server; - private NetDataWriter writer; + /// + /// Currently used to keep track of interactable objects, in the future this will be used to sync reconnects + /// + public class FikaHostWorld : World + { + private FikaServer server; + private NetDataWriter writer; - protected void Start() - { - server = Singleton.Instance; - writer = new(); - gameWorld_0 = GetComponent(); - } + protected void Start() + { + server = Singleton.Instance; + writer = new(); + gameWorld_0 = GetComponent(); + } - protected void FixedUpdate() - { - int grenadesCount = gameWorld_0.Grenades.Count; - if (grenadesCount > 0) - { - for (int i = 0; i < grenadesCount; i++) - { - Throwable throwable = gameWorld_0.Grenades.GetByIndex(i); - gameWorld_0.method_2(throwable); - } - } + protected void FixedUpdate() + { + int grenadesCount = gameWorld_0.Grenades.Count; + if (grenadesCount > 0) + { + for (int i = 0; i < grenadesCount; i++) + { + Throwable throwable = gameWorld_0.Grenades.GetByIndex(i); + gameWorld_0.method_2(throwable); + } + } - int grenadePacketsCount = gameWorld_0.GrenadesCriticalStates.Count; - if (grenadePacketsCount > 0) - { - for (int i = 0; i < grenadePacketsCount; i++) - { - ThrowablePacket packet = new() - { - Data = gameWorld_0.GrenadesCriticalStates[i] - }; - writer.Reset(); - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); - } - } + int grenadePacketsCount = gameWorld_0.GrenadesCriticalStates.Count; + if (grenadePacketsCount > 0) + { + for (int i = 0; i < grenadePacketsCount; i++) + { + ThrowablePacket packet = new() + { + Data = gameWorld_0.GrenadesCriticalStates[i] + }; + writer.Reset(); + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered); + } + } - gameWorld_0.GrenadesCriticalStates.Clear(); - } + gameWorld_0.GrenadesCriticalStates.Clear(); + } - /// - /// Sets up all the s on the map - /// - public override void SubscribeToBorderZones(BorderZone[] zones) - { - foreach (BorderZone borderZone in zones) - { - borderZone.PlayerShotEvent += OnBorderZoneShot; - } - } + /// + /// Sets up all the s on the map + /// + public override void SubscribeToBorderZones(BorderZone[] zones) + { + foreach (BorderZone borderZone in zones) + { + borderZone.PlayerShotEvent += OnBorderZoneShot; + } + } - /// - /// Triggered when a triggers (only runs on host) - /// - /// - /// - /// - /// - private void OnBorderZoneShot(IPlayerOwner player, BorderZone zone, float arg3, bool arg4) - { - BorderZonePacket packet = new() - { - ProfileId = player.iPlayer.ProfileId, - ZoneId = zone.Id - }; - Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); - } - } + /// + /// Triggered when a triggers (only runs on host) + /// + /// + /// + /// + /// + private void OnBorderZoneShot(IPlayerOwner player, BorderZone zone, float arg3, bool arg4) + { + BorderZonePacket packet = new() + { + ProfileId = player.iPlayer.ProfileId, + ZoneId = zone.Id + }; + Singleton.Instance.SendDataToAll(new NetDataWriter(), ref packet, DeliveryMethod.ReliableOrdered); + } + } } diff --git a/Fika.Core/Coop/GameMode/FikaWorld.cs b/Fika.Core/Coop/GameMode/FikaWorld.cs index 0d204187..50e53139 100644 --- a/Fika.Core/Coop/GameMode/FikaWorld.cs +++ b/Fika.Core/Coop/GameMode/FikaWorld.cs @@ -3,20 +3,20 @@ namespace Fika.Core.Coop.GameMode { - /// - /// Currently used to keep track of interactable objects, in the future this will be used to sync reconnects - /// - public class FikaWorld : World - { - /// - /// Sets up all the s on the map - /// - public override void SubscribeToBorderZones(BorderZone[] zones) - { - foreach (BorderZone borderZone in zones) - { - borderZone.RemoveAuthority(); - } - } - } + /// + /// Currently used to keep track of interactable objects, in the future this will be used to sync reconnects + /// + public class FikaWorld : World + { + /// + /// Sets up all the s on the map + /// + public override void SubscribeToBorderZones(BorderZone[] zones) + { + foreach (BorderZone borderZone in zones) + { + borderZone.RemoveAuthority(); + } + } + } } diff --git a/Fika.Core/Coop/GameMode/IFikaGame.cs b/Fika.Core/Coop/GameMode/IFikaGame.cs index 2c8fdb80..1ed1a959 100644 --- a/Fika.Core/Coop/GameMode/IFikaGame.cs +++ b/Fika.Core/Coop/GameMode/IFikaGame.cs @@ -3,14 +3,14 @@ namespace Fika.Core.Coop.GameMode { - public interface IFikaGame - { - public List ExtractedPlayers { get; } + public interface IFikaGame + { + public List ExtractedPlayers { get; } - ExitStatus MyExitStatus { get; set; } + ExitStatus MyExitStatus { get; set; } - string MyExitLocation { get; set; } + string MyExitLocation { get; set; } - public void Stop(string profileId, ExitStatus exitStatus, string exitName, float delay = 0f); - } + public void Stop(string profileId, ExitStatus exitStatus, string exitName, float delay = 0f); + } } diff --git a/Fika.Core/Coop/HostClasses/CoopHostGameWorld.cs b/Fika.Core/Coop/HostClasses/CoopHostGameWorld.cs index ca1f5146..42c4f664 100644 --- a/Fika.Core/Coop/HostClasses/CoopHostGameWorld.cs +++ b/Fika.Core/Coop/HostClasses/CoopHostGameWorld.cs @@ -6,31 +6,31 @@ namespace Fika.Core.Coop.ClientClasses { - public class CoopHostGameWorld : ClientLocalGameWorld - { - public static CoopHostGameWorld Create(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) - { - CoopHostGameWorld gameWorld = gameObject.AddComponent(); - gameWorld.ObjectsFactory = objectsFactory; - Traverse.Create(gameWorld).Field("eupdateQueue_0").Value = updateQueue; - gameWorld.SpeakerManager = gameObject.AddComponent(); - gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); - gameWorld.BufferZoneController = new BufferZoneControllerClass(); - gameWorld.CurrentProfileId = currentProfileId; - gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); - gameObject.AddComponent(); - return gameWorld; - } + public class CoopHostGameWorld : ClientLocalGameWorld + { + public static CoopHostGameWorld Create(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) + { + CoopHostGameWorld gameWorld = gameObject.AddComponent(); + gameWorld.ObjectsFactory = objectsFactory; + Traverse.Create(gameWorld).Field("eupdateQueue_0").Value = updateQueue; + gameWorld.SpeakerManager = gameObject.AddComponent(); + gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); + gameWorld.BufferZoneController = new BufferZoneControllerClass(); + gameWorld.CurrentProfileId = currentProfileId; + gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); + gameObject.AddComponent(); + return gameWorld; + } - public override GClass676 CreateGrenadeFactory() - { - return new HostGrenadeFactory(); - } + public override GClass676 CreateGrenadeFactory() + { + return new HostGrenadeFactory(); + } - public override void Start() - { - base.Start(); - RegisterBorderZones(); - } - } + public override void Start() + { + base.Start(); + RegisterBorderZones(); + } + } } diff --git a/Fika.Core/Coop/HostClasses/CoopHostGrenade.cs b/Fika.Core/Coop/HostClasses/CoopHostGrenade.cs index f9edbb56..01eb634f 100644 --- a/Fika.Core/Coop/HostClasses/CoopHostGrenade.cs +++ b/Fika.Core/Coop/HostClasses/CoopHostGrenade.cs @@ -2,8 +2,8 @@ namespace Fika.Core.Coop.HostClasses { - public class CoopHostGrenade : Grenade - { - public override bool HasNetData => true; - } + public class CoopHostGrenade : Grenade + { + public override bool HasNetData => true; + } } diff --git a/Fika.Core/Coop/HostClasses/CoopHostSmokeGrenade.cs b/Fika.Core/Coop/HostClasses/CoopHostSmokeGrenade.cs index 66392d64..d64b8637 100644 --- a/Fika.Core/Coop/HostClasses/CoopHostSmokeGrenade.cs +++ b/Fika.Core/Coop/HostClasses/CoopHostSmokeGrenade.cs @@ -1,7 +1,7 @@ namespace Fika.Core.Coop.HostClasses { - public class CoopHostSmokeGrenade : SmokeGrenade - { - public override bool HasNetData => true; - } + public class CoopHostSmokeGrenade : SmokeGrenade + { + public override bool HasNetData => true; + } } diff --git a/Fika.Core/Coop/HostClasses/CoopHostStunGrenade.cs b/Fika.Core/Coop/HostClasses/CoopHostStunGrenade.cs index 6cbd2a77..c711cbe9 100644 --- a/Fika.Core/Coop/HostClasses/CoopHostStunGrenade.cs +++ b/Fika.Core/Coop/HostClasses/CoopHostStunGrenade.cs @@ -1,7 +1,7 @@ namespace Fika.Core.Coop.HostClasses { - public class CoopHostStunGrenade : StunGrenade - { - public override bool HasNetData => true; - } + public class CoopHostStunGrenade : StunGrenade + { + public override bool HasNetData => true; + } } diff --git a/Fika.Core/Coop/HostClasses/HostGrenadeFactory.cs b/Fika.Core/Coop/HostClasses/HostGrenadeFactory.cs index 1c70adb7..77122974 100644 --- a/Fika.Core/Coop/HostClasses/HostGrenadeFactory.cs +++ b/Fika.Core/Coop/HostClasses/HostGrenadeFactory.cs @@ -3,21 +3,21 @@ namespace Fika.Core.Coop.HostClasses { - public class HostGrenadeFactory : GClass676 - { - public override Grenade AddGrenade(GameObject gameObject) - { - return gameObject.AddComponent(); - } + public class HostGrenadeFactory : GClass676 + { + public override Grenade AddGrenade(GameObject gameObject) + { + return gameObject.AddComponent(); + } - public override SmokeGrenade AddSmokeGrenade(GameObject gameObject) - { - return gameObject.AddComponent(); - } + public override SmokeGrenade AddSmokeGrenade(GameObject gameObject) + { + return gameObject.AddComponent(); + } - public override StunGrenade AddStunGrenade(GameObject gameObject) - { - return gameObject.AddComponent(); - } - } + public override StunGrenade AddStunGrenade(GameObject gameObject) + { + return gameObject.AddComponent(); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/CoopObservedGrenade.cs b/Fika.Core/Coop/ObservedClasses/CoopObservedGrenade.cs index 2472f5d0..417f42bb 100644 --- a/Fika.Core/Coop/ObservedClasses/CoopObservedGrenade.cs +++ b/Fika.Core/Coop/ObservedClasses/CoopObservedGrenade.cs @@ -2,11 +2,11 @@ namespace Fika.Core.Coop.ObservedClasses { - public class CoopObservedGrenade : Grenade - { - public override void ApplyNetPacket(GStruct128 packet) - { - base.ApplyNetPacket(packet); - } - } + public class CoopObservedGrenade : Grenade + { + public override void ApplyNetPacket(GStruct128 packet) + { + base.ApplyNetPacket(packet); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs b/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs index 635b9b38..d3dee662 100644 --- a/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs +++ b/Fika.Core/Coop/ObservedClasses/CoopObservedStatisticsManager.cs @@ -6,74 +6,74 @@ namespace Fika.Core.Coop.ObservedClasses { - /// - /// Created by: Paulov - /// Paulov: Uses stubs for all of Statistics Manager - /// - public sealed class CoopObservedStatisticsManager : IStatisticsManager - { - private Player Player; + /// + /// Created by: Paulov + /// Paulov: Uses stubs for all of Statistics Manager + /// + public sealed class CoopObservedStatisticsManager : IStatisticsManager + { + private Player Player; - public TimeSpan CurrentSessionLength - { - get - { - return default; - } - } + public TimeSpan CurrentSessionLength + { + get + { + return default; + } + } #pragma warning disable CS0067 - public event Action OnUniqueLoot; + public event Action OnUniqueLoot; #pragma warning restore CS0067 - public void AddDoorExperience(bool breached) - { - // Do nothing - } + public void AddDoorExperience(bool breached) + { + // Do nothing + } - public void BeginStatisticsSession() - { - // Do nothing - } + public void BeginStatisticsSession() + { + // Do nothing + } - public void EndStatisticsSession(ExitStatus exitStatus, float pastTime) - { - // Do nothing - } + public void EndStatisticsSession(ExitStatus exitStatus, float pastTime) + { + // Do nothing + } - public void Init(Player player) - { - Player = player; - } + public void Init(Player player) + { + Player = player; + } - 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 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, bool isAI) - { - // 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, bool isAI) + { + // Do nothing + } - public void OnGrabLoot(Item item) - { - // Do nothing - } + public void OnGrabLoot(Item item) + { + // Do nothing + } - public void OnGroupMemberConnected(Inventory inventory) - { - // Do nothing - } + public void OnGroupMemberConnected(Inventory inventory) + { + // Do nothing + } - public void OnInteractWithLootContainer(Item item) - { - // Do nothing - } + public void OnInteractWithLootContainer(Item item) + { + // Do nothing + } - public void OnShot(Weapon weapon, BulletClass ammo) - { - // Do nothing - } - } + public void OnShot(Weapon weapon, BulletClass ammo) + { + // Do nothing + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedEmptyHandsController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedEmptyHandsController.cs index 15290dcc..8501ad20 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedEmptyHandsController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedEmptyHandsController.cs @@ -4,33 +4,33 @@ namespace Fika.Core.Coop.ObservedClasses { - internal class CoopObservedEmptyHandsController : EFT.Player.EmptyHandsController - { - public CoopPlayer coopPlayer; + internal class CoopObservedEmptyHandsController : EFT.Player.EmptyHandsController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopObservedEmptyHandsController Create(CoopPlayer player) - { - return smethod_5(player); - } + public static CoopObservedEmptyHandsController Create(CoopPlayer player) + { + return smethod_5(player); + } - public override bool CanChangeCompassState(bool newState) - { - return false; - } + public override bool CanChangeCompassState(bool newState) + { + return false; + } - public override void OnCanUsePropChanged(bool canUse) - { - // Do nothing - } + public override void OnCanUsePropChanged(bool canUse) + { + // Do nothing + } - public override void SetCompassState(bool active) - { - // Do nothing - } - } + public override void SetCompassState(bool active) + { + // Do nothing + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs index 9b4c4f78..262f1782 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedFirearmController.cs @@ -15,531 +15,531 @@ namespace Fika.Core.Coop.ObservedClasses { - public class CoopObservedFirearmController : FirearmController - { - public CoopPlayer coopPlayer; - bool triggerPressed = false; - bool needsReset = false; - float lastFireTime = 0f; - public override bool IsTriggerPressed => triggerPressed; - private float overlapCounter = 0f; - private float aimMovementSpeed = 1f; - private bool hasFired = false; - private WeaponPrefab weaponPrefab; - private GClass1593 underBarrelManager; - public override bool IsAiming - { - get => base.IsAiming; - set - { - if (!value) - { - _player.Physical.HoldBreath(false); - } - if (_isAiming == value) - { - return; - } - - _isAiming = value; - _player.Skills.FastAimTimer.Target = value ? 0f : 2f; - _player.MovementContext.SetAimingSlowdown(IsAiming, 0.33f + aimMovementSpeed); - _player.Physical.Aim((!_isAiming || !(_player.MovementContext.StationaryWeapon == null)) ? 0f : ErgonomicWeight); - coopPlayer.ProceduralWeaponAnimation.IsAiming = _isAiming; - } - } - - public override Vector3 WeaponDirection => -CurrentFireport.up; - - protected void Awake() - { - coopPlayer = GetComponent(); - } - - protected void Start() - { - _objectInHandsAnimator.SetAiming(false); - aimMovementSpeed = coopPlayer.Skills.GetWeaponInfo(Item).AimMovementSpeed; - weaponPrefab = ControllerGameObject.GetComponent(); - if (UnderbarrelWeapon != null) - { - underBarrelManager = Traverse.Create(this).Field("gclass1593_0").Value; - } - } - - public static CoopObservedFirearmController Create(CoopPlayer player, Weapon weapon) - { - return smethod_5(player, weapon); - } - - public override void ManualUpdate(float deltaTime) - { - base.ManualUpdate(deltaTime); - if (Time.time - lastFireTime > 0.05f) - { - if (hasFired) - { - FirearmsAnimator.SetFire(false); - hasFired = false; - } - if (needsReset) - { - needsReset = false; - WeaponSoundPlayer.OnBreakLoop(); - } - } - } - - public override void WeaponOverlapping() - { - SetWeaponOverlapValue(coopPlayer.observedOverlap); - ObservedOverlapView(); - if (overlapCounter <= 1f) - { - overlapCounter += Time.deltaTime / 1f; - } - if (coopPlayer.leftStanceDisabled && coopPlayer.MovementContext.LeftStanceEnabled && overlapCounter > 1f) - { - coopPlayer.MovementContext.LeftStanceController.DisableLeftStanceAnimFromHandsAction(); - overlapCounter = 0f; - } - if (!coopPlayer.MovementContext.LeftStanceController.LastAnimValue && !coopPlayer.leftStanceDisabled && coopPlayer.MovementContext.LeftStanceEnabled && overlapCounter > 1f) - { - coopPlayer.MovementContext.LeftStanceController.SetAnimatorLeftStanceToCacheFromHandsAction(); - overlapCounter = 0f; - } - } - - private void ObservedOverlapView() - { - Vector3 vector = _player.ProceduralWeaponAnimation.HandsContainer.HandsPosition.Get(); - if (coopPlayer.observedOverlap < 0.02f) - { - _player.ProceduralWeaponAnimation.TurnAway.OverlapDepth = coopPlayer.observedOverlap; - _player.ProceduralWeaponAnimation.OverlappingAllowsBlindfire = true; - } - else - { - _player.ProceduralWeaponAnimation.OverlappingAllowsBlindfire = false; - _player.ProceduralWeaponAnimation.TurnAway.OriginZShift = vector.y; - _player.ProceduralWeaponAnimation.TurnAway.OverlapDepth = coopPlayer.observedOverlap; - } - } - - public override void OnPlayerDead() - { - triggerPressed = false; - SetTriggerPressed(false); - - needsReset = false; - WeaponSoundPlayer.OnBreakLoop(); - - coopPlayer.HandsAnimator.Animator.Update(Time.fixedDeltaTime); - ManualUpdate(Time.fixedDeltaTime); - if (CurrentOperation.State != EOperationState.Finished) - { - CurrentOperation.FastForward(); - } - - StartCoroutine(BreakFiringLoop()); - - base.OnPlayerDead(); - } - - private IEnumerator BreakFiringLoop() - { - Traverse isFiring = Traverse.Create(WeaponSoundPlayer).Field("_isFiring"); - int attempts = 0; - while (isFiring.Value && attempts < 10) - { - yield return new WaitForEndOfFrame(); - WeaponSoundPlayer.OnBreakLoop(); - attempts++; - } - } - - public override void SetScopeMode(FirearmScopeStateStruct[] scopeStates) - { - _player.ProceduralWeaponAnimation.ObservedCalibration(); - base.SetScopeMode(scopeStates); - } - - public override void AdjustShotVectors(ref Vector3 position, ref Vector3 direction) - { - // Do nothing - } - - public override bool CanChangeCompassState(bool newState) - { - return false; - } - - public override bool CanRemove() - { - return true; - } - - public override void OnCanUsePropChanged(bool canUse) - { - // Do nothing - } - - public override void SetCompassState(bool active) - { - // Do nothing - } - - public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass inventoryController) - { - if (packet.HasShotInfo) - { - // TODO: Flares, GClass2376::method_12 - - if (packet.ShotInfoPacket.ShotType != EShotType.RegularShot && packet.ShotInfoPacket.ShotType != EShotType.DryFire) - { - switch (packet.ShotInfoPacket.ShotType) - { - case EShotType.Misfire: - Weapon.MalfState.State = Weapon.EMalfunctionState.Misfire; - break; - case EShotType.Feed: - Weapon.MalfState.State = Weapon.EMalfunctionState.Feed; - break; - case EShotType.JamedShot: - Weapon.MalfState.State = Weapon.EMalfunctionState.Jam; - break; - case EShotType.SoftSlidedShot: - Weapon.MalfState.State = Weapon.EMalfunctionState.SoftSlide; - break; - case EShotType.HardSlidedShot: - Weapon.MalfState.State = Weapon.EMalfunctionState.HardSlide; - break; - } - - if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) - { - FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); - return; - } - - Weapon.MalfState.MalfunctionedAmmo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); - if (weaponPrefab != null) - { - weaponPrefab.InitMalfunctionState(Weapon, false, false, out _); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: WeaponPrefab was null!"); - } - } - else if (packet.ShotInfoPacket.ShotType == EShotType.DryFire) - { - FirearmsAnimator.SetFire(true); - DryShot(); - hasFired = true; - lastFireTime = Time.time; - } - else if (packet.ShotInfoPacket.ShotType == EShotType.RegularShot) - { - if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) - { - FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); - return; - } - - BulletClass ammo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); - InitiateShot(Item, ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, - packet.ShotInfoPacket.FireportPosition, packet.ShotInfoPacket.ChamberIndex, packet.ShotInfoPacket.Overheat); - - if (Weapon.SelectedFireMode == Weapon.EFireMode.fullauto) - { - triggerPressed = true; - } - - float pitchMult = method_57(); - WeaponSoundPlayer.FireBullet(ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, - pitchMult, Malfunction, false, IsBirstOf2Start); - - Weapon.MalfState.LastShotOverheat = packet.ShotInfoPacket.LastShotOverheat; - Weapon.MalfState.LastShotTime = packet.ShotInfoPacket.LastShotTime; - Weapon.MalfState.SlideOnOverheatReached = packet.ShotInfoPacket.SlideOnOverheatReached; - - triggerPressed = false; - hasFired = true; - lastFireTime = Time.time; - if (Weapon.SelectedFireMode == Weapon.EFireMode.fullauto) - { - needsReset = true; - } - - MagazineClass magazine = Weapon.GetCurrentMagazine(); - - FirearmsAnimator.SetFire(true); - - if (packet.ShotInfoPacket.UnderbarrelShot) - { - if (UnderbarrelWeapon.Chamber.ContainedItem is BulletClass grenadeBullet && !grenadeBullet.IsUsed) - { - grenadeBullet.IsUsed = true; - UnderbarrelWeapon.Chamber.RemoveItem(); - underBarrelManager?.DestroyPatronInWeapon(); - } - FirearmsAnimator.SetFire(false); - return; - } - - if (Weapon.HasChambers) - { - if (Weapon.ReloadMode is Weapon.EReloadMode.OnlyBarrel) - { - for (int i = 0; i < Weapon.Chambers.Length; i++) - { - if (Weapon.Chambers[i].ContainedItem is BulletClass bClass && !bClass.IsUsed) - { - bClass.IsUsed = true; - if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) - { - if (!bClass.AmmoTemplate.RemoveShellAfterFire) - { - weaponEffectsManager.MoveAmmoFromChamberToShellPort(bClass.IsUsed, i); - } - else - { - weaponEffectsManager.DestroyPatronInWeapon(); - } - } - if (!bClass.AmmoTemplate.RemoveShellAfterFire) - { - Weapon.ShellsInChambers[i] = bClass.AmmoTemplate; - } - break; - } - } - } - else - { - Weapon.Chambers[0].RemoveItem(false); - if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) - { - HandleShellEvent(weaponEffectsManager, packet, ammo, magazine); - } - } - } - - // Remember to check if classes increment - if (Weapon is GClass2711) - { - Weapon.CylinderHammerClosed = Weapon.FireMode.FireMode == Weapon.EFireMode.doubleaction; - - if (magazine is CylinderMagazineClass cylinderMagazine) - { - BulletClass cylinderAmmo = cylinderMagazine.GetFirstAmmo(!Weapon.CylinderHammerClosed); - if (cylinderAmmo != null) - { - cylinderAmmo.IsUsed = true; - cylinderMagazine.RemoveAmmoInCamora(cylinderAmmo, inventoryController); - FirearmsAnimator.SetAmmoOnMag(cylinderMagazine.Count); - if (!cylinderAmmo.AmmoTemplate.RemoveShellAfterFire) - { - Weapon.ShellsInChambers[cylinderMagazine.CurrentCamoraIndex] = cylinderAmmo.AmmoTemplate; - } - } - if (Weapon.CylinderHammerClosed) - { - cylinderMagazine.IncrementCamoraIndex(false); - } - FirearmsAnimator.SetCamoraIndex(cylinderMagazine.CurrentCamoraIndex); - FirearmsAnimator.SetDoubleAction(Convert.ToSingle(Weapon.CylinderHammerClosed)); - FirearmsAnimator.SetHammerArmed(!Weapon.CylinderHammerClosed); - } - } - - ammo.IsUsed = true; - - if (magazine != null && magazine is not CylinderMagazineClass && !Weapon.BoltAction) - { - if (Item.HasChambers) - { - magazine.Cartridges.PopTo(inventoryController, new GClass2783(Item.Chambers[0])); - } - else - { - magazine.Cartridges.PopToNowhere(inventoryController); - } - } - - if (Weapon.IsBoltCatch && Weapon.ChamberAmmoCount == 1 && !Weapon.ManualBoltCatch && !Weapon.MustBoltBeOpennedForExternalReload && !Weapon.MustBoltBeOpennedForInternalReload) - { - FirearmsAnimator.SetBoltCatch(false); - } - - if (ammo.AmmoTemplate.IsLightAndSoundShot) - { - method_58(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection); - LightAndSoundShot(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, ammo.AmmoTemplate); - } - } - } - - if (packet.ChangeFireMode) - { - ChangeFireMode(packet.FireMode); - } - - if (packet.ExamineWeapon) - { - ExamineWeapon(); - } - - if (packet.ToggleAim) - { - SetAim(packet.AimingIndex); - } - - if (packet.CheckAmmo) - { - CheckAmmo(); - } - - if (packet.CheckChamber) - { - CheckChamber(); - } - - if (packet.CheckFireMode) - { - CheckFireMode(); - } - - if (packet.ToggleTacticalCombo) - { - SetLightsState(packet.LightStatesPacket.LightStates, true); - } - - if (packet.ChangeSightMode) - { - SetScopeMode(packet.ScopeStatesPacket.FirearmScopeStateStruct); - } - - if (packet.ToggleLauncher) - { - ToggleLauncher(); - } - - if (packet.EnableInventory) - { - SetInventoryOpened(packet.InventoryStatus); - } - - if (packet.HasReloadMagPacket) - { - if (packet.ReloadMagPacket.Reload) - { - MagazineClass magazine = null; - try - { - Item item = coopPlayer.FindItem(packet.ReloadMagPacket.MagId); - if (item is MagazineClass magazineClass) - { - magazine = magazineClass; - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket::ReloadMagPacket: Item was not MagazineClass, it was {item.GetType()}"); - } - } - catch (Exception ex) - { - FikaPlugin.Instance.FikaLogger.LogError(ex); - FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket: There is no item {packet.ReloadMagPacket.MagId} in profile {coopPlayer.ProfileId}"); - throw; - } - ItemAddressClass gridItemAddress = null; - if (packet.ReloadMagPacket.LocationDescription != null) - { - using MemoryStream memoryStream = new(packet.ReloadMagPacket.LocationDescription); - using BinaryReader binaryReader = new(memoryStream); - try - { - if (packet.ReloadMagPacket.LocationDescription.Length != 0) - { - GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); - gridItemAddress = inventoryController.ToGridItemAddress(descriptor); - } - } - catch (GException4 exception2) - { - FikaPlugin.Instance.FikaLogger.LogError(exception2); - } - } - if (magazine != null) - { - ReloadMag(magazine, gridItemAddress, null); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::ReloadMag final variables were null! Mag: {magazine}, Address: {gridItemAddress}"); - } - } - } - - - if (packet.HasQuickReloadMagPacket) - { - if (packet.QuickReloadMagPacket.Reload) - { - MagazineClass magazine; - try - { - Item item = coopPlayer.FindItem(packet.QuickReloadMagPacket.MagId); - magazine = item as MagazineClass; - if (magazine == null) - { - FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::QuickReloadMag could not cast {packet.ReloadMagPacket.MagId} as a magazine, got {item.ShortName}"); - } - } - catch (Exception ex) - { - FikaPlugin.Instance.FikaLogger.LogError(ex); - FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController: There is no item {packet.ReloadMagPacket.MagId} in profile {coopPlayer.ProfileId}"); - throw; - } - QuickReloadMag(magazine, null); - } - } - - if (packet.HasReloadWithAmmoPacket) - { - if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.AbortReload) - { - CurrentOperation.SetTriggerPressed(true); - } - - if (packet.ReloadWithAmmo.Reload) - { - if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload) - { - List bullets = FindAmmoByIds(packet.ReloadWithAmmo.AmmoIds); - AmmoPackReloadingClass ammoPack = new(bullets); - if (!packet.HasCylinderMagPacket) - { - CurrentOperation.ReloadWithAmmo(ammoPack, null, null); - } - else - { - CurrentOperation.ReloadCylinderMagazine(ammoPack, null, null); - } - } - - if (packet.CylinderMag.Changed && Weapon.GetCurrentMagazine() is CylinderMagazineClass cylinderMagazine) - { - cylinderMagazine.SetCurrentCamoraIndex(packet.CylinderMag.CamoraIndex); - Weapon.CylinderHammerClosed = packet.CylinderMag.HammerClosed; - } - } - } - - /*if (packet.HasCylinderMagPacket) + public class CoopObservedFirearmController : FirearmController + { + public CoopPlayer coopPlayer; + bool triggerPressed = false; + bool needsReset = false; + float lastFireTime = 0f; + public override bool IsTriggerPressed => triggerPressed; + private float overlapCounter = 0f; + private float aimMovementSpeed = 1f; + private bool hasFired = false; + private WeaponPrefab weaponPrefab; + private GClass1593 underBarrelManager; + public override bool IsAiming + { + get => base.IsAiming; + set + { + if (!value) + { + _player.Physical.HoldBreath(false); + } + if (_isAiming == value) + { + return; + } + + _isAiming = value; + _player.Skills.FastAimTimer.Target = value ? 0f : 2f; + _player.MovementContext.SetAimingSlowdown(IsAiming, 0.33f + aimMovementSpeed); + _player.Physical.Aim((!_isAiming || !(_player.MovementContext.StationaryWeapon == null)) ? 0f : ErgonomicWeight); + coopPlayer.ProceduralWeaponAnimation.IsAiming = _isAiming; + } + } + + public override Vector3 WeaponDirection => -CurrentFireport.up; + + protected void Awake() + { + coopPlayer = GetComponent(); + } + + protected void Start() + { + _objectInHandsAnimator.SetAiming(false); + aimMovementSpeed = coopPlayer.Skills.GetWeaponInfo(Item).AimMovementSpeed; + weaponPrefab = ControllerGameObject.GetComponent(); + if (UnderbarrelWeapon != null) + { + underBarrelManager = Traverse.Create(this).Field("gclass1593_0").Value; + } + } + + public static CoopObservedFirearmController Create(CoopPlayer player, Weapon weapon) + { + return smethod_5(player, weapon); + } + + public override void ManualUpdate(float deltaTime) + { + base.ManualUpdate(deltaTime); + if (Time.time - lastFireTime > 0.05f) + { + if (hasFired) + { + FirearmsAnimator.SetFire(false); + hasFired = false; + } + if (needsReset) + { + needsReset = false; + WeaponSoundPlayer.OnBreakLoop(); + } + } + } + + public override void WeaponOverlapping() + { + SetWeaponOverlapValue(coopPlayer.observedOverlap); + ObservedOverlapView(); + if (overlapCounter <= 1f) + { + overlapCounter += Time.deltaTime / 1f; + } + if (coopPlayer.leftStanceDisabled && coopPlayer.MovementContext.LeftStanceEnabled && overlapCounter > 1f) + { + coopPlayer.MovementContext.LeftStanceController.DisableLeftStanceAnimFromHandsAction(); + overlapCounter = 0f; + } + if (!coopPlayer.MovementContext.LeftStanceController.LastAnimValue && !coopPlayer.leftStanceDisabled && coopPlayer.MovementContext.LeftStanceEnabled && overlapCounter > 1f) + { + coopPlayer.MovementContext.LeftStanceController.SetAnimatorLeftStanceToCacheFromHandsAction(); + overlapCounter = 0f; + } + } + + private void ObservedOverlapView() + { + Vector3 vector = _player.ProceduralWeaponAnimation.HandsContainer.HandsPosition.Get(); + if (coopPlayer.observedOverlap < 0.02f) + { + _player.ProceduralWeaponAnimation.TurnAway.OverlapDepth = coopPlayer.observedOverlap; + _player.ProceduralWeaponAnimation.OverlappingAllowsBlindfire = true; + } + else + { + _player.ProceduralWeaponAnimation.OverlappingAllowsBlindfire = false; + _player.ProceduralWeaponAnimation.TurnAway.OriginZShift = vector.y; + _player.ProceduralWeaponAnimation.TurnAway.OverlapDepth = coopPlayer.observedOverlap; + } + } + + public override void OnPlayerDead() + { + triggerPressed = false; + SetTriggerPressed(false); + + needsReset = false; + WeaponSoundPlayer.OnBreakLoop(); + + coopPlayer.HandsAnimator.Animator.Update(Time.fixedDeltaTime); + ManualUpdate(Time.fixedDeltaTime); + if (CurrentOperation.State != EOperationState.Finished) + { + CurrentOperation.FastForward(); + } + + StartCoroutine(BreakFiringLoop()); + + base.OnPlayerDead(); + } + + private IEnumerator BreakFiringLoop() + { + Traverse isFiring = Traverse.Create(WeaponSoundPlayer).Field("_isFiring"); + int attempts = 0; + while (isFiring.Value && attempts < 10) + { + yield return new WaitForEndOfFrame(); + WeaponSoundPlayer.OnBreakLoop(); + attempts++; + } + } + + public override void SetScopeMode(FirearmScopeStateStruct[] scopeStates) + { + _player.ProceduralWeaponAnimation.ObservedCalibration(); + base.SetScopeMode(scopeStates); + } + + public override void AdjustShotVectors(ref Vector3 position, ref Vector3 direction) + { + // Do nothing + } + + public override bool CanChangeCompassState(bool newState) + { + return false; + } + + public override bool CanRemove() + { + return true; + } + + public override void OnCanUsePropChanged(bool canUse) + { + // Do nothing + } + + public override void SetCompassState(bool active) + { + // Do nothing + } + + public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass inventoryController) + { + if (packet.HasShotInfo) + { + // TODO: Flares, GClass2376::method_12 + + if (packet.ShotInfoPacket.ShotType != EShotType.RegularShot && packet.ShotInfoPacket.ShotType != EShotType.DryFire) + { + switch (packet.ShotInfoPacket.ShotType) + { + case EShotType.Misfire: + Weapon.MalfState.State = Weapon.EMalfunctionState.Misfire; + break; + case EShotType.Feed: + Weapon.MalfState.State = Weapon.EMalfunctionState.Feed; + break; + case EShotType.JamedShot: + Weapon.MalfState.State = Weapon.EMalfunctionState.Jam; + break; + case EShotType.SoftSlidedShot: + Weapon.MalfState.State = Weapon.EMalfunctionState.SoftSlide; + break; + case EShotType.HardSlidedShot: + Weapon.MalfState.State = Weapon.EMalfunctionState.HardSlide; + break; + } + + if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); + return; + } + + Weapon.MalfState.MalfunctionedAmmo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); + if (weaponPrefab != null) + { + weaponPrefab.InitMalfunctionState(Weapon, false, false, out _); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: WeaponPrefab was null!"); + } + } + else if (packet.ShotInfoPacket.ShotType == EShotType.DryFire) + { + FirearmsAnimator.SetFire(true); + DryShot(); + hasFired = true; + lastFireTime = Time.time; + } + else if (packet.ShotInfoPacket.ShotType == EShotType.RegularShot) + { + if (string.IsNullOrEmpty(packet.ShotInfoPacket.AmmoTemplate)) + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket: AmmoTemplate was null or empty!"); + return; + } + + BulletClass ammo = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.ShotInfoPacket.AmmoTemplate, null); + InitiateShot(Item, ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, + packet.ShotInfoPacket.FireportPosition, packet.ShotInfoPacket.ChamberIndex, packet.ShotInfoPacket.Overheat); + + if (Weapon.SelectedFireMode == Weapon.EFireMode.fullauto) + { + triggerPressed = true; + } + + float pitchMult = method_57(); + WeaponSoundPlayer.FireBullet(ammo, packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, + pitchMult, Malfunction, false, IsBirstOf2Start); + + Weapon.MalfState.LastShotOverheat = packet.ShotInfoPacket.LastShotOverheat; + Weapon.MalfState.LastShotTime = packet.ShotInfoPacket.LastShotTime; + Weapon.MalfState.SlideOnOverheatReached = packet.ShotInfoPacket.SlideOnOverheatReached; + + triggerPressed = false; + hasFired = true; + lastFireTime = Time.time; + if (Weapon.SelectedFireMode == Weapon.EFireMode.fullauto) + { + needsReset = true; + } + + MagazineClass magazine = Weapon.GetCurrentMagazine(); + + FirearmsAnimator.SetFire(true); + + if (packet.ShotInfoPacket.UnderbarrelShot) + { + if (UnderbarrelWeapon.Chamber.ContainedItem is BulletClass grenadeBullet && !grenadeBullet.IsUsed) + { + grenadeBullet.IsUsed = true; + UnderbarrelWeapon.Chamber.RemoveItem(); + underBarrelManager?.DestroyPatronInWeapon(); + } + FirearmsAnimator.SetFire(false); + return; + } + + if (Weapon.HasChambers) + { + if (Weapon.ReloadMode is Weapon.EReloadMode.OnlyBarrel) + { + for (int i = 0; i < Weapon.Chambers.Length; i++) + { + if (Weapon.Chambers[i].ContainedItem is BulletClass bClass && !bClass.IsUsed) + { + bClass.IsUsed = true; + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) + { + if (!bClass.AmmoTemplate.RemoveShellAfterFire) + { + weaponEffectsManager.MoveAmmoFromChamberToShellPort(bClass.IsUsed, i); + } + else + { + weaponEffectsManager.DestroyPatronInWeapon(); + } + } + if (!bClass.AmmoTemplate.RemoveShellAfterFire) + { + Weapon.ShellsInChambers[i] = bClass.AmmoTemplate; + } + break; + } + } + } + else + { + Weapon.Chambers[0].RemoveItem(false); + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) + { + HandleShellEvent(weaponEffectsManager, packet, ammo, magazine); + } + } + } + + // Remember to check if classes increment + if (Weapon is GClass2711) + { + Weapon.CylinderHammerClosed = Weapon.FireMode.FireMode == Weapon.EFireMode.doubleaction; + + if (magazine is CylinderMagazineClass cylinderMagazine) + { + BulletClass cylinderAmmo = cylinderMagazine.GetFirstAmmo(!Weapon.CylinderHammerClosed); + if (cylinderAmmo != null) + { + cylinderAmmo.IsUsed = true; + cylinderMagazine.RemoveAmmoInCamora(cylinderAmmo, inventoryController); + FirearmsAnimator.SetAmmoOnMag(cylinderMagazine.Count); + if (!cylinderAmmo.AmmoTemplate.RemoveShellAfterFire) + { + Weapon.ShellsInChambers[cylinderMagazine.CurrentCamoraIndex] = cylinderAmmo.AmmoTemplate; + } + } + if (Weapon.CylinderHammerClosed) + { + cylinderMagazine.IncrementCamoraIndex(false); + } + FirearmsAnimator.SetCamoraIndex(cylinderMagazine.CurrentCamoraIndex); + FirearmsAnimator.SetDoubleAction(Convert.ToSingle(Weapon.CylinderHammerClosed)); + FirearmsAnimator.SetHammerArmed(!Weapon.CylinderHammerClosed); + } + } + + ammo.IsUsed = true; + + if (magazine != null && magazine is not CylinderMagazineClass && !Weapon.BoltAction) + { + if (Item.HasChambers) + { + magazine.Cartridges.PopTo(inventoryController, new GClass2783(Item.Chambers[0])); + } + else + { + magazine.Cartridges.PopToNowhere(inventoryController); + } + } + + if (Weapon.IsBoltCatch && Weapon.ChamberAmmoCount == 1 && !Weapon.ManualBoltCatch && !Weapon.MustBoltBeOpennedForExternalReload && !Weapon.MustBoltBeOpennedForInternalReload) + { + FirearmsAnimator.SetBoltCatch(false); + } + + if (ammo.AmmoTemplate.IsLightAndSoundShot) + { + method_58(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection); + LightAndSoundShot(packet.ShotInfoPacket.ShotPosition, packet.ShotInfoPacket.ShotDirection, ammo.AmmoTemplate); + } + } + } + + if (packet.ChangeFireMode) + { + ChangeFireMode(packet.FireMode); + } + + if (packet.ExamineWeapon) + { + ExamineWeapon(); + } + + if (packet.ToggleAim) + { + SetAim(packet.AimingIndex); + } + + if (packet.CheckAmmo) + { + CheckAmmo(); + } + + if (packet.CheckChamber) + { + CheckChamber(); + } + + if (packet.CheckFireMode) + { + CheckFireMode(); + } + + if (packet.ToggleTacticalCombo) + { + SetLightsState(packet.LightStatesPacket.LightStates, true); + } + + if (packet.ChangeSightMode) + { + SetScopeMode(packet.ScopeStatesPacket.FirearmScopeStateStruct); + } + + if (packet.ToggleLauncher) + { + ToggleLauncher(); + } + + if (packet.EnableInventory) + { + SetInventoryOpened(packet.InventoryStatus); + } + + if (packet.HasReloadMagPacket) + { + if (packet.ReloadMagPacket.Reload) + { + MagazineClass magazine = null; + try + { + Item item = coopPlayer.FindItem(packet.ReloadMagPacket.MagId); + if (item is MagazineClass magazineClass) + { + magazine = magazineClass; + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CoopObservedFirearmController::HandleFirearmPacket::ReloadMagPacket: Item was not MagazineClass, it was {item.GetType()}"); + } + } + catch (Exception ex) + { + FikaPlugin.Instance.FikaLogger.LogError(ex); + FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket: There is no item {packet.ReloadMagPacket.MagId} in profile {coopPlayer.ProfileId}"); + throw; + } + ItemAddressClass gridItemAddress = null; + if (packet.ReloadMagPacket.LocationDescription != null) + { + using MemoryStream memoryStream = new(packet.ReloadMagPacket.LocationDescription); + using BinaryReader binaryReader = new(memoryStream); + try + { + if (packet.ReloadMagPacket.LocationDescription.Length != 0) + { + GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); + gridItemAddress = inventoryController.ToGridItemAddress(descriptor); + } + } + catch (GException4 exception2) + { + FikaPlugin.Instance.FikaLogger.LogError(exception2); + } + } + if (magazine != null) + { + ReloadMag(magazine, gridItemAddress, null); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::ReloadMag final variables were null! Mag: {magazine}, Address: {gridItemAddress}"); + } + } + } + + + if (packet.HasQuickReloadMagPacket) + { + if (packet.QuickReloadMagPacket.Reload) + { + MagazineClass magazine; + try + { + Item item = coopPlayer.FindItem(packet.QuickReloadMagPacket.MagId); + magazine = item as MagazineClass; + if (magazine == null) + { + FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::QuickReloadMag could not cast {packet.ReloadMagPacket.MagId} as a magazine, got {item.ShortName}"); + } + } + catch (Exception ex) + { + FikaPlugin.Instance.FikaLogger.LogError(ex); + FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController: There is no item {packet.ReloadMagPacket.MagId} in profile {coopPlayer.ProfileId}"); + throw; + } + QuickReloadMag(magazine, null); + } + } + + if (packet.HasReloadWithAmmoPacket) + { + if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.AbortReload) + { + CurrentOperation.SetTriggerPressed(true); + } + + if (packet.ReloadWithAmmo.Reload) + { + if (packet.ReloadWithAmmo.Status == FikaSerialization.ReloadWithAmmoPacket.EReloadWithAmmoStatus.StartReload) + { + List bullets = FindAmmoByIds(packet.ReloadWithAmmo.AmmoIds); + AmmoPackReloadingClass ammoPack = new(bullets); + if (!packet.HasCylinderMagPacket) + { + CurrentOperation.ReloadWithAmmo(ammoPack, null, null); + } + else + { + CurrentOperation.ReloadCylinderMagazine(ammoPack, null, null); + } + } + + if (packet.CylinderMag.Changed && Weapon.GetCurrentMagazine() is CylinderMagazineClass cylinderMagazine) + { + cylinderMagazine.SetCurrentCamoraIndex(packet.CylinderMag.CamoraIndex); + Weapon.CylinderHammerClosed = packet.CylinderMag.HammerClosed; + } + } + } + + /*if (packet.HasCylinderMagPacket) { if (packet.ReloadWithAmmo.Reload && packet.CylinderMag.Changed) { @@ -559,135 +559,135 @@ public void HandleFirearmPacket(in WeaponPacket packet, InventoryControllerClass }*/ - if (packet.HasRollCylinder && Weapon is GClass2711 rollWeapon) - { - RollCylinder(packet.RollToZeroCamora); - } - - if (packet.HasReloadLauncherPacket) - { - if (packet.ReloadLauncher.Reload) - { - List ammo = FindAmmoByIds(packet.ReloadLauncher.AmmoIds); - AmmoPackReloadingClass ammoPack = new(ammo); - ReloadGrenadeLauncher(ammoPack, null); - } - } - - if (packet.HasReloadBarrelsPacket) - { - if (packet.ReloadBarrels.Reload) - { - List ammo = FindAmmoByIds(packet.ReloadBarrels.AmmoIds); - - AmmoPackReloadingClass ammoPack = new(ammo); - - ItemAddressClass gridItemAddress = null; - - using MemoryStream memoryStream = new(packet.ReloadBarrels.LocationDescription); - using BinaryReader binaryReader = new(memoryStream); - try - { - if (packet.ReloadBarrels.LocationDescription.Length > 0) - { - GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); - gridItemAddress = inventoryController.ToGridItemAddress(descriptor); - } - } - catch (GException4 exception2) - { - FikaPlugin.Instance.FikaLogger.LogError(exception2); - } - - if (ammoPack != null) - { - ReloadBarrels(ammoPack, gridItemAddress, null); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::ReloadBarrel final variables were null! Ammo: {ammoPack}, Address: {gridItemAddress}"); - } - } - } - - if (packet.HasStanceChange) - { - if (coopPlayer.MovementContext.LeftStanceEnabled != packet.LeftStanceState) - { - ChangeLeftStance(); - } - } - - if (packet.HasFlareShot) - { - BulletClass bulletClass = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.FlareShotPacket.AmmoTemplateId, null); - InitiateFlare(bulletClass, packet.FlareShotPacket.ShotPosition, packet.FlareShotPacket.ShotForward); - } - - if (packet.ReloadBoltAction) - { - StartCoroutine(ObservedBoltAction(FirearmsAnimator, this, inventoryController)); - } - - if (packet.UnderbarrelSightingRangeUp) - { - UnderbarrelSightingRangeUp(); - } - - if (packet.UnderbarrelSightingRangeDown) - { - UnderbarrelSightingRangeDown(); - } - } - - private IEnumerator ObservedBoltAction(FirearmsAnimator animator, FirearmController controller, InventoryControllerClass inventoryController) - { - animator.SetBoltActionReload(true); - animator.SetFire(true); - - yield return new WaitForSeconds(0.75f); - - if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) - { - weaponEffectsManager.StartSpawnShell(coopPlayer.Velocity * 0.33f, 0); - } - - MagazineClass magazine = controller.Item.GetCurrentMagazine(); - Weapon weapon = controller.Weapon; - - if (magazine != null && magazine is not CylinderMagazineClass && weapon.HasChambers) - { - magazine.Cartridges.PopTo(inventoryController, new GClass2783(controller.Item.Chambers[0])); - } - - animator.SetBoltActionReload(false); - animator.SetFire(false); - } - - private void HandleShellEvent(WeaponManagerClass weaponEffectsManager, WeaponPacket packet, BulletClass ammo, MagazineClass magazine) - { - weaponEffectsManager.DestroyPatronInWeapon(packet.ShotInfoPacket.ChamberIndex); - if (!ammo.AmmoTemplate.RemoveShellAfterFire) - { - weaponEffectsManager.CreatePatronInShellPort(ammo, packet.ShotInfoPacket.ChamberIndex); - FirearmsAnimator.SetShellsInWeapon(1); - } - else - { - FirearmsAnimator.SetShellsInWeapon(0); - } - - if (magazine != null && !Weapon.BoltAction) - { - weaponEffectsManager.SetRoundIntoWeapon(ammo, 0); - } - - if (Weapon is GClass2711 || Weapon.ReloadMode == Weapon.EReloadMode.OnlyBarrel || Weapon.BoltAction) - { - return; - } - - weaponEffectsManager.StartSpawnShell(coopPlayer.Velocity * 0.33f, 0); - } - } + if (packet.HasRollCylinder && Weapon is GClass2711 rollWeapon) + { + RollCylinder(packet.RollToZeroCamora); + } + + if (packet.HasReloadLauncherPacket) + { + if (packet.ReloadLauncher.Reload) + { + List ammo = FindAmmoByIds(packet.ReloadLauncher.AmmoIds); + AmmoPackReloadingClass ammoPack = new(ammo); + ReloadGrenadeLauncher(ammoPack, null); + } + } + + if (packet.HasReloadBarrelsPacket) + { + if (packet.ReloadBarrels.Reload) + { + List ammo = FindAmmoByIds(packet.ReloadBarrels.AmmoIds); + + AmmoPackReloadingClass ammoPack = new(ammo); + + ItemAddressClass gridItemAddress = null; + + using MemoryStream memoryStream = new(packet.ReloadBarrels.LocationDescription); + using BinaryReader binaryReader = new(memoryStream); + try + { + if (packet.ReloadBarrels.LocationDescription.Length > 0) + { + GridItemAddressDescriptorClass descriptor = binaryReader.ReadEFTGridItemAddressDescriptor(); + gridItemAddress = inventoryController.ToGridItemAddress(descriptor); + } + } + catch (GException4 exception2) + { + FikaPlugin.Instance.FikaLogger.LogError(exception2); + } + + if (ammoPack != null) + { + ReloadBarrels(ammoPack, gridItemAddress, null); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"CoopObservedFirearmController::HandleFirearmPacket::ReloadBarrel final variables were null! Ammo: {ammoPack}, Address: {gridItemAddress}"); + } + } + } + + if (packet.HasStanceChange) + { + if (coopPlayer.MovementContext.LeftStanceEnabled != packet.LeftStanceState) + { + ChangeLeftStance(); + } + } + + if (packet.HasFlareShot) + { + BulletClass bulletClass = (BulletClass)Singleton.Instance.CreateItem(MongoID.Generate(), packet.FlareShotPacket.AmmoTemplateId, null); + InitiateFlare(bulletClass, packet.FlareShotPacket.ShotPosition, packet.FlareShotPacket.ShotForward); + } + + if (packet.ReloadBoltAction) + { + StartCoroutine(ObservedBoltAction(FirearmsAnimator, this, inventoryController)); + } + + if (packet.UnderbarrelSightingRangeUp) + { + UnderbarrelSightingRangeUp(); + } + + if (packet.UnderbarrelSightingRangeDown) + { + UnderbarrelSightingRangeDown(); + } + } + + private IEnumerator ObservedBoltAction(FirearmsAnimator animator, FirearmController controller, InventoryControllerClass inventoryController) + { + animator.SetBoltActionReload(true); + animator.SetFire(true); + + yield return new WaitForSeconds(0.75f); + + if (weaponPrefab != null && weaponPrefab.ObjectInHands is WeaponManagerClass weaponEffectsManager) + { + weaponEffectsManager.StartSpawnShell(coopPlayer.Velocity * 0.33f, 0); + } + + MagazineClass magazine = controller.Item.GetCurrentMagazine(); + Weapon weapon = controller.Weapon; + + if (magazine != null && magazine is not CylinderMagazineClass && weapon.HasChambers) + { + magazine.Cartridges.PopTo(inventoryController, new GClass2783(controller.Item.Chambers[0])); + } + + animator.SetBoltActionReload(false); + animator.SetFire(false); + } + + private void HandleShellEvent(WeaponManagerClass weaponEffectsManager, WeaponPacket packet, BulletClass ammo, MagazineClass magazine) + { + weaponEffectsManager.DestroyPatronInWeapon(packet.ShotInfoPacket.ChamberIndex); + if (!ammo.AmmoTemplate.RemoveShellAfterFire) + { + weaponEffectsManager.CreatePatronInShellPort(ammo, packet.ShotInfoPacket.ChamberIndex); + FirearmsAnimator.SetShellsInWeapon(1); + } + else + { + FirearmsAnimator.SetShellsInWeapon(0); + } + + if (magazine != null && !Weapon.BoltAction) + { + weaponEffectsManager.SetRoundIntoWeapon(ammo, 0); + } + + if (Weapon is GClass2711 || Weapon.ReloadMode == Weapon.EReloadMode.OnlyBarrel || Weapon.BoltAction) + { + return; + } + + weaponEffectsManager.StartSpawnShell(coopPlayer.Velocity * 0.33f, 0); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs index 77d1e117..a90f5da7 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedGrenadeController.cs @@ -6,21 +6,21 @@ namespace Fika.Core.Coop.ObservedClasses { - internal class CoopObservedGrenadeController : EFT.Player.GrenadeController - { - public CoopPlayer coopPlayer; + internal class CoopObservedGrenadeController : EFT.Player.GrenadeController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopObservedGrenadeController Create(CoopPlayer player, GrenadeClass item) - { - return smethod_8(player, item); - } + public static CoopObservedGrenadeController Create(CoopPlayer player, GrenadeClass item) + { + return smethod_8(player, item); + } - /*public override Dictionary GetOperationFactoryDelegates() + /*public override Dictionary GetOperationFactoryDelegates() { return new Dictionary { @@ -51,63 +51,63 @@ public static CoopObservedGrenadeController Create(CoopPlayer player, GrenadeCla }; }*/ - /*private void CreateGrenadeClass1() + /*private void CreateGrenadeClass1() { }*/ - public override bool CanChangeCompassState(bool newState) - { - return false; - } + public override bool CanChangeCompassState(bool newState) + { + return false; + } - public override bool CanRemove() - { - return true; - } + public override bool CanRemove() + { + return true; + } - public override void OnCanUsePropChanged(bool canUse) - { - // Do nothing - } + public override void OnCanUsePropChanged(bool canUse) + { + // Do nothing + } - public override void SetCompassState(bool active) - { - // Do nothing - } + public override void SetCompassState(bool active) + { + // Do nothing + } - /// - /// Original method to spawn a grenade, we use instead - /// - /// - /// - /// - /// - /// - public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - // Do nothing, we use our own method - } + /// + /// Original method to spawn a grenade, we use instead + /// + /// + /// + /// + /// + /// + public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + // Do nothing, we use our own method + } - /// - /// Spawns a grenade, uses data from - /// - /// The time since the safety was removed, use 0f - /// The position to start from - /// The rotation of the grenade - /// The force of the grenade - /// If it's a low throw or not - public void SpawnGrenade(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); - } + /// + /// Spawns a grenade, uses data from + /// + /// The time since the safety was removed, use 0f + /// The position to start from + /// The rotation of the grenade + /// The force of the grenade + /// If it's a low throw or not + public void SpawnGrenade(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); + } - /*private class GrenadeClass1 : Class1028 + /*private class GrenadeClass1 : Class1028 { public GrenadeClass1(Player.GrenadeController controller) : base(controller) { } }*/ - } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedKnifeController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedKnifeController.cs index 16c02eb0..d6b897f1 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedKnifeController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedKnifeController.cs @@ -5,18 +5,18 @@ namespace Fika.Core.Coop.ObservedClasses { - internal class CoopObservedKnifeController : EFT.Player.KnifeController - { - public CoopPlayer coopPlayer; + internal class CoopObservedKnifeController : EFT.Player.KnifeController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopObservedKnifeController Create(CoopPlayer player, KnifeComponent item) - { - return smethod_8(player, item); - } - } + public static CoopObservedKnifeController Create(CoopPlayer player, KnifeComponent item) + { + return smethod_8(player, item); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedMedsController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedMedsController.cs index 2c78ec72..029089a5 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedMedsController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedMedsController.cs @@ -5,33 +5,33 @@ namespace Fika.Core.Coop.ObservedClasses { - internal class CoopObservedMedsController : EFT.Player.MedsController - { - public CoopPlayer coopPlayer; + internal class CoopObservedMedsController : EFT.Player.MedsController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopObservedMedsController Create(CoopPlayer player, Item item, EBodyPart bodyPart, float amount, int animationVariant) - { - return smethod_5(player, item, bodyPart, amount, animationVariant); - } + public static CoopObservedMedsController Create(CoopPlayer player, Item item, EBodyPart bodyPart, float amount, int animationVariant) + { + return smethod_5(player, item, bodyPart, amount, animationVariant); + } - public override bool CanChangeCompassState(bool newState) - { - return false; - } + public override bool CanChangeCompassState(bool newState) + { + return false; + } - public override void OnCanUsePropChanged(bool canUse) - { - // Do nothing - } + public override void OnCanUsePropChanged(bool canUse) + { + // Do nothing + } - public override void SetCompassState(bool active) - { - // Do nothing - } - } + public override void SetCompassState(bool active) + { + // Do nothing + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedQuickGrenadeController.cs b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedQuickGrenadeController.cs index 61b629c8..c614de3d 100644 --- a/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedQuickGrenadeController.cs +++ b/Fika.Core/Coop/ObservedClasses/HandsControllers/CoopObservedQuickGrenadeController.cs @@ -6,67 +6,67 @@ namespace Fika.Core.Coop.ObservedClasses { - /// - /// This is only used by AI - /// - internal class CoopObservedQuickGrenadeController : EFT.Player.QuickGrenadeThrowController - { - public CoopPlayer coopPlayer; + /// + /// This is only used by AI + /// + internal class CoopObservedQuickGrenadeController : EFT.Player.QuickGrenadeThrowController + { + public CoopPlayer coopPlayer; - private void Awake() - { - coopPlayer = GetComponent(); - } + private void Awake() + { + coopPlayer = GetComponent(); + } - public static CoopObservedQuickGrenadeController Create(CoopPlayer player, GrenadeClass item) - { - return smethod_8(player, item); - } + public static CoopObservedQuickGrenadeController Create(CoopPlayer player, GrenadeClass item) + { + return smethod_8(player, item); + } - public override bool CanChangeCompassState(bool newState) - { - return false; - } + public override bool CanChangeCompassState(bool newState) + { + return false; + } - public override bool CanRemove() - { - return true; - } + public override bool CanRemove() + { + return true; + } - public override void OnCanUsePropChanged(bool canUse) - { - // Do nothing - } + public override void OnCanUsePropChanged(bool canUse) + { + // Do nothing + } - public override void SetCompassState(bool active) - { - // Do nothing - } + public override void SetCompassState(bool active) + { + // Do nothing + } - /// - /// Original method to spawn a grenade, we use instead - /// - /// - /// - /// - /// - /// - public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - // Do nothing - } + /// + /// Original method to spawn a grenade, we use instead + /// + /// + /// + /// + /// + /// + public override void vmethod_2(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + // Do nothing + } - /// - /// Spawns a grenade, uses data from - /// - /// The time since the safety was removed, use 0f - /// The position to start from - /// The rotation of the grenade - /// The force of the grenade - /// If it's a low throw or not - public void SpawnGrenade(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) - { - base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); - } - } + /// + /// Spawns a grenade, uses data from + /// + /// The time since the safety was removed, use 0f + /// The position to start from + /// The rotation of the grenade + /// The force of the grenade + /// If it's a low throw or not + public void SpawnGrenade(float timeSinceSafetyLevelRemoved, Vector3 position, Quaternion rotation, Vector3 force, bool lowThrow) + { + base.vmethod_2(timeSinceSafetyLevelRemoved, position, rotation, force, lowThrow); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs index e90f9912..ecacbff1 100644 --- a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs +++ b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedRunState.cs @@ -4,24 +4,24 @@ namespace Fika.Core.Coop.ObservedClasses.MovementStates { - internal class ObservedRunState : GClass1717 - { - public ObservedRunState(MovementContext movementContext) : base(movementContext) - { - MovementContext = (ObservedMovementContext)movementContext; - } + internal class ObservedRunState : GClass1717 + { + public ObservedRunState(MovementContext movementContext) : base(movementContext) + { + MovementContext = (ObservedMovementContext)movementContext; + } - public override void UpdatePosition(float deltaTime) - { - if (!MovementContext.IsGrounded) - { - MovementContext.PlayerAnimatorEnableFallingDown(true); - } - } + public override void UpdatePosition(float deltaTime) + { + if (!MovementContext.IsGrounded) + { + MovementContext.PlayerAnimatorEnableFallingDown(true); + } + } - public override void EnableSprint(bool enabled, bool isToggle = false) - { - MovementContext.EnableSprint(enabled); - } - } + public override void EnableSprint(bool enabled, bool isToggle = false) + { + MovementContext.EnableSprint(enabled); + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs index b914a2b8..58a15565 100644 --- a/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs +++ b/Fika.Core/Coop/ObservedClasses/MovementStates/ObservedSprintState.cs @@ -5,51 +5,51 @@ namespace Fika.Core.Coop.ObservedClasses.MovementStates { - internal class ObservedSprintState : GClass1719 - { - public ObservedSprintState(MovementContext movementContext) : base(movementContext) - { - MovementContext = (ObservedMovementContext)movementContext; - } + internal class ObservedSprintState : GClass1719 + { + public ObservedSprintState(MovementContext movementContext) : base(movementContext) + { + MovementContext = (ObservedMovementContext)movementContext; + } - public override void UpdatePosition(float deltaTime) - { - if (!MovementContext.IsGrounded) - { - MovementContext.PlayerAnimatorEnableFallingDown(true); - } - } + public override void UpdatePosition(float deltaTime) + { + if (!MovementContext.IsGrounded) + { + MovementContext.PlayerAnimatorEnableFallingDown(true); + } + } - public override void EnableSprint(bool enabled, bool isToggle = false) - { - MovementContext.EnableSprint(enabled); - } + public override void EnableSprint(bool enabled, bool isToggle = false) + { + MovementContext.EnableSprint(enabled); + } - public override void ChangePose(float poseDelta) - { - MovementContext.SetPoseLevel(MovementContext.PoseLevel + poseDelta, false); - } + public override void ChangePose(float poseDelta) + { + MovementContext.SetPoseLevel(MovementContext.PoseLevel + poseDelta, false); + } - public override void ManualAnimatorMoveUpdate(float deltaTime) - { - if (MovementContext.IsSprintEnabled) - { - /*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(GClass1669.ConvertToMovementDirection(Direction)); - Direction = Vector2.zero; - MovementContext.SprintAcceleration(deltaTime); - UpdateRotationAndPosition(deltaTime); - } - else - { - MovementContext.PlayerAnimatorEnableSprint(false, false); - } - if (!MovementContext.PlayerAnimator.Animator.IsInTransition(0)) - { - MovementContext.ObstacleCollisionFacade.RecalculateCollision(velocityThreshold); - } - } - } + public override void ManualAnimatorMoveUpdate(float deltaTime) + { + if (MovementContext.IsSprintEnabled) + { + /*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(GClass1669.ConvertToMovementDirection(Direction)); + Direction = Vector2.zero; + MovementContext.SprintAcceleration(deltaTime); + UpdateRotationAndPosition(deltaTime); + } + else + { + MovementContext.PlayerAnimatorEnableSprint(false, false); + } + if (!MovementContext.PlayerAnimator.Animator.IsInTransition(0)) + { + MovementContext.ObstacleCollisionFacade.RecalculateCollision(velocityThreshold); + } + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs index b07bf4d0..9b473c68 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedHealthController.cs @@ -7,87 +7,87 @@ namespace Fika.Core.Coop.ObservedClasses { - 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) - { - return false; - } + 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) + { + return false; + } - public override void CancelApplyingItem() - { - // Do nothing - } + public override void CancelApplyingItem() + { + // Do nothing + } - public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass health = null) - { - Profile.ProfileHealthClass profileHealthClass; - if ((profileHealthClass = health) == null) - { - Profile.ProfileHealthClass profileHealthClass2 = new() - { - BodyParts = GClass769.GetDictWith(), - Energy = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_0.Current, - Minimum = healthValue_0.Minimum, - Maximum = healthValue_0.Maximum - }, - Hydration = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_1.Current, - Minimum = healthValue_1.Minimum, - Maximum = healthValue_1.Maximum - }, - Temperature = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_2.Current, - Minimum = healthValue_2.Minimum, - Maximum = healthValue_2.Maximum - } - }; - profileHealthClass = profileHealthClass2; - profileHealthClass2.Poison = new Profile.ProfileHealthClass.ValueInfo - { - Current = healthValue_3.Current, - Minimum = healthValue_3.Minimum, - Maximum = healthValue_3.Maximum - }; - } - health = profileHealthClass; - foreach (KeyValuePair keyValuePair in Dictionary_0) - { - keyValuePair.Deconstruct(out EBodyPart ebodyPart, out BodyPartState bodyPartState); - EBodyPart ebodyPart2 = ebodyPart; - BodyPartState bodyPartState2 = bodyPartState; - if (!health.BodyParts.TryGetValue(ebodyPart2, out Profile.ProfileHealthClass.GClass1770 gclass)) - { - gclass = new Profile.ProfileHealthClass.GClass1770(); - health.BodyParts.Add(ebodyPart2, gclass); - } - gclass.Health = new Profile.ProfileHealthClass.ValueInfo - { - Current = bodyPartState2.Health.Current, - Maximum = bodyPartState2.Health.Maximum - }; - gclass.Effects ??= []; - } + public override Profile.ProfileHealthClass Store(Profile.ProfileHealthClass health = null) + { + Profile.ProfileHealthClass profileHealthClass; + if ((profileHealthClass = health) == null) + { + Profile.ProfileHealthClass profileHealthClass2 = new() + { + BodyParts = GClass769.GetDictWith(), + Energy = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_0.Current, + Minimum = healthValue_0.Minimum, + Maximum = healthValue_0.Maximum + }, + Hydration = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_1.Current, + Minimum = healthValue_1.Minimum, + Maximum = healthValue_1.Maximum + }, + Temperature = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_2.Current, + Minimum = healthValue_2.Minimum, + Maximum = healthValue_2.Maximum + } + }; + profileHealthClass = profileHealthClass2; + profileHealthClass2.Poison = new Profile.ProfileHealthClass.ValueInfo + { + Current = healthValue_3.Current, + Minimum = healthValue_3.Minimum, + Maximum = healthValue_3.Maximum + }; + } + health = profileHealthClass; + foreach (KeyValuePair keyValuePair in Dictionary_0) + { + keyValuePair.Deconstruct(out EBodyPart ebodyPart, out BodyPartState bodyPartState); + EBodyPart ebodyPart2 = ebodyPart; + BodyPartState bodyPartState2 = bodyPartState; + if (!health.BodyParts.TryGetValue(ebodyPart2, out Profile.ProfileHealthClass.GClass1770 gclass)) + { + gclass = new Profile.ProfileHealthClass.GClass1770(); + health.BodyParts.Add(ebodyPart2, gclass); + } + gclass.Health = new Profile.ProfileHealthClass.ValueInfo + { + Current = bodyPartState2.Health.Current, + Maximum = bodyPartState2.Health.Maximum + }; + gclass.Effects ??= []; + } - foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) - { - if (gclass is GInterface251 && gclass.State != EEffectState.Residued) // We only resync effects that are in-game effects, check for GClass increments - { - Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; - gclass2.Effects ??= []; - gclass2.Effects.Add(gclass.GetType().Name, new() - { - Time = gclass.TimeLeft, - ExtraData = gclass.StoreObj - }); - } - } + foreach (NetworkBodyEffectsAbstractClass gclass in IReadOnlyList_0) + { + if (gclass is GInterface251 && gclass.State != EEffectState.Residued) // We only resync effects that are in-game effects, check for GClass increments + { + Profile.ProfileHealthClass.GClass1770 gclass2 = health.BodyParts[gclass.BodyPart]; + gclass2.Effects ??= []; + gclass2.Effects.Add(gclass.GetType().Name, new() + { + Time = gclass.TimeLeft, + ExtraData = gclass.StoreObj + }); + } + } - return health; - } - } + return health; + } + } } diff --git a/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs b/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs index a0c3852f..13e66a97 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedInventoryController.cs @@ -4,37 +4,37 @@ namespace Fika.Core.Coop.ObservedClasses { - public class ObservedInventoryController(Player player, Profile profile, bool examined) : Player.PlayerInventoryController(player, profile, examined) - { + public class ObservedInventoryController(Player player, Profile profile, bool examined) : Player.PlayerInventoryController(player, profile, examined) + { - public override bool HasDiscardLimits => false; + public override bool HasDiscardLimits => false; - public override void StrictCheckMagazine(MagazineClass magazine, bool status, int skill = 0, bool notify = false, bool useOperation = true) - { - // Do nothing - } + public override void StrictCheckMagazine(MagazineClass magazine, bool status, int skill = 0, bool notify = false, bool useOperation = true) + { + // Do nothing + } - public override void OnAmmoLoadedCall(int count) - { - // Do nothing - } + public override void OnAmmoLoadedCall(int count) + { + // Do nothing + } - public override void OnAmmoUnloadedCall(int count) - { - // Do nothing - } + public override void OnAmmoUnloadedCall(int count) + { + // Do nothing + } - public override void OnMagazineCheckCall() - { - // Do nothing - } + public override void OnMagazineCheckCall() + { + // Do nothing + } - public override bool IsInventoryBlocked() - { - return false; - } + public override bool IsInventoryBlocked() + { + return false; + } - /*public override void InProcess(TraderControllerClass executor, Item item, ItemAddress to, bool succeed, GInterface338 operation, Callback callback) + /*public override void InProcess(TraderControllerClass executor, Item item, ItemAddress to, bool succeed, GInterface338 operation, Callback callback) { if (!succeed) { @@ -48,5 +48,5 @@ public override bool IsInventoryBlocked() } callback.Succeed(); }*/ - } + } } diff --git a/Fika.Core/Coop/ObservedClasses/ObservedMovementContext.cs b/Fika.Core/Coop/ObservedClasses/ObservedMovementContext.cs index fd372d91..38d9d023 100644 --- a/Fika.Core/Coop/ObservedClasses/ObservedMovementContext.cs +++ b/Fika.Core/Coop/ObservedClasses/ObservedMovementContext.cs @@ -8,90 +8,90 @@ namespace Fika.Core.Coop.ObservedClasses { - public class ObservedMovementContext : MovementContext - { - public override bool CanJump => true; - public override bool CanMoveInProne => true; - public override bool CanProne => true; - public override bool CanSprint => true; - public override bool CanWalk => true; - public override Error CanInteract => null; - public override bool StateLocksInventory { set { } } - - public override void ApplyApproachMotion(Vector3 motion, float deltaTime) - { - base.DirectApplyMotion(motion, deltaTime); - } - - public override void Flash(ref Vector3 motion) - { - // Do nothing - } - - public override void LimitMotionXZ(ref Vector3 motion, float deltaTime, float threshold = 0.0001F) - { - InputMotionBeforeLimit = motion / deltaTime; - } - - public override void LimitProneMotion(ref Vector3 motion) - { - // Do nothing - } - - public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) - { - // Do nothing - } - - public override bool CanRoll(int direction) - { - return true; - } - - public override bool CanStandAt(float h) - { - return true; - } - - public override bool HasGround(float depth, Vector3? axis = null, float extraCastLn = 0) - { - return true; - } - - public override bool HeadBump(float velocity) - { - return false; - } - - public override void OnControllerColliderHit(ControllerColliderHit hit) - { - // Do nothing - } - - public override bool OverlapOrHasNoGround(float depth, Vector3? axis = null, float width = 0, float heightDivider = 4, float extraCastLn = 0) - { - return false; - } - - public override void ProjectMotionToSurface(ref Vector3 motion) - { - // Do nothing - } - - public override bool IsAbleToRotate(Vector3 motion, float deltaYaw, Quaternion predictionRotation, Transform pivot, out ECantRotate cause) - { - cause = ECantRotate.NotGround; - return true; - } - - public override void SetCharacterMovementSpeed(float characterMovementSpeed, bool force = false) - { - CharacterMovementSpeed = characterMovementSpeed; - SmoothedCharacterMovementSpeed = characterMovementSpeed; - UpdateCovertEfficiency(characterMovementSpeed, false); - } - - /*public override void ManualUpdate(float deltaTime) + public class ObservedMovementContext : MovementContext + { + public override bool CanJump => true; + public override bool CanMoveInProne => true; + public override bool CanProne => true; + public override bool CanSprint => true; + public override bool CanWalk => true; + public override Error CanInteract => null; + public override bool StateLocksInventory { set { } } + + public override void ApplyApproachMotion(Vector3 motion, float deltaTime) + { + base.DirectApplyMotion(motion, deltaTime); + } + + public override void Flash(ref Vector3 motion) + { + // Do nothing + } + + public override void LimitMotionXZ(ref Vector3 motion, float deltaTime, float threshold = 0.0001F) + { + InputMotionBeforeLimit = motion / deltaTime; + } + + public override void LimitProneMotion(ref Vector3 motion) + { + // Do nothing + } + + public override void ApplyGravity(ref Vector3 motion, float deltaTime, bool stickToGround) + { + // Do nothing + } + + public override bool CanRoll(int direction) + { + return true; + } + + public override bool CanStandAt(float h) + { + return true; + } + + public override bool HasGround(float depth, Vector3? axis = null, float extraCastLn = 0) + { + return true; + } + + public override bool HeadBump(float velocity) + { + return false; + } + + public override void OnControllerColliderHit(ControllerColliderHit hit) + { + // Do nothing + } + + public override bool OverlapOrHasNoGround(float depth, Vector3? axis = null, float width = 0, float heightDivider = 4, float extraCastLn = 0) + { + return false; + } + + public override void ProjectMotionToSurface(ref Vector3 motion) + { + // Do nothing + } + + public override bool IsAbleToRotate(Vector3 motion, float deltaYaw, Quaternion predictionRotation, Transform pivot, out ECantRotate cause) + { + cause = ECantRotate.NotGround; + return true; + } + + public override void SetCharacterMovementSpeed(float characterMovementSpeed, bool force = false) + { + CharacterMovementSpeed = characterMovementSpeed; + SmoothedCharacterMovementSpeed = characterMovementSpeed; + UpdateCovertEfficiency(characterMovementSpeed, false); + } + + /*public override void ManualUpdate(float deltaTime) { if (!_player.HealthController.IsAlive) { @@ -111,61 +111,61 @@ public override void SetCharacterMovementSpeed(float characterMovementSpeed, boo } }*/ - public override void SmoothPitchLimitations(float deltaTime) - { - // Do nothing - } - - public override void ProcessSpeedLimits(float deltaTime) - { - // Do nothing - } - - public override void UpdateGroundCollision(float deltaTime) - { - if (!_player.IsVisible) - { - return; - } - float num = 1f; - if (IsGrounded) - { - num = 0f; - FreefallTime = deltaTime; - } - else - { - FreefallTime += deltaTime; - } - PlayerAnimatorSetFallingDownFloat(num); - PlayerAnimator.SetIsGrounded(IsGrounded); - CheckFlying(deltaTime); - } - - public override void WeightRelatedValuesUpdated() - { - PlayerAnimatorTransitionSpeed = TransitionSpeed; - UpdateCovertEfficiency(ClampedSpeed, true); - _player.UpdateStepSoundRolloff(); - TiltInertia = EFTHardSettings.Instance.InertiaTiltCurve.Evaluate(_player.Physical.Inertia); - WalkInertia = InertiaSettings.WalkInertia.Evaluate(_player.Physical.Inertia); - SprintBrakeInertia = InertiaSettings.SprintBrakeInertia.Evaluate(_player.Physical.Inertia); - } - - public override BaseMovementState GetNewState(EPlayerState name, bool isAI = false) - { - if (name == EPlayerState.Run) - return new ObservedRunState(this); - if (name == EPlayerState.Sprint) - return new ObservedSprintState(this); - else - return base.GetNewState(name, isAI); - } - - public new static ObservedMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) - { - ObservedMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); - return movementContext; - } - } + public override void SmoothPitchLimitations(float deltaTime) + { + // Do nothing + } + + public override void ProcessSpeedLimits(float deltaTime) + { + // Do nothing + } + + public override void UpdateGroundCollision(float deltaTime) + { + if (!_player.IsVisible) + { + return; + } + float num = 1f; + if (IsGrounded) + { + num = 0f; + FreefallTime = deltaTime; + } + else + { + FreefallTime += deltaTime; + } + PlayerAnimatorSetFallingDownFloat(num); + PlayerAnimator.SetIsGrounded(IsGrounded); + CheckFlying(deltaTime); + } + + public override void WeightRelatedValuesUpdated() + { + PlayerAnimatorTransitionSpeed = TransitionSpeed; + UpdateCovertEfficiency(ClampedSpeed, true); + _player.UpdateStepSoundRolloff(); + TiltInertia = EFTHardSettings.Instance.InertiaTiltCurve.Evaluate(_player.Physical.Inertia); + WalkInertia = InertiaSettings.WalkInertia.Evaluate(_player.Physical.Inertia); + SprintBrakeInertia = InertiaSettings.SprintBrakeInertia.Evaluate(_player.Physical.Inertia); + } + + public override BaseMovementState GetNewState(EPlayerState name, bool isAI = false) + { + if (name == EPlayerState.Run) + return new ObservedRunState(this); + if (name == EPlayerState.Sprint) + return new ObservedSprintState(this); + else + return base.GetNewState(name, isAI); + } + + public new static ObservedMovementContext Create(Player player, Func animatorGetter, Func characterControllerGetter, LayerMask groundMask) + { + ObservedMovementContext movementContext = Create(player, animatorGetter, characterControllerGetter, groundMask); + return movementContext; + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs index 702236c7..371f78b8 100644 --- a/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/BotPacketSender.cs @@ -10,165 +10,165 @@ namespace Fika.Core.Coop.PacketHandlers { - public class BotPacketSender : MonoBehaviour, IPacketSender - { - private CoopPlayer player; - - public bool Enabled { get; set; } = true; - public FikaServer Server { get; set; } = Singleton.Instance; - public FikaClient Client { get; set; } - 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); - - protected void Awake() - { - player = GetComponent(); - } - - public void Init() - { - - } - - public void SendPacket(ref T packet) where T : INetSerializable - { - - } - - protected void FixedUpdate() - { - if (player == null || Writer == null) - { - return; - } - - if (player.AIData?.BotOwner == null) - { - return; - } - - BotMover mover = player.AIData.BotOwner.Mover; - if (mover == null) - { - 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); - - Writer.Reset(); - Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); - - if (!mover.IsMoving || mover.Pause) - { - player.LastDirection = Vector2.zero; - } - } - - protected void Update() - { - int firearmPackets = FirearmPackets.Count; - if (firearmPackets > 0) - { - for (int i = 0; i < firearmPackets; i++) - { - WeaponPacket firearmPacket = FirearmPackets.Dequeue(); - firearmPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); - } - } - 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 < armorDamagePackets; i++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - int inventoryPackets = InventoryPackets.Count; - if (inventoryPackets > 0) - { - for (int i = 0; i < inventoryPackets; i++) - { - InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); - inventoryPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); - } - } - int commonPlayerPackets = CommonPlayerPackets.Count; - if (commonPlayerPackets > 0) - { - for (int i = 0; i < commonPlayerPackets; i++) - { - CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); - commonPlayerPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); - } - } - int healthSyncPackets = HealthSyncPackets.Count; - if (healthSyncPackets > 0) - { - for (int i = 0; i < healthSyncPackets; i++) - { - HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); - healthSyncPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); - } - } - } - - public void DestroyThis() - { - Writer = null; - FirearmPackets.Clear(); - DamagePackets.Clear(); - InventoryPackets.Clear(); - CommonPlayerPackets.Clear(); - HealthSyncPackets.Clear(); - if (Server != null) - { - Server = null; - } - if (Client != null) - { - Client = null; - } - Destroy(this); - } - } + public class BotPacketSender : MonoBehaviour, IPacketSender + { + private CoopPlayer player; + + public bool Enabled { get; set; } = true; + public FikaServer Server { get; set; } = Singleton.Instance; + public FikaClient Client { get; set; } + 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); + + protected void Awake() + { + player = GetComponent(); + } + + public void Init() + { + + } + + public void SendPacket(ref T packet) where T : INetSerializable + { + + } + + protected void FixedUpdate() + { + if (player == null || Writer == null) + { + return; + } + + if (player.AIData?.BotOwner == null) + { + return; + } + + BotMover mover = player.AIData.BotOwner.Mover; + if (mover == null) + { + 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); + + Writer.Reset(); + Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); + + if (!mover.IsMoving || mover.Pause) + { + player.LastDirection = Vector2.zero; + } + } + + protected void Update() + { + int firearmPackets = FirearmPackets.Count; + if (firearmPackets > 0) + { + for (int i = 0; i < firearmPackets; i++) + { + WeaponPacket firearmPacket = FirearmPackets.Dequeue(); + firearmPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); + } + } + 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 < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + int inventoryPackets = InventoryPackets.Count; + if (inventoryPackets > 0) + { + for (int i = 0; i < inventoryPackets; i++) + { + InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); + inventoryPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); + } + } + int commonPlayerPackets = CommonPlayerPackets.Count; + if (commonPlayerPackets > 0) + { + for (int i = 0; i < commonPlayerPackets; i++) + { + CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); + commonPlayerPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); + } + } + int healthSyncPackets = HealthSyncPackets.Count; + if (healthSyncPackets > 0) + { + for (int i = 0; i < healthSyncPackets; i++) + { + HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); + healthSyncPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); + } + } + } + + public void DestroyThis() + { + Writer = null; + FirearmPackets.Clear(); + DamagePackets.Clear(); + InventoryPackets.Clear(); + CommonPlayerPackets.Clear(); + HealthSyncPackets.Clear(); + if (Server != null) + { + Server = null; + } + if (Client != null) + { + Client = null; + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs index 73d7c40e..b59826fa 100644 --- a/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ClientPacketSender.cs @@ -21,346 +21,346 @@ namespace Fika.Core.Coop.PacketHandlers { - public class ClientPacketSender : MonoBehaviour, IPacketSender - { - private CoopPlayer player; - - public bool Enabled { get; set; } = true; - public FikaServer Server { get; set; } - public FikaClient Client { get; set; } - 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); - private DateTime lastPingTime; - - protected void Awake() - { - player = GetComponent(); - Client = Singleton.Instance; - enabled = false; - lastPingTime = DateTime.Now; - } - - public void Init() - { - enabled = true; - if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.LateInit(); - } - StartCoroutine(SyncWorld()); - StartCoroutine(SyncWeather()); - } - - public void SendPacket(ref T packet) where T : INetSerializable - { - Writer.Reset(); - Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void FixedUpdate() - { - if (player == null || Writer == null || Client == null || !Enabled) - { - 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); - - Writer.Reset(); - Client.SendData(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); - - if (player.MovementIdlingTime > 0.05f) - { - player.LastDirection = Vector2.zero; - } - } - - protected void Update() - { - int firearmPackets = FirearmPackets.Count; - if (firearmPackets > 0) - { - for (int i = 0; i < firearmPackets; i++) - { - WeaponPacket firearmPacket = FirearmPackets.Dequeue(); - firearmPacket.NetId = player.NetId; - - Writer.Reset(); - Client.SendData(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); - } - } - 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 < armorDamagePackets; i++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; - - Writer.Reset(); - Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - int inventoryPackets = InventoryPackets.Count; - if (inventoryPackets > 0) - { - for (int i = 0; i < inventoryPackets; i++) - { - InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); - inventoryPacket.NetId = player.NetId; - - Writer.Reset(); - Client.SendData(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); - } - } - int commonPlayerPackets = CommonPlayerPackets.Count; - if (commonPlayerPackets > 0) - { - for (int i = 0; i < commonPlayerPackets; i++) - { - CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); - commonPlayerPacket.NetId = player.NetId; - - Writer.Reset(); - Client.SendData(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); - } - } - int healthSyncPackets = HealthSyncPackets.Count; - if (healthSyncPackets > 0) - { - for (int i = 0; i < healthSyncPackets; i++) - { - HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); - healthSyncPacket.NetId = player.NetId; - - Writer.Reset(); - Client.SendData(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); - } - } - if (FikaPlugin.UsePingSystem.Value - && player.IsYourPlayer - && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) - && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) - { - if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) - { - return; - } - SendPing(); - } - } - - private void SendPing() - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame.Status != GameStatus.Started) - { - return; - } - - if (lastPingTime < DateTime.Now.AddSeconds(-3)) - { - Transform origin; - FreeCameraController freeCamController = Singleton.Instance; - if (freeCamController != null && freeCamController.IsScriptActive) - { - origin = freeCamController.CameraMain.gameObject.transform; - } - else if (player.HealthController.IsAlive) - { - origin = player.CameraPosition; - } - else - { - return; - } - - Ray sourceRaycast = new(origin.position + origin.forward / 2f, - origin.forward); - int layer = LayerMask.GetMask(["HighPolyCollider", "Interactive", "Deadbody", "Player", "Loot", "Terrain"]); - if (Physics.Raycast(sourceRaycast, out RaycastHit hit, 500f, layer)) - { - lastPingTime = DateTime.Now; - //GameObject gameObject = new("Ping", typeof(FikaPing)); - //gameObject.transform.localPosition = hit.point; - Singleton.Instance.PlayUISound(PingFactory.GetPingSound()); - GameObject hitGameObject = hit.collider.gameObject; - int hitLayer = hitGameObject.layer; - - PingFactory.EPingType pingType = PingFactory.EPingType.Point; - object userData = null; - string localeId = null; + public class ClientPacketSender : MonoBehaviour, IPacketSender + { + private CoopPlayer player; + + public bool Enabled { get; set; } = true; + public FikaServer Server { get; set; } + public FikaClient Client { get; set; } + 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); + private DateTime lastPingTime; + + protected void Awake() + { + player = GetComponent(); + Client = Singleton.Instance; + enabled = false; + lastPingTime = DateTime.Now; + } + + public void Init() + { + enabled = true; + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.LateInit(); + } + StartCoroutine(SyncWorld()); + StartCoroutine(SyncWeather()); + } + + public void SendPacket(ref T packet) where T : INetSerializable + { + Writer.Reset(); + Client.SendData(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void FixedUpdate() + { + if (player == null || Writer == null || Client == null || !Enabled) + { + 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); + + Writer.Reset(); + Client.SendData(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); + + if (player.MovementIdlingTime > 0.05f) + { + player.LastDirection = Vector2.zero; + } + } + + protected void Update() + { + int firearmPackets = FirearmPackets.Count; + if (firearmPackets > 0) + { + for (int i = 0; i < firearmPackets; i++) + { + WeaponPacket firearmPacket = FirearmPackets.Dequeue(); + firearmPacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); + } + } + 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 < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + int inventoryPackets = InventoryPackets.Count; + if (inventoryPackets > 0) + { + for (int i = 0; i < inventoryPackets; i++) + { + InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); + inventoryPacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); + } + } + int commonPlayerPackets = CommonPlayerPackets.Count; + if (commonPlayerPackets > 0) + { + for (int i = 0; i < commonPlayerPackets; i++) + { + CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); + commonPlayerPacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); + } + } + int healthSyncPackets = HealthSyncPackets.Count; + if (healthSyncPackets > 0) + { + for (int i = 0; i < healthSyncPackets; i++) + { + HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); + healthSyncPacket.NetId = player.NetId; + + Writer.Reset(); + Client.SendData(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); + } + } + if (FikaPlugin.UsePingSystem.Value + && player.IsYourPlayer + && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) + && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) + { + if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) + { + return; + } + SendPing(); + } + } + + private void SendPing() + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame.Status != GameStatus.Started) + { + return; + } + + if (lastPingTime < DateTime.Now.AddSeconds(-3)) + { + Transform origin; + FreeCameraController freeCamController = Singleton.Instance; + if (freeCamController != null && freeCamController.IsScriptActive) + { + origin = freeCamController.CameraMain.gameObject.transform; + } + else if (player.HealthController.IsAlive) + { + origin = player.CameraPosition; + } + else + { + return; + } + + Ray sourceRaycast = new(origin.position + origin.forward / 2f, + origin.forward); + int layer = LayerMask.GetMask(["HighPolyCollider", "Interactive", "Deadbody", "Player", "Loot", "Terrain"]); + if (Physics.Raycast(sourceRaycast, out RaycastHit hit, 500f, layer)) + { + lastPingTime = DateTime.Now; + //GameObject gameObject = new("Ping", typeof(FikaPing)); + //gameObject.transform.localPosition = hit.point; + Singleton.Instance.PlayUISound(PingFactory.GetPingSound()); + GameObject hitGameObject = hit.collider.gameObject; + int hitLayer = hitGameObject.layer; + + PingFactory.EPingType pingType = PingFactory.EPingType.Point; + object userData = null; + string localeId = null; #if DEBUG - ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); + ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); #endif - if (LayerMask.LayerToName(hitLayer) == "Player") - { - if (hitGameObject.TryGetComponent(out Player player)) - { - pingType = PingFactory.EPingType.Player; - userData = player; - } - } - else if (LayerMask.LayerToName(hitLayer) == "Deadbody") - { - pingType = PingFactory.EPingType.DeadBody; - userData = hitGameObject; - } - else if (hitGameObject.TryGetComponent(out LootableContainer container)) - { - pingType = PingFactory.EPingType.LootContainer; - userData = container; - localeId = container.ItemOwner.Name; - } - else if (hitGameObject.TryGetComponent(out LootItem lootItem)) - { - pingType = PingFactory.EPingType.LootItem; - userData = lootItem; - localeId = lootItem.Item.ShortName; - } - else if (hitGameObject.TryGetComponent(out Door door)) - { - pingType = PingFactory.EPingType.Door; - userData = door; - } - else if (hitGameObject.TryGetComponent(out InteractableObject interactable)) - { - pingType = PingFactory.EPingType.Interactable; - userData = interactable; - } - - GameObject basePingPrefab = PingFactory.AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); - GameObject basePing = GameObject.Instantiate(basePingPrefab); - Vector3 hitPoint = hit.point; - PingFactory.AbstractPing abstractPing = PingFactory.FromPingType(pingType, basePing); - Color pingColor = FikaPlugin.PingColor.Value; - pingColor = new(pingColor.r, pingColor.g, pingColor.b, 1); - // ref so that we can mutate it if we want to, ex: if I ping a switch I want it at the switch.gameObject.position + Vector3.up - abstractPing.Initialize(ref hitPoint, userData, pingColor); - - GenericPacket genericPacket = new() - { - NetId = player.NetId, - PacketType = EPackageType.Ping, - PingLocation = hitPoint, - PingType = pingType, - PingColor = pingColor, - Nickname = player.Profile.Nickname, - LocaleId = string.IsNullOrEmpty(localeId) ? string.Empty : localeId - }; - - SendPacket(ref genericPacket); - - if (FikaPlugin.PlayPingAnimation.Value) - { - player.vmethod_3(EGesture.ThatDirection); - } - } - } - } - - private IEnumerator SyncWorld() - { - while (Client.NetClient.FirstPeer == null) - { - yield return null; - } - - CoopGame coopGame = (CoopGame)Singleton.Instance; - - if (coopGame == null) - { - yield break; - } - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - yield return new WaitForSeconds(10f); - - Writer.Reset(); - GameTimerPacket gameTimerPacket = new(true); - Client.SendData(Writer, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); - - Writer.Reset(); - ExfiltrationPacket exfilPacket = new(true); - Client.SendData(Writer, ref exfilPacket, DeliveryMethod.ReliableOrdered); - } - - private IEnumerator SyncWeather() - { - if (WeatherController.Instance == null) - { - yield break; - } - - CoopGame coopGame = (CoopGame)Singleton.Instance; - - if (coopGame == null) - { - yield break; - } - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - WeatherPacket packet = new() - { - IsRequest = true, - HasData = false - }; - - Writer.Reset(); - Client.SendData(Writer, ref packet, DeliveryMethod.ReliableOrdered); - } - - public void DestroyThis() - { - Writer = null; - FirearmPackets.Clear(); - DamagePackets.Clear(); - InventoryPackets.Clear(); - CommonPlayerPackets.Clear(); - HealthSyncPackets.Clear(); - if (Server != null) - { - Server = null; - } - if (Client != null) - { - Client = null; - } - Destroy(this); - } - } + if (LayerMask.LayerToName(hitLayer) == "Player") + { + if (hitGameObject.TryGetComponent(out Player player)) + { + pingType = PingFactory.EPingType.Player; + userData = player; + } + } + else if (LayerMask.LayerToName(hitLayer) == "Deadbody") + { + pingType = PingFactory.EPingType.DeadBody; + userData = hitGameObject; + } + else if (hitGameObject.TryGetComponent(out LootableContainer container)) + { + pingType = PingFactory.EPingType.LootContainer; + userData = container; + localeId = container.ItemOwner.Name; + } + else if (hitGameObject.TryGetComponent(out LootItem lootItem)) + { + pingType = PingFactory.EPingType.LootItem; + userData = lootItem; + localeId = lootItem.Item.ShortName; + } + else if (hitGameObject.TryGetComponent(out Door door)) + { + pingType = PingFactory.EPingType.Door; + userData = door; + } + else if (hitGameObject.TryGetComponent(out InteractableObject interactable)) + { + pingType = PingFactory.EPingType.Interactable; + userData = interactable; + } + + GameObject basePingPrefab = PingFactory.AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); + GameObject basePing = GameObject.Instantiate(basePingPrefab); + Vector3 hitPoint = hit.point; + PingFactory.AbstractPing abstractPing = PingFactory.FromPingType(pingType, basePing); + Color pingColor = FikaPlugin.PingColor.Value; + pingColor = new(pingColor.r, pingColor.g, pingColor.b, 1); + // ref so that we can mutate it if we want to, ex: if I ping a switch I want it at the switch.gameObject.position + Vector3.up + abstractPing.Initialize(ref hitPoint, userData, pingColor); + + GenericPacket genericPacket = new() + { + NetId = player.NetId, + PacketType = EPackageType.Ping, + PingLocation = hitPoint, + PingType = pingType, + PingColor = pingColor, + Nickname = player.Profile.Nickname, + LocaleId = string.IsNullOrEmpty(localeId) ? string.Empty : localeId + }; + + SendPacket(ref genericPacket); + + if (FikaPlugin.PlayPingAnimation.Value) + { + player.vmethod_3(EGesture.ThatDirection); + } + } + } + } + + private IEnumerator SyncWorld() + { + while (Client.NetClient.FirstPeer == null) + { + yield return null; + } + + CoopGame coopGame = (CoopGame)Singleton.Instance; + + if (coopGame == null) + { + yield break; + } + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + yield return new WaitForSeconds(10f); + + Writer.Reset(); + GameTimerPacket gameTimerPacket = new(true); + Client.SendData(Writer, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); + + Writer.Reset(); + ExfiltrationPacket exfilPacket = new(true); + Client.SendData(Writer, ref exfilPacket, DeliveryMethod.ReliableOrdered); + } + + private IEnumerator SyncWeather() + { + if (WeatherController.Instance == null) + { + yield break; + } + + CoopGame coopGame = (CoopGame)Singleton.Instance; + + if (coopGame == null) + { + yield break; + } + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + WeatherPacket packet = new() + { + IsRequest = true, + HasData = false + }; + + Writer.Reset(); + Client.SendData(Writer, ref packet, DeliveryMethod.ReliableOrdered); + } + + public void DestroyThis() + { + Writer = null; + FirearmPackets.Clear(); + DamagePackets.Clear(); + InventoryPackets.Clear(); + CommonPlayerPackets.Clear(); + HealthSyncPackets.Clear(); + if (Server != null) + { + Server = null; + } + if (Client != null) + { + Client = null; + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/DedicatedPacketSender.cs b/Fika.Core/Coop/PacketHandlers/DedicatedPacketSender.cs index 7ccc67ca..60302dc4 100644 --- a/Fika.Core/Coop/PacketHandlers/DedicatedPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/DedicatedPacketSender.cs @@ -17,187 +17,187 @@ namespace Fika.Core.Coop.PacketHandlers { - public class DedicatedPacketSender : MonoBehaviour, IPacketSender - { - private CoopPlayer player; - - public bool Enabled { get; set; } = true; - public FikaServer Server { get; set; } = Singleton.Instance; - public FikaClient Client { get; set; } - 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); - - private ManualLogSource logger; - - protected void Awake() - { - logger = BepInEx.Logging.Logger.CreateLogSource("ServerPacketSender"); - player = GetComponent(); - enabled = false; - } - - public void Init() - { - enabled = true; - StartCoroutine(SendTrainTime()); - } - - public void SendPacket(ref T packet) where T : INetSerializable - { - Writer.Reset(); - Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void Update() - { - int firearmPackets = FirearmPackets.Count; - if (firearmPackets > 0) - { - for (int i = 0; i < firearmPackets; i++) - { - WeaponPacket firearmPacket = FirearmPackets.Dequeue(); - firearmPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); - } - } - 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 < armorDamagePackets; i++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - int inventoryPackets = InventoryPackets.Count; - if (inventoryPackets > 0) - { - for (int i = 0; i < inventoryPackets; i++) - { - InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); - inventoryPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); - } - } - int commonPlayerPackets = CommonPlayerPackets.Count; - if (commonPlayerPackets > 0) - { - for (int i = 0; i < commonPlayerPackets; i++) - { - CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); - commonPlayerPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); - } - } - int healthSyncPackets = HealthSyncPackets.Count; - if (healthSyncPackets > 0) - { - for (int i = 0; i < healthSyncPackets; i++) - { - HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); - healthSyncPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); - } - } - } - - private IEnumerator SendTrainTime() - { - while (!Singleton.Instantiated) - { - yield return null; - } - - while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) - { - yield return null; - } - - string location = Singleton.Instance.MainPlayer.Location; - - if (location.Contains("RezervBase") || location.Contains("Lighthouse")) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - // Trains take around 20 minutes to come in by default so we can safely wait 20 seconds to make sure everyone is loaded in - yield return new WaitForSeconds(20); - - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - long time = Traverse.Create(locomotive).Field("_depart").Value.Ticks; - - GenericPacket packet = new() - { - NetId = player.NetId, - PacketType = EPackageType.TrainSync, - DepartureTime = time - }; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError("SendTrainTime: Could not find locomotive!"); - } - } - else - { - yield break; - } - } - - public void DestroyThis() - { - Writer = null; - FirearmPackets.Clear(); - DamagePackets.Clear(); - InventoryPackets.Clear(); - CommonPlayerPackets.Clear(); - HealthSyncPackets.Clear(); - if (Server != null) - { - Server = null; - } - if (Client != null) - { - Client = null; - } - Destroy(this); - } - } + public class DedicatedPacketSender : MonoBehaviour, IPacketSender + { + private CoopPlayer player; + + public bool Enabled { get; set; } = true; + public FikaServer Server { get; set; } = Singleton.Instance; + public FikaClient Client { get; set; } + 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); + + private ManualLogSource logger; + + protected void Awake() + { + logger = BepInEx.Logging.Logger.CreateLogSource("ServerPacketSender"); + player = GetComponent(); + enabled = false; + } + + public void Init() + { + enabled = true; + StartCoroutine(SendTrainTime()); + } + + public void SendPacket(ref T packet) where T : INetSerializable + { + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void Update() + { + int firearmPackets = FirearmPackets.Count; + if (firearmPackets > 0) + { + for (int i = 0; i < firearmPackets; i++) + { + WeaponPacket firearmPacket = FirearmPackets.Dequeue(); + firearmPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); + } + } + 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 < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + int inventoryPackets = InventoryPackets.Count; + if (inventoryPackets > 0) + { + for (int i = 0; i < inventoryPackets; i++) + { + InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); + inventoryPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); + } + } + int commonPlayerPackets = CommonPlayerPackets.Count; + if (commonPlayerPackets > 0) + { + for (int i = 0; i < commonPlayerPackets; i++) + { + CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); + commonPlayerPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); + } + } + int healthSyncPackets = HealthSyncPackets.Count; + if (healthSyncPackets > 0) + { + for (int i = 0; i < healthSyncPackets; i++) + { + HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); + healthSyncPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); + } + } + } + + private IEnumerator SendTrainTime() + { + while (!Singleton.Instantiated) + { + yield return null; + } + + while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) + { + yield return null; + } + + string location = Singleton.Instance.MainPlayer.Location; + + if (location.Contains("RezervBase") || location.Contains("Lighthouse")) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + // Trains take around 20 minutes to come in by default so we can safely wait 20 seconds to make sure everyone is loaded in + yield return new WaitForSeconds(20); + + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) + { + long time = Traverse.Create(locomotive).Field("_depart").Value.Ticks; + + GenericPacket packet = new() + { + NetId = player.NetId, + PacketType = EPackageType.TrainSync, + DepartureTime = time + }; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError("SendTrainTime: Could not find locomotive!"); + } + } + else + { + yield break; + } + } + + public void DestroyThis() + { + Writer = null; + FirearmPackets.Clear(); + DamagePackets.Clear(); + InventoryPackets.Clear(); + CommonPlayerPackets.Clear(); + HealthSyncPackets.Clear(); + if (Server != null) + { + Server = null; + } + if (Client != null) + { + Client = null; + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs index 3a5b753c..4d2454b1 100644 --- a/Fika.Core/Coop/PacketHandlers/IPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/IPacketSender.cs @@ -6,21 +6,21 @@ namespace Fika.Core.Coop.PacketHandlers { - public interface IPacketSender - { - public bool Enabled { get; set; } - public FikaServer Server { get; set; } - public FikaClient Client { get; set; } - 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 interface IPacketSender + { + public bool Enabled { get; set; } + public FikaServer Server { get; set; } + public FikaClient Client { get; set; } + 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 SendPacket(ref T packet) where T : INetSerializable; - public void DestroyThis(); - } + public void Init(); + public void SendPacket(ref T packet) where T : INetSerializable; + public void DestroyThis(); + } } diff --git a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs index 478709ac..5b61f876 100644 --- a/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ObservedPacketSender.cs @@ -11,116 +11,116 @@ namespace Fika.Core.Coop.PacketHandlers { - public class ObservedPacketSender : MonoBehaviour, IPacketSender - { - private CoopPlayer player; - private bool isServer; - public bool Enabled { get; set; } = true; - public FikaServer Server { get; set; } - public FikaClient Client { get; set; } - 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); + public class ObservedPacketSender : MonoBehaviour, IPacketSender + { + private CoopPlayer player; + private bool isServer; + public bool Enabled { get; set; } = true; + public FikaServer Server { get; set; } + public FikaClient Client { get; set; } + 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); - protected void Awake() - { - player = GetComponent(); - isServer = FikaBackendUtils.IsServer; - if (isServer) - { - Server = Singleton.Instance; - } - else - { - Client = Singleton.Instance; - } - } + protected void Awake() + { + player = GetComponent(); + isServer = FikaBackendUtils.IsServer; + if (isServer) + { + Server = Singleton.Instance; + } + else + { + Client = Singleton.Instance; + } + } - public void Init() - { + public void Init() + { - } + } - public void SendPacket(ref T packet) where T : INetSerializable - { + public void SendPacket(ref T packet) where T : INetSerializable + { - } + } - protected void Update() - { - if (player == null || Writer == null) - { - return; - } + protected void Update() + { + if (player == null || Writer == null) + { + return; + } - if (DamagePackets.Count > 0) - { - if (isServer) - { - int damagePackets = DamagePackets.Count; - for (int i = 0; i < damagePackets; i++) - { - DamagePacket damagePacket = DamagePackets.Dequeue(); - damagePacket.NetId = player.NetId; + if (DamagePackets.Count > 0) + { + if (isServer) + { + 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++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; + Writer.Reset(); + Server.SendDataToAll(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + int armorDamagePackets = ArmorDamagePackets.Count; + for (int i = 0; i < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; - Writer.Reset(); - Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - else - { - 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 armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + else + { + 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++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; + Writer.Reset(); + Client.SendData(Writer, ref damagePacket, DeliveryMethod.ReliableOrdered); + } + int armorDamagePackets = ArmorDamagePackets.Count; + for (int i = 0; i < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; - Writer.Reset(); - Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - } - } + Writer.Reset(); + Client.SendData(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + } + } - public void DestroyThis() - { - Writer = null; - FirearmPackets.Clear(); - DamagePackets.Clear(); - InventoryPackets.Clear(); - CommonPlayerPackets.Clear(); - HealthSyncPackets.Clear(); - if (Server != null) - { - Server = null; - } - if (Client != null) - { - Client = null; - } - Destroy(this); - } - } + public void DestroyThis() + { + Writer = null; + FirearmPackets.Clear(); + DamagePackets.Clear(); + InventoryPackets.Clear(); + CommonPlayerPackets.Clear(); + HealthSyncPackets.Clear(); + if (Server != null) + { + Server = null; + } + if (Client != null) + { + Client = null; + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs index bd8b878a..9c2b2f68 100644 --- a/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs +++ b/Fika.Core/Coop/PacketHandlers/PacketReceiver.cs @@ -9,128 +9,128 @@ namespace Fika.Core.Coop.PacketHandlers { - public class PacketReceiver : MonoBehaviour - { - private CoopPlayer player; - private ObservedCoopPlayer observedPlayer; - public FikaServer Server { get; private set; } - public FikaClient Client { get; private set; } - public PlayerStatePacket LastState { get; set; } - 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); + public class PacketReceiver : MonoBehaviour + { + private CoopPlayer player; + private ObservedCoopPlayer observedPlayer; + public FikaServer Server { get; private set; } + public FikaClient Client { get; private set; } + public PlayerStatePacket LastState { get; set; } + 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); - protected void Awake() - { - player = GetComponent(); - if (!player.IsYourPlayer) - { - observedPlayer = GetComponent(); - } - } + protected void Awake() + { + player = GetComponent(); + if (!player.IsYourPlayer) + { + observedPlayer = GetComponent(); + } + } - protected void Start() - { - if (FikaBackendUtils.IsServer) - { - Server = Singleton.Instance; - } - else - { - Client = Singleton.Instance; - } + protected void Start() + { + if (FikaBackendUtils.IsServer) + { + Server = Singleton.Instance; + } + else + { + Client = Singleton.Instance; + } - LastState = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, player.LastDirection, - player.CurrentManagedState.Name, player.MovementContext.Tilt, 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, false, 0, Vector3.zero); + LastState = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, player.LastDirection, + player.CurrentManagedState.Name, player.MovementContext.Tilt, 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, false, 0, Vector3.zero); - NewState = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, player.LastDirection, - player.CurrentManagedState.Name, player.MovementContext.Tilt, 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, false, 0, Vector3.zero); - } + NewState = new(player.NetId, player.Position, player.Rotation, player.HeadRotation, player.LastDirection, + player.CurrentManagedState.Name, player.MovementContext.Tilt, 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, false, 0, Vector3.zero); + } - protected void Update() - { - if (observedPlayer != null) - { - LastState = observedPlayer.Interpolate(NewState, LastState); - int healthSyncPackets = HealthSyncPackets.Count; - if (healthSyncPackets > 0) - { - for (int i = 0; i < healthSyncPackets; i++) - { - HealthSyncPacket packet = HealthSyncPackets.Dequeue(); - if (packet.Packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Packet.Data.IsAlive.IsAlive) - { - observedPlayer.SetAggressor(packet.KillerId); - observedPlayer.SetInventory(packet.Equipment); - observedPlayer.RagdollPacket = packet.RagdollPacket; - if (packet.TriggerZones.Length > 0) - { - observedPlayer.TriggerZones.Clear(); - observedPlayer.TriggerZones = new(packet.TriggerZones); - } - } - observedPlayer.NetworkHealthController.HandleSyncPacket(packet.Packet); - } - } - } - if (player == null) - { - return; - } - int firearmPackets = FirearmPackets.Count; - if (firearmPackets > 0) - { - for (int i = 0; i < firearmPackets; i++) - { - player.HandleWeaponPacket(FirearmPackets.Dequeue()); - } - } - int damagePackets = DamagePackets.Count; - if (damagePackets > 0) - { - for (int i = 0; i < damagePackets; i++) - { - 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; - if (inventoryPackets > 0) - { - for (int i = 0; i < inventoryPackets; i++) - { - player.HandleInventoryPacket(InventoryPackets.Dequeue()); - } - } - int commonPlayerPackets = CommonPlayerPackets.Count; - if (commonPlayerPackets > 0) - { - for (int i = 0; i < commonPlayerPackets; i++) - { - player.HandleCommonPacket(CommonPlayerPackets.Dequeue()); - } - } - } - } + protected void Update() + { + if (observedPlayer != null) + { + LastState = observedPlayer.Interpolate(NewState, LastState); + int healthSyncPackets = HealthSyncPackets.Count; + if (healthSyncPackets > 0) + { + for (int i = 0; i < healthSyncPackets; i++) + { + HealthSyncPacket packet = HealthSyncPackets.Dequeue(); + if (packet.Packet.SyncType == GStruct346.ESyncType.IsAlive && !packet.Packet.Data.IsAlive.IsAlive) + { + observedPlayer.SetAggressor(packet.KillerId); + observedPlayer.SetInventory(packet.Equipment); + observedPlayer.RagdollPacket = packet.RagdollPacket; + if (packet.TriggerZones.Length > 0) + { + observedPlayer.TriggerZones.Clear(); + observedPlayer.TriggerZones = new(packet.TriggerZones); + } + } + observedPlayer.NetworkHealthController.HandleSyncPacket(packet.Packet); + } + } + } + if (player == null) + { + return; + } + int firearmPackets = FirearmPackets.Count; + if (firearmPackets > 0) + { + for (int i = 0; i < firearmPackets; i++) + { + player.HandleWeaponPacket(FirearmPackets.Dequeue()); + } + } + int damagePackets = DamagePackets.Count; + if (damagePackets > 0) + { + for (int i = 0; i < damagePackets; i++) + { + 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; + if (inventoryPackets > 0) + { + for (int i = 0; i < inventoryPackets; i++) + { + player.HandleInventoryPacket(InventoryPackets.Dequeue()); + } + } + int commonPlayerPackets = CommonPlayerPackets.Count; + if (commonPlayerPackets > 0) + { + for (int i = 0; i < commonPlayerPackets; i++) + { + player.HandleCommonPacket(CommonPlayerPackets.Dequeue()); + } + } + } + } } diff --git a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs index 23f5e8db..a06d027f 100644 --- a/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs +++ b/Fika.Core/Coop/PacketHandlers/ServerPacketSender.cs @@ -23,340 +23,340 @@ namespace Fika.Core.Coop.PacketHandlers { - public class ServerPacketSender : MonoBehaviour, IPacketSender - { - private CoopPlayer player; - - public bool Enabled { get; set; } = true; - public FikaServer Server { get; set; } = Singleton.Instance; - public FikaClient Client { get; set; } - 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); - private DateTime lastPingTime; - - private ManualLogSource logger; - - protected void Awake() - { - logger = BepInEx.Logging.Logger.CreateLogSource("ServerPacketSender"); - player = GetComponent(); - enabled = false; - lastPingTime = DateTime.Now; - } - - public void Init() - { - enabled = true; - if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.LateInit(); - } - StartCoroutine(SendTrainTime()); - } - - public void SendPacket(ref T packet) where T : INetSerializable - { - Writer.Reset(); - Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void FixedUpdate() - { - if (player == null || Writer == null || Server == null || !Enabled) - { - 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); - - Writer.Reset(); - Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); - - if (player.MovementIdlingTime > 0.05f) - { - player.LastDirection = Vector2.zero; - } - } - - protected void Update() - { - int firearmPackets = FirearmPackets.Count; - if (firearmPackets > 0) - { - for (int i = 0; i < firearmPackets; i++) - { - WeaponPacket firearmPacket = FirearmPackets.Dequeue(); - firearmPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); - } - } - 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 < armorDamagePackets; i++) - { - ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); - armorDamagePacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); - } - } - int inventoryPackets = InventoryPackets.Count; - if (inventoryPackets > 0) - { - for (int i = 0; i < inventoryPackets; i++) - { - InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); - inventoryPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); - } - } - int commonPlayerPackets = CommonPlayerPackets.Count; - if (commonPlayerPackets > 0) - { - for (int i = 0; i < commonPlayerPackets; i++) - { - CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); - commonPlayerPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); - } - } - int healthSyncPackets = HealthSyncPackets.Count; - if (healthSyncPackets > 0) - { - for (int i = 0; i < healthSyncPackets; i++) - { - HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); - healthSyncPacket.NetId = player.NetId; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); - } - } - if (FikaPlugin.UsePingSystem.Value - && player.IsYourPlayer - && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) - && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) - { - if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) - { - return; - } - SendPing(); - } - } - - private void SendPing() - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame.Status != GameStatus.Started) - { - return; - } - - if (lastPingTime < DateTime.Now.AddSeconds(-3)) - { - Transform origin; - FreeCameraController freeCamController = Singleton.Instance; - if (freeCamController != null && freeCamController.IsScriptActive) - { - origin = freeCamController.CameraMain.gameObject.transform; - } - else if (player.HealthController.IsAlive) - { - origin = player.CameraPosition; - } - else - { - return; - } - - Ray sourceRaycast = new(origin.position + origin.forward / 2f, - origin.forward); - int layer = LayerMask.GetMask(["HighPolyCollider", "Interactive", "Deadbody", "Player", "Loot", "Terrain"]); - if (Physics.Raycast(sourceRaycast, out RaycastHit hit, 500f, layer)) - { - lastPingTime = DateTime.Now; - //GameObject gameObject = new("Ping", typeof(FikaPing)); - //gameObject.transform.localPosition = hit.point; - Singleton.Instance.PlayUISound(PingFactory.GetPingSound()); - GameObject hitGameObject = hit.collider.gameObject; - int hitLayer = hitGameObject.layer; - - PingFactory.EPingType pingType = PingFactory.EPingType.Point; - object userData = null; - string localeId = null; + public class ServerPacketSender : MonoBehaviour, IPacketSender + { + private CoopPlayer player; + + public bool Enabled { get; set; } = true; + public FikaServer Server { get; set; } = Singleton.Instance; + public FikaClient Client { get; set; } + 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); + private DateTime lastPingTime; + + private ManualLogSource logger; + + protected void Awake() + { + logger = BepInEx.Logging.Logger.CreateLogSource("ServerPacketSender"); + player = GetComponent(); + enabled = false; + lastPingTime = DateTime.Now; + } + + public void Init() + { + enabled = true; + if (player.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.LateInit(); + } + StartCoroutine(SendTrainTime()); + } + + public void SendPacket(ref T packet) where T : INetSerializable + { + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void FixedUpdate() + { + if (player == null || Writer == null || Server == null || !Enabled) + { + 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); + + Writer.Reset(); + Server.SendDataToAll(Writer, ref playerStatePacket, DeliveryMethod.Unreliable); + + if (player.MovementIdlingTime > 0.05f) + { + player.LastDirection = Vector2.zero; + } + } + + protected void Update() + { + int firearmPackets = FirearmPackets.Count; + if (firearmPackets > 0) + { + for (int i = 0; i < firearmPackets; i++) + { + WeaponPacket firearmPacket = FirearmPackets.Dequeue(); + firearmPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref firearmPacket, DeliveryMethod.ReliableOrdered); + } + } + 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 < armorDamagePackets; i++) + { + ArmorDamagePacket armorDamagePacket = ArmorDamagePackets.Dequeue(); + armorDamagePacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref armorDamagePacket, DeliveryMethod.ReliableOrdered); + } + } + int inventoryPackets = InventoryPackets.Count; + if (inventoryPackets > 0) + { + for (int i = 0; i < inventoryPackets; i++) + { + InventoryPacket inventoryPacket = InventoryPackets.Dequeue(); + inventoryPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref inventoryPacket, DeliveryMethod.ReliableOrdered); + } + } + int commonPlayerPackets = CommonPlayerPackets.Count; + if (commonPlayerPackets > 0) + { + for (int i = 0; i < commonPlayerPackets; i++) + { + CommonPlayerPacket commonPlayerPacket = CommonPlayerPackets.Dequeue(); + commonPlayerPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref commonPlayerPacket, DeliveryMethod.ReliableOrdered); + } + } + int healthSyncPackets = HealthSyncPackets.Count; + if (healthSyncPackets > 0) + { + for (int i = 0; i < healthSyncPackets; i++) + { + HealthSyncPacket healthSyncPacket = HealthSyncPackets.Dequeue(); + healthSyncPacket.NetId = player.NetId; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref healthSyncPacket, DeliveryMethod.ReliableOrdered); + } + } + if (FikaPlugin.UsePingSystem.Value + && player.IsYourPlayer + && Input.GetKey(FikaPlugin.PingButton.Value.MainKey) + && FikaPlugin.PingButton.Value.Modifiers.All(Input.GetKey)) + { + if (MonoBehaviourSingleton.Instance.Console.IsConsoleVisible) + { + return; + } + SendPing(); + } + } + + private void SendPing() + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame.Status != GameStatus.Started) + { + return; + } + + if (lastPingTime < DateTime.Now.AddSeconds(-3)) + { + Transform origin; + FreeCameraController freeCamController = Singleton.Instance; + if (freeCamController != null && freeCamController.IsScriptActive) + { + origin = freeCamController.CameraMain.gameObject.transform; + } + else if (player.HealthController.IsAlive) + { + origin = player.CameraPosition; + } + else + { + return; + } + + Ray sourceRaycast = new(origin.position + origin.forward / 2f, + origin.forward); + int layer = LayerMask.GetMask(["HighPolyCollider", "Interactive", "Deadbody", "Player", "Loot", "Terrain"]); + if (Physics.Raycast(sourceRaycast, out RaycastHit hit, 500f, layer)) + { + lastPingTime = DateTime.Now; + //GameObject gameObject = new("Ping", typeof(FikaPing)); + //gameObject.transform.localPosition = hit.point; + Singleton.Instance.PlayUISound(PingFactory.GetPingSound()); + GameObject hitGameObject = hit.collider.gameObject; + int hitLayer = hitGameObject.layer; + + PingFactory.EPingType pingType = PingFactory.EPingType.Point; + object userData = null; + string localeId = null; #if DEBUG - ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); + ConsoleScreen.Log(statement: $"{hit.collider.GetFullPath()}: {LayerMask.LayerToName(hitLayer)}/{hitGameObject.name}"); #endif - if (LayerMask.LayerToName(hitLayer) == "Player") - { - if (hitGameObject.TryGetComponent(out Player player)) - { - pingType = PingFactory.EPingType.Player; - userData = player; - } - } - else if (LayerMask.LayerToName(hitLayer) == "Deadbody") - { - pingType = PingFactory.EPingType.DeadBody; - userData = hitGameObject; - } - else if (hitGameObject.TryGetComponent(out LootableContainer container)) - { - pingType = PingFactory.EPingType.LootContainer; - userData = container; - localeId = container.ItemOwner.Name; - } - else if (hitGameObject.TryGetComponent(out LootItem lootItem)) - { - pingType = PingFactory.EPingType.LootItem; - userData = lootItem; - localeId = lootItem.Item.ShortName; - } - else if (hitGameObject.TryGetComponent(out Door door)) - { - pingType = PingFactory.EPingType.Door; - userData = door; - } - else if (hitGameObject.TryGetComponent(out InteractableObject interactable)) - { - pingType = PingFactory.EPingType.Interactable; - userData = interactable; - } - - GameObject basePingPrefab = PingFactory.AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); - GameObject basePing = GameObject.Instantiate(basePingPrefab); - Vector3 hitPoint = hit.point; - PingFactory.AbstractPing abstractPing = PingFactory.FromPingType(pingType, basePing); - Color pingColor = FikaPlugin.PingColor.Value; - pingColor = new(pingColor.r, pingColor.g, pingColor.b, 1); - // ref so that we can mutate it if we want to, ex: if I ping a switch I want it at the switch.gameObject.position + Vector3.up - abstractPing.Initialize(ref hitPoint, userData, pingColor); - - GenericPacket genericPacket = new() - { - NetId = player.NetId, - PacketType = EPackageType.Ping, - PingLocation = hitPoint, - PingType = pingType, - PingColor = pingColor, - Nickname = player.Profile.Nickname, - LocaleId = string.IsNullOrEmpty(localeId) ? string.Empty : localeId - }; - - SendPacket(ref genericPacket); - - if (FikaPlugin.PlayPingAnimation.Value) - { - player.vmethod_3(EGesture.ThatDirection); - } - } - } - } - - private IEnumerator SendTrainTime() - { - while (!Singleton.Instantiated) - { - yield return null; - } - - while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) - { - yield return null; - } - - string location = Singleton.Instance.MainPlayer.Location; - - if (location.Contains("RezervBase") || location.Contains("Lighthouse")) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - // Trains take around 20 minutes to come in by default so we can safely wait 20 seconds to make sure everyone is loaded in - yield return new WaitForSeconds(20); - - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - long time = Traverse.Create(locomotive).Field("_depart").Value.Ticks; - - GenericPacket packet = new() - { - NetId = player.NetId, - PacketType = EPackageType.TrainSync, - DepartureTime = time - }; - - Writer.Reset(); - Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError("SendTrainTime: Could not find locomotive!"); - } - } - else - { - yield break; - } - } - - public void DestroyThis() - { - Writer = null; - FirearmPackets.Clear(); - DamagePackets.Clear(); - InventoryPackets.Clear(); - CommonPlayerPackets.Clear(); - HealthSyncPackets.Clear(); - if (Server != null) - { - Server = null; - } - if (Client != null) - { - Client = null; - } - Destroy(this); - } - } + if (LayerMask.LayerToName(hitLayer) == "Player") + { + if (hitGameObject.TryGetComponent(out Player player)) + { + pingType = PingFactory.EPingType.Player; + userData = player; + } + } + else if (LayerMask.LayerToName(hitLayer) == "Deadbody") + { + pingType = PingFactory.EPingType.DeadBody; + userData = hitGameObject; + } + else if (hitGameObject.TryGetComponent(out LootableContainer container)) + { + pingType = PingFactory.EPingType.LootContainer; + userData = container; + localeId = container.ItemOwner.Name; + } + else if (hitGameObject.TryGetComponent(out LootItem lootItem)) + { + pingType = PingFactory.EPingType.LootItem; + userData = lootItem; + localeId = lootItem.Item.ShortName; + } + else if (hitGameObject.TryGetComponent(out Door door)) + { + pingType = PingFactory.EPingType.Door; + userData = door; + } + else if (hitGameObject.TryGetComponent(out InteractableObject interactable)) + { + pingType = PingFactory.EPingType.Interactable; + userData = interactable; + } + + GameObject basePingPrefab = PingFactory.AbstractPing.pingBundle.LoadAsset("BasePingPrefab"); + GameObject basePing = GameObject.Instantiate(basePingPrefab); + Vector3 hitPoint = hit.point; + PingFactory.AbstractPing abstractPing = PingFactory.FromPingType(pingType, basePing); + Color pingColor = FikaPlugin.PingColor.Value; + pingColor = new(pingColor.r, pingColor.g, pingColor.b, 1); + // ref so that we can mutate it if we want to, ex: if I ping a switch I want it at the switch.gameObject.position + Vector3.up + abstractPing.Initialize(ref hitPoint, userData, pingColor); + + GenericPacket genericPacket = new() + { + NetId = player.NetId, + PacketType = EPackageType.Ping, + PingLocation = hitPoint, + PingType = pingType, + PingColor = pingColor, + Nickname = player.Profile.Nickname, + LocaleId = string.IsNullOrEmpty(localeId) ? string.Empty : localeId + }; + + SendPacket(ref genericPacket); + + if (FikaPlugin.PlayPingAnimation.Value) + { + player.vmethod_3(EGesture.ThatDirection); + } + } + } + } + + private IEnumerator SendTrainTime() + { + while (!Singleton.Instantiated) + { + yield return null; + } + + while (string.IsNullOrEmpty(Singleton.Instance.MainPlayer.Location)) + { + yield return null; + } + + string location = Singleton.Instance.MainPlayer.Location; + + if (location.Contains("RezervBase") || location.Contains("Lighthouse")) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + // Trains take around 20 minutes to come in by default so we can safely wait 20 seconds to make sure everyone is loaded in + yield return new WaitForSeconds(20); + + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) + { + long time = Traverse.Create(locomotive).Field("_depart").Value.Ticks; + + GenericPacket packet = new() + { + NetId = player.NetId, + PacketType = EPackageType.TrainSync, + DepartureTime = time + }; + + Writer.Reset(); + Server.SendDataToAll(Writer, ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError("SendTrainTime: Could not find locomotive!"); + } + } + else + { + yield break; + } + } + + public void DestroyThis() + { + Writer = null; + FirearmPackets.Clear(); + DamagePackets.Clear(); + InventoryPackets.Clear(); + CommonPlayerPackets.Clear(); + HealthSyncPackets.Clear(); + if (Server != null) + { + Server = null; + } + if (Client != null) + { + Client = null; + } + Destroy(this); + } + } } diff --git a/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs index 354f9127..7139e3ff 100644 --- a/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs +++ b/Fika.Core/Coop/Patches/AbstractGame/AbstractGame_InRaid_Patch.cs @@ -5,21 +5,21 @@ namespace Fika.Core.Coop.Patches { - /// - /// Used to support mods that rely on the property, which normally casts to - /// - internal class AbstractGame_InRaid_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(AbstractGame).GetProperty(nameof(AbstractGame.InRaid)).GetGetMethod(); - } + /// + /// Used to support mods that rely on the property, which normally casts to + /// + internal class AbstractGame_InRaid_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(AbstractGame).GetProperty(nameof(AbstractGame.InRaid)).GetGetMethod(); + } - [PatchPrefix] - private static bool PreFix(AbstractGame __instance, ref bool __result) - { - __result = __instance is CoopGame; - return false; - } - } + [PatchPrefix] + private static bool PreFix(AbstractGame __instance, ref bool __result) + { + __result = __instance is CoopGame; + return false; + } + } } diff --git a/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs b/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs index 543d0b39..7a0a7778 100644 --- a/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs +++ b/Fika.Core/Coop/Patches/Airdrop/AirdropBox_Patch.cs @@ -6,21 +6,21 @@ namespace Fika.Core.Coop.Patches.Airdrop { - public class AirdropBox_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => AccessTools.Method(typeof(AirdropBox), "AddNavMeshObstacle"); + public class AirdropBox_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => AccessTools.Method(typeof(AirdropBox), "AddNavMeshObstacle"); - [PatchPrefix] - public static bool PatchPrefix(AirdropBox __instance) - { - //Allow method to go through - if (FikaBackendUtils.IsServer) - { - return true; - } + [PatchPrefix] + public static bool PatchPrefix(AirdropBox __instance) + { + //Allow method to go through + if (FikaBackendUtils.IsServer) + { + return true; + } - //Stop running method. - return false; - } - } + //Stop running method. + return false; + } + } } diff --git a/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs b/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs index aeba1dcf..2db9bb34 100644 --- a/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs +++ b/Fika.Core/Coop/Patches/Airdrop/FikaAirdropFlare_Patch.cs @@ -8,27 +8,27 @@ namespace Fika.Core.Coop.Patches.Airdrop { - public class FikaAirdropFlare_Patch : ModulePatch - { - private static readonly string[] _usableFlares = ["624c09cfbc2e27219346d955", "62389ba9a63f32501b1b4451"]; + public class FikaAirdropFlare_Patch : ModulePatch + { + private static readonly string[] _usableFlares = ["624c09cfbc2e27219346d955", "62389ba9a63f32501b1b4451"]; - protected override MethodBase GetTargetMethod() - { - return typeof(FlareCartridge).GetMethod(nameof(FlareCartridge.Init), - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - } + protected override MethodBase GetTargetMethod() + { + return typeof(FlareCartridge).GetMethod(nameof(FlareCartridge.Init), + BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + } - [PatchPostfix] - private static void PatchPostfix(BulletClass flareCartridge) - { - GameWorld gameWorld = Singleton.Instance; - bool points = LocationScene.GetAll().Any(); + [PatchPostfix] + private static void PatchPostfix(BulletClass flareCartridge) + { + GameWorld gameWorld = Singleton.Instance; + bool points = LocationScene.GetAll().Any(); - if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id)) - { - FikaAirdropsManager airdropsManager = gameWorld.gameObject.AddComponent(); - airdropsManager.isFlareDrop = true; - } - } - } + if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id)) + { + FikaAirdropsManager airdropsManager = gameWorld.gameObject.AddComponent(); + airdropsManager.isFlareDrop = true; + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Patches/BotCacher_Patch.cs b/Fika.Core/Coop/Patches/BotCacher_Patch.cs index 8e636099..6e781aa7 100644 --- a/Fika.Core/Coop/Patches/BotCacher_Patch.cs +++ b/Fika.Core/Coop/Patches/BotCacher_Patch.cs @@ -6,69 +6,69 @@ namespace Fika.Core.Coop.Patches { - public class BotCacher_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(GClass531).GetMethod(nameof(GClass531.LoadInternal), BindingFlags.Static | BindingFlags.Public); - } + public class BotCacher_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(GClass531).GetMethod(nameof(GClass531.LoadInternal), BindingFlags.Static | BindingFlags.Public); + } - [PatchPrefix] - private static bool PatchPrefix(out CoreBotSettingsClass core, ref bool __result) - { - if (FikaPlugin.Instance.BotDifficulties != null) - { - core = FikaPlugin.Instance.BotDifficulties.GetCoreSettings(); - } - else - { - string text = GClass531.LoadCoreByString(); - if (text == null) - { - core = null; - __result = false; - return false; - } - core = CoreBotSettingsClass.Create(text); - } + [PatchPrefix] + private static bool PatchPrefix(out CoreBotSettingsClass core, ref bool __result) + { + if (FikaPlugin.Instance.BotDifficulties != null) + { + core = FikaPlugin.Instance.BotDifficulties.GetCoreSettings(); + } + else + { + string text = GClass531.LoadCoreByString(); + if (text == null) + { + core = null; + __result = false; + return false; + } + core = CoreBotSettingsClass.Create(text); + } - if (FikaBackendUtils.IsServer) - { - foreach (object type in Enum.GetValues(typeof(WildSpawnType))) - { - foreach (object difficulty in Enum.GetValues(typeof(BotDifficulty))) - { - BotSettingsComponents botSettingsComponents; - botSettingsComponents = FikaPlugin.Instance.BotDifficulties.GetComponent((BotDifficulty)difficulty, (WildSpawnType)type); - if (botSettingsComponents != null) - { - if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) - { - GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); - } - } - else - { - botSettingsComponents = GClass531.smethod_1(GClass531.CheckOnExclude((BotDifficulty)difficulty, (WildSpawnType)type), (WildSpawnType)type, false); - if (botSettingsComponents != null) - { - if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) - { - GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); - } - } - else - { - __result = false; - return false; - } - } - } - } - __result = true; - } + if (FikaBackendUtils.IsServer) + { + foreach (object type in Enum.GetValues(typeof(WildSpawnType))) + { + foreach (object difficulty in Enum.GetValues(typeof(BotDifficulty))) + { + BotSettingsComponents botSettingsComponents; + botSettingsComponents = FikaPlugin.Instance.BotDifficulties.GetComponent((BotDifficulty)difficulty, (WildSpawnType)type); + if (botSettingsComponents != null) + { + if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) + { + GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); + } + } + else + { + botSettingsComponents = GClass531.smethod_1(GClass531.CheckOnExclude((BotDifficulty)difficulty, (WildSpawnType)type), (WildSpawnType)type, false); + if (botSettingsComponents != null) + { + if (!GClass531.AllSettings.ContainsKey((BotDifficulty)difficulty, (WildSpawnType)type)) + { + GClass531.AllSettings.Add((BotDifficulty)difficulty, (WildSpawnType)type, botSettingsComponents); + } + } + else + { + __result = false; + return false; + } + } + } + } + __result = true; + } - return false; - } - } + return false; + } + } } diff --git a/Fika.Core/Coop/Patches/LocalGame/GameWorld_Create_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/GameWorld_Create_Patch.cs index 3d8f00ef..1a78eb08 100644 --- a/Fika.Core/Coop/Patches/LocalGame/GameWorld_Create_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/GameWorld_Create_Patch.cs @@ -9,47 +9,47 @@ namespace Fika.Core.Coop.Patches.LocalGame { - public class GameWorld_Create_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(GameWorld).GetMethod(nameof(GameWorld.Create), BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(ClientLocalGameWorld)); - } + public class GameWorld_Create_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(GameWorld).GetMethod(nameof(GameWorld.Create), BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(ClientLocalGameWorld)); + } - [PatchPrefix] - public static bool Prefix(ref GameWorld __result, GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) - { + [PatchPrefix] + public static bool Prefix(ref GameWorld __result, GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) + { - if (!FikaBackendUtils.RequestFikaWorld) - { - __result = CreateHideoutWorld(gameObject, objectsFactory, updateQueue, currentProfileId); - return false; - } + if (!FikaBackendUtils.RequestFikaWorld) + { + __result = CreateHideoutWorld(gameObject, objectsFactory, updateQueue, currentProfileId); + return false; + } - if (FikaBackendUtils.IsServer) - { - __result = CoopHostGameWorld.Create(gameObject, objectsFactory, updateQueue, currentProfileId); - } - else - { - __result = CoopClientGameWorld.Create(gameObject, objectsFactory, updateQueue, currentProfileId); - } - return false; - } + if (FikaBackendUtils.IsServer) + { + __result = CoopHostGameWorld.Create(gameObject, objectsFactory, updateQueue, currentProfileId); + } + else + { + __result = CoopClientGameWorld.Create(gameObject, objectsFactory, updateQueue, currentProfileId); + } + return false; + } - private static GameWorld CreateHideoutWorld(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) - { - HideoutGameWorld gameWorld = gameObject.AddComponent(); - Traverse gameWorldTraverse = Traverse.Create(gameWorld); - gameWorldTraverse.Field("ObjectsFactory").Value = objectsFactory; - gameWorldTraverse.Field("eupdateQueue_0").Value = updateQueue; - gameWorld.SpeakerManager = gameObject.AddComponent(); - gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); - gameWorld.BufferZoneController = new BufferZoneControllerClass(); - gameWorld.CurrentProfileId = currentProfileId; - gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); - gameObject.AddComponent(); - return gameWorld; - } - } + private static GameWorld CreateHideoutWorld(GameObject gameObject, PoolManager objectsFactory, EUpdateQueue updateQueue, string currentProfileId) + { + HideoutGameWorld gameWorld = gameObject.AddComponent(); + Traverse gameWorldTraverse = Traverse.Create(gameWorld); + gameWorldTraverse.Field("ObjectsFactory").Value = objectsFactory; + gameWorldTraverse.Field("eupdateQueue_0").Value = updateQueue; + gameWorld.SpeakerManager = gameObject.AddComponent(); + gameWorld.ExfiltrationController = new ExfiltrationControllerClass(); + gameWorld.BufferZoneController = new BufferZoneControllerClass(); + gameWorld.CurrentProfileId = currentProfileId; + gameWorld.UnityTickListener = GameWorldUnityTickListener.Create(gameObject, gameWorld); + gameObject.AddComponent(); + return gameWorld; + } + } } diff --git a/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs index 05c583d8..ba767c4d 100644 --- a/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/NonWaveSpawnScenario_Patch.cs @@ -5,16 +5,16 @@ namespace Fika.Core.Coop.Patches.LocalGame { - internal class NonWaveSpawnScenario_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(NonWavesSpawnScenario).GetMethod(nameof(NonWavesSpawnScenario.Run)); + internal class NonWaveSpawnScenario_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(NonWavesSpawnScenario).GetMethod(nameof(NonWavesSpawnScenario.Run)); - [PatchPrefix] - public static bool PatchPrefix(NonWavesSpawnScenario __instance) - { - bool result = FikaBackendUtils.IsServer; - typeof(NonWavesSpawnScenario).GetProperty(nameof(NonWavesSpawnScenario.Enabled)).SetValue(__instance, result); - return result; - } - } + [PatchPrefix] + public static bool PatchPrefix(NonWavesSpawnScenario __instance) + { + 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 cb70d016..bb9c2c45 100644 --- a/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGameCreator_Patch.cs @@ -18,136 +18,136 @@ namespace Fika.Core.Coop.Patches.LocalGame { - /// - /// Created by: Paulov - /// Paulov: Overwrite and use our own CoopGame instance instead - /// - internal class TarkovApplication_LocalGameCreator_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_47)); - - [PatchPrefix] - public static bool Prefix(ref Task __result, TarkovApplication __instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController, - RaidSettings ____raidSettings, InputTree ____inputTree, GameDateTime ____localGameDateTime, float ____fixedDeltaTime, string ____backendUrl) - { - Logger.LogDebug("TarkovApplication_LocalGameCreator_Patch:Prefix"); - - __result = CreateFikaGame(__instance, timeAndWeather, timeHasComeScreenController, ____raidSettings, ____inputTree, ____localGameDateTime, ____fixedDeltaTime, ____backendUrl); - return false; - } - - public static async Task CreateFikaGame(TarkovApplication instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController, - RaidSettings raidSettings, InputTree inputTree, GameDateTime localGameDateTime, float fixedDeltaTime, string backendUrl) - { - if (raidSettings == null) - { - Logger.LogError("RaidSettings is Null"); - throw new ArgumentNullException("RaidSettings"); - } - - if (timeHasComeScreenController == null) - { - Logger.LogError("timeHasComeScreenController is Null"); - throw new ArgumentNullException("timeHasComeScreenController"); - } - bool isServer = FikaBackendUtils.IsServer; - - LocationSettingsClass.Location location = raidSettings.SelectedLocation; - - FikaBackendUtils.ScreenController = timeHasComeScreenController; - - if (Singleton.Instantiated) - { - Singleton.Instance.Deactivate(); - } - - ISession session = instance.Session; - - if (session == null) - { - throw new NullReferenceException("Backend session was null when initializing game!"); - } - - Profile profile = session.GetProfileBySide(raidSettings.Side); - - bool isDedicatedHost = session.Profile.Nickname.StartsWith("dedicated_"); - if (isDedicatedHost) - { - FikaBackendUtils.IsDedicated = true; - } - - profile.Inventory.Stash = null; - profile.Inventory.QuestStashItems = null; - profile.Inventory.DiscardLimits = Singleton.Instance.GetDiscardLimits(); - - Logger.LogDebug("TarkovApplication_LocalGameCreator_Patch:Postfix: Attempt to set Raid Settings"); - - await session.SendRaidSettings(raidSettings); - - if (!isServer) - { - timeHasComeScreenController.ChangeStatus("Joining coop game..."); - - RaidSettingsRequest data = new(); - RaidSettingsResponse raidSettingsResponse = await FikaRequestHandler.GetRaidSettings(data); - - raidSettings.MetabolismDisabled = raidSettingsResponse.MetabolismDisabled; - raidSettings.PlayersSpawnPlace = (EPlayersSpawnPlace)Enum.Parse(typeof(EPlayersSpawnPlace), raidSettingsResponse.PlayersSpawnPlace); - } - else - { - timeHasComeScreenController.ChangeStatus("Creating coop game..."); - } - - StartHandler startHandler = new(instance, session.Profile, session.ProfileOfPet, raidSettings.SelectedLocation, timeHasComeScreenController); - - 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(coopGame); - FikaEventDispatcher.DispatchEvent(new AbstractGameCreatedEvent(coopGame)); - - if (!isServer) - { - coopGame.SetMatchmakerStatus("Coop game joined"); - } - else - { - coopGame.SetMatchmakerStatus("Coop game created"); - } - - Task finishTask = coopGame.InitPlayer(raidSettings.BotSettings, backendUrl, new Callback(startHandler.HandleLoadComplete)); - await Task.WhenAll(finishTask); - } - - 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.TimeHasComeScreenClass timeHasComeScreenController = timeHasComeScreenController; - - public void HandleStop(Result result) - { - tarkovApplication.method_50(pmcProfile.Id, scavProfile, location, result, timeHasComeScreenController); - } - - public void HandleLoadComplete(IResult error) - { - using (CounterCreatorAbstractClass.StartWithToken("LoadingScreen.LoadComplete")) - { - GameObject.DestroyImmediate(MonoBehaviourSingleton.Instance.gameObject); - MainMenuController mmc = (MainMenuController)typeof(TarkovApplication).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Where(x => x.FieldType == typeof(MainMenuController)).FirstOrDefault().GetValue(tarkovApplication); - mmc?.Unsubscribe(); - GameWorld gameWorld = Singleton.Instance; - gameWorld.OnGameStarted(); - FikaEventDispatcher.DispatchEvent(new GameWorldStartedEvent(gameWorld)); - } - } - } - } + /// + /// Created by: Paulov + /// Paulov: Overwrite and use our own CoopGame instance instead + /// + internal class TarkovApplication_LocalGameCreator_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_47)); + + [PatchPrefix] + public static bool Prefix(ref Task __result, TarkovApplication __instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController, + RaidSettings ____raidSettings, InputTree ____inputTree, GameDateTime ____localGameDateTime, float ____fixedDeltaTime, string ____backendUrl) + { + Logger.LogDebug("TarkovApplication_LocalGameCreator_Patch:Prefix"); + + __result = CreateFikaGame(__instance, timeAndWeather, timeHasComeScreenController, ____raidSettings, ____inputTree, ____localGameDateTime, ____fixedDeltaTime, ____backendUrl); + return false; + } + + public static async Task CreateFikaGame(TarkovApplication instance, TimeAndWeatherSettings timeAndWeather, MatchmakerTimeHasCome.TimeHasComeScreenClass timeHasComeScreenController, + RaidSettings raidSettings, InputTree inputTree, GameDateTime localGameDateTime, float fixedDeltaTime, string backendUrl) + { + if (raidSettings == null) + { + Logger.LogError("RaidSettings is Null"); + throw new ArgumentNullException("RaidSettings"); + } + + if (timeHasComeScreenController == null) + { + Logger.LogError("timeHasComeScreenController is Null"); + throw new ArgumentNullException("timeHasComeScreenController"); + } + bool isServer = FikaBackendUtils.IsServer; + + LocationSettingsClass.Location location = raidSettings.SelectedLocation; + + FikaBackendUtils.ScreenController = timeHasComeScreenController; + + if (Singleton.Instantiated) + { + Singleton.Instance.Deactivate(); + } + + ISession session = instance.Session; + + if (session == null) + { + throw new NullReferenceException("Backend session was null when initializing game!"); + } + + Profile profile = session.GetProfileBySide(raidSettings.Side); + + bool isDedicatedHost = session.Profile.Nickname.StartsWith("dedicated_"); + if (isDedicatedHost) + { + FikaBackendUtils.IsDedicated = true; + } + + profile.Inventory.Stash = null; + profile.Inventory.QuestStashItems = null; + profile.Inventory.DiscardLimits = Singleton.Instance.GetDiscardLimits(); + + Logger.LogDebug("TarkovApplication_LocalGameCreator_Patch:Postfix: Attempt to set Raid Settings"); + + await session.SendRaidSettings(raidSettings); + + if (!isServer) + { + timeHasComeScreenController.ChangeStatus("Joining coop game..."); + + RaidSettingsRequest data = new(); + RaidSettingsResponse raidSettingsResponse = await FikaRequestHandler.GetRaidSettings(data); + + raidSettings.MetabolismDisabled = raidSettingsResponse.MetabolismDisabled; + raidSettings.PlayersSpawnPlace = (EPlayersSpawnPlace)Enum.Parse(typeof(EPlayersSpawnPlace), raidSettingsResponse.PlayersSpawnPlace); + } + else + { + timeHasComeScreenController.ChangeStatus("Creating coop game..."); + } + + StartHandler startHandler = new(instance, session.Profile, session.ProfileOfPet, raidSettings.SelectedLocation, timeHasComeScreenController); + + 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(coopGame); + FikaEventDispatcher.DispatchEvent(new AbstractGameCreatedEvent(coopGame)); + + if (!isServer) + { + coopGame.SetMatchmakerStatus("Coop game joined"); + } + else + { + coopGame.SetMatchmakerStatus("Coop game created"); + } + + Task finishTask = coopGame.InitPlayer(raidSettings.BotSettings, backendUrl, new Callback(startHandler.HandleLoadComplete)); + await Task.WhenAll(finishTask); + } + + 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.TimeHasComeScreenClass timeHasComeScreenController = timeHasComeScreenController; + + public void HandleStop(Result result) + { + tarkovApplication.method_50(pmcProfile.Id, scavProfile, location, result, timeHasComeScreenController); + } + + public void HandleLoadComplete(IResult error) + { + using (CounterCreatorAbstractClass.StartWithToken("LoadingScreen.LoadComplete")) + { + GameObject.DestroyImmediate(MonoBehaviourSingleton.Instance.gameObject); + MainMenuController mmc = (MainMenuController)typeof(TarkovApplication).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Where(x => x.FieldType == typeof(MainMenuController)).FirstOrDefault().GetValue(tarkovApplication); + mmc?.Unsubscribe(); + GameWorld gameWorld = Singleton.Instance; + gameWorld.OnGameStarted(); + FikaEventDispatcher.DispatchEvent(new GameWorldStartedEvent(gameWorld)); + } + } + } + } } diff --git a/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGamePreparer_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGamePreparer_Patch.cs index 8b95c2fd..dfe4c6b4 100644 --- a/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGamePreparer_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/TarkovApplication_LocalGamePreparer_Patch.cs @@ -10,66 +10,66 @@ namespace Fika.Core.Coop.Patches.LocalGame { - /// - /// Created by: Lacyway - /// - internal class TarkovApplication_LocalGamePreparer_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_38)); + /// + /// Created by: Lacyway + /// + internal class TarkovApplication_LocalGamePreparer_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_38)); - [PatchPrefix] - public static async void Prefix(TarkovApplication __instance, RaidSettings ____raidSettings) - { - Logger.LogDebug("TarkovApplication_LocalGamePreparer_Patch:Prefix"); + [PatchPrefix] + public static async void Prefix(TarkovApplication __instance, RaidSettings ____raidSettings) + { + Logger.LogDebug("TarkovApplication_LocalGamePreparer_Patch:Prefix"); - FikaBackendUtils.RequestFikaWorld = true; + FikaBackendUtils.RequestFikaWorld = true; - bool isServer = FikaBackendUtils.IsServer; - if (!isServer) - { - if (!string.IsNullOrEmpty(FikaBackendUtils.HostLocationId)) - { - if (____raidSettings.LocationId.ToLower() == "sandbox" && FikaBackendUtils.HostLocationId.ToLower() == "sandbox_high") - { - LocationSettingsClass.Location sandboxHigh = __instance.Session.LocationSettings.locations.Values.FirstOrDefault - (new Func(IsSandboxHigh)); - ____raidSettings.SelectedLocation = sandboxHigh; + bool isServer = FikaBackendUtils.IsServer; + if (!isServer) + { + if (!string.IsNullOrEmpty(FikaBackendUtils.HostLocationId)) + { + if (____raidSettings.LocationId.ToLower() == "sandbox" && FikaBackendUtils.HostLocationId.ToLower() == "sandbox_high") + { + LocationSettingsClass.Location sandboxHigh = __instance.Session.LocationSettings.locations.Values.FirstOrDefault + (new Func(IsSandboxHigh)); + ____raidSettings.SelectedLocation = sandboxHigh; - NotificationManagerClass.DisplayMessageNotification("Notification/HighLevelQueue".Localized(null), - ENotificationDurationType.Default, ENotificationIconType.Default, null); - } + NotificationManagerClass.DisplayMessageNotification("Notification/HighLevelQueue".Localized(null), + ENotificationDurationType.Default, ENotificationIconType.Default, null); + } - if (____raidSettings.LocationId.ToLower() == "sandbox_high" && FikaBackendUtils.HostLocationId.ToLower() == "sandbox") - { - LocationSettingsClass.Location sandbox = __instance.Session.LocationSettings.locations.Values.FirstOrDefault - (new Func(IsSandbox)); - ____raidSettings.SelectedLocation = sandbox; - } - } - } + if (____raidSettings.LocationId.ToLower() == "sandbox_high" && FikaBackendUtils.HostLocationId.ToLower() == "sandbox") + { + LocationSettingsClass.Location sandbox = __instance.Session.LocationSettings.locations.Values.FirstOrDefault + (new Func(IsSandbox)); + ____raidSettings.SelectedLocation = sandbox; + } + } + } - NetManagerUtils.CreateNetManager(FikaBackendUtils.IsServer); - if (isServer) - { - NetManagerUtils.StartPinger(); - } - await NetManagerUtils.InitNetManager(isServer); + NetManagerUtils.CreateNetManager(FikaBackendUtils.IsServer); + if (isServer) + { + NetManagerUtils.StartPinger(); + } + await NetManagerUtils.InitNetManager(isServer); - if (isServer) - { - SetStatusModel status = new(FikaBackendUtils.GetGroupId(), LobbyEntry.ELobbyStatus.COMPLETE); - await FikaRequestHandler.UpdateSetStatus(status); - } - } + if (isServer) + { + SetStatusModel status = new(FikaBackendUtils.GetGroupId(), LobbyEntry.ELobbyStatus.COMPLETE); + await FikaRequestHandler.UpdateSetStatus(status); + } + } - private static bool IsSandboxHigh(LocationSettingsClass.Location location) - { - return location.Id.ToLower() == "sandbox_high"; - } + private static bool IsSandboxHigh(LocationSettingsClass.Location location) + { + return location.Id.ToLower() == "sandbox_high"; + } - private static bool IsSandbox(LocationSettingsClass.Location location) - { - return location.Id.ToLower() == "sandbox"; - } - } + private static bool IsSandbox(LocationSettingsClass.Location location) + { + return location.Id.ToLower() == "sandbox"; + } + } } diff --git a/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs index 849d14a9..a249e570 100644 --- a/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/WaveSpawnScenario_Patch.cs @@ -5,16 +5,16 @@ namespace Fika.Core.Coop.Patches.LocalGame { - internal class WaveSpawnScenario_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(WavesSpawnScenario).GetMethod(nameof(WavesSpawnScenario.Run)); + internal class WaveSpawnScenario_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(WavesSpawnScenario).GetMethod(nameof(WavesSpawnScenario.Run)); - [PatchPrefix] - public static bool PatchPrefix(WavesSpawnScenario __instance) - { - bool result = FikaBackendUtils.IsServer; - typeof(WavesSpawnScenario).GetProperty(nameof(WavesSpawnScenario.Enabled)).SetValue(__instance, result); - return result; - } - } + [PatchPrefix] + public static bool PatchPrefix(WavesSpawnScenario __instance) + { + bool result = FikaBackendUtils.IsServer; + typeof(WavesSpawnScenario).GetProperty(nameof(WavesSpawnScenario.Enabled)).SetValue(__instance, result); + return result; + } + } } diff --git a/Fika.Core/Coop/Patches/LocalGame/World_AddSpawnQuestLootPacket_Patch.cs b/Fika.Core/Coop/Patches/LocalGame/World_AddSpawnQuestLootPacket_Patch.cs index 287b58d8..74510e2a 100644 --- a/Fika.Core/Coop/Patches/LocalGame/World_AddSpawnQuestLootPacket_Patch.cs +++ b/Fika.Core/Coop/Patches/LocalGame/World_AddSpawnQuestLootPacket_Patch.cs @@ -4,20 +4,20 @@ namespace Fika.Core.Coop.Patches.LocalGame { - /// - /// Used to prevent the queue on the world to be stuck in an endless loop - /// - public class World_AddSpawnQuestLootPacket_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(World).GetMethod(nameof(World.AddSpawnQuestLootPacket)); - } + /// + /// Used to prevent the queue on the world to be stuck in an endless loop + /// + public class World_AddSpawnQuestLootPacket_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(World).GetMethod(nameof(World.AddSpawnQuestLootPacket)); + } - [PatchPrefix] - public static bool Prefix() - { - return false; - } - } + [PatchPrefix] + public static bool Prefix() + { + return false; + } + } } \ No newline at end of file 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 57cac2ed..722d7ddb 100644 --- a/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs +++ b/Fika.Core/Coop/Patches/Minefield/Minefield_method_2_Patch.cs @@ -12,89 +12,89 @@ namespace Fika.Core.Coop.Patches { - /// - /// This patch prevents a null exception when an is hit by a mine explosion - /// - internal class Minefield_method_2_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(Minefield).GetMethod(nameof(Minefield.method_2)); - } + /// + /// This patch prevents a null exception when an is hit by a mine explosion + /// + internal class Minefield_method_2_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(Minefield).GetMethod(nameof(Minefield.method_2)); + } - [PatchPrefix] - public static bool Prefix(IPlayer player, bool first, List ___TargetedPlayers, - List ___NotTargetedPlayers, float ____collateralContusionRange, float ____collateralDamageRange, - float ____firstExplosionDamage, float ____secondExplosionDamage, Minefield __instance) - { - if (player is ObservedCoopPlayer) - { - if (FikaBackendUtils.IsServer) - { - Vector3 position = player.Position; - foreach (IPlayer player2 in ___TargetedPlayers.Concat(___NotTargetedPlayers).ToList()) - { - DoReplicatedMineDamage(player2, Vector3.Distance(position, player2.Position), first, - player != player2, ____collateralContusionRange, ____collateralDamageRange, - ____firstExplosionDamage, ____secondExplosionDamage, __instance); - } - } - return false; - } - return true; - } + [PatchPrefix] + public static bool Prefix(IPlayer player, bool first, List ___TargetedPlayers, + List ___NotTargetedPlayers, float ____collateralContusionRange, float ____collateralDamageRange, + float ____firstExplosionDamage, float ____secondExplosionDamage, Minefield __instance) + { + if (player is ObservedCoopPlayer) + { + if (FikaBackendUtils.IsServer) + { + Vector3 position = player.Position; + foreach (IPlayer player2 in ___TargetedPlayers.Concat(___NotTargetedPlayers).ToList()) + { + DoReplicatedMineDamage(player2, Vector3.Distance(position, player2.Position), first, + player != player2, ____collateralContusionRange, ____collateralDamageRange, + ____firstExplosionDamage, ____secondExplosionDamage, __instance); + } + } + return false; + } + return true; + } - private static void DoReplicatedMineDamage(IPlayer player, float distance, bool first, bool isCollateral, - float collateralContusionRange, float collateralDamageRange, float firstExplosionDamage, - float secondExplosionDamage, Minefield minefield) - { - if (isCollateral && distance > collateralContusionRange) - { - return; - } + private static void DoReplicatedMineDamage(IPlayer player, float distance, bool first, bool isCollateral, + float collateralContusionRange, float collateralDamageRange, float firstExplosionDamage, + float secondExplosionDamage, Minefield minefield) + { + if (isCollateral && distance > collateralContusionRange) + { + return; + } - CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.GetAlivePlayerByProfileID(player.ProfileId); - if (isCollateral && distance > collateralDamageRange) - { - return; - } + CoopPlayer coopPlayer = (CoopPlayer)Singleton.Instance.GetAlivePlayerByProfileID(player.ProfileId); + if (isCollateral && distance > collateralDamageRange) + { + return; + } - if (coopPlayer != null) - { - float num2 = 1f - distance / collateralDamageRange; - IEnumerable enumerable = isCollateral ? player.PlayerBones.BodyPartColliders.Where(new Func(minefield.method_4)) - : player.PlayerBones.BodyPartColliders.Where(new Func(minefield.method_5)); + if (coopPlayer != null) + { + float num2 = 1f - distance / collateralDamageRange; + 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.Class2316.class2316_0.method_0)).ToArray(); - enumerable = enumerable.Randomize(); + 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; - float num4 = (isCollateral || first) ? firstExplosionDamage : secondExplosionDamage; - int num5 = 0; + int num3 = (isCollateral || first) ? UnityEngine.Random.Range(2, enumerable.Count()) : int.MaxValue; + float num4 = (isCollateral || first) ? firstExplosionDamage : secondExplosionDamage; + int num5 = 0; - foreach (BodyPartCollider bodyPartCollider in enumerable) - { - coopPlayer.PacketSender.DamagePackets.Enqueue(new() - { - 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) - { - break; - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"DoReplicatedMineDamage: Could not find player with ProfileId: {player.ProfileId}, Nickname: {player.Profile.Nickname}!"); - } - } - } + foreach (BodyPartCollider bodyPartCollider in enumerable) + { + coopPlayer.PacketSender.DamagePackets.Enqueue(new() + { + 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) + { + break; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"DoReplicatedMineDamage: Could not find player with ProfileId: {player.ProfileId}, Nickname: {player.Profile.Nickname}!"); + } + } + } } diff --git a/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs index 11ad8634..d00c7706 100644 --- a/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/AddEnemyToAllGroupsInBotZonePatch_Override.cs @@ -7,69 +7,69 @@ namespace Fika.Core.Coop.Patches.Overrides { - internal class AddEnemyToAllGroupsInBotZonePatch_Override : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return AccessTools.Method(typeof(BotsController), nameof(BotsController.AddEnemyToAllGroupsInBotZone)); - } + internal class AddEnemyToAllGroupsInBotZonePatch_Override : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(BotsController), nameof(BotsController.AddEnemyToAllGroupsInBotZone)); + } - /// - /// AddEnemyToAllGroupsInBotZone() - /// Goal: by default, AddEnemyToAllGroupsInBotZone doesn't check if the bot group is on the same side as the player. - /// The effect of this is that when you are a Scav and kill a Usec, every bot group in the zone will aggro you including other Scavs. - /// This should fix that. - /// - [PatchPrefix] - private static bool PatchPrefix(BotsController __instance, IPlayer aggressor, IPlayer groupOwner, IPlayer target) - { - if (!groupOwner.IsAI) - { - return false; - } + /// + /// AddEnemyToAllGroupsInBotZone() + /// Goal: by default, AddEnemyToAllGroupsInBotZone doesn't check if the bot group is on the same side as the player. + /// The effect of this is that when you are a Scav and kill a Usec, every bot group in the zone will aggro you including other Scavs. + /// This should fix that. + /// + [PatchPrefix] + private static bool PatchPrefix(BotsController __instance, IPlayer aggressor, IPlayer groupOwner, IPlayer target) + { + if (!groupOwner.IsAI) + { + return false; + } - // If you damage yourself exit early as we dont want to try add ourself to our own enemy list - if (aggressor.IsYourPlayer && target.IsYourPlayer) - { - return false; - } + // If you damage yourself exit early as we dont want to try add ourself to our own enemy list + if (aggressor.IsYourPlayer && target.IsYourPlayer) + { + return false; + } - if (aggressor is ObservedCoopPlayer) - { - if (target.IsYourPlayer) - { - return false; - } - } + if (aggressor is ObservedCoopPlayer) + { + if (target.IsYourPlayer) + { + return false; + } + } - BotZone botZone = groupOwner.AIData.BotOwner.BotsGroup.BotZone; - foreach (KeyValuePair item in __instance.Groups()) - { - if (item.Key != botZone) - { - continue; - } + BotZone botZone = groupOwner.AIData.BotOwner.BotsGroup.BotZone; + foreach (KeyValuePair item in __instance.Groups()) + { + if (item.Key != botZone) + { + continue; + } - foreach (BotsGroup group in item.Value.GetGroups(notNull: true)) - { - bool differentSide = aggressor.Side != group.Side; - bool sameSide = aggressor.Side == target.Side; + foreach (BotsGroup group in item.Value.GetGroups(notNull: true)) + { + bool differentSide = aggressor.Side != group.Side; + bool sameSide = aggressor.Side == target.Side; - if (!group.HaveFollowTarget(aggressor) - && !group.Enemies.ContainsKey(aggressor) - && (differentSide || !sameSide) - && !group.HaveMemberWithRole(WildSpawnType.gifter) - && !group.HaveMemberWithRole(WildSpawnType.sectantWarrior) - && !group.HaveMemberWithRole(WildSpawnType.sectantPriest) - && !group.InitialFileSettings.Boss.NOT_ADD_TO_ENEMY_ON_KILLS - && group.ShallRevengeFor(target)) - { - group.AddEnemy(aggressor, EBotEnemyCause.AddEnemyToAllGroupsInBotZone); - } - } - } + if (!group.HaveFollowTarget(aggressor) + && !group.Enemies.ContainsKey(aggressor) + && (differentSide || !sameSide) + && !group.HaveMemberWithRole(WildSpawnType.gifter) + && !group.HaveMemberWithRole(WildSpawnType.sectantWarrior) + && !group.HaveMemberWithRole(WildSpawnType.sectantPriest) + && !group.InitialFileSettings.Boss.NOT_ADD_TO_ENEMY_ON_KILLS + && group.ShallRevengeFor(target)) + { + group.AddEnemy(aggressor, EBotEnemyCause.AddEnemyToAllGroupsInBotZone); + } + } + } - return false; - } - } + return false; + } + } } diff --git a/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs index a7a5d0ab..602e89a4 100644 --- a/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/BotDifficultyPatch_Override.cs @@ -6,31 +6,31 @@ namespace Fika.Core.Coop.Patches.Overrides { - public class BotDifficultyPatch_Override : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return typeof(GClass531).GetMethod(nameof(GClass531.LoadDifficultyStringInternal)); - } + public class BotDifficultyPatch_Override : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(GClass531).GetMethod(nameof(GClass531.LoadDifficultyStringInternal)); + } - [PatchPrefix] - private static bool PatchPrefix(ref string __result, BotDifficulty botDifficulty, WildSpawnType role) - { - if (FikaBackendUtils.IsServer) - { - __result = RequestHandler.GetJson($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}"); - bool resultIsNullEmpty = string.IsNullOrWhiteSpace(__result); - if (resultIsNullEmpty) - { - FikaPlugin.Instance.FikaLogger.LogError($"BotDifficultyPatchOverride: Unable to get difficulty settings for {role} {botDifficulty}"); - } + [PatchPrefix] + private static bool PatchPrefix(ref string __result, BotDifficulty botDifficulty, WildSpawnType role) + { + if (FikaBackendUtils.IsServer) + { + __result = RequestHandler.GetJson($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}"); + bool resultIsNullEmpty = string.IsNullOrWhiteSpace(__result); + if (resultIsNullEmpty) + { + FikaPlugin.Instance.FikaLogger.LogError($"BotDifficultyPatchOverride: Unable to get difficulty settings for {role} {botDifficulty}"); + } - return resultIsNullEmpty; // Server data returned = false = skip original method - } - else - { - return false; - } - } - } + return resultIsNullEmpty; // Server data returned = false = skip original method + } + else + { + return false; + } + } + } } diff --git a/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs index f19493c3..e21f39e5 100644 --- a/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/BotTemplateLimitPatch_Override.cs @@ -8,45 +8,45 @@ namespace Fika.Core.Coop.Patches.Overrides { - internal class BotTemplateLimitPatch_Override : ModulePatch - { - static BotTemplateLimitPatch_Override() - { - _ = nameof(BotsPresets.CreateProfile); - _ = nameof(WaveInfo.Difficulty); - } + internal class BotTemplateLimitPatch_Override : ModulePatch + { + static BotTemplateLimitPatch_Override() + { + _ = nameof(BotsPresets.CreateProfile); + _ = nameof(WaveInfo.Difficulty); + } - protected override MethodBase GetTargetMethod() => AccessTools.Method(typeof(BotsPresets), nameof(BotsPresets.method_1)); + protected override MethodBase GetTargetMethod() => AccessTools.Method(typeof(BotsPresets), nameof(BotsPresets.method_1)); - [PatchPostfix] - private static void PatchPostfix(List __result, List wavesProfiles, List delayed) - { - /* + [PatchPostfix] + private static void PatchPostfix(List __result, List wavesProfiles, List delayed) + { + /* Method sums Limits by grouping wavesPropfiles collection by Role and Difficulty then in each group sets Limit to 30, the remainder is stored in "delayed" collection. So we change Limit of each group. Clear delayed waves, we don't need them if we have enough loaded profiles and in method_2 it creates a lot of garbage. */ - delayed?.Clear(); + delayed?.Clear(); - if (FikaBackendUtils.IsServer) - { - foreach (WaveInfo wave in __result) - { - string json = RequestHandler.GetJson($"/singleplayer/settings/bot/limit/{wave.Role}"); - wave.Limit = string.IsNullOrWhiteSpace(json) - ? 30 - : Convert.ToInt32(json); - } - } - else - { - foreach (WaveInfo wave in __result) - { - wave.Limit = 30; - } - } - } - } + if (FikaBackendUtils.IsServer) + { + foreach (WaveInfo wave in __result) + { + string json = RequestHandler.GetJson($"/singleplayer/settings/bot/limit/{wave.Role}"); + wave.Limit = string.IsNullOrWhiteSpace(json) + ? 30 + : Convert.ToInt32(json); + } + } + else + { + foreach (WaveInfo wave in __result) + { + wave.Limit = 30; + } + } + } + } } diff --git a/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs index 6dd33996..7ea1bb50 100644 --- a/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/MaxBotPatch_Override.cs @@ -8,54 +8,54 @@ 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); + /// + /// 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}"); + Logger.LogDebug($"{GetType().Name} Type: {desiredType?.Name}"); + Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); - return desiredMethod; - } + 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"; - } + 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; + [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; - } - } - } + 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/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs index 8046ba41..24a7748d 100644 --- a/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/OfflineRaidSettingsMenuPatch_Override.cs @@ -10,102 +10,102 @@ namespace Fika.Core.Coop.Patches.Overrides { - public class OfflineRaidSettingsMenuPatch_Override : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.Show)); - } + public class OfflineRaidSettingsMenuPatch_Override : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.Show)); + } - private static RaidSettingsWindow instance; - private static List weatherCanvasGroups; - private static bool randomWeather; - public static bool UseCustomWeather - { - get => randomWeather; - set => randomWeather = value; - } + private static RaidSettingsWindow instance; + private static List weatherCanvasGroups; + private static bool randomWeather; + public static bool UseCustomWeather + { + get => randomWeather; + set => randomWeather = value; + } - [PatchPostfix] - private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, - List ____weatherCanvasGroups, UpdatableToggle ____randomTimeToggle, - UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups, - List ____playersSpawnPlaceCanvasGroups, DropDownBox ____playersSpawnPlaceDropdown, - RaidSettings raidSettings) - { - randomWeather = false; - // Always disable the Coop Mode checkbox - ____coopModeBlocker.SetBlock(true, "Co-op is always enabled in Fika"); + [PatchPostfix] + private static void PatchPostfix(RaidSettingsWindow __instance, UiElementBlocker ____coopModeBlocker, + List ____weatherCanvasGroups, UpdatableToggle ____randomTimeToggle, + UpdatableToggle ____randomWeatherToggle, List ____waterAndFoodCanvasGroups, + List ____playersSpawnPlaceCanvasGroups, DropDownBox ____playersSpawnPlaceDropdown, + RaidSettings raidSettings) + { + randomWeather = false; + // Always disable the Coop Mode checkbox + ____coopModeBlocker.SetBlock(true, "Co-op is always enabled in Fika"); - if (____weatherCanvasGroups != null) - { - weatherCanvasGroups = ____weatherCanvasGroups; - } + if (____weatherCanvasGroups != null) + { + weatherCanvasGroups = ____weatherCanvasGroups; + } - foreach (CanvasGroup canvasGroup in ____waterAndFoodCanvasGroups) - { - canvasGroup.SetUnlockStatus(true, true); - } + foreach (CanvasGroup canvasGroup in ____waterAndFoodCanvasGroups) + { + canvasGroup.SetUnlockStatus(true, true); + } - foreach (CanvasGroup canvasGroup in ____playersSpawnPlaceCanvasGroups) - { - canvasGroup.SetUnlockStatus(true, true); - } + foreach (CanvasGroup canvasGroup in ____playersSpawnPlaceCanvasGroups) + { + canvasGroup.SetUnlockStatus(true, true); + } - // 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").Value; - labelList.Clear(); - labelList.Add(new() - { - Label = "Together", - Enabled = true - }); - labelList.Add(new() - { - Label = "Random", - Enabled = true - }); - ____playersSpawnPlaceDropdown.SetTextInternal("Together"); + // 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").Value; + labelList.Clear(); + labelList.Add(new() + { + Label = "Together", + Enabled = true + }); + labelList.Add(new() + { + Label = "Random", + Enabled = true + }); + ____playersSpawnPlaceDropdown.SetTextInternal("Together"); - instance = __instance; + instance = __instance; - // If enforced from server, this will be true by default - if (!raidSettings.TimeAndWeatherSettings.IsRandomWeather) - { - ____randomWeatherToggle.Bind(new Action(ToggleWeather)); - ____randomTimeToggle.gameObject.GetComponent().SetUnlockStatus(false, false); + // If enforced from server, this will be true by default + if (!raidSettings.TimeAndWeatherSettings.IsRandomWeather) + { + ____randomWeatherToggle.Bind(new Action(ToggleWeather)); + ____randomTimeToggle.gameObject.GetComponent().SetUnlockStatus(false, false); - GameObject weatherToggle = GameObject.Find("RandomWeatherCheckmark"); - if (weatherToggle != null) - { - CustomTextMeshProUGUI customTmp = weatherToggle.GetComponentInChildren(); - if (customTmp != null) - { - customTmp.text = "Use custom weather"; - } - } - } - } + GameObject weatherToggle = GameObject.Find("RandomWeatherCheckmark"); + if (weatherToggle != null) + { + CustomTextMeshProUGUI customTmp = weatherToggle.GetComponentInChildren(); + if (customTmp != null) + { + customTmp.text = "Use custom weather"; + } + } + } + } - private static void ToggleWeather(bool enabled) - { - if (instance == null) - { - return; - } + private static void ToggleWeather(bool enabled) + { + if (instance == null) + { + return; + } - if (weatherCanvasGroups == null) - { - return; - } + if (weatherCanvasGroups == null) + { + return; + } - foreach (CanvasGroup item in weatherCanvasGroups) - { - item.SetUnlockStatus(enabled, enabled); - } + foreach (CanvasGroup item in weatherCanvasGroups) + { + item.SetUnlockStatus(enabled, enabled); + } - randomWeather = enabled; - instance.method_4(); - } - } + randomWeather = enabled; + instance.method_4(); + } + } } diff --git a/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs b/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs index d5da1d5f..2054698a 100644 --- a/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs +++ b/Fika.Core/Coop/Patches/Overrides/ScavProfileLoad_Override.cs @@ -4,17 +4,17 @@ namespace Fika.Core.Coop.Patches.Overrides { - internal class ScavProfileLoad_Override : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_50)); + internal class ScavProfileLoad_Override : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(TarkovApplication).GetMethod(nameof(TarkovApplication.method_50)); - [PatchPrefix] - private static void PatchPrefix(ref string profileId, Profile savageProfile, RaidSettings ____raidSettings) - { - if (!____raidSettings.IsPmc) - { - profileId = savageProfile.Id; - } - } - } + [PatchPrefix] + private static void PatchPrefix(ref string profileId, Profile savageProfile, RaidSettings ____raidSettings) + { + if (!____raidSettings.IsPmc) + { + profileId = savageProfile.Id; + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Coop/Patches/Weather/WeatherNode_Patch.cs b/Fika.Core/Coop/Patches/Weather/WeatherNode_Patch.cs index 47a68810..c6bedd21 100644 --- a/Fika.Core/Coop/Patches/Weather/WeatherNode_Patch.cs +++ b/Fika.Core/Coop/Patches/Weather/WeatherNode_Patch.cs @@ -7,19 +7,19 @@ namespace Fika.Core.Coop.Patches.Weather { - internal class WeatherNode_Patch : ModulePatch - { - protected override MethodBase GetTargetMethod() => typeof(WeatherController).GetMethod(nameof(WeatherController.method_0)); + internal class WeatherNode_Patch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(WeatherController).GetMethod(nameof(WeatherController.method_0)); - [PatchPostfix] - public static void Postfix(WeatherController __instance, WeatherClass[] nodes) - { - if (FikaBackendUtils.IsClient) - { - return; - } + [PatchPostfix] + public static void Postfix(WeatherController __instance, WeatherClass[] nodes) + { + if (FikaBackendUtils.IsClient) + { + return; + } - FikaBackendUtils.Nodes = nodes; - } - } + FikaBackendUtils.Nodes = nodes; + } + } } diff --git a/Fika.Core/Coop/Players/CoopBot.cs b/Fika.Core/Coop/Players/CoopBot.cs index d38c2e7b..373de80a 100644 --- a/Fika.Core/Coop/Players/CoopBot.cs +++ b/Fika.Core/Coop/Players/CoopBot.cs @@ -24,186 +24,186 @@ namespace Fika.Core.Coop.Players { - /// - /// Used to simulate bots for the host. - /// - public class CoopBot : CoopPlayer - { - public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - /// - /// The amount of players that have loaded this bot - /// - public int loadedPlayers = 0; - 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, IViewFilter filter) - { - CoopBot player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, - bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, aiControl); - - player.IsYourPlayer = false; - - InventoryControllerClass inventoryController = new CoopBotInventoryController(player, profile, true); - - player.PacketSender = player.gameObject.AddComponent(); - player.PacketReceiver = player.gameObject.AddComponent(); - - await player.Init(rotation, layerName, pointOfView, profile, inventoryController, - new CoopBotHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), - new CoopObservedStatisticsManager(), null, null, filter, - EVoipState.NotAvailable, aiControl, false); - - 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; - - return player; - } - - public override void OnVaulting() - { - // Do nothing - } - - public override void OnSkillLevelChanged(GClass1778 skill) - { - // Do nothing - } - - public override void OnWeaponMastered(MasterSkillClass masterSkill) - { - // Do nothing - } - - public override void CreateMovementContext() - { - LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; - MovementContext = BotMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); - } - - public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) - { - base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); - - if (FikaPlugin.EasyKillConditions.Value) - { - if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) - { - CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - if (mainPlayer != null) - { - float distance = Vector3.Distance(aggressor.Position, Position); - mainPlayer.HandleTeammateKill(damageInfo, bodyPart, Side, Profile.Info.Settings.Role, ProfileId, - distance, CurrentHour, Inventory.EquippedInSlotsTemplateIds, HealthController.BodyPartEffects, TriggerZones, - (CoopPlayer)aggressor); - } - } - } - } - - public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) - { - if (damageInfo.Player != null && damageInfo.Player.iPlayer is ObservedCoopPlayer) - { - LastDamageInfo.BodyPartColliderType = damageInfo.BodyPartColliderType; - LastDamageInfo.Direction = damageInfo.Direction; - LastDamageInfo.HitPoint = damageInfo.HitPoint; - LastDamageInfo.PenetrationPower = damageInfo.PenetrationPower; - return null; - } - - 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 hitInfo = new() - { - PoV = PointOfView, - Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), - Material = materialType - }; - float num = damage - damageInfo.Damage; - if (num > 0) - { - damageInfo.DidArmorDamage = num; - } - ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); - ShotReactions(damageInfo, bodyPartType); - ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); - - if (list != null) - { - QueueArmorDamagePackets([.. list]); - } - - return hitInfo; - } - - public override void BtrInteraction() - { - // Do nothing - } - - public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) - { - BotFirearmControllerHandler handler = new(this, weapon); - - bool flag = false; - FirearmController firearmController; - if ((firearmController = _handsController as FirearmController) != null) - { - flag = firearmController.CheckForFastWeaponSwitch(handler.weapon); - } - Func func = new(handler.ReturnController); - handler.process = new Process(this, func, handler.weapon, flag); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void OnDead(EDamageType damageType) - { - PacketSender.FirearmPackets.Clear(); - - float num = EFTHardSettings.Instance.HIT_FORCE; - num *= 0.3f + 0.7f * Mathf.InverseLerp(50f, 20f, LastDamageInfo.PenetrationPower); - _corpseAppliedForce = num; - - if (Side is EPlayerSide.Usec or EPlayerSide.Bear) - { - SetupDogTag(); - } - - if (FikaPlugin.ShowNotifications.Value) - { - if (IsBoss(Profile.Info.Settings.Role, out string name) && LastAggressor != null) - { - if (LastAggressor is CoopPlayer aggressor) - { - if (aggressor.gameObject.name.StartsWith("Player_") || aggressor.IsYourPlayer) - NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, LastAggressor.Profile.Info.MainProfileNickname)} killed boss {ColorizeText(Colors.BROWN, name)}", iconType: EFT.Communications.ENotificationIconType.Friend); - } - } - } - - /*if (Side == EPlayerSide.Savage) + /// + /// Used to simulate bots for the host. + /// + public class CoopBot : CoopPlayer + { + public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; + /// + /// The amount of players that have loaded this bot + /// + public int loadedPlayers = 0; + 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, IViewFilter filter) + { + CoopBot player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, + bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, aiControl); + + player.IsYourPlayer = false; + + InventoryControllerClass inventoryController = new CoopBotInventoryController(player, profile, true); + + player.PacketSender = player.gameObject.AddComponent(); + player.PacketReceiver = player.gameObject.AddComponent(); + + await player.Init(rotation, layerName, pointOfView, profile, inventoryController, + new CoopBotHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), + new CoopObservedStatisticsManager(), null, null, filter, + EVoipState.NotAvailable, aiControl, false); + + 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; + + return player; + } + + public override void OnVaulting() + { + // Do nothing + } + + public override void OnSkillLevelChanged(GClass1778 skill) + { + // Do nothing + } + + public override void OnWeaponMastered(MasterSkillClass masterSkill) + { + // Do nothing + } + + public override void CreateMovementContext() + { + LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; + MovementContext = BotMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); + } + + public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) + { + base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); + + if (FikaPlugin.EasyKillConditions.Value) + { + if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) + { + CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + if (mainPlayer != null) + { + float distance = Vector3.Distance(aggressor.Position, Position); + mainPlayer.HandleTeammateKill(damageInfo, bodyPart, Side, Profile.Info.Settings.Role, ProfileId, + distance, CurrentHour, Inventory.EquippedInSlotsTemplateIds, HealthController.BodyPartEffects, TriggerZones, + (CoopPlayer)aggressor); + } + } + } + } + + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) + { + if (damageInfo.Player != null && damageInfo.Player.iPlayer is ObservedCoopPlayer) + { + LastDamageInfo.BodyPartColliderType = damageInfo.BodyPartColliderType; + LastDamageInfo.Direction = damageInfo.Direction; + LastDamageInfo.HitPoint = damageInfo.HitPoint; + LastDamageInfo.PenetrationPower = damageInfo.PenetrationPower; + return null; + } + + 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 hitInfo = new() + { + PoV = PointOfView, + Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), + Material = materialType + }; + float num = damage - damageInfo.Damage; + if (num > 0) + { + damageInfo.DidArmorDamage = num; + } + ApplyDamageInfo(damageInfo, bodyPartType, colliderType, 0f); + ShotReactions(damageInfo, bodyPartType); + ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); + + if (list != null) + { + QueueArmorDamagePackets([.. list]); + } + + return hitInfo; + } + + public override void BtrInteraction() + { + // Do nothing + } + + public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) + { + BotFirearmControllerHandler handler = new(this, weapon); + + bool flag = false; + FirearmController firearmController; + if ((firearmController = _handsController as FirearmController) != null) + { + flag = firearmController.CheckForFastWeaponSwitch(handler.weapon); + } + Func func = new(handler.ReturnController); + handler.process = new Process(this, func, handler.weapon, flag); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void OnDead(EDamageType damageType) + { + PacketSender.FirearmPackets.Clear(); + + float num = EFTHardSettings.Instance.HIT_FORCE; + num *= 0.3f + 0.7f * Mathf.InverseLerp(50f, 20f, LastDamageInfo.PenetrationPower); + _corpseAppliedForce = num; + + if (Side is EPlayerSide.Usec or EPlayerSide.Bear) + { + SetupDogTag(); + } + + if (FikaPlugin.ShowNotifications.Value) + { + if (IsBoss(Profile.Info.Settings.Role, out string name) && LastAggressor != null) + { + if (LastAggressor is CoopPlayer aggressor) + { + if (aggressor.gameObject.name.StartsWith("Player_") || aggressor.IsYourPlayer) + NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, LastAggressor.Profile.Info.MainProfileNickname)} killed boss {ColorizeText(Colors.BROWN, name)}", iconType: EFT.Communications.ENotificationIconType.Friend); + } + } + } + + /*if (Side == EPlayerSide.Savage) { if (LastAggressor != null) { @@ -236,157 +236,157 @@ public override void OnDead(EDamageType damageType) } }*/ - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame.Bots.ContainsKey(ProfileId)) - { - coopGame.Bots.Remove(ProfileId); - } - - base.OnDead(damageType); - } - - private IEnumerator DestroyNetworkedComponents() - { - yield return new WaitForSeconds(2); - - if (PacketSender != null) - { - PacketSender.DestroyThis(); - } - } - - public override void ShowHelloNotification(string sender) - { - // Do nothing - } - - public override void UpdateTick() - { - base.UpdateTick(); - } - - protected void OnEnable() - { - if (!firstEnabled) - { - firstEnabled = true; - return; - } - - if (Singleton.Instantiated) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null && coopGame.Status == GameStatus.Started) - { - FikaServer server = Singleton.Instance; - GenericPacket packet = new(EPackageType.EnableBot) - { - NetId = MainPlayer.NetId, - BotNetId = NetId - }; - server.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - protected void OnDisable() - { - if (Singleton.Instantiated) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null && coopGame.Status == GameStatus.Started) - { - FikaServer server = Singleton.Instance; - GenericPacket packet = new(EPackageType.DisableBot) - { - NetId = MainPlayer.NetId, - BotNetId = NetId - }; - server.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - public override void OnDestroy() - { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame.Bots.ContainsKey(ProfileId)) + { + coopGame.Bots.Remove(ProfileId); + } + + base.OnDead(damageType); + } + + private IEnumerator DestroyNetworkedComponents() + { + yield return new WaitForSeconds(2); + + if (PacketSender != null) + { + PacketSender.DestroyThis(); + } + } + + public override void ShowHelloNotification(string sender) + { + // Do nothing + } + + public override void UpdateTick() + { + base.UpdateTick(); + } + + protected void OnEnable() + { + if (!firstEnabled) + { + firstEnabled = true; + return; + } + + if (Singleton.Instantiated) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null && coopGame.Status == GameStatus.Started) + { + FikaServer server = Singleton.Instance; + GenericPacket packet = new(EPackageType.EnableBot) + { + NetId = MainPlayer.NetId, + BotNetId = NetId + }; + server.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + protected void OnDisable() + { + if (Singleton.Instantiated) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null && coopGame.Status == GameStatus.Started) + { + FikaServer server = Singleton.Instance; + GenericPacket packet = new(EPackageType.DisableBot) + { + NetId = MainPlayer.NetId, + BotNetId = NetId + }; + server.SendDataToAll(new NetDataWriter(), ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + public override void OnDestroy() + { #if DEBUG - FikaPlugin.Instance.FikaLogger.LogInfo("Destroying " + ProfileId); + FikaPlugin.Instance.FikaLogger.LogInfo("Destroying " + ProfileId); #endif - if (Singleton.Instantiated) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null && coopGame.Status == GameStatus.Started) - { - FikaServer server = Singleton.Instance; - GenericPacket packet = new(EPackageType.DisposeBot) - { - NetId = MainPlayer.NetId, - 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)) - { - if (!coopHandler.Players.Remove(NetId)) - { - FikaPlugin.Instance.FikaLogger.LogWarning("Unable to remove " + NetId + " from CoopHandler.Players when Destroying"); - } - } - base.OnDestroy(); - } - - public override void SendHandsInteractionStateChanged(bool value, int animationId) - { - if (value) - { - MovementContext.SetBlindFire(0); - } - } - - private class BotFirearmControllerHandler(CoopBot coopBot, Weapon weapon) - { - private readonly CoopBot coopBot = coopBot; - public readonly Weapon weapon = weapon; - public Process process; - public Action confirmCallback; - - internal BotFirearmController ReturnController() - { - return BotFirearmController.Create(coopBot, weapon); - } - - internal void SendPacket() - { - if (weapon.IsStationaryWeapon) - { - return; - } - - coopBot.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.Weapon, - ItemId = weapon.Id, - ItemTemplateId = weapon.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - } + if (Singleton.Instantiated) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null && coopGame.Status == GameStatus.Started) + { + FikaServer server = Singleton.Instance; + GenericPacket packet = new(EPackageType.DisposeBot) + { + NetId = MainPlayer.NetId, + 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)) + { + if (!coopHandler.Players.Remove(NetId)) + { + FikaPlugin.Instance.FikaLogger.LogWarning("Unable to remove " + NetId + " from CoopHandler.Players when Destroying"); + } + } + base.OnDestroy(); + } + + public override void SendHandsInteractionStateChanged(bool value, int animationId) + { + if (value) + { + MovementContext.SetBlindFire(0); + } + } + + private class BotFirearmControllerHandler(CoopBot coopBot, Weapon weapon) + { + private readonly CoopBot coopBot = coopBot; + public readonly Weapon weapon = weapon; + public Process process; + public Action confirmCallback; + + internal BotFirearmController ReturnController() + { + return BotFirearmController.Create(coopBot, weapon); + } + + internal void SendPacket() + { + if (weapon.IsStationaryWeapon) + { + return; + } + + coopBot.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.Weapon, + ItemId = weapon.Id, + ItemTemplateId = weapon.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + } } diff --git a/Fika.Core/Coop/Players/CoopPlayer.cs b/Fika.Core/Coop/Players/CoopPlayer.cs index 65bce2e1..83f414b9 100644 --- a/Fika.Core/Coop/Players/CoopPlayer.cs +++ b/Fika.Core/Coop/Players/CoopPlayer.cs @@ -29,389 +29,389 @@ namespace Fika.Core.Coop.Players { - /// - /// 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; - public bool hasSkilledScav = false; - public float observedOverlap = 0f; - public bool leftStanceDisabled = false; - public Vector2 LastDirection = Vector2.zero; - public RagdollPacket RagdollPacket = default; - public bool hasGround = false; - public Transform RaycastCameraTransform; - public int NetId; - public bool IsObservedAI = false; - public Dictionary> OperationCallbacks = []; - public ClientMovementContext ClientMovementContext - { - get - { - return MovementContext as ClientMovementContext; - } - } - #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, IViewFilter filter, int netId, IStatisticsManager statisticsManager) - { - CoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, - bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, false); - - player.IsYourPlayer = true; - player.NetId = netId; - - CoopClientInventoryController inventoryController = new(player, profile, false); - - ISession session = Singleton>.Instance.GetClientBackEndSession(); - - 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(); - - GClass3233 achievementsController = new(profile, inventoryController, session, true); - achievementsController.Init(); - achievementsController.Run(); - - if (FikaBackendUtils.IsServer) - { - if (FikaBackendUtils.IsDedicated) - { - player.PacketSender = player.gameObject.AddComponent(); - } - else - { - player.PacketSender = player.gameObject.AddComponent(); - } - } - else if (FikaBackendUtils.IsClient) - { - player.PacketSender = player.gameObject.AddComponent(); - } - - player.PacketReceiver = player.gameObject.AddComponent(); - - await player.Init(rotation, layerName, pointOfView, profile, inventoryController, - new CoopClientHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), - statisticsManager, questController, achievementsController, filter, - EVoipState.NotAvailable, false, false); - - foreach (MagazineClass magazineClass in player.Inventory.GetPlayerItems(EPlayerItems.NonQuestItems).OfType()) - { - player.InventoryControllerClass.StrictCheckMagazine(magazineClass, true, player.Profile.MagDrillsMastering, false, false); - } - - player._handsController = EmptyHandsController.smethod_5(player); - 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 = FikaBackendUtils.PMCName; - - return player; - } - - public override void CreateMovementContext() - { - LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; - if (FikaPlugin.Instance.UseInertia) - { - MovementContext = ClientMovementContext.Create(this, new Func(GetBodyAnimatorCommon), - new Func(GetCharacterControllerCommon), movement_MASK); - } - else - { - MovementContext = NoInertiaMovementContext.Create(this, new Func(GetBodyAnimatorCommon), - new Func(GetCharacterControllerCommon), movement_MASK); - } - } - - public override void OnSkillLevelChanged(GClass1778 skill) - { - NotificationManagerClass.DisplayNotification(new GClass2044(skill)); - } - - public override bool CheckSurface() - { - hasGround = base.CheckSurface(); - return hasGround; - } - - public override void OnWeaponMastered(MasterSkillClass masterSkill) - { - NotificationManagerClass.DisplayMessageNotification(string.Format("MasteringLevelUpMessage".Localized(null), - masterSkill.MasteringGroup.Id.Localized(null), - masterSkill.Level.ToString()), ENotificationDurationType.Default, ENotificationIconType.Default, null); - } - - public override void BtrInteraction(BTRSide btr, byte placeId, EInteractionType interaction) - { - base.BtrInteraction(btr, placeId, interaction); - if (FikaBackendUtils.IsClient) - { - BTRInteractionPacket packet = new(NetId) - { - HasInteractPacket = true, - InteractPacket = btr.GetInteractWithBtrPacket(placeId, interaction) - }; - - PacketSender.Writer.Reset(); - PacketSender.Client.SendData(PacketSender.Writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - else if (FikaBackendUtils.IsServer) - { - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - PlayerInteractPacket interactPacket = btr.GetInteractWithBtrPacket(placeId, interaction); - - bool success = coopHandler.serverBTR.HostInteraction(this, interactPacket); - if (success) - { - BTRInteractionPacket packet = new(NetId) - { - HasInteractPacket = true, - InteractPacket = interactPacket - }; - - PacketSender.Writer.Reset(); - PacketSender.Server.SendDataToAll(PacketSender.Writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - UpdateInteractionCast(); - } - - public void ProcessInteractWithBTR(BTRInteractionPacket packet) - { - if (packet.HasInteractPacket) - { - if (packet.InteractPacket.HasInteraction) - { - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - if (coopHandler.clientBTR != null) - { - coopHandler.clientBTR.ClientInteraction(this, packet.InteractPacket); - } - } - } - } - else if (IsYourPlayer) - { - GlobalEventHandlerClass.CreateEvent().Invoke(PlayerId, EBtrInteractionStatus.Blacklisted); - } - } - - public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) - { - if (IsYourPlayer) - { - if (damageInfo.Player != null) - { - if (!FikaPlugin.Instance.FriendlyFire && damageInfo.Player.iPlayer is ObservedCoopPlayer observedCoopPlayer && !observedCoopPlayer.IsObservedAI) - { - return; - } - } - if (colliderType == EBodyPartColliderType.HeadCommon) - { - damageInfo.Damage *= FikaPlugin.HeadDamageMultiplier.Value; - } - - if (colliderType is EBodyPartColliderType.RightSideChestUp or EBodyPartColliderType.LeftSideChestUp) - { - damageInfo.Damage *= FikaPlugin.ArmpitDamageMultiplier.Value; - } - - if (bodyPartType is EBodyPart.Stomach) - { - damageInfo.Damage *= FikaPlugin.StomachDamageMultiplier.Value; - } - } - - base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); - } - - public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) - { - if (damageInfo.DamageType is EDamageType.Sniper or EDamageType.Landmine) - { - return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); - } - - if (damageInfo.Player?.iPlayer is CoopBot) - { - return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); - } - - return null; - } - - 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; - } - - #region Proceed - public override void Proceed(bool withNetwork, Callback callback, bool scheduled = true) - { - base.Proceed(withNetwork, callback, scheduled); - PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.EmptyHands, - Scheduled = scheduled - } - }); - } - - public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) - { - FoodControllerHandler handler = new(this, foodDrink, amount, EBodyPart.Head, animationVariant); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, foodDrink, false); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(Item item, Callback callback, bool scheduled = true) - { - QuickUseItemControllerHandler handler = new(this, item); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, item, true); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) - { - KnifeControllerHandler handler = new(this, knife); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, handler.knife.Item, false); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) - { - QuickKnifeControllerHandler handler = new(this, knife); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, handler.knife.Item, true); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) - { - MedsControllerHandler handler = new(this, meds, bodyPart, animationVariant); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, meds, false); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) - { - QuickGrenadeControllerHandler handler = new(this, throwWeap); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, throwWeap, false); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) - { - GrenadeControllerHandler handler = new(this, throwWeap); - - Func func = new(handler.ReturnController); - handler.process = new(this, func, throwWeap, false); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) - { - FirearmControllerHandler handler = new(this, weapon); - bool flag = false; - FirearmController firearmController; - if ((firearmController = _handsController as FirearmController) != null) - { - flag = firearmController.CheckForFastWeaponSwitch(handler.weapon); - } - Func func = new(handler.ReturnController); - handler.process = new Process(this, func, handler.weapon, flag); - handler.confirmCallback = new(handler.SendPacket); - handler.process.method_0(new(handler.HandleResult), callback, scheduled); - } - - public override void Proceed(Item item, Callback callback, bool scheduled = true) - { - // what is this - base.Proceed(item, callback, scheduled); - } - #endregion - - public override void DropCurrentController(Action callback, bool fastDrop, Item nextControllerItem = null) - { - base.DropCurrentController(callback, fastDrop, nextControllerItem); - - /*PacketSender.CommonPlayerPackets.Enqueue(new() + /// + /// 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; + public bool hasSkilledScav = false; + public float observedOverlap = 0f; + public bool leftStanceDisabled = false; + public Vector2 LastDirection = Vector2.zero; + public RagdollPacket RagdollPacket = default; + public bool hasGround = false; + public Transform RaycastCameraTransform; + public int NetId; + public bool IsObservedAI = false; + public Dictionary> OperationCallbacks = []; + public ClientMovementContext ClientMovementContext + { + get + { + return MovementContext as ClientMovementContext; + } + } + #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, IViewFilter filter, int netId, IStatisticsManager statisticsManager) + { + CoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, armsUpdateMode, + bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, false); + + player.IsYourPlayer = true; + player.NetId = netId; + + CoopClientInventoryController inventoryController = new(player, profile, false); + + ISession session = Singleton>.Instance.GetClientBackEndSession(); + + 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(); + + GClass3233 achievementsController = new(profile, inventoryController, session, true); + achievementsController.Init(); + achievementsController.Run(); + + if (FikaBackendUtils.IsServer) + { + if (FikaBackendUtils.IsDedicated) + { + player.PacketSender = player.gameObject.AddComponent(); + } + else + { + player.PacketSender = player.gameObject.AddComponent(); + } + } + else if (FikaBackendUtils.IsClient) + { + player.PacketSender = player.gameObject.AddComponent(); + } + + player.PacketReceiver = player.gameObject.AddComponent(); + + await player.Init(rotation, layerName, pointOfView, profile, inventoryController, + new CoopClientHealthController(profile.Health, player, inventoryController, profile.Skills, aiControl), + statisticsManager, questController, achievementsController, filter, + EVoipState.NotAvailable, false, false); + + foreach (MagazineClass magazineClass in player.Inventory.GetPlayerItems(EPlayerItems.NonQuestItems).OfType()) + { + player.InventoryControllerClass.StrictCheckMagazine(magazineClass, true, player.Profile.MagDrillsMastering, false, false); + } + + player._handsController = EmptyHandsController.smethod_5(player); + 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 = FikaBackendUtils.PMCName; + + return player; + } + + public override void CreateMovementContext() + { + LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; + if (FikaPlugin.Instance.UseInertia) + { + MovementContext = ClientMovementContext.Create(this, new Func(GetBodyAnimatorCommon), + new Func(GetCharacterControllerCommon), movement_MASK); + } + else + { + MovementContext = NoInertiaMovementContext.Create(this, new Func(GetBodyAnimatorCommon), + new Func(GetCharacterControllerCommon), movement_MASK); + } + } + + public override void OnSkillLevelChanged(GClass1778 skill) + { + NotificationManagerClass.DisplayNotification(new GClass2044(skill)); + } + + public override bool CheckSurface() + { + hasGround = base.CheckSurface(); + return hasGround; + } + + public override void OnWeaponMastered(MasterSkillClass masterSkill) + { + NotificationManagerClass.DisplayMessageNotification(string.Format("MasteringLevelUpMessage".Localized(null), + masterSkill.MasteringGroup.Id.Localized(null), + masterSkill.Level.ToString()), ENotificationDurationType.Default, ENotificationIconType.Default, null); + } + + public override void BtrInteraction(BTRSide btr, byte placeId, EInteractionType interaction) + { + base.BtrInteraction(btr, placeId, interaction); + if (FikaBackendUtils.IsClient) + { + BTRInteractionPacket packet = new(NetId) + { + HasInteractPacket = true, + InteractPacket = btr.GetInteractWithBtrPacket(placeId, interaction) + }; + + PacketSender.Writer.Reset(); + PacketSender.Client.SendData(PacketSender.Writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + else if (FikaBackendUtils.IsServer) + { + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + PlayerInteractPacket interactPacket = btr.GetInteractWithBtrPacket(placeId, interaction); + + bool success = coopHandler.serverBTR.HostInteraction(this, interactPacket); + if (success) + { + BTRInteractionPacket packet = new(NetId) + { + HasInteractPacket = true, + InteractPacket = interactPacket + }; + + PacketSender.Writer.Reset(); + PacketSender.Server.SendDataToAll(PacketSender.Writer, ref packet, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + UpdateInteractionCast(); + } + + public void ProcessInteractWithBTR(BTRInteractionPacket packet) + { + if (packet.HasInteractPacket) + { + if (packet.InteractPacket.HasInteraction) + { + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + if (coopHandler.clientBTR != null) + { + coopHandler.clientBTR.ClientInteraction(this, packet.InteractPacket); + } + } + } + } + else if (IsYourPlayer) + { + GlobalEventHandlerClass.CreateEvent().Invoke(PlayerId, EBtrInteractionStatus.Blacklisted); + } + } + + public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) + { + if (IsYourPlayer) + { + if (damageInfo.Player != null) + { + if (!FikaPlugin.Instance.FriendlyFire && damageInfo.Player.iPlayer is ObservedCoopPlayer observedCoopPlayer && !observedCoopPlayer.IsObservedAI) + { + return; + } + } + if (colliderType == EBodyPartColliderType.HeadCommon) + { + damageInfo.Damage *= FikaPlugin.HeadDamageMultiplier.Value; + } + + if (colliderType is EBodyPartColliderType.RightSideChestUp or EBodyPartColliderType.LeftSideChestUp) + { + damageInfo.Damage *= FikaPlugin.ArmpitDamageMultiplier.Value; + } + + if (bodyPartType is EBodyPart.Stomach) + { + damageInfo.Damage *= FikaPlugin.StomachDamageMultiplier.Value; + } + } + + base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); + } + + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) + { + if (damageInfo.DamageType is EDamageType.Sniper or EDamageType.Landmine) + { + return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); + } + + if (damageInfo.Player?.iPlayer is CoopBot) + { + return SimulatedApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, shotId); + } + + return null; + } + + 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; + } + + #region Proceed + public override void Proceed(bool withNetwork, Callback callback, bool scheduled = true) + { + base.Proceed(withNetwork, callback, scheduled); + PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.EmptyHands, + Scheduled = scheduled + } + }); + } + + public override void Proceed(FoodClass foodDrink, float amount, Callback callback, int animationVariant, bool scheduled = true) + { + FoodControllerHandler handler = new(this, foodDrink, amount, EBodyPart.Head, animationVariant); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, foodDrink, false); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(Item item, Callback callback, bool scheduled = true) + { + QuickUseItemControllerHandler handler = new(this, item); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, item, true); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) + { + KnifeControllerHandler handler = new(this, knife); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, handler.knife.Item, false); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) + { + QuickKnifeControllerHandler handler = new(this, knife); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, handler.knife.Item, true); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) + { + MedsControllerHandler handler = new(this, meds, bodyPart, animationVariant); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, meds, false); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) + { + QuickGrenadeControllerHandler handler = new(this, throwWeap); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, throwWeap, false); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) + { + GrenadeControllerHandler handler = new(this, throwWeap); + + Func func = new(handler.ReturnController); + handler.process = new(this, func, throwWeap, false); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) + { + FirearmControllerHandler handler = new(this, weapon); + bool flag = false; + FirearmController firearmController; + if ((firearmController = _handsController as FirearmController) != null) + { + flag = firearmController.CheckForFastWeaponSwitch(handler.weapon); + } + Func func = new(handler.ReturnController); + handler.process = new Process(this, func, handler.weapon, flag); + handler.confirmCallback = new(handler.SendPacket); + handler.process.method_0(new(handler.HandleResult), callback, scheduled); + } + + public override void Proceed(Item item, Callback callback, bool scheduled = true) + { + // what is this + base.Proceed(item, callback, scheduled); + } + #endregion + + public override void DropCurrentController(Action callback, bool fastDrop, Item nextControllerItem = null) + { + base.DropCurrentController(callback, fastDrop, nextControllerItem); + + /*PacketSender.CommonPlayerPackets.Enqueue(new() { HasDrop = true, DropPacket = new() @@ -420,834 +420,834 @@ public override void DropCurrentController(Action callback, bool fastDrop, Item HasItemId = false } });*/ - } - - public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) - { - base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); - - // Handle 'Help Scav' rep gains - if (aggressor is CoopPlayer coopPlayer) - { - if (coopPlayer.Side == EPlayerSide.Savage) - { - coopPlayer.Loyalty.method_1(this); - } - - if (Side == EPlayerSide.Savage && coopPlayer.Side != EPlayerSide.Savage && !coopPlayer.hasSkilledScav) - { - coopPlayer.hasSkilledScav = true; - return; - } - else if (Side != EPlayerSide.Savage && hasSkilledScav && aggressor.Side == EPlayerSide.Savage) - { - coopPlayer.Profile?.FenceInfo?.AddStanding(Profile.Info.Settings.StandingForKill, EFT.Counters.EFenceStandingSource.ScavHelp); - } - } - } - - public void HandleTeammateKill(DamageInfo damage, EBodyPart bodyPart, - EPlayerSide playerSide, WildSpawnType role, string playerProfileId, - float distance, int hour, List targetEquipment, - HealthEffects enemyEffects, List zoneIds, CoopPlayer killer) - { - if (role != WildSpawnType.pmcBEAR) - { - if (role == WildSpawnType.pmcUSEC) - { - playerSide = EPlayerSide.Usec; - } - } - else - { - playerSide = EPlayerSide.Bear; - } - - List list = ["Any"]; - - switch (playerSide) - { - case EPlayerSide.Usec: - list.Add("Usec"); - list.Add("AnyPmc"); - list.Add("Enemy"); - break; - case EPlayerSide.Bear: - list.Add("Bear"); - list.Add("AnyPmc"); - list.Add("Enemy"); - break; - case EPlayerSide.Savage: - list.Add("Savage"); - list.Add("Bot"); - break; - } - - foreach (string value in list) - { - AbstractQuestControllerClass.CheckKillConditionCounter(value, playerProfileId, targetEquipment, damage.Weapon, - bodyPart, Location, distance, role.ToStringNoBox(), hour, enemyEffects, - killer.HealthController.BodyPartEffects, zoneIds, killer.HealthController.ActiveBuffsNames()); - - /*AbstractAchievementControllerClass.CheckKillConditionCounter(value, playerProfileId, targetEquipment, damage.Weapon, + } + + public override void OnBeenKilledByAggressor(IPlayer aggressor, DamageInfo damageInfo, EBodyPart bodyPart, EDamageType lethalDamageType) + { + base.OnBeenKilledByAggressor(aggressor, damageInfo, bodyPart, lethalDamageType); + + // Handle 'Help Scav' rep gains + if (aggressor is CoopPlayer coopPlayer) + { + if (coopPlayer.Side == EPlayerSide.Savage) + { + coopPlayer.Loyalty.method_1(this); + } + + if (Side == EPlayerSide.Savage && coopPlayer.Side != EPlayerSide.Savage && !coopPlayer.hasSkilledScav) + { + coopPlayer.hasSkilledScav = true; + return; + } + else if (Side != EPlayerSide.Savage && hasSkilledScav && aggressor.Side == EPlayerSide.Savage) + { + coopPlayer.Profile?.FenceInfo?.AddStanding(Profile.Info.Settings.StandingForKill, EFT.Counters.EFenceStandingSource.ScavHelp); + } + } + } + + public void HandleTeammateKill(DamageInfo damage, EBodyPart bodyPart, + EPlayerSide playerSide, WildSpawnType role, string playerProfileId, + float distance, int hour, List targetEquipment, + HealthEffects enemyEffects, List zoneIds, CoopPlayer killer) + { + if (role != WildSpawnType.pmcBEAR) + { + if (role == WildSpawnType.pmcUSEC) + { + playerSide = EPlayerSide.Usec; + } + } + else + { + playerSide = EPlayerSide.Bear; + } + + List list = ["Any"]; + + switch (playerSide) + { + case EPlayerSide.Usec: + list.Add("Usec"); + list.Add("AnyPmc"); + list.Add("Enemy"); + break; + case EPlayerSide.Bear: + list.Add("Bear"); + list.Add("AnyPmc"); + list.Add("Enemy"); + break; + case EPlayerSide.Savage: + list.Add("Savage"); + list.Add("Bot"); + break; + } + + foreach (string value in list) + { + AbstractQuestControllerClass.CheckKillConditionCounter(value, playerProfileId, targetEquipment, damage.Weapon, + bodyPart, Location, distance, role.ToStringNoBox(), hour, enemyEffects, + killer.HealthController.BodyPartEffects, zoneIds, killer.HealthController.ActiveBuffsNames()); + + /*AbstractAchievementControllerClass.CheckKillConditionCounter(value, playerProfileId, targetEquipment, damage.Weapon, bodyPart, Location, distance, role.ToStringNoBox(), hour, enemyEffects, killer.HealthController.BodyPartEffects, zoneIds, killer.HealthController.ActiveBuffsNames());*/ - } + } - } + } #if DEBUG - public override void ShowStringNotification(string message) - { - if (IsYourPlayer) - { - ConsoleScreen.Log(message); - FikaPlugin.Instance.FikaLogger.LogInfo(message); - } - } + public override void ShowStringNotification(string message) + { + if (IsYourPlayer) + { + ConsoleScreen.Log(message); + FikaPlugin.Instance.FikaLogger.LogInfo(message); + } + } #endif - public override void SetInventoryOpened(bool opened) - { - if (this is ObservedCoopPlayer) - { - base.SetInventoryOpened(opened); - return; - } - - base.SetInventoryOpened(opened); - PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasInventoryChanged = true, - SetInventoryOpen = opened - }); - } - - public override void SetCompassState(bool value) - { - base.SetCompassState(value); - PacketSender.FirearmPackets.Enqueue(new() - { - HasCompassChange = true, - CompassState = value - }); - } - - public void ClientApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) - { - base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); - } - - public void ClientApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider) - { - _ = base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, GStruct389.EMPTY_SHOT_ID); - } - - public override void SendHeadlightsPacket(bool isSilent) - { - FirearmLightStateStruct[] lightStates = _helmetLightControllers.Select(new Func(ClientPlayer.Class1456.class1456_0.method_0)).ToArray(); - - if (PacketSender != null) - { - PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasHeadLightsPacket = true, - HeadLightsPacket = new() - { - Amount = lightStates.Count(), - IsSilent = isSilent, - LightStates = lightStates - } - }); - } - } - - public override void OnItemAddedOrRemoved(Item item, ItemAddress location, bool added) - { - base.OnItemAddedOrRemoved(item, location, added); - } - - public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBank bank, PhraseSpeakerClass speaker) - { - base.OnPhraseTold(@event, clip, bank, speaker); - - if (ActiveHealthController.IsAlive) - { - PacketSender.CommonPlayerPackets.Enqueue(new() - { - Phrase = @event, - PhraseIndex = clip.NetId - }); - } - } - - public override void OperateStationaryWeapon(StationaryWeapon stationaryWeapon, GStruct170.EStationaryCommand command) - { - base.OperateStationaryWeapon(stationaryWeapon, command); - PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasStationaryPacket = true, - StationaryPacket = new() - { - Command = (StationaryPacket.EStationaryCommand)command, - Id = stationaryWeapon.Id - } - }); - } - - protected virtual void ReceiveSay(EPhraseTrigger trigger, int index) - { - if (HealthController.IsAlive) - { - Speaker.PlayDirect(trigger, index); - } - } - - // Start - public override void vmethod_0(WorldInteractiveObject interactiveObject, InteractionResult interactionResult, Action callback) - { - if (this is ObservedCoopPlayer) - { - base.vmethod_0(interactiveObject, interactionResult, callback); - return; - } - - base.vmethod_0(interactiveObject, interactionResult, callback); - - CommonPlayerPacket packet = new() - { - HasWorldInteractionPacket = true, - WorldInteractionPacket = new() - { - InteractiveId = interactiveObject.Id, - InteractionType = interactionResult.InteractionType, - InteractionStage = EInteractionStage.Start, - ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty - } - }; - PacketSender.CommonPlayerPackets.Enqueue(packet); - } - - // Execute - public override void vmethod_1(WorldInteractiveObject door, InteractionResult interactionResult) - { - if (this is ObservedCoopPlayer) - { - base.vmethod_1(door, interactionResult); - return; - } - - base.vmethod_1(door, interactionResult); - - CommonPlayerPacket packet = new() - { - HasWorldInteractionPacket = true, - WorldInteractionPacket = new() - { - InteractiveId = door.Id, - InteractionType = interactionResult.InteractionType, - InteractionStage = EInteractionStage.Execute, - ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty - } - }; - PacketSender.CommonPlayerPackets.Enqueue(packet); - - UpdateInteractionCast(); - } - - public override void vmethod_3(EGesture gesture) - { - base.vmethod_3(gesture); - PacketSender.FirearmPackets.Enqueue(new() - { - Gesture = gesture - }); - } - - public override void ApplyCorpseImpulse() - { - Corpse.Ragdoll.ApplyImpulse(LastDamageInfo.HitCollider, LastDamageInfo.Direction, LastDamageInfo.HitPoint, _corpseAppliedForce); - } - - public HealthSyncPacket SetupDeathPacket(GStruct346 packet) - { - float num = EFTHardSettings.Instance.HIT_FORCE; - num *= 0.3f + 0.7f * Mathf.InverseLerp(50f, 20f, LastDamageInfo.PenetrationPower); - _corpseAppliedForce = num; - - if (LastAggressor != null) - { - SetupDogTag(); - } - - return new(NetId) - { - Packet = packet, - KillerId = !string.IsNullOrEmpty(KillerId) ? KillerId : null, - RagdollPacket = new() - { - BodyPartColliderType = LastDamageInfo.BodyPartColliderType, - Direction = LastDamageInfo.Direction, - Point = LastDamageInfo.HitPoint, - Force = _corpseAppliedForce, - OverallVelocity = Velocity - }, - Equipment = Equipment, - TriggerZones = TriggerZones.Count > 0 ? [.. TriggerZones] : null, - }; - } - - public override void OnDead(EDamageType damageType) - { - base.OnDead(damageType); - - StartCoroutine(DisableNetworkedComponents()); - if (IsYourPlayer) - { - StartCoroutine(LocalPlayerDied()); - } - } - - private IEnumerator DisableNetworkedComponents() - { - yield return new WaitForSeconds(2); - - if (PacketSender != null) - { - PacketSender.Enabled = false; - } - } - - private IEnumerator LocalPlayerDied() - { - AddPlayerRequest request = new(FikaBackendUtils.GetGroupId(), ProfileId); - Task diedTask = FikaRequestHandler.PlayerDied(request); - while (!diedTask.IsCompleted) - { - yield return new WaitForEndOfFrame(); - } - } - - public override void Move(Vector2 direction) - { - if (direction.sqrMagnitude > 0) - { - base.Move(direction); - LastDirection = direction; - } - } - - /// - /// Used to determine whether this player was a boss - /// - /// - /// true if it's a boss, false if not - public bool IsBoss(WildSpawnType wildSpawnType, out string name) - { - name = null; - switch (wildSpawnType) - { - case WildSpawnType.bossBoar: - { - name = "Kaban"; - break; - } - case WildSpawnType.bossBully: - { - name = "Reshala"; - break; - } - case WildSpawnType.bossGluhar: - { - name = "Glukhar"; - break; - } - case WildSpawnType.bossKilla: - { - name = "Killa"; - break; - } - case WildSpawnType.bossKnight: - { - name = "Knight"; - break; - } - case WildSpawnType.bossKojaniy: - { - name = "Shturman"; - break; - } - case WildSpawnType.bossSanitar: - { - name = "Sanitar"; - break; - } - case WildSpawnType.bossTagilla: - { - name = "Tagilla"; - break; - } - case WildSpawnType.bossZryachiy: - { - name = "Zryachiy"; - break; - } - case WildSpawnType.followerBigPipe: - { - name = "Big Pipe"; - break; - } - case WildSpawnType.followerBirdEye: - { - name = "Bird Eye"; - break; - } - case WildSpawnType.sectantPriest: - { - name = "Cultist Priest"; - break; - } - } - return name != null; - } - - private void HandleInteractPacket(WorldInteractionPacket packet) - { - if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) - { - WorldInteractiveObject worldInteractiveObject = Singleton.Instance.FindDoor(packet.InteractiveId); - if (worldInteractiveObject != null) - { - if (worldInteractiveObject.isActiveAndEnabled) - { - InteractionResult interactionResult; - Action action; - if (packet.InteractionType == EInteractionType.Unlock) - { - KeyHandler keyHandler = new(this); - - if (string.IsNullOrEmpty(packet.ItemId)) - { - FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: ItemID was null!"); - return; - } - - Item keyItem = FindItem(packet.ItemId); - if (keyItem == null) - { - FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: Could not find item: " + packet.ItemId); - return; - } - - KeyComponent keyComponent = keyItem.GetItemComponent(); - if (keyComponent == null) - { - FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: keyComponent was null!"); - return; - } - - keyHandler.unlockResult = worldInteractiveObject.UnlockOperation(keyComponent, this); - if (keyHandler.unlockResult.Error != null) - { - FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: Error when processing unlockResult: " + keyHandler.unlockResult.Error); - return; - } - - interactionResult = keyHandler.unlockResult.Value; - keyHandler.unlockResult.Value.RaiseEvents(_inventoryController, CommandStatus.Begin); - action = new(keyHandler.HandleKeyEvent); - } - else - { - interactionResult = new InteractionResult(packet.InteractionType); - action = null; - } - - if (packet.InteractionStage == EInteractionStage.Start) - { - vmethod_0(worldInteractiveObject, interactionResult, action); - return; - } - - if (packet.InteractionStage != EInteractionStage.Execute) - { - worldInteractiveObject.Interact(interactionResult); - return; - } - - vmethod_1(worldInteractiveObject, interactionResult); - } - - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("HandleInteractPacket: WorldInteractiveObject was null or disabled!"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("HandleInteractPacket: CoopHandler was null!"); - } - } - - public override void TryInteractionCallback(LootableContainer container) - { - LootableContainerInteractionHandler handler = new(this, container); - if (handler.container != null && _openAction != null) - { - _openAction(new Action(handler.Handle)); - } - _openAction = null; - } - - public void SetupMainPlayer() - { - // Set own group id, ignore if dedicated - if (!Profile.Info.Nickname.Contains("dedicated_")) - { - 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); - if (result.Error != null) - { - FikaPlugin.Instance.FikaLogger.LogWarning("CoopPlayer::SetupMainPlayer: Error removing dog tag!"); - } - } - - string templateId = GetDogTagTemplateId(); - - if (!string.IsNullOrEmpty(templateId)) - { - Item item = Singleton.Instance.CreateItem(MongoID.Generate(), templateId, null); - - 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; - - 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.LogError("Could not get templateId for DogTag!"); - } - } - - if (!string.IsNullOrEmpty(Location)) - { - // Delete labs card on labs - if (Location.ToLower() == "laboratory") - { - foreach (Item item in Inventory.AllRealPlayerItems) - { - if (item.TemplateId == "5c94bbff86f7747ee735c08f") - { - InteractionsHandlerClass.Remove(item, _inventoryController, false, true); - break; - } - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CoopPlayer::SetupMainPlayer: Location was null!"); - } - } - - private string GetDogTagTemplateId() - { - if (Side is EPlayerSide.Usec) - { - switch (Profile.Info.SelectedMemberCategory) - { - case EMemberCategory.Default: - return "59f32c3b86f77472a31742f0"; - case EMemberCategory.UniqueId: - return "6662e9f37fa79a6d83730fa0"; - case EMemberCategory.Unheard: - return "6662ea05f6259762c56f3189"; - } - } - else if (Side is EPlayerSide.Bear) - { - switch (Profile.Info.SelectedMemberCategory) - { - 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) - { - ReceiveSay(packet.Phrase, packet.PhraseIndex); - } - - if (packet.HasWorldInteractionPacket) - { - HandleInteractPacket(packet.WorldInteractionPacket); - } - - if (packet.HasContainerInteractionPacket) - { - WorldInteractiveObject lootableContainer = Singleton.Instance.FindDoor(packet.ContainerInteractionPacket.InteractiveId); - if (lootableContainer != null) - { - if (lootableContainer.isActiveAndEnabled) - { - string methodName = string.Empty; - switch (packet.ContainerInteractionPacket.InteractionType) - { - case EInteractionType.Open: - methodName = "Open"; - break; - case EInteractionType.Close: - methodName = "Close"; - break; - case EInteractionType.Unlock: - methodName = "Unlock"; - break; - case EInteractionType.Breach: - break; - case EInteractionType.Lock: - methodName = "Lock"; - break; - } - - if (!string.IsNullOrEmpty(methodName)) - { - void Interact() => lootableContainer.Invoke(methodName, 0); - - if (packet.ContainerInteractionPacket.InteractionType == EInteractionType.Unlock) - { - Interact(); - } - else - { - lootableContainer.StartBehaviourTimer(EFTHardSettings.Instance.DelayToOpenContainer, Interact); - } - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CommonPlayerPacket::ContainerInteractionPacket: LootableContainer was null!"); - } - } - - if (packet.HasProceedPacket) - { - if (this is ObservedCoopPlayer observedCoopPlayer) - { - observedCoopPlayer.HandleProceedPacket(packet.ProceedPacket); - } - } - - if (packet.HasHeadLightsPacket) - { - try - { - if (_helmetLightControllers != null) - { - for (int i = 0; i < _helmetLightControllers.Count(); i++) - { - _helmetLightControllers.ElementAt(i)?.LightMod?.SetLightState(packet.HeadLightsPacket.LightStates[i]); - } - if (!packet.HeadLightsPacket.IsSilent) - { - SwitchHeadLightsAnimation(); - } - } - } - catch (Exception) - { - // Do nothing - } - } - - if (packet.HasInventoryChanged) - { - base.SetInventoryOpened(packet.SetInventoryOpen); - } - - if (packet.HasDrop) - { - if (packet.DropPacket.HasItemId) - { - Item item = FindItem(packet.ProceedPacket.ItemId); - base.DropCurrentController(null, packet.DropPacket.FastDrop, item ?? null); - } - else - { - base.DropCurrentController(null, packet.DropPacket.FastDrop, null); - } - } - - if (packet.HasStationaryPacket) - { - StationaryWeapon stationaryWeapon = (packet.StationaryPacket.Command == StationaryPacket.EStationaryCommand.Occupy) ? Singleton.Instance.FindStationaryWeapon(packet.StationaryPacket.Id) : null; - base.OperateStationaryWeapon(stationaryWeapon, (GStruct170.EStationaryCommand)packet.StationaryPacket.Command); - } - - if (packet.Pickup) - { - MovementContext.SetInteractInHands(packet.Pickup, packet.PickupAnimation); - } - - if (packet.HasVaultPacket) - { - DoObservedVault(packet.VaultPacket); - } - } - - public virtual void DoObservedVault(VaultPacket vaultPacket) - { - - } - - public void HandleCallbackFromServer(in OperationCallbackPacket operationCallbackPacket) - { - if (OperationCallbacks.TryGetValue(operationCallbackPacket.CallbackId, out Callback callback)) - { - if (operationCallbackPacket.OperationStatus != EOperationStatus.Started) - { - OperationCallbacks.Remove(operationCallbackPacket.CallbackId); - } - if (operationCallbackPacket.OperationStatus != EOperationStatus.Failed) - { - callback(new Result(operationCallbackPacket.OperationStatus)); - } - else - { - callback(new Result(EOperationStatus.Failed) - { - Error = operationCallbackPacket.Error - }); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"Could not find CallbackId {operationCallbackPacket.CallbackId}!"); - } - } - - public virtual void HandleInventoryPacket(in InventoryPacket packet) - { - if (packet.HasItemControllerExecutePacket) - { - if (_inventoryController != null) - { - using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); - using BinaryReader binaryReader = new(memoryStream); - try - { - GStruct411 result = ToInventoryOperation(binaryReader.ReadPolymorph()); - - InventoryOperationHandler opHandler = new(result); - - opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); - - // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. - // Unknown what problems this might cause so far. - if (result.Value is UnloadOperationClass unloadOperation) - { - if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) - { - Item item = internalSplitOperation.To.Item; - if (item != null) - { - if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) - { - item.Id = internalSplitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - } - - // TODO: Same as above. - if (result.Value is SplitOperationClass splitOperation) - { - Item item = splitOperation.To.Item; - if (item != null) - { - if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) - { - item.Id = splitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - } - catch (Exception exception) - { - FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); - if (FikaBackendUtils.IsServer) - { - OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); - Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("ItemControllerExecutePacket: inventory was null!"); - if (FikaBackendUtils.IsServer) - { - OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); - Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - // Currently unused - /*if (packet.HasSearchPacket) + public override void SetInventoryOpened(bool opened) + { + if (this is ObservedCoopPlayer) + { + base.SetInventoryOpened(opened); + return; + } + + base.SetInventoryOpened(opened); + PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasInventoryChanged = true, + SetInventoryOpen = opened + }); + } + + public override void SetCompassState(bool value) + { + base.SetCompassState(value); + PacketSender.FirearmPackets.Enqueue(new() + { + HasCompassChange = true, + CompassState = value + }); + } + + public void ClientApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) + { + base.ApplyDamageInfo(damageInfo, bodyPartType, colliderType, absorbed); + } + + public void ClientApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider) + { + _ = base.ApplyShot(damageInfo, bodyPartType, colliderType, armorPlateCollider, GStruct389.EMPTY_SHOT_ID); + } + + public override void SendHeadlightsPacket(bool isSilent) + { + FirearmLightStateStruct[] lightStates = _helmetLightControllers.Select(new Func(ClientPlayer.Class1456.class1456_0.method_0)).ToArray(); + + if (PacketSender != null) + { + PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasHeadLightsPacket = true, + HeadLightsPacket = new() + { + Amount = lightStates.Count(), + IsSilent = isSilent, + LightStates = lightStates + } + }); + } + } + + public override void OnItemAddedOrRemoved(Item item, ItemAddress location, bool added) + { + base.OnItemAddedOrRemoved(item, location, added); + } + + public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBank bank, PhraseSpeakerClass speaker) + { + base.OnPhraseTold(@event, clip, bank, speaker); + + if (ActiveHealthController.IsAlive) + { + PacketSender.CommonPlayerPackets.Enqueue(new() + { + Phrase = @event, + PhraseIndex = clip.NetId + }); + } + } + + public override void OperateStationaryWeapon(StationaryWeapon stationaryWeapon, GStruct170.EStationaryCommand command) + { + base.OperateStationaryWeapon(stationaryWeapon, command); + PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasStationaryPacket = true, + StationaryPacket = new() + { + Command = (StationaryPacket.EStationaryCommand)command, + Id = stationaryWeapon.Id + } + }); + } + + protected virtual void ReceiveSay(EPhraseTrigger trigger, int index) + { + if (HealthController.IsAlive) + { + Speaker.PlayDirect(trigger, index); + } + } + + // Start + public override void vmethod_0(WorldInteractiveObject interactiveObject, InteractionResult interactionResult, Action callback) + { + if (this is ObservedCoopPlayer) + { + base.vmethod_0(interactiveObject, interactionResult, callback); + return; + } + + base.vmethod_0(interactiveObject, interactionResult, callback); + + CommonPlayerPacket packet = new() + { + HasWorldInteractionPacket = true, + WorldInteractionPacket = new() + { + InteractiveId = interactiveObject.Id, + InteractionType = interactionResult.InteractionType, + InteractionStage = EInteractionStage.Start, + ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty + } + }; + PacketSender.CommonPlayerPackets.Enqueue(packet); + } + + // Execute + public override void vmethod_1(WorldInteractiveObject door, InteractionResult interactionResult) + { + if (this is ObservedCoopPlayer) + { + base.vmethod_1(door, interactionResult); + return; + } + + base.vmethod_1(door, interactionResult); + + CommonPlayerPacket packet = new() + { + HasWorldInteractionPacket = true, + WorldInteractionPacket = new() + { + InteractiveId = door.Id, + InteractionType = interactionResult.InteractionType, + InteractionStage = EInteractionStage.Execute, + ItemId = (interactionResult is KeyInteractionResultClass keyInteractionResult) ? keyInteractionResult.Key.Item.Id : string.Empty + } + }; + PacketSender.CommonPlayerPackets.Enqueue(packet); + + UpdateInteractionCast(); + } + + public override void vmethod_3(EGesture gesture) + { + base.vmethod_3(gesture); + PacketSender.FirearmPackets.Enqueue(new() + { + Gesture = gesture + }); + } + + public override void ApplyCorpseImpulse() + { + Corpse.Ragdoll.ApplyImpulse(LastDamageInfo.HitCollider, LastDamageInfo.Direction, LastDamageInfo.HitPoint, _corpseAppliedForce); + } + + public HealthSyncPacket SetupDeathPacket(GStruct346 packet) + { + float num = EFTHardSettings.Instance.HIT_FORCE; + num *= 0.3f + 0.7f * Mathf.InverseLerp(50f, 20f, LastDamageInfo.PenetrationPower); + _corpseAppliedForce = num; + + if (LastAggressor != null) + { + SetupDogTag(); + } + + return new(NetId) + { + Packet = packet, + KillerId = !string.IsNullOrEmpty(KillerId) ? KillerId : null, + RagdollPacket = new() + { + BodyPartColliderType = LastDamageInfo.BodyPartColliderType, + Direction = LastDamageInfo.Direction, + Point = LastDamageInfo.HitPoint, + Force = _corpseAppliedForce, + OverallVelocity = Velocity + }, + Equipment = Equipment, + TriggerZones = TriggerZones.Count > 0 ? [.. TriggerZones] : null, + }; + } + + public override void OnDead(EDamageType damageType) + { + base.OnDead(damageType); + + StartCoroutine(DisableNetworkedComponents()); + if (IsYourPlayer) + { + StartCoroutine(LocalPlayerDied()); + } + } + + private IEnumerator DisableNetworkedComponents() + { + yield return new WaitForSeconds(2); + + if (PacketSender != null) + { + PacketSender.Enabled = false; + } + } + + private IEnumerator LocalPlayerDied() + { + AddPlayerRequest request = new(FikaBackendUtils.GetGroupId(), ProfileId); + Task diedTask = FikaRequestHandler.PlayerDied(request); + while (!diedTask.IsCompleted) + { + yield return new WaitForEndOfFrame(); + } + } + + public override void Move(Vector2 direction) + { + if (direction.sqrMagnitude > 0) + { + base.Move(direction); + LastDirection = direction; + } + } + + /// + /// Used to determine whether this player was a boss + /// + /// + /// true if it's a boss, false if not + public bool IsBoss(WildSpawnType wildSpawnType, out string name) + { + name = null; + switch (wildSpawnType) + { + case WildSpawnType.bossBoar: + { + name = "Kaban"; + break; + } + case WildSpawnType.bossBully: + { + name = "Reshala"; + break; + } + case WildSpawnType.bossGluhar: + { + name = "Glukhar"; + break; + } + case WildSpawnType.bossKilla: + { + name = "Killa"; + break; + } + case WildSpawnType.bossKnight: + { + name = "Knight"; + break; + } + case WildSpawnType.bossKojaniy: + { + name = "Shturman"; + break; + } + case WildSpawnType.bossSanitar: + { + name = "Sanitar"; + break; + } + case WildSpawnType.bossTagilla: + { + name = "Tagilla"; + break; + } + case WildSpawnType.bossZryachiy: + { + name = "Zryachiy"; + break; + } + case WildSpawnType.followerBigPipe: + { + name = "Big Pipe"; + break; + } + case WildSpawnType.followerBirdEye: + { + name = "Bird Eye"; + break; + } + case WildSpawnType.sectantPriest: + { + name = "Cultist Priest"; + break; + } + } + return name != null; + } + + private void HandleInteractPacket(WorldInteractionPacket packet) + { + if (CoopHandler.TryGetCoopHandler(out CoopHandler coopHandler)) + { + WorldInteractiveObject worldInteractiveObject = Singleton.Instance.FindDoor(packet.InteractiveId); + if (worldInteractiveObject != null) + { + if (worldInteractiveObject.isActiveAndEnabled) + { + InteractionResult interactionResult; + Action action; + if (packet.InteractionType == EInteractionType.Unlock) + { + KeyHandler keyHandler = new(this); + + if (string.IsNullOrEmpty(packet.ItemId)) + { + FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: ItemID was null!"); + return; + } + + Item keyItem = FindItem(packet.ItemId); + if (keyItem == null) + { + FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: Could not find item: " + packet.ItemId); + return; + } + + KeyComponent keyComponent = keyItem.GetItemComponent(); + if (keyComponent == null) + { + FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: keyComponent was null!"); + return; + } + + keyHandler.unlockResult = worldInteractiveObject.UnlockOperation(keyComponent, this); + if (keyHandler.unlockResult.Error != null) + { + FikaPlugin.Instance.FikaLogger.LogWarning("HandleInteractPacket: Error when processing unlockResult: " + keyHandler.unlockResult.Error); + return; + } + + interactionResult = keyHandler.unlockResult.Value; + keyHandler.unlockResult.Value.RaiseEvents(_inventoryController, CommandStatus.Begin); + action = new(keyHandler.HandleKeyEvent); + } + else + { + interactionResult = new InteractionResult(packet.InteractionType); + action = null; + } + + if (packet.InteractionStage == EInteractionStage.Start) + { + vmethod_0(worldInteractiveObject, interactionResult, action); + return; + } + + if (packet.InteractionStage != EInteractionStage.Execute) + { + worldInteractiveObject.Interact(interactionResult); + return; + } + + vmethod_1(worldInteractiveObject, interactionResult); + } + + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("HandleInteractPacket: WorldInteractiveObject was null or disabled!"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("HandleInteractPacket: CoopHandler was null!"); + } + } + + public override void TryInteractionCallback(LootableContainer container) + { + LootableContainerInteractionHandler handler = new(this, container); + if (handler.container != null && _openAction != null) + { + _openAction(new Action(handler.Handle)); + } + _openAction = null; + } + + public void SetupMainPlayer() + { + // Set own group id, ignore if dedicated + if (!Profile.Info.Nickname.Contains("dedicated_")) + { + 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); + if (result.Error != null) + { + FikaPlugin.Instance.FikaLogger.LogWarning("CoopPlayer::SetupMainPlayer: Error removing dog tag!"); + } + } + + string templateId = GetDogTagTemplateId(); + + if (!string.IsNullOrEmpty(templateId)) + { + Item item = Singleton.Instance.CreateItem(MongoID.Generate(), templateId, null); + + 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; + + 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.LogError("Could not get templateId for DogTag!"); + } + } + + if (!string.IsNullOrEmpty(Location)) + { + // Delete labs card on labs + if (Location.ToLower() == "laboratory") + { + foreach (Item item in Inventory.AllRealPlayerItems) + { + if (item.TemplateId == "5c94bbff86f7747ee735c08f") + { + InteractionsHandlerClass.Remove(item, _inventoryController, false, true); + break; + } + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CoopPlayer::SetupMainPlayer: Location was null!"); + } + } + + private string GetDogTagTemplateId() + { + if (Side is EPlayerSide.Usec) + { + switch (Profile.Info.SelectedMemberCategory) + { + case EMemberCategory.Default: + return "59f32c3b86f77472a31742f0"; + case EMemberCategory.UniqueId: + return "6662e9f37fa79a6d83730fa0"; + case EMemberCategory.Unheard: + return "6662ea05f6259762c56f3189"; + } + } + else if (Side is EPlayerSide.Bear) + { + switch (Profile.Info.SelectedMemberCategory) + { + 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) + { + ReceiveSay(packet.Phrase, packet.PhraseIndex); + } + + if (packet.HasWorldInteractionPacket) + { + HandleInteractPacket(packet.WorldInteractionPacket); + } + + if (packet.HasContainerInteractionPacket) + { + WorldInteractiveObject lootableContainer = Singleton.Instance.FindDoor(packet.ContainerInteractionPacket.InteractiveId); + if (lootableContainer != null) + { + if (lootableContainer.isActiveAndEnabled) + { + string methodName = string.Empty; + switch (packet.ContainerInteractionPacket.InteractionType) + { + case EInteractionType.Open: + methodName = "Open"; + break; + case EInteractionType.Close: + methodName = "Close"; + break; + case EInteractionType.Unlock: + methodName = "Unlock"; + break; + case EInteractionType.Breach: + break; + case EInteractionType.Lock: + methodName = "Lock"; + break; + } + + if (!string.IsNullOrEmpty(methodName)) + { + void Interact() => lootableContainer.Invoke(methodName, 0); + + if (packet.ContainerInteractionPacket.InteractionType == EInteractionType.Unlock) + { + Interact(); + } + else + { + lootableContainer.StartBehaviourTimer(EFTHardSettings.Instance.DelayToOpenContainer, Interact); + } + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CommonPlayerPacket::ContainerInteractionPacket: LootableContainer was null!"); + } + } + + if (packet.HasProceedPacket) + { + if (this is ObservedCoopPlayer observedCoopPlayer) + { + observedCoopPlayer.HandleProceedPacket(packet.ProceedPacket); + } + } + + if (packet.HasHeadLightsPacket) + { + try + { + if (_helmetLightControllers != null) + { + for (int i = 0; i < _helmetLightControllers.Count(); i++) + { + _helmetLightControllers.ElementAt(i)?.LightMod?.SetLightState(packet.HeadLightsPacket.LightStates[i]); + } + if (!packet.HeadLightsPacket.IsSilent) + { + SwitchHeadLightsAnimation(); + } + } + } + catch (Exception) + { + // Do nothing + } + } + + if (packet.HasInventoryChanged) + { + base.SetInventoryOpened(packet.SetInventoryOpen); + } + + if (packet.HasDrop) + { + if (packet.DropPacket.HasItemId) + { + Item item = FindItem(packet.ProceedPacket.ItemId); + base.DropCurrentController(null, packet.DropPacket.FastDrop, item ?? null); + } + else + { + base.DropCurrentController(null, packet.DropPacket.FastDrop, null); + } + } + + if (packet.HasStationaryPacket) + { + StationaryWeapon stationaryWeapon = (packet.StationaryPacket.Command == StationaryPacket.EStationaryCommand.Occupy) ? Singleton.Instance.FindStationaryWeapon(packet.StationaryPacket.Id) : null; + base.OperateStationaryWeapon(stationaryWeapon, (GStruct170.EStationaryCommand)packet.StationaryPacket.Command); + } + + if (packet.Pickup) + { + MovementContext.SetInteractInHands(packet.Pickup, packet.PickupAnimation); + } + + if (packet.HasVaultPacket) + { + DoObservedVault(packet.VaultPacket); + } + } + + public virtual void DoObservedVault(VaultPacket vaultPacket) + { + + } + + public void HandleCallbackFromServer(in OperationCallbackPacket operationCallbackPacket) + { + if (OperationCallbacks.TryGetValue(operationCallbackPacket.CallbackId, out Callback callback)) + { + if (operationCallbackPacket.OperationStatus != EOperationStatus.Started) + { + OperationCallbacks.Remove(operationCallbackPacket.CallbackId); + } + if (operationCallbackPacket.OperationStatus != EOperationStatus.Failed) + { + callback(new Result(operationCallbackPacket.OperationStatus)); + } + else + { + callback(new Result(EOperationStatus.Failed) + { + Error = operationCallbackPacket.Error + }); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"Could not find CallbackId {operationCallbackPacket.CallbackId}!"); + } + } + + public virtual void HandleInventoryPacket(in InventoryPacket packet) + { + if (packet.HasItemControllerExecutePacket) + { + if (_inventoryController != null) + { + using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); + using BinaryReader binaryReader = new(memoryStream); + try + { + GStruct411 result = ToInventoryOperation(binaryReader.ReadPolymorph()); + + InventoryOperationHandler opHandler = new(result); + + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); + + // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. + // Unknown what problems this might cause so far. + if (result.Value is UnloadOperationClass unloadOperation) + { + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) + { + Item item = internalSplitOperation.To.Item; + if (item != null) + { + if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) + { + item.Id = internalSplitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + + // TODO: Same as above. + if (result.Value is SplitOperationClass splitOperation) + { + Item item = splitOperation.To.Item; + if (item != null) + { + if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) + { + item.Id = splitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + catch (Exception exception) + { + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + if (FikaBackendUtils.IsServer) + { + OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("ItemControllerExecutePacket: inventory was null!"); + if (FikaBackendUtils.IsServer) + { + OperationCallbackPacket callbackPacket = new(NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + Singleton.Instance.SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + // Currently unused + /*if (packet.HasSearchPacket) { if (!packet.SearchPacket.IsStop) { @@ -1266,692 +1266,692 @@ public virtual void HandleInventoryPacket(in InventoryPacket packet) } } }*/ - } - - public virtual void HandleWeaponPacket(in WeaponPacket packet) - { - if (HandsController is CoopObservedFirearmController firearmController) - { - firearmController.HandleFirearmPacket(packet, _inventoryController); - } - - if (packet.Gesture != EGesture.None) - { - vmethod_3(packet.Gesture); - } - - if (packet.Loot) - { - HandsController.Loot(packet.Loot); - } - - if (packet.HasGrenadePacket) - { - if (HandsController is CoopObservedGrenadeController grenadeController) - { - switch (packet.GrenadePacket.PacketType) - { - case GrenadePacket.GrenadePacketType.ExamineWeapon: - { - grenadeController.ExamineWeapon(); - break; - } - case GrenadePacket.GrenadePacketType.HighThrow: - { - grenadeController.HighThrow(); - break; - } - case GrenadePacket.GrenadePacketType.LowThrow: - { - grenadeController.LowThrow(); - break; - } - case GrenadePacket.GrenadePacketType.PullRingForHighThrow: - { - grenadeController.PullRingForHighThrow(); - break; - } - case GrenadePacket.GrenadePacketType.PullRingForLowThrow: - { - grenadeController.PullRingForLowThrow(); - break; - } - } - if (packet.GrenadePacket.HasGrenade) - { - grenadeController.SpawnGrenade(0f, packet.GrenadePacket.GrenadePosition, packet.GrenadePacket.GrenadeRotation, packet.GrenadePacket.ThrowForce, packet.GrenadePacket.LowThrow); - } - } - else if (HandsController is CoopObservedQuickGrenadeController quickGrenadeController) - { - if (packet.GrenadePacket.HasGrenade) - { - quickGrenadeController.SpawnGrenade(0f, packet.GrenadePacket.GrenadePosition, packet.GrenadePacket.GrenadeRotation, packet.GrenadePacket.ThrowForce, packet.GrenadePacket.LowThrow); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandleFirearmPacket::GrenadePacket: HandsController was not of type CoopObservedGrenadeController! Was {HandsController.GetType().Name}"); - } - } - - if (packet.CancelGrenade) - { - if (HandsController is CoopObservedGrenadeController grenadeController) - { - grenadeController.vmethod_3(); - } - } - - if (packet.HasCompassChange) - { - if (HandsController is ItemHandsController handsController) - { - handsController.ApplyCompassPacket(new() - { - Toggle = true, - Status = packet.CompassState - }); - } - } - - if (packet.HasKnifePacket) - { - if (HandsController is CoopObservedKnifeController knifeController) - { - if (packet.KnifePacket.Examine) - { - knifeController.ExamineWeapon(); - } - - if (packet.KnifePacket.Kick) - { - knifeController.MakeKnifeKick(); - } - - if (packet.KnifePacket.AltKick) - { - knifeController.MakeAlternativeKick(); - } - - if (packet.KnifePacket.BreakCombo) - { - knifeController.BrakeCombo(); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"HandleFirearmPacket::KnifePacket: HandsController was not of type CoopObservedKnifeController! Was {HandsController.GetType().Name}"); - } - } - } - - 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.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.ProfileId)) - { - IPlayerOwner player = Singleton.Instance.GetAlivePlayerBridgeByProfileID(packet.ProfileId); - - if (player != null) - { - damageInfo.Player = player; - if (IsYourPlayer) - { - if (!FikaPlugin.Instance.FriendlyFire && damageInfo.Player.iPlayer is ObservedCoopPlayer observedCoopPlayer && !observedCoopPlayer.IsObservedAI) - { - return; - } - } - } - - // TODO: Fix this and consistently get the correct data... - if (Singleton.Instance.GetAlivePlayerByProfileID(packet.ProfileId).HandsController.Item is Weapon weapon) - { - damageInfo.Weapon = weapon; - } - } - - 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() - { - if (LastAggressor != null) - { - Item item = Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; - if (item != null) - { - DogtagComponent dogtagComponent = item.GetItemComponent(); - if (dogtagComponent != null) - { - dogtagComponent.Item.SpawnedInSession = true; - dogtagComponent.AccountId = AccountId; - dogtagComponent.ProfileId = ProfileId; - dogtagComponent.Nickname = Profile.Nickname; - dogtagComponent.KillerAccountId = KillerAccountId; - dogtagComponent.KillerProfileId = KillerId; - dogtagComponent.KillerName = LastAggressor.Profile.Nickname; - dogtagComponent.Side = Side; - dogtagComponent.Level = Profile.Info.Experience > 0 ? Profile.Info.Level : 1; - dogtagComponent.Time = DateTime.Now; - dogtagComponent.Status = "Killed by "; - dogtagComponent.WeaponName = LastDamageInfo.Weapon != null ? LastDamageInfo.Weapon.Name : "Unknown"; - dogtagComponent.GroupId = GroupId; - } - } - } - } - - public void CheckAndResetControllers(ExitStatus exitStatus, float pastTime, string locationId, string exitName) - { - _questController?.CheckExitConditionCounters(exitStatus, pastTime, locationId, exitName, HealthController.BodyPartEffects, TriggerZones); - _questController?.ResetCurrentNullableCounters(); - - _achievementsController?.CheckExitConditionCounters(exitStatus, pastTime, locationId, exitName, HealthController.BodyPartEffects, TriggerZones); - _achievementsController?.ResetCurrentNullableCounters(); - } - - public virtual void SetInventory(EquipmentClass equipmentClass) - { - // Do nothing - } - - public override void OnDestroy() - { - base.OnDestroy(); - } - - public override void Dispose() - { - base.Dispose(); - if (PacketSender != null) - { - PacketSender.DestroyThis(); - } - } - - public override void SendHandsInteractionStateChanged(bool value, int animationId) - { - base.SendHandsInteractionStateChanged(value, animationId); - if (value) - { - PacketSender.CommonPlayerPackets.Enqueue(new() - { - Pickup = value, - PickupAnimation = animationId - }); - } - } - - public override void OnVaulting() - { - PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasVaultPacket = true, - VaultPacket = new() - { - VaultingStrategy = VaultingParameters.GetVaultingStrategy(), - VaultingPoint = VaultingParameters.MaxWeightPointPosition, - VaultingHeight = VaultingParameters.VaultingHeight, - VaultingLength = VaultingParameters.VaultingLength, - VaultingSpeed = MovementContext.VaultingSpeed, - BehindObstacleHeight = VaultingParameters.BehindObstacleRatio, - AbsoluteForwardVelocity = VaultingParameters.AbsoluteForwardVelocity - } - }); - } - - 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) - { - return item; - } - - GStruct416 itemResult = FindItemById(itemId); - if (itemResult.Error != null) - { - FikaPlugin.Instance.FikaLogger.LogError($"CoopPlayer::FindItem: Could not find item with id '{itemId}' in the world at all."); - } - return itemResult.Value; - } - - #region handlers - private class KeyHandler(CoopPlayer player) - { - private readonly CoopPlayer player = player; - public GStruct416 unlockResult; - - internal void HandleKeyEvent() - { - unlockResult.Value.RaiseEvents(player._inventoryController, CommandStatus.Succeed); - } - } - - private class InventoryOperationHandler(GStruct411 opResult) - { - public readonly GStruct411 opResult = opResult; - - internal void HandleResult(IResult result) - { - if (!result.Succeed || !string.IsNullOrEmpty(result.Error)) - { - FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error}"); - } - } - } - - private class LootableContainerInteractionHandler(CoopPlayer player, LootableContainer container) - { - private readonly CoopPlayer player = player; - public readonly LootableContainer container = container; - - public void Handle() - { - player.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasContainerInteractionPacket = true, - ContainerInteractionPacket = new() - { - InteractiveId = container.Id, - InteractionType = EInteractionType.Close - } - }); - - container.Interact(new InteractionResult(EInteractionType.Close)); - if (player.MovementContext.LevelOnApproachStart > 0f) - { - player.MovementContext.SetPoseLevel(player.MovementContext.LevelOnApproachStart, false); - player.MovementContext.LevelOnApproachStart = -1f; - } - } - } - - private class FirearmControllerHandler(CoopPlayer coopPlayer, Weapon weapon) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - public readonly Weapon weapon = weapon; - public Process process; - public Action confirmCallback; - - internal CoopClientFirearmController ReturnController() - { - return CoopClientFirearmController.Create(coopPlayer, weapon); - } - - internal void SendPacket() - { - if (weapon.IsStationaryWeapon) - { - return; - } - - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.Weapon, - ItemId = weapon.Id, - ItemTemplateId = weapon.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class QuickUseItemControllerHandler(CoopPlayer coopPlayer, Item item) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly Item item = item; - public Process process; - public Action confirmCallback; - - internal QuickUseItemController ReturnController() - { - return QuickUseItemController.smethod_5(coopPlayer, item); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.QuickUse, - ItemId = item.Id, - ItemTemplateId = item.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class MedsControllerHandler(CoopPlayer coopPlayer, MedsClass meds, EBodyPart bodyPart, int animationVariant) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly MedsClass meds = meds; - private readonly EBodyPart bodyPart = bodyPart; - private readonly int animationVariant = animationVariant; - public Process process; - public Action confirmCallback; - - internal MedsController ReturnController() - { - return MedsController.smethod_5(coopPlayer, meds, bodyPart, 1f, animationVariant); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.MedsClass, - ItemId = meds.Id, - ItemTemplateId = meds.TemplateId, - AnimationVariant = animationVariant, - BodyPart = bodyPart - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class FoodControllerHandler(CoopPlayer coopPlayer, FoodClass foodDrink, float amount, EBodyPart bodyPart, int animationVariant) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly FoodClass foodDrink = foodDrink; - private readonly float amount = amount; - private readonly EBodyPart bodyPart = bodyPart; - private readonly int animationVariant = animationVariant; - public Process process; - public Action confirmCallback; - - internal MedsController ReturnController() - { - return MedsController.smethod_5(coopPlayer, foodDrink, EBodyPart.Head, amount, animationVariant); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.MedsClass, - ItemId = foodDrink.Id, - Amount = amount, - ItemTemplateId = foodDrink.TemplateId, - AnimationVariant = animationVariant, - BodyPart = bodyPart - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class KnifeControllerHandler(CoopPlayer coopPlayer, KnifeComponent knife) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - public readonly KnifeComponent knife = knife; - public Process process; - public Action confirmCallback; - - internal CoopClientKnifeController ReturnController() - { - return CoopClientKnifeController.Create(coopPlayer, knife); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.Knife, - ItemId = knife.Item.Id, - ItemTemplateId = knife.Item.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class QuickKnifeControllerHandler(CoopPlayer coopPlayer, KnifeComponent knife) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - public readonly KnifeComponent knife = knife; - public Process process; - public Action confirmCallback; - - internal QuickKnifeKickController ReturnController() - { - return QuickKnifeKickController.smethod_8(coopPlayer, knife); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.QuickKnifeKick, - ItemId = knife.Item.Id, - ItemTemplateId = knife.Item.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class GrenadeControllerHandler(CoopPlayer coopPlayer, GrenadeClass throwWeap) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly GrenadeClass throwWeap = throwWeap; - public Process process; - public Action confirmCallback; - - internal CoopClientGrenadeController ReturnController() - { - return CoopClientGrenadeController.Create(coopPlayer, throwWeap); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.GrenadeClass, - ItemId = throwWeap.Id, - ItemTemplateId = throwWeap.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - private class QuickGrenadeControllerHandler(CoopPlayer coopPlayer, GrenadeClass throwWeap) - { - private readonly CoopPlayer coopPlayer = coopPlayer; - private readonly GrenadeClass throwWeap = throwWeap; - public Process process; - public Action confirmCallback; - - internal CoopClientQuickGrenadeController ReturnController() - { - return CoopClientQuickGrenadeController.Create(coopPlayer, throwWeap); - } - - internal void SendPacket() - { - coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() - { - HasProceedPacket = true, - ProceedPacket = new() - { - ProceedType = EProceedType.QuickGrenadeThrow, - ItemId = throwWeap.Id, - ItemTemplateId = throwWeap.TemplateId - } - }); - } - - internal void HandleResult(IResult result) - { - if (result.Succeed) - { - confirmCallback(); - } - } - } - - } - #endregion + } + + public virtual void HandleWeaponPacket(in WeaponPacket packet) + { + if (HandsController is CoopObservedFirearmController firearmController) + { + firearmController.HandleFirearmPacket(packet, _inventoryController); + } + + if (packet.Gesture != EGesture.None) + { + vmethod_3(packet.Gesture); + } + + if (packet.Loot) + { + HandsController.Loot(packet.Loot); + } + + if (packet.HasGrenadePacket) + { + if (HandsController is CoopObservedGrenadeController grenadeController) + { + switch (packet.GrenadePacket.PacketType) + { + case GrenadePacket.GrenadePacketType.ExamineWeapon: + { + grenadeController.ExamineWeapon(); + break; + } + case GrenadePacket.GrenadePacketType.HighThrow: + { + grenadeController.HighThrow(); + break; + } + case GrenadePacket.GrenadePacketType.LowThrow: + { + grenadeController.LowThrow(); + break; + } + case GrenadePacket.GrenadePacketType.PullRingForHighThrow: + { + grenadeController.PullRingForHighThrow(); + break; + } + case GrenadePacket.GrenadePacketType.PullRingForLowThrow: + { + grenadeController.PullRingForLowThrow(); + break; + } + } + if (packet.GrenadePacket.HasGrenade) + { + grenadeController.SpawnGrenade(0f, packet.GrenadePacket.GrenadePosition, packet.GrenadePacket.GrenadeRotation, packet.GrenadePacket.ThrowForce, packet.GrenadePacket.LowThrow); + } + } + else if (HandsController is CoopObservedQuickGrenadeController quickGrenadeController) + { + if (packet.GrenadePacket.HasGrenade) + { + quickGrenadeController.SpawnGrenade(0f, packet.GrenadePacket.GrenadePosition, packet.GrenadePacket.GrenadeRotation, packet.GrenadePacket.ThrowForce, packet.GrenadePacket.LowThrow); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandleFirearmPacket::GrenadePacket: HandsController was not of type CoopObservedGrenadeController! Was {HandsController.GetType().Name}"); + } + } + + if (packet.CancelGrenade) + { + if (HandsController is CoopObservedGrenadeController grenadeController) + { + grenadeController.vmethod_3(); + } + } + + if (packet.HasCompassChange) + { + if (HandsController is ItemHandsController handsController) + { + handsController.ApplyCompassPacket(new() + { + Toggle = true, + Status = packet.CompassState + }); + } + } + + if (packet.HasKnifePacket) + { + if (HandsController is CoopObservedKnifeController knifeController) + { + if (packet.KnifePacket.Examine) + { + knifeController.ExamineWeapon(); + } + + if (packet.KnifePacket.Kick) + { + knifeController.MakeKnifeKick(); + } + + if (packet.KnifePacket.AltKick) + { + knifeController.MakeAlternativeKick(); + } + + if (packet.KnifePacket.BreakCombo) + { + knifeController.BrakeCombo(); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"HandleFirearmPacket::KnifePacket: HandsController was not of type CoopObservedKnifeController! Was {HandsController.GetType().Name}"); + } + } + } + + 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.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.ProfileId)) + { + IPlayerOwner player = Singleton.Instance.GetAlivePlayerBridgeByProfileID(packet.ProfileId); + + if (player != null) + { + damageInfo.Player = player; + if (IsYourPlayer) + { + if (!FikaPlugin.Instance.FriendlyFire && damageInfo.Player.iPlayer is ObservedCoopPlayer observedCoopPlayer && !observedCoopPlayer.IsObservedAI) + { + return; + } + } + } + + // TODO: Fix this and consistently get the correct data... + if (Singleton.Instance.GetAlivePlayerByProfileID(packet.ProfileId).HandsController.Item is Weapon weapon) + { + damageInfo.Weapon = weapon; + } + } + + 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() + { + if (LastAggressor != null) + { + Item item = Equipment.GetSlot(EquipmentSlot.Dogtag).ContainedItem; + if (item != null) + { + DogtagComponent dogtagComponent = item.GetItemComponent(); + if (dogtagComponent != null) + { + dogtagComponent.Item.SpawnedInSession = true; + dogtagComponent.AccountId = AccountId; + dogtagComponent.ProfileId = ProfileId; + dogtagComponent.Nickname = Profile.Nickname; + dogtagComponent.KillerAccountId = KillerAccountId; + dogtagComponent.KillerProfileId = KillerId; + dogtagComponent.KillerName = LastAggressor.Profile.Nickname; + dogtagComponent.Side = Side; + dogtagComponent.Level = Profile.Info.Experience > 0 ? Profile.Info.Level : 1; + dogtagComponent.Time = DateTime.Now; + dogtagComponent.Status = "Killed by "; + dogtagComponent.WeaponName = LastDamageInfo.Weapon != null ? LastDamageInfo.Weapon.Name : "Unknown"; + dogtagComponent.GroupId = GroupId; + } + } + } + } + + public void CheckAndResetControllers(ExitStatus exitStatus, float pastTime, string locationId, string exitName) + { + _questController?.CheckExitConditionCounters(exitStatus, pastTime, locationId, exitName, HealthController.BodyPartEffects, TriggerZones); + _questController?.ResetCurrentNullableCounters(); + + _achievementsController?.CheckExitConditionCounters(exitStatus, pastTime, locationId, exitName, HealthController.BodyPartEffects, TriggerZones); + _achievementsController?.ResetCurrentNullableCounters(); + } + + public virtual void SetInventory(EquipmentClass equipmentClass) + { + // Do nothing + } + + public override void OnDestroy() + { + base.OnDestroy(); + } + + public override void Dispose() + { + base.Dispose(); + if (PacketSender != null) + { + PacketSender.DestroyThis(); + } + } + + public override void SendHandsInteractionStateChanged(bool value, int animationId) + { + base.SendHandsInteractionStateChanged(value, animationId); + if (value) + { + PacketSender.CommonPlayerPackets.Enqueue(new() + { + Pickup = value, + PickupAnimation = animationId + }); + } + } + + public override void OnVaulting() + { + PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasVaultPacket = true, + VaultPacket = new() + { + VaultingStrategy = VaultingParameters.GetVaultingStrategy(), + VaultingPoint = VaultingParameters.MaxWeightPointPosition, + VaultingHeight = VaultingParameters.VaultingHeight, + VaultingLength = VaultingParameters.VaultingLength, + VaultingSpeed = MovementContext.VaultingSpeed, + BehindObstacleHeight = VaultingParameters.BehindObstacleRatio, + AbsoluteForwardVelocity = VaultingParameters.AbsoluteForwardVelocity + } + }); + } + + 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) + { + return item; + } + + GStruct416 itemResult = FindItemById(itemId); + if (itemResult.Error != null) + { + FikaPlugin.Instance.FikaLogger.LogError($"CoopPlayer::FindItem: Could not find item with id '{itemId}' in the world at all."); + } + return itemResult.Value; + } + + #region handlers + private class KeyHandler(CoopPlayer player) + { + private readonly CoopPlayer player = player; + public GStruct416 unlockResult; + + internal void HandleKeyEvent() + { + unlockResult.Value.RaiseEvents(player._inventoryController, CommandStatus.Succeed); + } + } + + private class InventoryOperationHandler(GStruct411 opResult) + { + public readonly GStruct411 opResult = opResult; + + internal void HandleResult(IResult result) + { + if (!result.Succeed || !string.IsNullOrEmpty(result.Error)) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error}"); + } + } + } + + private class LootableContainerInteractionHandler(CoopPlayer player, LootableContainer container) + { + private readonly CoopPlayer player = player; + public readonly LootableContainer container = container; + + public void Handle() + { + player.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasContainerInteractionPacket = true, + ContainerInteractionPacket = new() + { + InteractiveId = container.Id, + InteractionType = EInteractionType.Close + } + }); + + container.Interact(new InteractionResult(EInteractionType.Close)); + if (player.MovementContext.LevelOnApproachStart > 0f) + { + player.MovementContext.SetPoseLevel(player.MovementContext.LevelOnApproachStart, false); + player.MovementContext.LevelOnApproachStart = -1f; + } + } + } + + private class FirearmControllerHandler(CoopPlayer coopPlayer, Weapon weapon) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + public readonly Weapon weapon = weapon; + public Process process; + public Action confirmCallback; + + internal CoopClientFirearmController ReturnController() + { + return CoopClientFirearmController.Create(coopPlayer, weapon); + } + + internal void SendPacket() + { + if (weapon.IsStationaryWeapon) + { + return; + } + + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.Weapon, + ItemId = weapon.Id, + ItemTemplateId = weapon.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class QuickUseItemControllerHandler(CoopPlayer coopPlayer, Item item) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly Item item = item; + public Process process; + public Action confirmCallback; + + internal QuickUseItemController ReturnController() + { + return QuickUseItemController.smethod_5(coopPlayer, item); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.QuickUse, + ItemId = item.Id, + ItemTemplateId = item.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class MedsControllerHandler(CoopPlayer coopPlayer, MedsClass meds, EBodyPart bodyPart, int animationVariant) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly MedsClass meds = meds; + private readonly EBodyPart bodyPart = bodyPart; + private readonly int animationVariant = animationVariant; + public Process process; + public Action confirmCallback; + + internal MedsController ReturnController() + { + return MedsController.smethod_5(coopPlayer, meds, bodyPart, 1f, animationVariant); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.MedsClass, + ItemId = meds.Id, + ItemTemplateId = meds.TemplateId, + AnimationVariant = animationVariant, + BodyPart = bodyPart + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class FoodControllerHandler(CoopPlayer coopPlayer, FoodClass foodDrink, float amount, EBodyPart bodyPart, int animationVariant) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly FoodClass foodDrink = foodDrink; + private readonly float amount = amount; + private readonly EBodyPart bodyPart = bodyPart; + private readonly int animationVariant = animationVariant; + public Process process; + public Action confirmCallback; + + internal MedsController ReturnController() + { + return MedsController.smethod_5(coopPlayer, foodDrink, EBodyPart.Head, amount, animationVariant); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.MedsClass, + ItemId = foodDrink.Id, + Amount = amount, + ItemTemplateId = foodDrink.TemplateId, + AnimationVariant = animationVariant, + BodyPart = bodyPart + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class KnifeControllerHandler(CoopPlayer coopPlayer, KnifeComponent knife) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + public readonly KnifeComponent knife = knife; + public Process process; + public Action confirmCallback; + + internal CoopClientKnifeController ReturnController() + { + return CoopClientKnifeController.Create(coopPlayer, knife); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.Knife, + ItemId = knife.Item.Id, + ItemTemplateId = knife.Item.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class QuickKnifeControllerHandler(CoopPlayer coopPlayer, KnifeComponent knife) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + public readonly KnifeComponent knife = knife; + public Process process; + public Action confirmCallback; + + internal QuickKnifeKickController ReturnController() + { + return QuickKnifeKickController.smethod_8(coopPlayer, knife); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.QuickKnifeKick, + ItemId = knife.Item.Id, + ItemTemplateId = knife.Item.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class GrenadeControllerHandler(CoopPlayer coopPlayer, GrenadeClass throwWeap) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly GrenadeClass throwWeap = throwWeap; + public Process process; + public Action confirmCallback; + + internal CoopClientGrenadeController ReturnController() + { + return CoopClientGrenadeController.Create(coopPlayer, throwWeap); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.GrenadeClass, + ItemId = throwWeap.Id, + ItemTemplateId = throwWeap.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + private class QuickGrenadeControllerHandler(CoopPlayer coopPlayer, GrenadeClass throwWeap) + { + private readonly CoopPlayer coopPlayer = coopPlayer; + private readonly GrenadeClass throwWeap = throwWeap; + public Process process; + public Action confirmCallback; + + internal CoopClientQuickGrenadeController ReturnController() + { + return CoopClientQuickGrenadeController.Create(coopPlayer, throwWeap); + } + + internal void SendPacket() + { + coopPlayer.PacketSender.CommonPlayerPackets.Enqueue(new() + { + HasProceedPacket = true, + ProceedPacket = new() + { + ProceedType = EProceedType.QuickGrenadeThrow, + ItemId = throwWeap.Id, + ItemTemplateId = throwWeap.TemplateId + } + }); + } + + internal void HandleResult(IResult result) + { + if (result.Succeed) + { + confirmCallback(); + } + } + } + + } + #endregion } diff --git a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs index b9b32a74..c03a9c65 100644 --- a/Fika.Core/Coop/Players/ObservedCoopPlayer.cs +++ b/Fika.Core/Coop/Players/ObservedCoopPlayer.cs @@ -26,1027 +26,1027 @@ namespace Fika.Core.Coop.Players { - /// - /// Observed players are any other players in the world for a client, including bots. - /// They are all being handled by the server that moves them through packets. - /// As a host this is only other clients. - /// - public class ObservedCoopPlayer : CoopPlayer - { - #region Fields and Properties - public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; - private float observedFixedTime = 0f; - public FikaHealthBar HealthBar - { - get => healthBar; - } - private FikaHealthBar healthBar = null; - private Coroutine waitForStartRoutine; - private bool isServer; - public ObservedHealthController NetworkHealthController - { - get => HealthController as ObservedHealthController; - } - private readonly ObservedVaultingParametersClass ObservedVaultingParameters = new(); - public override bool CanBeSnapped => false; - public override EPointOfView PointOfView { get => EPointOfView.ThirdPerson; } - public override AbstractHandsController HandsController - { - get => base.HandsController; - set - { - base.HandsController = value; - PlayerAnimator.EWeaponAnimationType weaponAnimationType = GetWeaponAnimationType(_handsController); - MovementContext.PlayerAnimatorSetWeaponId(weaponAnimationType); - } - } - - public override Ray InteractionRay - { - get - { - Vector3 vector = HandsRotation * Vector3.forward; - return new(_playerLookRaycastTransform.position, vector); - } - } - - public override float ProtagonistHearing => Mathf.Max(1f, Singleton.Instance.ProtagonistHearing + 1f); - - private FollowerCullingObject followerCullingObject; - //private List cullingRenderers = new(256); - private List cullingObjects = new(); - private bool CollidingWithReporter - { - get - { - if (cullingObjects.Count > 0) - { - for (int i = 0; i < cullingObjects.Count; i++) - { - if (cullingObjects[i].HasEntered) - { - return true; - } - } - return false; - } - return true; - } - } - public override bool IsVisible - { - get - { - if (followerCullingObject != null) - { - return followerCullingObject.IsVisible && CollidingWithReporter; - } - return OnScreen; - } - set - { - - } - } - public override float SqrCameraDistance - { - get - { - if (followerCullingObject != null) - { - return followerCullingObject.SqrCameraDistance; - } - return base.SqrCameraDistance; - } - } - #endregion - - public static async Task CreateObservedPlayer(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, IViewFilter filter) - { - ObservedCoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, - armsUpdateMode, bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, - aiControl); - - player.IsYourPlayer = false; - - InventoryControllerClass inventoryController = new ObservedInventoryController(player, profile, true); - - PlayerHealthController tempController = new(profile.Health, player, inventoryController, profile.Skills, aiControl); - byte[] healthBytes = tempController.SerializeState(); - - ObservedHealthController healthController = new(healthBytes, inventoryController, profile.Skills); - - CoopObservedStatisticsManager statisticsManager = new(); - - await player.Init(rotation, layerName, pointOfView, profile, inventoryController, healthController, - statisticsManager, null, null, filter, EVoipState.NotAvailable, aiControl, false); - - 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; - - player.isServer = FikaBackendUtils.IsServer; - - return player; - } - - public override BasePhysicalClass CreatePhysical() - { - return new BasePhysicalClass(); - } - - public override bool CheckSurface() - { - float spreadRange = 42f * ProtagonistHearing; - return !(Distance - spreadRange > 0); - } - - public override void PlayGroundedSound(float fallHeight, float jumpHeight) - { - (bool hit, BaseBallistic.ESurfaceSound surfaceSound) values = method_57(); - method_58(values.hit, values.surfaceSound); - base.PlayGroundedSound(fallHeight, jumpHeight); - } - - public override void OnSkillLevelChanged(GClass1778 skill) - { - //base.OnSkillLevelChanged(skill); - } - - public override void OnWeaponMastered(MasterSkillClass masterSkill) - { - //base.OnWeaponMastered(masterSkill); - } - - public override void StartInflictSelfDamageCoroutine() - { - // Do nothing - } - - public override void AddStateSpeedLimit(float speedDelta, ESpeedLimit cause) - { - // Do nothing - } - - public override void UpdateSpeedLimit(float speedDelta, ESpeedLimit cause) - { - // Do nothing - } - - public override void UpdateSpeedLimitByHealth() - { - // Do nothing - } - - public override void UpdatePhones() - { - // Do nothing - } - - public override void FaceshieldMarkOperation(FaceShieldComponent armor, bool hasServerOrigin) - { - // Do nothing - } - - public override void SetAudioProtagonist() - { - // Do nothing - } - - public override void ManageAggressor(DamageInfo damageInfo, EBodyPart bodyPart, EBodyPartColliderType colliderType) - { - if (_isDeadAlready) - { - return; - } - if (!HealthController.IsAlive) - { - _isDeadAlready = true; - } - Player player = (damageInfo.Player == null) ? null : Singleton.Instance.GetAlivePlayerByProfileID(damageInfo.Player.iPlayer.ProfileId); - if (player == this) - { - return; - } - if (player == null) - { - return; - } - if (damageInfo.Weapon != null) - { - player.ExecuteShotSkill(damageInfo.Weapon); - } - - if (player.IsYourPlayer) - { - bool flag = damageInfo.DidBodyDamage / HealthController.GetBodyPartHealth(bodyPart, false).Maximum >= 0.6f && HealthController.FindExistingEffect(bodyPart) != null; - 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); - return; - } - } - - public override void UpdateArmsCondition() - { - // Do nothing - } - - public override bool ShouldVocalizeDeath(EBodyPart bodyPart) - { - return true; - } - - public override void SendHeadlightsPacket(bool isSilent) - { - // Do nothing - } - - public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) - { - if (damageInfo.DamageType == EDamageType.Landmine && FikaBackendUtils.IsServer) - { - PacketSender.DamagePackets.Enqueue(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 - }); - - return; - } - - if (damageInfo.Player == null) - { - return; - } - - if (!damageInfo.Player.iPlayer.IsYourPlayer) - { - return; - } - - LastAggressor = damageInfo.Player.iPlayer; - LastDamagedBodyPart = bodyPartType; - LastBodyPart = bodyPartType; - LastDamageInfo = damageInfo; - LastDamageType = damageInfo.DamageType; - } - - public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) - { - ShotReactions(damageInfo, bodyPartType); - - if (damageInfo.DamageType == EDamageType.Sniper && isServer) - { - PacketSender.DamagePackets.Enqueue(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 - }); - - return null; - } - - if (damageInfo.Player != null) - { - LastAggressor = damageInfo.Player.iPlayer; - LastDamagedBodyPart = bodyPartType; - LastBodyPart = bodyPartType; - LastDamageInfo = damageInfo; - LastDamageType = damageInfo.DamageType; - - // There should never be other instances than CoopPlayer or its derived types - CoopPlayer player = (CoopPlayer)damageInfo.Player.iPlayer; - - if (player.IsYourPlayer || 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); - ShotInfoClass hitInfo = new() - { - PoV = PointOfView, - Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), - Material = materialType - }; - float num = damage - damageInfo.Damage; - if (num > 0) - { - damageInfo.DidArmorDamage = num; - } - damageInfo.DidBodyDamage = damageInfo.Damage; - ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); - - PacketSender.DamagePackets.Enqueue(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, - ProfileId = damageInfo.Player.iPlayer.ProfileId, - Material = materialType - }); - - if (list != null) - { - QueueArmorDamagePackets([.. list]); - } - - // Run this to get weapon skill - ManageAggressor(damageInfo, bodyPartType, colliderType); - - return hitInfo; - } - 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); - } - - public override void ApplyCorpseImpulse() - { - if (RagdollPacket.BodyPartColliderType != EBodyPartColliderType.None) - { - Collider collider = PlayerBones.BodyPartCollidersDictionary[RagdollPacket.BodyPartColliderType].Collider; - Corpse.Ragdoll.ApplyImpulse(collider, RagdollPacket.Direction, RagdollPacket.Point, RagdollPacket.Force); - } - } - - public override void CreateMovementContext() - { - LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; - MovementContext = ObservedMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); - } - - public override void OnHealthEffectAdded(IEffect effect) - { - // Remember to check if classes increment - 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); - } - } - - public override void OnHealthEffectRemoved(IEffect effect) - { - // Do nothing - } - - #region proceed - public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) - { - HandsControllerFactory factory = new(this, knifeComponent: knife); - Func func = new(factory.CreateObservedKnifeController); - new Process(this, func, factory.knifeComponent.Item) - .method_0(null, callback, scheduled); - } - - public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) - { - HandsControllerFactory factory = new(this, throwWeap); - Func func = new(factory.CreateObservedGrenadeController); - new Process(this, func, throwWeap, false).method_0(null, callback, scheduled); - } - - 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); - } - - public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) - { - HandsControllerFactory factory = new(this, weapon); - Func func = new(factory.CreateObservedFirearmController); - new Process(this, func, factory.item, true) - .method_0(null, callback, scheduled); - } - - public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) - { - HandsControllerFactory factory = new(this) - { - meds = meds, - bodyPart = bodyPart, - animationVariant = animationVariant - }; - Func func = new(factory.CreateObservedMedsController); - 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) - { - HandsControllerFactory factory = new(this) - { - food = foodDrink, - amount = amount, - animationVariant = animationVariant - }; - Func func = new(factory.CreateObservedMedsController); - new Process(this, func, foodDrink, false) - .method_0(null, callback, scheduled); - } - #endregion - - public override void vmethod_3(EGesture gesture) - { - if (gesture == EGesture.Hello) - { - InteractionRaycast(); - if (InteractablePlayer != null) - { - InteractablePlayer.ShowHelloNotification(Profile.Nickname); - } - } - base.vmethod_3(gesture); - } - - public override void OnFovUpdatedEvent(int fov) - { - // Do nothing - } - - public override void ShowHelloNotification(string sender) - { - // Do nothing - } - - public override void BtrInteraction() - { - // Do nothing - } - - public override void DropCurrentController(Action callback, bool fastDrop, Item nextControllerItem = null) - { - base.DropCurrentController(callback, fastDrop, nextControllerItem); - } - - public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBank bank, PhraseSpeakerClass speaker) - { - method_34(clip); - } - - public PlayerStatePacket Interpolate(in PlayerStatePacket newState, in PlayerStatePacket lastState) - { - float interpolate = 0.3f; - - method_58(newState.HasGround, newState.SurfaceSound); - - Rotation = new Vector2(Mathf.LerpAngle(MovementContext.Rotation.x, newState.Rotation.x, interpolate), - Mathf.Lerp(MovementContext.Rotation.y, newState.Rotation.y, interpolate)); - - HeadRotation = newState.HeadRotation; - ProceduralWeaponAnimation.SetHeadRotation(HeadRotation); - - MovementContext.IsGrounded = newState.IsGrounded; - - EPlayerState oldMovementState = MovementContext.CurrentState.Name; - EPlayerState newMovementState = newState.State; - - if (newMovementState == EPlayerState.Jump) - { - MovementContext.PlayerAnimatorEnableJump(true); - if (isServer) - { - MovementContext.method_2(1f); - } - } - if (MovementContext.PlayerAnimator.IsJumpSetted() && newState.IsGrounded) - { - MovementContext.PlayerAnimatorEnableJump(false); - MovementContext.PlayerAnimatorEnableLanding(true); - } - if ((oldMovementState == EPlayerState.ProneIdle || oldMovementState == EPlayerState.ProneMove) && newMovementState != EPlayerState.ProneMove - && newMovementState != EPlayerState.Transit2Prone && newMovementState != EPlayerState.ProneIdle) - { - MovementContext.IsInPronePose = false; - } - if ((newMovementState == EPlayerState.ProneIdle || newMovementState == EPlayerState.ProneMove) && oldMovementState != EPlayerState.ProneMove - && oldMovementState != EPlayerState.Prone2Stand && oldMovementState != EPlayerState.Transit2Prone && oldMovementState != EPlayerState.ProneIdle) - { - MovementContext.IsInPronePose = true; - } - if (oldMovementState == EPlayerState.Sprint && newMovementState == EPlayerState.Transition) - { - MovementContext.UpdateSprintInertia(); - MovementContext.PlayerAnimatorEnableInert(false); - } - - Physical.SerializationStruct = newState.Stamina; - - if (!Mathf.Approximately(MovementContext.Step, newState.Step)) - { - CurrentManagedState.SetStep(newState.Step); - } - - if (IsSprintEnabled != newState.IsSprinting) - { - CurrentManagedState.EnableSprint(newState.IsSprinting); - } - - if (MovementContext.IsInPronePose != newState.IsProne) - { - MovementContext.IsInPronePose = newState.IsProne; - } - - if (!Mathf.Approximately(PoseLevel, newState.PoseLevel)) - { - MovementContext.SetPoseLevel(PoseLevel + (newState.PoseLevel - PoseLevel)); - } - - MovementContext.SetCurrentClientAnimatorStateIndex(newState.AnimatorStateIndex); - MovementContext.SetCharacterMovementSpeed(newState.CharacterMovementSpeed, true); - - if (MovementContext.BlindFire != newState.Blindfire) - { - MovementContext.SetBlindFire(newState.Blindfire); - } - - if (!IsInventoryOpened) - { - Move(Vector2.Lerp(newState.MovementDirection, lastState.MovementDirection, interpolate)); - if (isServer) - { - MovementContext.method_1(newState.MovementDirection); - } - } - - Vector3 newPosition = Vector3.Lerp(MovementContext.TransformPosition, newState.Position, interpolate); - CharacterController.Move(newPosition - MovementContext.TransformPosition, interpolate); - - if (!Mathf.Approximately(MovementContext.Tilt, newState.Tilt)) - { - MovementContext.SetTilt(newState.Tilt, true); - } - - observedOverlap = newState.WeaponOverlap; - leftStanceDisabled = newState.LeftStanceDisabled; - MovementContext.SurfaceNormal = newState.SurfaceNormal; - - return newState; - } - - public override void OnDead(EDamageType damageType) - { - StartCoroutine(DestroyNetworkedComponents()); - - if (HealthBar != null) - { - Destroy(HealthBar); - } - - if (FikaPlugin.ShowNotifications.Value) - { - if (!IsObservedAI) - { - string nickname = !string.IsNullOrEmpty(Profile.Info.MainProfileNickname) ? Profile.Info.MainProfileNickname : Profile.Nickname; - if (damageType != EDamageType.Undefined) - { - NotificationManagerClass.DisplayWarningNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has died from {ColorizeText(Colors.RED, ("DamageType_" + damageType.ToString()).Localized())}"); - } - else - { - NotificationManagerClass.DisplayWarningNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has died"); - } - } - if (IsBoss(Profile.Info.Settings.Role, out string name) && IsObservedAI && LastAggressor != null) - { - if (LastAggressor is CoopPlayer aggressor) - { - string aggressorNickname = !string.IsNullOrEmpty(LastAggressor.Profile.Info.MainProfileNickname) ? LastAggressor.Profile.Info.MainProfileNickname : LastAggressor.Profile.Nickname; - if (aggressor.gameObject.name.StartsWith("Player_") || aggressor.IsYourPlayer) - { - NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, LastAggressor.Profile.Info.MainProfileNickname)} killed boss {ColorizeText(Colors.BROWN, name)}", iconType: EFT.Communications.ENotificationIconType.Friend); - } - } - } - } - - /*if (Side == EPlayerSide.Savage) - { - if (LastAggressor != null) - { - if (LastAggressor is CoopPlayer coopPlayer) - { - coopPlayer.hasKilledScav = true; - } - } - } - - if (hasKilledScav) - { - if (LastAggressor != null) - { - if (LastAggressor.IsYourPlayer && LastAggressor.Side == EPlayerSide.Savage) - { - if (Side is EPlayerSide.Usec or EPlayerSide.Bear) - { - // 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); - } - else if (Side == EPlayerSide.Savage) - { - LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); - LastAggressor.Profile.FenceInfo.AddStanding(0.03, EFenceStandingSource.TraitorKill); - } - } - } - }*/ - - Singleton.Instance.ProtagonistHearingChanged -= SetSoundRollOff; - /*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); - return; - } - - if (FikaPlugin.EasyKillConditions.Value) - { - if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) - { - CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; - if (mainPlayer != null) - { - float distance = Vector3.Distance(aggressor.Position, Position); - mainPlayer.HandleTeammateKill(damageInfo, bodyPart, Side, Profile.Info.Settings.Role, ProfileId, - distance, CurrentHour, Inventory.EquippedInSlotsTemplateIds, HealthController.BodyPartEffects, TriggerZones, - (CoopPlayer)aggressor); - } - } - } - } - - public override void SetupDogTag() - { - // Do nothing - } - - public override void SetInventory(EquipmentClass equipmentClass) - { - Inventory.Equipment = equipmentClass; - - BindableState itemInHands = Traverse.Create(this).Field>("_itemInHands").Value; - if (HandsController != null && HandsController.Item != null) - { - Item item = FindItem(HandsController.Item.Id); - if (item != null) - { - itemInHands.Value = item; - } - } - - EquipmentSlot[] equipmentSlots = Traverse.Create().Field("SlotNames").Value; - foreach (EquipmentSlot equipmentSlot in equipmentSlots) - { - Transform slotBone = PlayerBody.GetSlotBone(equipmentSlot); - Transform alternativeHolsterBone = PlayerBody.GetAlternativeHolsterBone(equipmentSlot); - 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(); - } - } - - //PlayerBody.Init(PlayerBody.BodyCustomization, Inventory.Equipment, shouldSet ? itemInHands : null, LayerMask.NameToLayer("Player"), Side); - } - - public override void DoObservedVault(VaultPacket packet) - { - if (packet.VaultingStrategy != EVaultingStrategy.Vault) - { - if (packet.VaultingStrategy != EVaultingStrategy.Climb) - { - return; - } - MovementContext.PlayerAnimator.SetDoClimb(true); - } - else - { - MovementContext.PlayerAnimator.SetDoVault(true); - } - - ObservedVaultingParameters.MaxWeightPointPosition = packet.VaultingPoint; - ObservedVaultingParameters.VaultingHeight = packet.VaultingHeight; - ObservedVaultingParameters.VaultingLength = packet.VaultingLength; - ObservedVaultingParameters.VaultingSpeed = packet.VaultingSpeed; - ObservedVaultingParameters.AbsoluteForwardVelocity = packet.AbsoluteForwardVelocity; - ObservedVaultingParameters.BehindObstacleRatio = packet.BehindObstacleHeight; - - MovementContext.PlayerAnimator.SetVaultingSpeed(packet.VaultingSpeed); - MovementContext.PlayerAnimator.SetVaultingHeight(packet.VaultingHeight); - MovementContext.PlayerAnimator.SetVaultingLength(packet.VaultingLength); - MovementContext.PlayerAnimator.SetBehindObstacleRatio(packet.BehindObstacleHeight); - MovementContext.PlayerAnimator.SetAbsoluteForwardVelocity(packet.AbsoluteForwardVelocity); - - MovementContext.PlayerAnimator.SetIsGrounded(true); - } - - private IEnumerator DestroyNetworkedComponents() - { - yield return new WaitForSeconds(2); - - if (Speaker != null) - { - Speaker.Shut(); - Speaker.OnPhraseTold -= OnPhraseTold; - Speaker.OnDestroy(); - } - - // Try to mitigate infinite firing loop further - if (HandsController is CoopObservedFirearmController firearmController) - { - if (firearmController.WeaponSoundPlayer != null && firearmController.WeaponSoundPlayer.enabled) - { - firearmController.WeaponSoundPlayer.enabled = false; - } - } - } - - public void InitObservedPlayer(bool isDedicatedHost) - { - if (gameObject.name.StartsWith("Bot_")) - { - IsObservedAI = true; - } - - PacketSender = gameObject.AddComponent(); - Traverse playerTraverse = Traverse.Create(this); - - if (IsObservedAI) - { - GenericPacket genericPacket = new(EPackageType.LoadBot) - { - NetId = NetId, - BotNetId = NetId - }; - PacketSender.Writer.Reset(); - PacketSender.Client.SendData(PacketSender.Writer, ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - - 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); - playerTraverse.Field("_vaultingGameplayRestrictions").SetValue(null); - playerTraverse.Field("_vaultAudioController").SetValue(null); - playerTraverse.Field("_sprintVaultAudioController").SetValue(null); - playerTraverse.Field("_climbAudioController").SetValue(null); - - /*if (FikaPlugin.CullPlayers.Value) - { - SetupCulling(); - }*/ - } - - PacketReceiver = gameObject.AddComponent(); - - if (!IsObservedAI) + /// + /// Observed players are any other players in the world for a client, including bots. + /// They are all being handled by the server that moves them through packets. + /// As a host this is only other clients. + /// + public class ObservedCoopPlayer : CoopPlayer + { + #region Fields and Properties + public CoopPlayer MainPlayer => (CoopPlayer)Singleton.Instance.MainPlayer; + private float observedFixedTime = 0f; + public FikaHealthBar HealthBar + { + get => healthBar; + } + private FikaHealthBar healthBar = null; + private Coroutine waitForStartRoutine; + private bool isServer; + public ObservedHealthController NetworkHealthController + { + get => HealthController as ObservedHealthController; + } + private readonly ObservedVaultingParametersClass ObservedVaultingParameters = new(); + public override bool CanBeSnapped => false; + public override EPointOfView PointOfView { get => EPointOfView.ThirdPerson; } + public override AbstractHandsController HandsController + { + get => base.HandsController; + set + { + base.HandsController = value; + PlayerAnimator.EWeaponAnimationType weaponAnimationType = GetWeaponAnimationType(_handsController); + MovementContext.PlayerAnimatorSetWeaponId(weaponAnimationType); + } + } + + public override Ray InteractionRay + { + get + { + Vector3 vector = HandsRotation * Vector3.forward; + return new(_playerLookRaycastTransform.position, vector); + } + } + + public override float ProtagonistHearing => Mathf.Max(1f, Singleton.Instance.ProtagonistHearing + 1f); + + private FollowerCullingObject followerCullingObject; + //private List cullingRenderers = new(256); + private List cullingObjects = new(); + private bool CollidingWithReporter + { + get + { + if (cullingObjects.Count > 0) + { + for (int i = 0; i < cullingObjects.Count; i++) + { + if (cullingObjects[i].HasEntered) + { + return true; + } + } + return false; + } + return true; + } + } + public override bool IsVisible + { + get + { + if (followerCullingObject != null) + { + return followerCullingObject.IsVisible && CollidingWithReporter; + } + return OnScreen; + } + set + { + + } + } + public override float SqrCameraDistance + { + get + { + if (followerCullingObject != null) + { + return followerCullingObject.SqrCameraDistance; + } + return base.SqrCameraDistance; + } + } + #endregion + + public static async Task CreateObservedPlayer(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, IViewFilter filter) + { + ObservedCoopPlayer player = Create(ResourceKeyManagerAbstractClass.PLAYER_BUNDLE_NAME, playerId, position, updateQueue, + armsUpdateMode, bodyUpdateMode, characterControllerMode, getSensitivity, getAimingSensitivity, prefix, + aiControl); + + player.IsYourPlayer = false; + + InventoryControllerClass inventoryController = new ObservedInventoryController(player, profile, true); + + PlayerHealthController tempController = new(profile.Health, player, inventoryController, profile.Skills, aiControl); + byte[] healthBytes = tempController.SerializeState(); + + ObservedHealthController healthController = new(healthBytes, inventoryController, profile.Skills); + + CoopObservedStatisticsManager statisticsManager = new(); + + await player.Init(rotation, layerName, pointOfView, profile, inventoryController, healthController, + statisticsManager, null, null, filter, EVoipState.NotAvailable, aiControl, false); + + 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; + + player.isServer = FikaBackendUtils.IsServer; + + return player; + } + + public override BasePhysicalClass CreatePhysical() + { + return new BasePhysicalClass(); + } + + public override bool CheckSurface() + { + float spreadRange = 42f * ProtagonistHearing; + return !(Distance - spreadRange > 0); + } + + public override void PlayGroundedSound(float fallHeight, float jumpHeight) + { + (bool hit, BaseBallistic.ESurfaceSound surfaceSound) values = method_57(); + method_58(values.hit, values.surfaceSound); + base.PlayGroundedSound(fallHeight, jumpHeight); + } + + public override void OnSkillLevelChanged(GClass1778 skill) + { + //base.OnSkillLevelChanged(skill); + } + + public override void OnWeaponMastered(MasterSkillClass masterSkill) + { + //base.OnWeaponMastered(masterSkill); + } + + public override void StartInflictSelfDamageCoroutine() + { + // Do nothing + } + + public override void AddStateSpeedLimit(float speedDelta, ESpeedLimit cause) + { + // Do nothing + } + + public override void UpdateSpeedLimit(float speedDelta, ESpeedLimit cause) + { + // Do nothing + } + + public override void UpdateSpeedLimitByHealth() + { + // Do nothing + } + + public override void UpdatePhones() + { + // Do nothing + } + + public override void FaceshieldMarkOperation(FaceShieldComponent armor, bool hasServerOrigin) + { + // Do nothing + } + + public override void SetAudioProtagonist() + { + // Do nothing + } + + public override void ManageAggressor(DamageInfo damageInfo, EBodyPart bodyPart, EBodyPartColliderType colliderType) + { + if (_isDeadAlready) + { + return; + } + if (!HealthController.IsAlive) + { + _isDeadAlready = true; + } + Player player = (damageInfo.Player == null) ? null : Singleton.Instance.GetAlivePlayerByProfileID(damageInfo.Player.iPlayer.ProfileId); + if (player == this) + { + return; + } + if (player == null) + { + return; + } + if (damageInfo.Weapon != null) + { + player.ExecuteShotSkill(damageInfo.Weapon); + } + + if (player.IsYourPlayer) + { + bool flag = damageInfo.DidBodyDamage / HealthController.GetBodyPartHealth(bodyPart, false).Maximum >= 0.6f && HealthController.FindExistingEffect(bodyPart) != null; + 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); + return; + } + } + + public override void UpdateArmsCondition() + { + // Do nothing + } + + public override bool ShouldVocalizeDeath(EBodyPart bodyPart) + { + return true; + } + + public override void SendHeadlightsPacket(bool isSilent) + { + // Do nothing + } + + public override void ApplyDamageInfo(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, float absorbed) + { + if (damageInfo.DamageType == EDamageType.Landmine && FikaBackendUtils.IsServer) + { + PacketSender.DamagePackets.Enqueue(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 + }); + + return; + } + + if (damageInfo.Player == null) + { + return; + } + + if (!damageInfo.Player.iPlayer.IsYourPlayer) + { + return; + } + + LastAggressor = damageInfo.Player.iPlayer; + LastDamagedBodyPart = bodyPartType; + LastBodyPart = bodyPartType; + LastDamageInfo = damageInfo; + LastDamageType = damageInfo.DamageType; + } + + public override ShotInfoClass ApplyShot(DamageInfo damageInfo, EBodyPart bodyPartType, EBodyPartColliderType colliderType, EArmorPlateCollider armorPlateCollider, GStruct389 shotId) + { + ShotReactions(damageInfo, bodyPartType); + + if (damageInfo.DamageType == EDamageType.Sniper && isServer) + { + PacketSender.DamagePackets.Enqueue(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 + }); + + return null; + } + + if (damageInfo.Player != null) + { + LastAggressor = damageInfo.Player.iPlayer; + LastDamagedBodyPart = bodyPartType; + LastBodyPart = bodyPartType; + LastDamageInfo = damageInfo; + LastDamageType = damageInfo.DamageType; + + // There should never be other instances than CoopPlayer or its derived types + CoopPlayer player = (CoopPlayer)damageInfo.Player.iPlayer; + + if (player.IsYourPlayer || 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); + ShotInfoClass hitInfo = new() + { + PoV = PointOfView, + Penetrated = string.IsNullOrEmpty(damageInfo.BlockedBy) || string.IsNullOrEmpty(damageInfo.DeflectedBy), + Material = materialType + }; + float num = damage - damageInfo.Damage; + if (num > 0) + { + damageInfo.DidArmorDamage = num; + } + damageInfo.DidBodyDamage = damageInfo.Damage; + ReceiveDamage(damageInfo.Damage, bodyPartType, damageInfo.DamageType, num, hitInfo.Material); + + PacketSender.DamagePackets.Enqueue(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, + ProfileId = damageInfo.Player.iPlayer.ProfileId, + Material = materialType + }); + + if (list != null) + { + QueueArmorDamagePackets([.. list]); + } + + // Run this to get weapon skill + ManageAggressor(damageInfo, bodyPartType, colliderType); + + return hitInfo; + } + 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); + } + + public override void ApplyCorpseImpulse() + { + if (RagdollPacket.BodyPartColliderType != EBodyPartColliderType.None) + { + Collider collider = PlayerBones.BodyPartCollidersDictionary[RagdollPacket.BodyPartColliderType].Collider; + Corpse.Ragdoll.ApplyImpulse(collider, RagdollPacket.Direction, RagdollPacket.Point, RagdollPacket.Force); + } + } + + public override void CreateMovementContext() + { + LayerMask movement_MASK = EFTHardSettings.Instance.MOVEMENT_MASK; + MovementContext = ObservedMovementContext.Create(this, new Func(GetBodyAnimatorCommon), new Func(GetCharacterControllerCommon), movement_MASK); + } + + public override void OnHealthEffectAdded(IEffect effect) + { + // Remember to check if classes increment + 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); + } + } + + public override void OnHealthEffectRemoved(IEffect effect) + { + // Do nothing + } + + #region proceed + public override void Proceed(KnifeComponent knife, Callback callback, bool scheduled = true) + { + HandsControllerFactory factory = new(this, knifeComponent: knife); + Func func = new(factory.CreateObservedKnifeController); + new Process(this, func, factory.knifeComponent.Item) + .method_0(null, callback, scheduled); + } + + public override void Proceed(GrenadeClass throwWeap, Callback callback, bool scheduled = true) + { + HandsControllerFactory factory = new(this, throwWeap); + Func func = new(factory.CreateObservedGrenadeController); + new Process(this, func, throwWeap, false).method_0(null, callback, scheduled); + } + + 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); + } + + public override void Proceed(Weapon weapon, Callback callback, bool scheduled = true) + { + HandsControllerFactory factory = new(this, weapon); + Func func = new(factory.CreateObservedFirearmController); + new Process(this, func, factory.item, true) + .method_0(null, callback, scheduled); + } + + public override void Proceed(MedsClass meds, EBodyPart bodyPart, Callback callback, int animationVariant, bool scheduled = true) + { + HandsControllerFactory factory = new(this) + { + meds = meds, + bodyPart = bodyPart, + animationVariant = animationVariant + }; + Func func = new(factory.CreateObservedMedsController); + 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) + { + HandsControllerFactory factory = new(this) + { + food = foodDrink, + amount = amount, + animationVariant = animationVariant + }; + Func func = new(factory.CreateObservedMedsController); + new Process(this, func, foodDrink, false) + .method_0(null, callback, scheduled); + } + #endregion + + public override void vmethod_3(EGesture gesture) + { + if (gesture == EGesture.Hello) + { + InteractionRaycast(); + if (InteractablePlayer != null) + { + InteractablePlayer.ShowHelloNotification(Profile.Nickname); + } + } + base.vmethod_3(gesture); + } + + public override void OnFovUpdatedEvent(int fov) + { + // Do nothing + } + + public override void ShowHelloNotification(string sender) + { + // Do nothing + } + + public override void BtrInteraction() + { + // Do nothing + } + + public override void DropCurrentController(Action callback, bool fastDrop, Item nextControllerItem = null) + { + base.DropCurrentController(callback, fastDrop, nextControllerItem); + } + + public override void OnPhraseTold(EPhraseTrigger @event, TaggedClip clip, TagBank bank, PhraseSpeakerClass speaker) + { + method_34(clip); + } + + public PlayerStatePacket Interpolate(in PlayerStatePacket newState, in PlayerStatePacket lastState) + { + float interpolate = 0.3f; + + method_58(newState.HasGround, newState.SurfaceSound); + + Rotation = new Vector2(Mathf.LerpAngle(MovementContext.Rotation.x, newState.Rotation.x, interpolate), + Mathf.Lerp(MovementContext.Rotation.y, newState.Rotation.y, interpolate)); + + HeadRotation = newState.HeadRotation; + ProceduralWeaponAnimation.SetHeadRotation(HeadRotation); + + MovementContext.IsGrounded = newState.IsGrounded; + + EPlayerState oldMovementState = MovementContext.CurrentState.Name; + EPlayerState newMovementState = newState.State; + + if (newMovementState == EPlayerState.Jump) + { + MovementContext.PlayerAnimatorEnableJump(true); + if (isServer) + { + MovementContext.method_2(1f); + } + } + if (MovementContext.PlayerAnimator.IsJumpSetted() && newState.IsGrounded) + { + MovementContext.PlayerAnimatorEnableJump(false); + MovementContext.PlayerAnimatorEnableLanding(true); + } + if ((oldMovementState == EPlayerState.ProneIdle || oldMovementState == EPlayerState.ProneMove) && newMovementState != EPlayerState.ProneMove + && newMovementState != EPlayerState.Transit2Prone && newMovementState != EPlayerState.ProneIdle) + { + MovementContext.IsInPronePose = false; + } + if ((newMovementState == EPlayerState.ProneIdle || newMovementState == EPlayerState.ProneMove) && oldMovementState != EPlayerState.ProneMove + && oldMovementState != EPlayerState.Prone2Stand && oldMovementState != EPlayerState.Transit2Prone && oldMovementState != EPlayerState.ProneIdle) + { + MovementContext.IsInPronePose = true; + } + if (oldMovementState == EPlayerState.Sprint && newMovementState == EPlayerState.Transition) + { + MovementContext.UpdateSprintInertia(); + MovementContext.PlayerAnimatorEnableInert(false); + } + + Physical.SerializationStruct = newState.Stamina; + + if (!Mathf.Approximately(MovementContext.Step, newState.Step)) + { + CurrentManagedState.SetStep(newState.Step); + } + + if (IsSprintEnabled != newState.IsSprinting) + { + CurrentManagedState.EnableSprint(newState.IsSprinting); + } + + if (MovementContext.IsInPronePose != newState.IsProne) + { + MovementContext.IsInPronePose = newState.IsProne; + } + + if (!Mathf.Approximately(PoseLevel, newState.PoseLevel)) + { + MovementContext.SetPoseLevel(PoseLevel + (newState.PoseLevel - PoseLevel)); + } + + MovementContext.SetCurrentClientAnimatorStateIndex(newState.AnimatorStateIndex); + MovementContext.SetCharacterMovementSpeed(newState.CharacterMovementSpeed, true); + + if (MovementContext.BlindFire != newState.Blindfire) + { + MovementContext.SetBlindFire(newState.Blindfire); + } + + if (!IsInventoryOpened) + { + Move(Vector2.Lerp(newState.MovementDirection, lastState.MovementDirection, interpolate)); + if (isServer) + { + MovementContext.method_1(newState.MovementDirection); + } + } + + Vector3 newPosition = Vector3.Lerp(MovementContext.TransformPosition, newState.Position, interpolate); + CharacterController.Move(newPosition - MovementContext.TransformPosition, interpolate); + + if (!Mathf.Approximately(MovementContext.Tilt, newState.Tilt)) + { + MovementContext.SetTilt(newState.Tilt, true); + } + + observedOverlap = newState.WeaponOverlap; + leftStanceDisabled = newState.LeftStanceDisabled; + MovementContext.SurfaceNormal = newState.SurfaceNormal; + + return newState; + } + + public override void OnDead(EDamageType damageType) + { + StartCoroutine(DestroyNetworkedComponents()); + + if (HealthBar != null) + { + Destroy(HealthBar); + } + + if (FikaPlugin.ShowNotifications.Value) + { + if (!IsObservedAI) + { + string nickname = !string.IsNullOrEmpty(Profile.Info.MainProfileNickname) ? Profile.Info.MainProfileNickname : Profile.Nickname; + if (damageType != EDamageType.Undefined) + { + NotificationManagerClass.DisplayWarningNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has died from {ColorizeText(Colors.RED, ("DamageType_" + damageType.ToString()).Localized())}"); + } + else + { + NotificationManagerClass.DisplayWarningNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has died"); + } + } + if (IsBoss(Profile.Info.Settings.Role, out string name) && IsObservedAI && LastAggressor != null) + { + if (LastAggressor is CoopPlayer aggressor) + { + string aggressorNickname = !string.IsNullOrEmpty(LastAggressor.Profile.Info.MainProfileNickname) ? LastAggressor.Profile.Info.MainProfileNickname : LastAggressor.Profile.Nickname; + if (aggressor.gameObject.name.StartsWith("Player_") || aggressor.IsYourPlayer) + { + NotificationManagerClass.DisplayMessageNotification($"{ColorizeText(Colors.GREEN, LastAggressor.Profile.Info.MainProfileNickname)} killed boss {ColorizeText(Colors.BROWN, name)}", iconType: EFT.Communications.ENotificationIconType.Friend); + } + } + } + } + + /*if (Side == EPlayerSide.Savage) { - if (!isDedicatedHost) - { - Profile.Info.GroupId = "Fika"; - waitForStartRoutine = StartCoroutine(CreateHealthBar()); - } - - 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); - playerTraverse.Field("_vaultingGameplayRestrictions").SetValue(null); - - InitVaultingAudioControllers(ObservedVaultingParameters); - - if (FikaPlugin.ShowNotifications.Value && !isDedicatedHost) - { - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, (Side == EPlayerSide.Savage ? Profile.Info.MainProfileNickname : Profile.Nickname))} has spawned", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); - } - - Singleton.Instance.MainPlayer.StatisticsManager.OnGroupMemberConnected(Inventory); - - RaycastCameraTransform = playerTraverse.Field("_playerLookRaycastTransform").Value; - } - } - - private IEnumerator CreateHealthBar() - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - - if (coopGame == null) - { - yield break; - } - - while (coopGame.Status != GameStatus.Started) - { - yield return null; - } - - healthBar = gameObject.AddComponent(); - - yield break; - } - - public override void LateUpdate() - { - DistanceDirty = true; - OcclusionDirty = true; - if (UpdateQueue == EUpdateQueue.FixedUpdate && !_manuallyUpdated) - { - return; - } - _manuallyUpdated = false; - if (HealthController == null || !HealthController.IsAlive) - { - return; - } - Physical.LateUpdate(); - VisualPass(); - PropUpdate(); - _armsupdated = false; - _bodyupdated = false; - if (_nFixedFrames > 0) - { - _nFixedFrames = 0; - _fixedTime = 0f; - } - float fixedTime = Time.fixedTime; - if (fixedTime - observedFixedTime > 1f) - { - observedFixedTime = fixedTime; - OcclusionDirty = true; - UpdateOcclusion(); - } - } - - public override void LandingAdjustments(float d) - { - // Do nothing - } - - public new void CreateCompass() - { - bool compassInstantiated = Traverse.Create(this).Field("_compassInstantiated").Value; - if (!compassInstantiated) - { - Transform transform = Singleton.Instance.CreateFromPool(new ResourceKey - { - path = "assets/content/weapons/additional_hands/item_compass.bundle" - }); - transform.SetParent(PlayerBones.Ribcage.Original, false); - transform.localRotation = Quaternion.identity; - transform.localPosition = Vector3.zero; - method_29(transform.gameObject); - Traverse.Create(this).Field("_compassInstantiated").SetValue(true); - return; - } - } - - private void SetupCulling() - { - followerCullingObject = gameObject.AddComponent(); - 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.OnVisibilityChanged += OnObservedVisibilityChanged; - - if (_triggerColliderSearcher != null) - { - _triggerColliderSearcher.OnEnter += AddColliderReporters; - _triggerColliderSearcher.OnExit += RemoveColliderReporters; - } - } - - private void UnregisterCulling() - { - if (followerCullingObject != null) - { - followerCullingObject.enabled = false; - } - - if (_triggerColliderSearcher != null) - { - _triggerColliderSearcher.OnEnter -= AddColliderReporters; - _triggerColliderSearcher.OnExit -= RemoveColliderReporters; - } - } - - private void AddColliderReporters(IPhysicsTrigger trigger) - { - ColliderReporter colliderReporter = trigger as ColliderReporter; - if (colliderReporter != null) - { - for (int i = 0; i < colliderReporter.Owners.Count; i++) + if (LastAggressor != null) { - DisablerCullingObject disablerCullingObject = colliderReporter.Owners[i] as DisablerCullingObject; - if (disablerCullingObject != null) + if (LastAggressor is CoopPlayer coopPlayer) { - cullingObjects.Add(disablerCullingObject); + coopPlayer.hasKilledScav = true; } } } - } - private void RemoveColliderReporters(IPhysicsTrigger trigger) - { - ColliderReporter colliderReporter = trigger as ColliderReporter; - if (colliderReporter != null) + if (hasKilledScav) { - for (int i = 0; i < colliderReporter.Owners.Count; i++) + if (LastAggressor != null) { - DisablerCullingObject disablerCullingObject = colliderReporter.Owners[i] as DisablerCullingObject; - if (disablerCullingObject != null) + if (LastAggressor.IsYourPlayer && LastAggressor.Side == EPlayerSide.Savage) { - cullingObjects.Remove(disablerCullingObject); + if (Side is EPlayerSide.Usec or EPlayerSide.Bear) + { + // 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); + } + else if (Side == EPlayerSide.Savage) + { + LastAggressor.Profile.EftStats.SessionCounters.AddDouble(0.03, [CounterTag.FenceStanding, EFenceStandingSource.TraitorKill]); + LastAggressor.Profile.FenceInfo.AddStanding(0.03, EFenceStandingSource.TraitorKill); + } } } - } - } + }*/ + + Singleton.Instance.ProtagonistHearingChanged -= SetSoundRollOff; + /*if (FikaPlugin.CullPlayers.Value) + { + UnregisterCulling(); + }*/ - /*private void OnObservedVisibilityChanged(bool visible) + 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); + return; + } + + if (FikaPlugin.EasyKillConditions.Value) + { + if (aggressor.Profile.Info.GroupId == "Fika" && !aggressor.IsYourPlayer) + { + CoopPlayer mainPlayer = (CoopPlayer)Singleton.Instance.MainPlayer; + if (mainPlayer != null) + { + float distance = Vector3.Distance(aggressor.Position, Position); + mainPlayer.HandleTeammateKill(damageInfo, bodyPart, Side, Profile.Info.Settings.Role, ProfileId, + distance, CurrentHour, Inventory.EquippedInSlotsTemplateIds, HealthController.BodyPartEffects, TriggerZones, + (CoopPlayer)aggressor); + } + } + } + } + + public override void SetupDogTag() + { + // Do nothing + } + + public override void SetInventory(EquipmentClass equipmentClass) + { + Inventory.Equipment = equipmentClass; + + BindableState itemInHands = Traverse.Create(this).Field>("_itemInHands").Value; + if (HandsController != null && HandsController.Item != null) + { + Item item = FindItem(HandsController.Item.Id); + if (item != null) + { + itemInHands.Value = item; + } + } + + EquipmentSlot[] equipmentSlots = Traverse.Create().Field("SlotNames").Value; + foreach (EquipmentSlot equipmentSlot in equipmentSlots) + { + Transform slotBone = PlayerBody.GetSlotBone(equipmentSlot); + Transform alternativeHolsterBone = PlayerBody.GetAlternativeHolsterBone(equipmentSlot); + 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(); + } + } + + //PlayerBody.Init(PlayerBody.BodyCustomization, Inventory.Equipment, shouldSet ? itemInHands : null, LayerMask.NameToLayer("Player"), Side); + } + + public override void DoObservedVault(VaultPacket packet) + { + if (packet.VaultingStrategy != EVaultingStrategy.Vault) + { + if (packet.VaultingStrategy != EVaultingStrategy.Climb) + { + return; + } + MovementContext.PlayerAnimator.SetDoClimb(true); + } + else + { + MovementContext.PlayerAnimator.SetDoVault(true); + } + + ObservedVaultingParameters.MaxWeightPointPosition = packet.VaultingPoint; + ObservedVaultingParameters.VaultingHeight = packet.VaultingHeight; + ObservedVaultingParameters.VaultingLength = packet.VaultingLength; + ObservedVaultingParameters.VaultingSpeed = packet.VaultingSpeed; + ObservedVaultingParameters.AbsoluteForwardVelocity = packet.AbsoluteForwardVelocity; + ObservedVaultingParameters.BehindObstacleRatio = packet.BehindObstacleHeight; + + MovementContext.PlayerAnimator.SetVaultingSpeed(packet.VaultingSpeed); + MovementContext.PlayerAnimator.SetVaultingHeight(packet.VaultingHeight); + MovementContext.PlayerAnimator.SetVaultingLength(packet.VaultingLength); + MovementContext.PlayerAnimator.SetBehindObstacleRatio(packet.BehindObstacleHeight); + MovementContext.PlayerAnimator.SetAbsoluteForwardVelocity(packet.AbsoluteForwardVelocity); + + MovementContext.PlayerAnimator.SetIsGrounded(true); + } + + private IEnumerator DestroyNetworkedComponents() + { + yield return new WaitForSeconds(2); + + if (Speaker != null) + { + Speaker.Shut(); + Speaker.OnPhraseTold -= OnPhraseTold; + Speaker.OnDestroy(); + } + + // Try to mitigate infinite firing loop further + if (HandsController is CoopObservedFirearmController firearmController) + { + if (firearmController.WeaponSoundPlayer != null && firearmController.WeaponSoundPlayer.enabled) + { + firearmController.WeaponSoundPlayer.enabled = false; + } + } + } + + public void InitObservedPlayer(bool isDedicatedHost) + { + if (gameObject.name.StartsWith("Bot_")) + { + IsObservedAI = true; + } + + PacketSender = gameObject.AddComponent(); + Traverse playerTraverse = Traverse.Create(this); + + if (IsObservedAI) + { + GenericPacket genericPacket = new(EPackageType.LoadBot) + { + NetId = NetId, + BotNetId = NetId + }; + PacketSender.Writer.Reset(); + PacketSender.Client.SendData(PacketSender.Writer, ref genericPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + + 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); + playerTraverse.Field("_vaultingGameplayRestrictions").SetValue(null); + playerTraverse.Field("_vaultAudioController").SetValue(null); + playerTraverse.Field("_sprintVaultAudioController").SetValue(null); + playerTraverse.Field("_climbAudioController").SetValue(null); + + /*if (FikaPlugin.CullPlayers.Value) + { + SetupCulling(); + }*/ + } + + PacketReceiver = gameObject.AddComponent(); + + if (!IsObservedAI) + { + if (!isDedicatedHost) + { + Profile.Info.GroupId = "Fika"; + waitForStartRoutine = StartCoroutine(CreateHealthBar()); + } + + 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); + playerTraverse.Field("_vaultingGameplayRestrictions").SetValue(null); + + InitVaultingAudioControllers(ObservedVaultingParameters); + + if (FikaPlugin.ShowNotifications.Value && !isDedicatedHost) + { + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, (Side == EPlayerSide.Savage ? Profile.Info.MainProfileNickname : Profile.Nickname))} has spawned", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); + } + + Singleton.Instance.MainPlayer.StatisticsManager.OnGroupMemberConnected(Inventory); + + RaycastCameraTransform = playerTraverse.Field("_playerLookRaycastTransform").Value; + } + } + + private IEnumerator CreateHealthBar() + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + + if (coopGame == null) + { + yield break; + } + + while (coopGame.Status != GameStatus.Started) + { + yield return null; + } + + healthBar = gameObject.AddComponent(); + + yield break; + } + + public override void LateUpdate() + { + DistanceDirty = true; + OcclusionDirty = true; + if (UpdateQueue == EUpdateQueue.FixedUpdate && !_manuallyUpdated) + { + return; + } + _manuallyUpdated = false; + if (HealthController == null || !HealthController.IsAlive) + { + return; + } + Physical.LateUpdate(); + VisualPass(); + PropUpdate(); + _armsupdated = false; + _bodyupdated = false; + if (_nFixedFrames > 0) + { + _nFixedFrames = 0; + _fixedTime = 0f; + } + float fixedTime = Time.fixedTime; + if (fixedTime - observedFixedTime > 1f) + { + observedFixedTime = fixedTime; + OcclusionDirty = true; + UpdateOcclusion(); + } + } + + public override void LandingAdjustments(float d) + { + // Do nothing + } + + public new void CreateCompass() + { + bool compassInstantiated = Traverse.Create(this).Field("_compassInstantiated").Value; + if (!compassInstantiated) + { + Transform transform = Singleton.Instance.CreateFromPool(new ResourceKey + { + path = "assets/content/weapons/additional_hands/item_compass.bundle" + }); + transform.SetParent(PlayerBones.Ribcage.Original, false); + transform.localRotation = Quaternion.identity; + transform.localPosition = Vector3.zero; + method_29(transform.gameObject); + Traverse.Create(this).Field("_compassInstantiated").SetValue(true); + return; + } + } + + private void SetupCulling() + { + followerCullingObject = gameObject.AddComponent(); + 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.OnVisibilityChanged += OnObservedVisibilityChanged; + + if (_triggerColliderSearcher != null) + { + _triggerColliderSearcher.OnEnter += AddColliderReporters; + _triggerColliderSearcher.OnExit += RemoveColliderReporters; + } + } + + private void UnregisterCulling() + { + if (followerCullingObject != null) + { + followerCullingObject.enabled = false; + } + + if (_triggerColliderSearcher != null) + { + _triggerColliderSearcher.OnEnter -= AddColliderReporters; + _triggerColliderSearcher.OnExit -= RemoveColliderReporters; + } + } + + private void AddColliderReporters(IPhysicsTrigger trigger) + { + ColliderReporter colliderReporter = trigger as ColliderReporter; + if (colliderReporter != null) + { + for (int i = 0; i < colliderReporter.Owners.Count; i++) + { + DisablerCullingObject disablerCullingObject = colliderReporter.Owners[i] as DisablerCullingObject; + if (disablerCullingObject != null) + { + cullingObjects.Add(disablerCullingObject); + } + } + } + } + + private void RemoveColliderReporters(IPhysicsTrigger trigger) + { + ColliderReporter colliderReporter = trigger as ColliderReporter; + if (colliderReporter != null) + { + for (int i = 0; i < colliderReporter.Owners.Count; i++) + { + DisablerCullingObject disablerCullingObject = colliderReporter.Owners[i] as DisablerCullingObject; + if (disablerCullingObject != null) + { + cullingObjects.Remove(disablerCullingObject); + } + } + } + } + + /*private void OnObservedVisibilityChanged(bool visible) { for (int i = 0; i < cullingRenderers.Count; i++) { @@ -1083,301 +1083,301 @@ private void RemoveColliderReporters(IPhysicsTrigger trigger) } }*/ - private Transform GetPlayerBones() - { - return PlayerBones.BodyTransform.Original; - } - - public override void OnVaulting() - { - // Do nothing - } - - public override void ManualUpdate(float deltaTime, float? platformDeltaTime = null, int loop = 1) - { - _bodyupdated = true; - _bodyTime = deltaTime; - - method_15(deltaTime); - - UpdateTriggerColliderSearcher(deltaTime, SqrCameraDistance < 1600); - } - - public override void InitAudioController() - { - base.InitAudioController(); - Singleton.Instance.ProtagonistHearingChanged += SetSoundRollOff; - } - - private void SetSoundRollOff() - { - if (NestedStepSoundSource != null) - { - NestedStepSoundSource.SetRolloff(60f * ProtagonistHearing); - } - } - - public override bool UpdateGrenadeAnimatorDuePoV() - { - return true; - } - - public override void InitialProfileExamineAll() - { - // Do nothing - } - - public override void FixedUpdateTick() - { - // Do nothing - } - - public override void OnDestroy() - { - if (HandsController != null) - { - AbstractHandsController handsController = HandsController; - if (handsController != null && handsController.ControllerGameObject != null) - { - HandsController.OnGameSessionEnd(); - HandsController.Destroy(); - } - } - if (HealthBar != null) - { - Destroy(HealthBar); - } - if (Singleton.Instantiated) - { - Singleton.Instance.ProtagonistHearingChanged -= SetSoundRollOff; - } - base.OnDestroy(); - } - - public override void Dispose() - { - /*if (FikaPlugin.CullPlayers.Value) + private Transform GetPlayerBones() + { + return PlayerBones.BodyTransform.Original; + } + + public override void OnVaulting() + { + // Do nothing + } + + public override void ManualUpdate(float deltaTime, float? platformDeltaTime = null, int loop = 1) + { + _bodyupdated = true; + _bodyTime = deltaTime; + + method_15(deltaTime); + + UpdateTriggerColliderSearcher(deltaTime, SqrCameraDistance < 1600); + } + + public override void InitAudioController() + { + base.InitAudioController(); + Singleton.Instance.ProtagonistHearingChanged += SetSoundRollOff; + } + + private void SetSoundRollOff() + { + if (NestedStepSoundSource != null) + { + NestedStepSoundSource.SetRolloff(60f * ProtagonistHearing); + } + } + + public override bool UpdateGrenadeAnimatorDuePoV() + { + return true; + } + + public override void InitialProfileExamineAll() + { + // Do nothing + } + + public override void FixedUpdateTick() + { + // Do nothing + } + + public override void OnDestroy() + { + if (HandsController != null) + { + AbstractHandsController handsController = HandsController; + if (handsController != null && handsController.ControllerGameObject != null) + { + HandsController.OnGameSessionEnd(); + HandsController.Destroy(); + } + } + if (HealthBar != null) + { + Destroy(HealthBar); + } + if (Singleton.Instantiated) + { + Singleton.Instance.ProtagonistHearingChanged -= SetSoundRollOff; + } + base.OnDestroy(); + } + + public override void Dispose() + { + /*if (FikaPlugin.CullPlayers.Value) { UnregisterCulling(); }*/ - base.Dispose(); - } - - public override void SendHandsInteractionStateChanged(bool value, int animationId) - { - if (value) - { - MovementContext.SetBlindFire(0); - } - } - - public override void HandleDamagePacket(ref DamagePacket packet) - { - // Do nothing - } - - public void HandleProceedPacket(ProceedPacket packet) - { - switch (packet.ProceedType) - { - case EProceedType.EmptyHands: - { - CreateEmptyHandsController(); - break; - } - case EProceedType.FoodClass: - case EProceedType.MedsClass: - { - CreateMedsController(packet.ItemId, packet.BodyPart, packet.Amount, packet.AnimationVariant); - break; - } - case EProceedType.GrenadeClass: - { - CreateGrenadeController(packet.ItemId); - break; - } - case EProceedType.QuickGrenadeThrow: - { - CreateQuickGrenadeController(packet.ItemId); - break; - } - case EProceedType.QuickKnifeKick: - { - CreateQuickKnifeController(packet.ItemId); - break; - } - case EProceedType.QuickUse: - { - CreateQuickUseItemController(packet.ItemId); - break; - } - case EProceedType.Weapon: - { - if (HandsController == null || HandsController.Item.Id != packet.ItemId) - { - CreateFirearmController(packet.ItemId); - } - break; - } - case EProceedType.Knife: - { - CreateKnifeController(packet.ItemId); - break; - } - } - } - - #region handControllers - private void CreateHandsController(Func controllerFactory, Item item) - { - CreateHandsControllerHandler handler = new((item != null) ? method_71(item) : null); - - handler.setInHandsOperation?.Confirm(true); - - if (HandsController != null) - { - AbstractHandsController handsController = HandsController; - HandsController.FastForwardCurrentState(); - if (HandsController != handsController && HandsController != null) - { - HandsController.FastForwardCurrentState(); - } - HandsController.Destroy(); - HandsController = null; - } - - base.SpawnController(controllerFactory(), new Action(handler.DisposeHandler)); - } - - private void CreateEmptyHandsController() - { - CreateHandsController(new Func(ReturnEmptyHandsController), null); - } - - private AbstractHandsController ReturnEmptyHandsController() - { - return CoopObservedEmptyHandsController.Create(this); - } - - private void CreateFirearmController(string itemId) - { - CreateFirearmControllerHandler handler = new(this); - - if (MovementContext.StationaryWeapon != null && MovementContext.StationaryWeapon.Id == itemId) - { - handler.item = MovementContext.StationaryWeapon.Item; - } - else - { - handler.item = FindItem(itemId); - } - - if (handler.item != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateFirearmController: item was null!"); - } - } - - private void CreateGrenadeController(string itemId) - { - CreateGrenadeControllerHandler handler = new(this); - - Item item = FindItem(itemId); - handler.item = item; - if ((handler.item = item as GrenadeClass) != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateGrenadeController: item was null!"); - } - } - - private void CreateMedsController(string itemId, EBodyPart bodyPart, float amount, int animationVariant) - { - CreateMedsControllerHandler handler = new(this, FindItem(itemId), bodyPart, amount, animationVariant); - if (handler.item != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateMedsController: item was null!"); - } - } - - private void CreateKnifeController(string itemId) - { - CreateKnifeControllerHandler handler = new(this); - - Item item = FindItem(itemId); - handler.knife = item.GetItemComponent(); - if (handler.knife != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.knife.Item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateKnifeController: item was null!"); - } - } - - private void CreateQuickGrenadeController(string itemId) - { - CreateQuickGrenadeControllerHandler handler = new(this); - - Item item = FindItem(itemId); - if ((handler.item = item as GrenadeClass) != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateQuickGrenadeController: item was null!"); - } - } - - private void CreateQuickKnifeController(string itemId) - { - CreateQuickKnifeControllerHandler handler = new(this); - - Item item = FindItem(itemId); - handler.knife = item.GetItemComponent(); - if (handler.knife != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.knife.Item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateQuickKnifeController: item was null!"); - } - } - - private void CreateQuickUseItemController(string itemId) - { - CreateQuickUseItemControllerHandler handler = new(this, FindItem(itemId)); - if (handler.item != null) - { - CreateHandsController(new Func(handler.ReturnController), handler.item); - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("CreateMedsController: item was null!"); - } - } - - public void SetAggressor(string killerId) - { - Player killer = Singleton.Instance.GetEverExistedPlayerByID(killerId); - if (killer != null) - { - LastAggressor = killer; - /*if (killer.IsYourPlayer) + base.Dispose(); + } + + public override void SendHandsInteractionStateChanged(bool value, int animationId) + { + if (value) + { + MovementContext.SetBlindFire(0); + } + } + + public override void HandleDamagePacket(ref DamagePacket packet) + { + // Do nothing + } + + public void HandleProceedPacket(ProceedPacket packet) + { + switch (packet.ProceedType) + { + case EProceedType.EmptyHands: + { + CreateEmptyHandsController(); + break; + } + case EProceedType.FoodClass: + case EProceedType.MedsClass: + { + CreateMedsController(packet.ItemId, packet.BodyPart, packet.Amount, packet.AnimationVariant); + break; + } + case EProceedType.GrenadeClass: + { + CreateGrenadeController(packet.ItemId); + break; + } + case EProceedType.QuickGrenadeThrow: + { + CreateQuickGrenadeController(packet.ItemId); + break; + } + case EProceedType.QuickKnifeKick: + { + CreateQuickKnifeController(packet.ItemId); + break; + } + case EProceedType.QuickUse: + { + CreateQuickUseItemController(packet.ItemId); + break; + } + case EProceedType.Weapon: + { + if (HandsController == null || HandsController.Item.Id != packet.ItemId) + { + CreateFirearmController(packet.ItemId); + } + break; + } + case EProceedType.Knife: + { + CreateKnifeController(packet.ItemId); + break; + } + } + } + + #region handControllers + private void CreateHandsController(Func controllerFactory, Item item) + { + CreateHandsControllerHandler handler = new((item != null) ? method_71(item) : null); + + handler.setInHandsOperation?.Confirm(true); + + if (HandsController != null) + { + AbstractHandsController handsController = HandsController; + HandsController.FastForwardCurrentState(); + if (HandsController != handsController && HandsController != null) + { + HandsController.FastForwardCurrentState(); + } + HandsController.Destroy(); + HandsController = null; + } + + base.SpawnController(controllerFactory(), new Action(handler.DisposeHandler)); + } + + private void CreateEmptyHandsController() + { + CreateHandsController(new Func(ReturnEmptyHandsController), null); + } + + private AbstractHandsController ReturnEmptyHandsController() + { + return CoopObservedEmptyHandsController.Create(this); + } + + private void CreateFirearmController(string itemId) + { + CreateFirearmControllerHandler handler = new(this); + + if (MovementContext.StationaryWeapon != null && MovementContext.StationaryWeapon.Id == itemId) + { + handler.item = MovementContext.StationaryWeapon.Item; + } + else + { + handler.item = FindItem(itemId); + } + + if (handler.item != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateFirearmController: item was null!"); + } + } + + private void CreateGrenadeController(string itemId) + { + CreateGrenadeControllerHandler handler = new(this); + + Item item = FindItem(itemId); + handler.item = item; + if ((handler.item = item as GrenadeClass) != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateGrenadeController: item was null!"); + } + } + + private void CreateMedsController(string itemId, EBodyPart bodyPart, float amount, int animationVariant) + { + CreateMedsControllerHandler handler = new(this, FindItem(itemId), bodyPart, amount, animationVariant); + if (handler.item != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateMedsController: item was null!"); + } + } + + private void CreateKnifeController(string itemId) + { + CreateKnifeControllerHandler handler = new(this); + + Item item = FindItem(itemId); + handler.knife = item.GetItemComponent(); + if (handler.knife != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.knife.Item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateKnifeController: item was null!"); + } + } + + private void CreateQuickGrenadeController(string itemId) + { + CreateQuickGrenadeControllerHandler handler = new(this); + + Item item = FindItem(itemId); + if ((handler.item = item as GrenadeClass) != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateQuickGrenadeController: item was null!"); + } + } + + private void CreateQuickKnifeController(string itemId) + { + CreateQuickKnifeControllerHandler handler = new(this); + + Item item = FindItem(itemId); + handler.knife = item.GetItemComponent(); + if (handler.knife != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.knife.Item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateQuickKnifeController: item was null!"); + } + } + + private void CreateQuickUseItemController(string itemId) + { + CreateQuickUseItemControllerHandler handler = new(this, FindItem(itemId)); + if (handler.item != null) + { + CreateHandsController(new Func(handler.ReturnController), handler.item); + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("CreateMedsController: item was null!"); + } + } + + public void SetAggressor(string killerId) + { + Player killer = Singleton.Instance.GetEverExistedPlayerByID(killerId); + if (killer != null) + { + LastAggressor = killer; + /*if (killer.IsYourPlayer) { Item weapon = FindItem(weaponId); if (weapon != null) @@ -1388,117 +1388,117 @@ public void SetAggressor(string killerId) }; } }*/ - } - } - - private class RemoveHandsControllerHandler(ObservedCoopPlayer coopPlayer, Callback callback) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - private readonly Callback callback = callback; - - public void Handle(Result result) - { - if (coopPlayer._removeFromHandsCallback == callback) - { - coopPlayer._removeFromHandsCallback = null; - } - callback.Invoke(result); - } - } - - private class CreateHandsControllerHandler(Class1082 setInHandsOperation) - { - public readonly Class1082 setInHandsOperation = setInHandsOperation; - - internal void DisposeHandler() - { - Class1082 handler = setInHandsOperation; - if (handler == null) - return; - handler.Dispose(); - } - } - - private class CreateFirearmControllerHandler(ObservedCoopPlayer coopPlayer) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public Item item; - - internal AbstractHandsController ReturnController() - { - return CoopObservedFirearmController.Create(coopPlayer, (Weapon)item); - } - } - - private class CreateGrenadeControllerHandler(ObservedCoopPlayer coopPlayer) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public Item item; - - internal AbstractHandsController ReturnController() - { - return CoopObservedGrenadeController.Create(coopPlayer, (GrenadeClass)item); - } - } - - private class CreateMedsControllerHandler(ObservedCoopPlayer coopPlayer, Item item, EBodyPart bodyPart, float amount, int animationVariant) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public readonly Item item = item; - private readonly EBodyPart bodyPart = bodyPart; - private readonly float amount = amount; - private readonly int animationVariant = animationVariant; - - internal AbstractHandsController ReturnController() - { - return CoopObservedMedsController.Create(coopPlayer, item, bodyPart, amount, animationVariant); - } - } - - private class CreateKnifeControllerHandler(ObservedCoopPlayer coopPlayer) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public KnifeComponent knife; - - internal AbstractHandsController ReturnController() - { - return CoopObservedKnifeController.Create(coopPlayer, knife); - } - } - - private class CreateQuickGrenadeControllerHandler(ObservedCoopPlayer coopPlayer) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public Item item; - - internal AbstractHandsController ReturnController() - { - return CoopObservedQuickGrenadeController.Create(coopPlayer, (GrenadeClass)item); - } - } - - private class CreateQuickKnifeControllerHandler(ObservedCoopPlayer coopPlayer) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public KnifeComponent knife; - - internal AbstractHandsController ReturnController() - { - return QuickKnifeKickController.smethod_8(coopPlayer, knife); - } - } - - private class CreateQuickUseItemControllerHandler(ObservedCoopPlayer coopPlayer, Item item) - { - private readonly ObservedCoopPlayer coopPlayer = coopPlayer; - public readonly Item item = item; - - internal AbstractHandsController ReturnController() - { - return QuickUseItemController.smethod_5(coopPlayer, item); - } - } - } + } + } + + private class RemoveHandsControllerHandler(ObservedCoopPlayer coopPlayer, Callback callback) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + private readonly Callback callback = callback; + + public void Handle(Result result) + { + if (coopPlayer._removeFromHandsCallback == callback) + { + coopPlayer._removeFromHandsCallback = null; + } + callback.Invoke(result); + } + } + + private class CreateHandsControllerHandler(Class1082 setInHandsOperation) + { + public readonly Class1082 setInHandsOperation = setInHandsOperation; + + internal void DisposeHandler() + { + Class1082 handler = setInHandsOperation; + if (handler == null) + return; + handler.Dispose(); + } + } + + private class CreateFirearmControllerHandler(ObservedCoopPlayer coopPlayer) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public Item item; + + internal AbstractHandsController ReturnController() + { + return CoopObservedFirearmController.Create(coopPlayer, (Weapon)item); + } + } + + private class CreateGrenadeControllerHandler(ObservedCoopPlayer coopPlayer) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public Item item; + + internal AbstractHandsController ReturnController() + { + return CoopObservedGrenadeController.Create(coopPlayer, (GrenadeClass)item); + } + } + + private class CreateMedsControllerHandler(ObservedCoopPlayer coopPlayer, Item item, EBodyPart bodyPart, float amount, int animationVariant) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public readonly Item item = item; + private readonly EBodyPart bodyPart = bodyPart; + private readonly float amount = amount; + private readonly int animationVariant = animationVariant; + + internal AbstractHandsController ReturnController() + { + return CoopObservedMedsController.Create(coopPlayer, item, bodyPart, amount, animationVariant); + } + } + + private class CreateKnifeControllerHandler(ObservedCoopPlayer coopPlayer) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public KnifeComponent knife; + + internal AbstractHandsController ReturnController() + { + return CoopObservedKnifeController.Create(coopPlayer, knife); + } + } + + private class CreateQuickGrenadeControllerHandler(ObservedCoopPlayer coopPlayer) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public Item item; + + internal AbstractHandsController ReturnController() + { + return CoopObservedQuickGrenadeController.Create(coopPlayer, (GrenadeClass)item); + } + } + + private class CreateQuickKnifeControllerHandler(ObservedCoopPlayer coopPlayer) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public KnifeComponent knife; + + internal AbstractHandsController ReturnController() + { + return QuickKnifeKickController.smethod_8(coopPlayer, knife); + } + } + + private class CreateQuickUseItemControllerHandler(ObservedCoopPlayer coopPlayer, Item item) + { + private readonly ObservedCoopPlayer coopPlayer = coopPlayer; + public readonly Item item = item; + + internal AbstractHandsController ReturnController() + { + return QuickUseItemController.smethod_5(coopPlayer, item); + } + } + } } #endregion \ No newline at end of file diff --git a/Fika.Core/Coop/Utils/FikaBackendUtils.cs b/Fika.Core/Coop/Utils/FikaBackendUtils.cs index cef95128..6b141634 100644 --- a/Fika.Core/Coop/Utils/FikaBackendUtils.cs +++ b/Fika.Core/Coop/Utils/FikaBackendUtils.cs @@ -11,127 +11,127 @@ namespace Fika.Core.Coop.Utils { - public enum EMatchmakerType - { - Single = 0, - GroupPlayer = 1, - GroupLeader = 2 - } - - public static class FikaBackendUtils - { - 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 IsDedicated = false; - public static bool IsReconnect = false; - public static bool IsSinglePlayer - { - get - { - return Singleton.Instantiated - && Singleton.Instance.NetServer.ConnectedPeersCount == 0; - } - } - public static bool IsDedicatedGame = false; - 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; - public static string HostLocationId; - public static bool RequestFikaWorld = false; - public static Vector3 ReconnectPosition = Vector3.zero; - private static string groupId; - private static string raidCode; - - public static MatchmakerTimeHasCome.TimeHasComeScreenClass ScreenController; - - public static string GetGroupId() - { - return groupId; - } - - public static void SetGroupId(string newId) - { - groupId = newId; - } - - public static void SetRaidCode(string newCode) - { - raidCode = newCode; - } - - public static string GetRaidCode() - { - return raidCode; - } - - public static bool JoinMatch(string profileId, string serverId, out CreateMatch result, out string errorMessage) - { - result = new CreateMatch(); - errorMessage = $"No server matches the data provided or the server no longer exists"; - - if (MatchMakerAcceptScreenInstance == null) - { - return false; - } - - 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 than what the server is running.\nClient: {FikaPlugin.EFTVersionMajor}\nServer: {result.GameVersion}"; - return false; - } - - Version detectedFikaVersion = Assembly.GetExecutingAssembly().GetName().Version; - if (result.FikaVersion != detectedFikaVersion) - { - 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 async Task CreateMatch(string profileId, string hostUsername, RaidSettings raidSettings) - { - NotificationManagerClass.DisplayWarningNotification("Starting raid, please wait..."); - long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); - string raidCode = GenerateRaidCode(6); - CreateMatch body = new(raidCode, profileId, hostUsername, timestamp, raidSettings, - HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime); - - await FikaRequestHandler.RaidCreate(body); - - SetGroupId(profileId); - MatchingType = EMatchmakerType.GroupLeader; - - SetRaidCode(raidCode); - } - - public static string GenerateRaidCode(int length) - { - System.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; - } - } + public enum EMatchmakerType + { + Single = 0, + GroupPlayer = 1, + GroupLeader = 2 + } + + public static class FikaBackendUtils + { + 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 IsDedicated = false; + public static bool IsReconnect = false; + public static bool IsSinglePlayer + { + get + { + return Singleton.Instantiated + && Singleton.Instance.NetServer.ConnectedPeersCount == 0; + } + } + public static bool IsDedicatedGame = false; + 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; + public static string HostLocationId; + public static bool RequestFikaWorld = false; + public static Vector3 ReconnectPosition = Vector3.zero; + private static string groupId; + private static string raidCode; + + public static MatchmakerTimeHasCome.TimeHasComeScreenClass ScreenController; + + public static string GetGroupId() + { + return groupId; + } + + public static void SetGroupId(string newId) + { + groupId = newId; + } + + public static void SetRaidCode(string newCode) + { + raidCode = newCode; + } + + public static string GetRaidCode() + { + return raidCode; + } + + public static bool JoinMatch(string profileId, string serverId, out CreateMatch result, out string errorMessage) + { + result = new CreateMatch(); + errorMessage = $"No server matches the data provided or the server no longer exists"; + + if (MatchMakerAcceptScreenInstance == null) + { + return false; + } + + 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 than what the server is running.\nClient: {FikaPlugin.EFTVersionMajor}\nServer: {result.GameVersion}"; + return false; + } + + Version detectedFikaVersion = Assembly.GetExecutingAssembly().GetName().Version; + if (result.FikaVersion != detectedFikaVersion) + { + 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 async Task CreateMatch(string profileId, string hostUsername, RaidSettings raidSettings) + { + NotificationManagerClass.DisplayWarningNotification("Starting raid, please wait..."); + long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + string raidCode = GenerateRaidCode(6); + CreateMatch body = new(raidCode, profileId, hostUsername, timestamp, raidSettings, + HostExpectedNumberOfPlayers, raidSettings.Side, raidSettings.SelectedDateTime); + + await FikaRequestHandler.RaidCreate(body); + + SetGroupId(profileId); + MatchingType = EMatchmakerType.GroupLeader; + + SetRaidCode(raidCode); + } + + public static string GenerateRaidCode(int length) + { + System.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 index fdf76d9d..c5a0d17d 100644 --- a/Fika.Core/Coop/Utils/NetManagerUtils.cs +++ b/Fika.Core/Coop/Utils/NetManagerUtils.cs @@ -11,229 +11,229 @@ 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"); - GameObject.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!"); - } - } - - /// - /// Sends a reliable unordered packet - /// - /// - /// - public static void SendPacket(ref T packet) where T : INetSerializable - { - if (FikaBackendUtils.IsServer) - { - FikaServer server = Singleton.Instance; - if (server != null) - { - server.Writer.Reset(); - server.SendDataToAll(server.Writer, ref packet, DeliveryMethod.ReliableUnordered); - return; - } - } - - FikaClient client = Singleton.Instance; - if (client != null) - { - client.Writer.Reset(); - client.SendData(client.Writer, ref packet, DeliveryMethod.ReliableUnordered); - } - } - - 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) - { - FikaServer server = Singleton.Instance; - try - { - server.NetServer.Stop(); - } - catch (Exception ex) - { - logger.LogError("DestroyNetManager: " + ex.Message); - } - Singleton.TryRelease(server); - GameObject.Destroy(server); - logger.LogInfo("Destroyed FikaServer"); - } - else - { - FikaClient client = Singleton.Instance; - try - { - client.NetClient.Stop(); - } - catch (Exception ex) - { - logger.LogError("DestroyNetManager: " + ex.Message); - } - Singleton.TryRelease(client); - GameObject.Destroy(client); - logger.LogInfo("Destroyed FikaClient"); - } - } - } - - public static void DestroyPingingClient() - { - if (FikaGameObject != null) - { - FikaPingingClient pingingClient = Singleton.Instance; - pingingClient.StopKeepAliveRoutine(); - pingingClient.NetClient.Stop(); - Singleton.TryRelease(pingingClient); - GameObject.Destroy(pingingClient); - 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!"); - throw new NullReferenceException("FikaGameObject was null"); - } - - 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) - { - GameObject.Destroy(fikaPinger); - } - else - { - logger.LogError("StopPinger: Could not find FikaPinger!"); - } - } - } - - public static Task CreateCoopHandler() - { - logger.LogInfo("Creating CoopHandler..."); - CoopHandler coopHandler = CoopHandler.GetCoopHandler(); - if (coopHandler != null) - { - GameObject.Destroy(coopHandler); - } - - if (CoopHandler.CoopHandlerParent != null) - { - GameObject.Destroy(CoopHandler.CoopHandlerParent); - CoopHandler.CoopHandlerParent = null; - } - - if (CoopHandler.CoopHandlerParent == null) - { - CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); - GameObject.DontDestroyOnLoad(CoopHandler.CoopHandlerParent); - } - - coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); - - if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) - { - coopHandler.ServerId = FikaBackendUtils.GetGroupId(); - } - else - { - GameObject.Destroy(coopHandler); - logger.LogError("No Server Id found, Deleting Coop Handler"); - throw new MissingReferenceException("No Server Id found"); - } - - return Task.CompletedTask; - } - } + 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"); + GameObject.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!"); + } + } + + /// + /// Sends a reliable unordered packet + /// + /// + /// + public static void SendPacket(ref T packet) where T : INetSerializable + { + if (FikaBackendUtils.IsServer) + { + FikaServer server = Singleton.Instance; + if (server != null) + { + server.Writer.Reset(); + server.SendDataToAll(server.Writer, ref packet, DeliveryMethod.ReliableUnordered); + return; + } + } + + FikaClient client = Singleton.Instance; + if (client != null) + { + client.Writer.Reset(); + client.SendData(client.Writer, ref packet, DeliveryMethod.ReliableUnordered); + } + } + + 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) + { + FikaServer server = Singleton.Instance; + try + { + server.NetServer.Stop(); + } + catch (Exception ex) + { + logger.LogError("DestroyNetManager: " + ex.Message); + } + Singleton.TryRelease(server); + GameObject.Destroy(server); + logger.LogInfo("Destroyed FikaServer"); + } + else + { + FikaClient client = Singleton.Instance; + try + { + client.NetClient.Stop(); + } + catch (Exception ex) + { + logger.LogError("DestroyNetManager: " + ex.Message); + } + Singleton.TryRelease(client); + GameObject.Destroy(client); + logger.LogInfo("Destroyed FikaClient"); + } + } + } + + public static void DestroyPingingClient() + { + if (FikaGameObject != null) + { + FikaPingingClient pingingClient = Singleton.Instance; + pingingClient.StopKeepAliveRoutine(); + pingingClient.NetClient.Stop(); + Singleton.TryRelease(pingingClient); + GameObject.Destroy(pingingClient); + 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!"); + throw new NullReferenceException("FikaGameObject was null"); + } + + 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) + { + GameObject.Destroy(fikaPinger); + } + else + { + logger.LogError("StopPinger: Could not find FikaPinger!"); + } + } + } + + public static Task CreateCoopHandler() + { + logger.LogInfo("Creating CoopHandler..."); + CoopHandler coopHandler = CoopHandler.GetCoopHandler(); + if (coopHandler != null) + { + GameObject.Destroy(coopHandler); + } + + if (CoopHandler.CoopHandlerParent != null) + { + GameObject.Destroy(CoopHandler.CoopHandlerParent); + CoopHandler.CoopHandlerParent = null; + } + + if (CoopHandler.CoopHandlerParent == null) + { + CoopHandler.CoopHandlerParent = new GameObject("CoopHandlerParent"); + GameObject.DontDestroyOnLoad(CoopHandler.CoopHandlerParent); + } + + coopHandler = CoopHandler.CoopHandlerParent.AddComponent(); + + if (!string.IsNullOrEmpty(FikaBackendUtils.GetGroupId())) + { + coopHandler.ServerId = FikaBackendUtils.GetGroupId(); + } + else + { + GameObject.Destroy(coopHandler); + logger.LogError("No Server Id found, Deleting Coop Handler"); + throw new MissingReferenceException("No Server Id found"); + } + + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/Fika.Core/FikaPlugin.cs b/Fika.Core/FikaPlugin.cs index e4c8536d..655bf801 100644 --- a/Fika.Core/FikaPlugin.cs +++ b/Fika.Core/FikaPlugin.cs @@ -42,665 +42,665 @@ namespace Fika.Core { - /// - /// Fika.Core Plugin.

- /// Originally by: Paulov
- /// Re-written by: Lacyway - ///
- [BepInPlugin("com.fika.core", "Fika.Core", "0.9.8983")] - [BepInProcess("EscapeFromTarkov.exe")] - [BepInDependency("com.SPT.custom", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after spt-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 spt-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 spt-core, that way we can disable its patches - [BepInDependency("com.SPT.debugging", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after spt-debugging, that way we can disable its patches - public class FikaPlugin : BaseUnityPlugin - { - public static FikaPlugin Instance; - public static InternalBundleLoader BundleLoaderPlugin { get; private set; } - public static string EFTVersionMajor { get; internal set; } - public ManualLogSource FikaLogger { get => Logger; } - public BotDifficulties BotDifficulties; - public FikaModHandler ModHandler = new(); - public string Locale { get; private set; } = "en"; - public string[] LocalIPs; - public static DedicatedRaidWebSocketClient DedicatedRaidWebSocket { get; set; } - - public static Dictionary RespectedPlayersList = new() - { - { "samswat", "godfather of modern SPT modding ~ SSH" }, - { "katto", "kmc leader & founder. OG revolutionary of custom assets ~ SSH" }, - { "polivilas", "who started it all -- #emutarkov2019 ~ Senko-san" }, - { "balist0n", "author of the first singleplayer-focussed mechanics and good friend ~ Senko-san" }, - { "ghostfenixx", "keeps asking me to fix bugs ~ TheSparta" }, - { "thurman", "aka TwistedGA, helped a lot of new modders, including me when I first started ~ TheSparta" }, - { "chomp", "literally unstoppable, carrying SPT development every single day ~ TheSparta" }, - { "nimbul", "Sat with Lacy many night and is loved by both Lacy & me. We miss you <3 ~ SSH" }, - { "vox", "My favourite american. ~ Lacyway" }, - { "rairai", "Very nice and caring person, someone I've appreciated getting to know. ~ Lacyway" }, - { "cwx", "Active and dedicated tester who has contributed a lot of good ideas to Fika. ~ Lacyway" } - }; - - public static Dictionary DevelopersList = new() - { - { "lacyway", "no one unified the community as much as you ~ Senko-san" }, - { "ssh_", "my little favorite gremlin. ~ Lacyway" }, - { "nexus4880", "the one who taught me everything I know now. ~ SSH" }, - { "thesparta", "I keep asking him to fix these darn bugs ~ GhostFenixx" }, - { "senko-san", "creator of SPT, extremely talented dev, a blast to work with ~ TheSparta" }, - { "leaves", "Maybe gurls are allowed ~ ssh" } - }; - - #region config values - - // Hidden - public static ConfigEntry AcceptedTOS { get; set; } - - //Advanced - public static ConfigEntry OfficialVersion { get; set; } - public static ConfigEntry DisableSPTAIPatches { get; set; } - - // Coop - public static ConfigEntry ShowNotifications { get; set; } - public static ConfigEntry AutoExtract { get; set; } - public static ConfigEntry ShowExtractMessage { get; set; } - //public static ConfigEntry FasterInventoryScroll { get; set; } - //public static ConfigEntry FasterInventoryScrollSpeed { get; set; } - public static ConfigEntry ExtractKey { get; set; } - public static ConfigEntry EnableChat { get; set; } - public static ConfigEntry ChatKey { get; set; } - - // Coop | Name Plates - public static ConfigEntry UseNamePlates { get; set; } - public static ConfigEntry HideHealthBar { get; set; } - 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; } - public static ConfigEntry MaxDistanceToShow { get; set; } - public static ConfigEntry MinimumOpacity { get; set; } - public static ConfigEntry MinimumNamePlateScale { get; set; } - public static ConfigEntry ShowEffects { get; set; } - public static ConfigEntry UseOcclusion { get; set; } - - // Coop | Quest Sharing - public static ConfigEntry QuestTypesToShareAndReceive { get; set; } - public static ConfigEntry QuestSharingNotifications { get; set; } - public static ConfigEntry EasyKillConditions { get; set; } - - // Coop | Custom - public static ConfigEntry UsePingSystem { get; set; } - public static ConfigEntry PingButton { get; set; } - public static ConfigEntry PingColor { get; set; } - public static ConfigEntry PingSize { get; set; } - 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; } - public static ConfigEntry PingSound { 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 DynamicAIIgnoreSnipers { get; set; } - //public static ConfigEntry CullPlayers { get; set; } - //public static ConfigEntry CullingRange { get; set; } - - // Performance | Bot Limits - public static ConfigEntry EnforcedSpawnLimits { get; set; } - public static ConfigEntry DespawnFurthest { get; set; } - public static ConfigEntry DespawnMinimumDistance { get; set; } - public static ConfigEntry MaxBotsFactory { get; set; } - public static ConfigEntry MaxBotsCustoms { get; set; } - public static ConfigEntry MaxBotsInterchange { get; set; } - public static ConfigEntry MaxBotsReserve { get; set; } - public static ConfigEntry MaxBotsGroundZero { get; set; } - public static ConfigEntry MaxBotsWoods { get; set; } - public static ConfigEntry MaxBotsStreets { get; set; } - public static ConfigEntry MaxBotsShoreline { get; set; } - public static ConfigEntry MaxBotsLabs { get; set; } - public static ConfigEntry MaxBotsLighthouse { get; set; } - - // Network - public static ConfigEntry NativeSockets { get; set; } - public static ConfigEntry ForceIP { get; set; } - public static ConfigEntry ForceBindIP { get; set; } - public static ConfigEntry ForceBindIP2 { get; set; } - public static ConfigEntry AutoRefreshRate { get; set; } - public static ConfigEntry UDPPort { get; set; } - public static ConfigEntry UseUPnP { get; set; } - public static ConfigEntry UseNatPunching { get; set; } - public static ConfigEntry ConnectionTimeout { get; set; } - - // Gameplay - public static ConfigEntry HeadDamageMultiplier { get; set; } - public static ConfigEntry ArmpitDamageMultiplier { get; set; } - public static ConfigEntry StomachDamageMultiplier { get; set; } - public static ConfigEntry DisableBotMetabolism { get; set; } - #endregion - - #region client config - public bool UseBTR; - public bool FriendlyFire; - 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; - - GetNatPunchServerConfig(); - SetupConfig(); - - new FikaVersionLabel_Patch().Enable(); - new DisableReadyButton_Patch().Enable(); - new DisableInsuranceReadyButton_Patch().Enable(); - new DisableMatchSettingsReadyButton_Patch().Enable(); - new TarkovApplication_LocalGamePreparer_Patch().Enable(); - new TarkovApplication_LocalGameCreator_Patch().Enable(); - new DeathFade_Patch().Enable(); - new NonWaveSpawnScenario_Patch().Enable(); - new WaveSpawnScenario_Patch().Enable(); - new WeatherNode_Patch().Enable(); - new MatchmakerAcceptScreen_Awake_Patch().Enable(); - new MatchmakerAcceptScreen_Show_Patch().Enable(); - new Minefield_method_2_Patch().Enable(); - new BotCacher_Patch().Enable(); - new AbstractGame_InRaid_Patch().Enable(); - new DisconnectButton_Patch().Enable(); - new ChangeGameModeButton_Patch().Enable(); - new MenuTaskBar_Patch().Enable(); - new GameWorld_Create_Patch().Enable(); - new World_AddSpawnQuestLootPacket_Patch().Enable(); - - gameObject.AddComponent(); + /// + /// Fika.Core Plugin.

+ /// Originally by: Paulov
+ /// Re-written by: Lacyway + ///
+ [BepInPlugin("com.fika.core", "Fika.Core", "0.9.8983")] + [BepInProcess("EscapeFromTarkov.exe")] + [BepInDependency("com.SPT.custom", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after spt-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 spt-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 spt-core, that way we can disable its patches + [BepInDependency("com.SPT.debugging", BepInDependency.DependencyFlags.HardDependency)] // This is used so that we guarantee to load after spt-debugging, that way we can disable its patches + public class FikaPlugin : BaseUnityPlugin + { + public static FikaPlugin Instance; + public static InternalBundleLoader BundleLoaderPlugin { get; private set; } + public static string EFTVersionMajor { get; internal set; } + public ManualLogSource FikaLogger { get => Logger; } + public BotDifficulties BotDifficulties; + public FikaModHandler ModHandler = new(); + public string Locale { get; private set; } = "en"; + public string[] LocalIPs; + public static DedicatedRaidWebSocketClient DedicatedRaidWebSocket { get; set; } + + public static Dictionary RespectedPlayersList = new() + { + { "samswat", "godfather of modern SPT modding ~ SSH" }, + { "katto", "kmc leader & founder. OG revolutionary of custom assets ~ SSH" }, + { "polivilas", "who started it all -- #emutarkov2019 ~ Senko-san" }, + { "balist0n", "author of the first singleplayer-focussed mechanics and good friend ~ Senko-san" }, + { "ghostfenixx", "keeps asking me to fix bugs ~ TheSparta" }, + { "thurman", "aka TwistedGA, helped a lot of new modders, including me when I first started ~ TheSparta" }, + { "chomp", "literally unstoppable, carrying SPT development every single day ~ TheSparta" }, + { "nimbul", "Sat with Lacy many night and is loved by both Lacy & me. We miss you <3 ~ SSH" }, + { "vox", "My favourite american. ~ Lacyway" }, + { "rairai", "Very nice and caring person, someone I've appreciated getting to know. ~ Lacyway" }, + { "cwx", "Active and dedicated tester who has contributed a lot of good ideas to Fika. ~ Lacyway" } + }; + + public static Dictionary DevelopersList = new() + { + { "lacyway", "no one unified the community as much as you ~ Senko-san" }, + { "ssh_", "my little favorite gremlin. ~ Lacyway" }, + { "nexus4880", "the one who taught me everything I know now. ~ SSH" }, + { "thesparta", "I keep asking him to fix these darn bugs ~ GhostFenixx" }, + { "senko-san", "creator of SPT, extremely talented dev, a blast to work with ~ TheSparta" }, + { "leaves", "Maybe gurls are allowed ~ ssh" } + }; + + #region config values + + // Hidden + public static ConfigEntry AcceptedTOS { get; set; } + + //Advanced + public static ConfigEntry OfficialVersion { get; set; } + public static ConfigEntry DisableSPTAIPatches { get; set; } + + // Coop + public static ConfigEntry ShowNotifications { get; set; } + public static ConfigEntry AutoExtract { get; set; } + public static ConfigEntry ShowExtractMessage { get; set; } + //public static ConfigEntry FasterInventoryScroll { get; set; } + //public static ConfigEntry FasterInventoryScrollSpeed { get; set; } + public static ConfigEntry ExtractKey { get; set; } + public static ConfigEntry EnableChat { get; set; } + public static ConfigEntry ChatKey { get; set; } + + // Coop | Name Plates + public static ConfigEntry UseNamePlates { get; set; } + public static ConfigEntry HideHealthBar { get; set; } + 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; } + public static ConfigEntry MaxDistanceToShow { get; set; } + public static ConfigEntry MinimumOpacity { get; set; } + public static ConfigEntry MinimumNamePlateScale { get; set; } + public static ConfigEntry ShowEffects { get; set; } + public static ConfigEntry UseOcclusion { get; set; } + + // Coop | Quest Sharing + public static ConfigEntry QuestTypesToShareAndReceive { get; set; } + public static ConfigEntry QuestSharingNotifications { get; set; } + public static ConfigEntry EasyKillConditions { get; set; } + + // Coop | Custom + public static ConfigEntry UsePingSystem { get; set; } + public static ConfigEntry PingButton { get; set; } + public static ConfigEntry PingColor { get; set; } + public static ConfigEntry PingSize { get; set; } + 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; } + public static ConfigEntry PingSound { 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 DynamicAIIgnoreSnipers { get; set; } + //public static ConfigEntry CullPlayers { get; set; } + //public static ConfigEntry CullingRange { get; set; } + + // Performance | Bot Limits + public static ConfigEntry EnforcedSpawnLimits { get; set; } + public static ConfigEntry DespawnFurthest { get; set; } + public static ConfigEntry DespawnMinimumDistance { get; set; } + public static ConfigEntry MaxBotsFactory { get; set; } + public static ConfigEntry MaxBotsCustoms { get; set; } + public static ConfigEntry MaxBotsInterchange { get; set; } + public static ConfigEntry MaxBotsReserve { get; set; } + public static ConfigEntry MaxBotsGroundZero { get; set; } + public static ConfigEntry MaxBotsWoods { get; set; } + public static ConfigEntry MaxBotsStreets { get; set; } + public static ConfigEntry MaxBotsShoreline { get; set; } + public static ConfigEntry MaxBotsLabs { get; set; } + public static ConfigEntry MaxBotsLighthouse { get; set; } + + // Network + public static ConfigEntry NativeSockets { get; set; } + public static ConfigEntry ForceIP { get; set; } + public static ConfigEntry ForceBindIP { get; set; } + public static ConfigEntry ForceBindIP2 { get; set; } + public static ConfigEntry AutoRefreshRate { get; set; } + public static ConfigEntry UDPPort { get; set; } + public static ConfigEntry UseUPnP { get; set; } + public static ConfigEntry UseNatPunching { get; set; } + public static ConfigEntry ConnectionTimeout { get; set; } + + // Gameplay + public static ConfigEntry HeadDamageMultiplier { get; set; } + public static ConfigEntry ArmpitDamageMultiplier { get; set; } + public static ConfigEntry StomachDamageMultiplier { get; set; } + public static ConfigEntry DisableBotMetabolism { get; set; } + #endregion + + #region client config + public bool UseBTR; + public bool FriendlyFire; + 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; + + GetNatPunchServerConfig(); + SetupConfig(); + + new FikaVersionLabel_Patch().Enable(); + new DisableReadyButton_Patch().Enable(); + new DisableInsuranceReadyButton_Patch().Enable(); + new DisableMatchSettingsReadyButton_Patch().Enable(); + new TarkovApplication_LocalGamePreparer_Patch().Enable(); + new TarkovApplication_LocalGameCreator_Patch().Enable(); + new DeathFade_Patch().Enable(); + new NonWaveSpawnScenario_Patch().Enable(); + new WaveSpawnScenario_Patch().Enable(); + new WeatherNode_Patch().Enable(); + new MatchmakerAcceptScreen_Awake_Patch().Enable(); + new MatchmakerAcceptScreen_Show_Patch().Enable(); + new Minefield_method_2_Patch().Enable(); + new BotCacher_Patch().Enable(); + new AbstractGame_InRaid_Patch().Enable(); + new DisconnectButton_Patch().Enable(); + new ChangeGameModeButton_Patch().Enable(); + new MenuTaskBar_Patch().Enable(); + new GameWorld_Create_Patch().Enable(); + new World_AddSpawnQuestLootPacket_Patch().Enable(); + + gameObject.AddComponent(); #if GOLDMASTER new TOS_Patch().Enable(); #endif - OfficialVersion.SettingChanged += OfficialVersion_SettingChanged; + OfficialVersion.SettingChanged += OfficialVersion_SettingChanged; - DisableSPTPatches(); - EnableOverridePatches(); + DisableSPTPatches(); + EnableOverridePatches(); - GetClientConfig(); + GetClientConfig(); - string fikaVersion = Assembly.GetAssembly(typeof(FikaPlugin)).GetName().Version.ToString(); + string fikaVersion = Assembly.GetAssembly(typeof(FikaPlugin)).GetName().Version.ToString(); - Logger.LogInfo($"Fika is loaded! Running version: " + fikaVersion); + Logger.LogInfo($"Fika is loaded! Running version: " + fikaVersion); - BundleLoaderPlugin = new(); - BundleLoaderPlugin.Create(); + BundleLoaderPlugin = new(); + BundleLoaderPlugin.Create(); - FikaAirdropUtil.GetConfigFromServer(); - BotSettingsRepoAbstractClass.Init(); + FikaAirdropUtil.GetConfigFromServer(); + BotSettingsRepoAbstractClass.Init(); - BotDifficulties = FikaRequestHandler.GetBotDifficulties(); - ConsoleScreen.Processor.RegisterCommandGroup(); + BotDifficulties = FikaRequestHandler.GetBotDifficulties(); + ConsoleScreen.Processor.RegisterCommandGroup(); - if (AllowItemSending) - { - new ItemContext_Patch().Enable(); - } + if (AllowItemSending) + { + new ItemContext_Patch().Enable(); + } - StartCoroutine(RunModHandler()); - } + StartCoroutine(RunModHandler()); + } - /// - /// Coroutine to ensure all mods are loaded by waiting 5 seconds - /// - /// - private IEnumerator RunModHandler() - { - yield return new WaitForSeconds(5); - ModHandler.VerifyMods(); - } + /// + /// Coroutine to ensure all mods are loaded by waiting 5 seconds + /// + /// + private IEnumerator RunModHandler() + { + yield return new WaitForSeconds(5); + ModHandler.VerifyMods(); + } - private void GetClientConfig() - { - ClientConfigModel clientConfig = FikaRequestHandler.GetClientConfig(); + private void GetClientConfig() + { + ClientConfigModel clientConfig = FikaRequestHandler.GetClientConfig(); - UseBTR = clientConfig.UseBTR; - FriendlyFire = clientConfig.FriendlyFire; - DynamicVExfils = clientConfig.DynamicVExfils; - AllowFreeCam = clientConfig.AllowFreeCam; - AllowItemSending = clientConfig.AllowItemSending; - BlacklistedItems = clientConfig.BlacklistedItems; - ForceSaveOnDeath = clientConfig.ForceSaveOnDeath; - UseInertia = clientConfig.UseInertia; - SharedQuestProgression = clientConfig.SharedQuestProgression; + UseBTR = clientConfig.UseBTR; + FriendlyFire = clientConfig.FriendlyFire; + DynamicVExfils = clientConfig.DynamicVExfils; + AllowFreeCam = clientConfig.AllowFreeCam; + AllowItemSending = clientConfig.AllowItemSending; + BlacklistedItems = clientConfig.BlacklistedItems; + ForceSaveOnDeath = clientConfig.ForceSaveOnDeath; + UseInertia = clientConfig.UseInertia; + SharedQuestProgression = clientConfig.SharedQuestProgression; - clientConfig.LogValues(); - } + clientConfig.LogValues(); + } - private void GetNatPunchServerConfig() - { - NatPunchServerConfigModel natPunchServerConfig = FikaRequestHandler.GetNatPunchServerConfig(); + private void GetNatPunchServerConfig() + { + NatPunchServerConfigModel natPunchServerConfig = FikaRequestHandler.GetNatPunchServerConfig(); - NatPunchServerEnable = natPunchServerConfig.Enable; - NatPunchServerIP = RequestHandler.Host.Replace("http://", "").Split(':')[0]; - NatPunchServerPort = natPunchServerConfig.Port; - NatPunchServerNatIntroduceAmount = natPunchServerConfig.NatIntroduceAmount; + NatPunchServerEnable = natPunchServerConfig.Enable; + NatPunchServerIP = RequestHandler.Host.Replace("http://", "").Split(':')[0]; + NatPunchServerPort = natPunchServerConfig.Port; + NatPunchServerNatIntroduceAmount = natPunchServerConfig.NatIntroduceAmount; - natPunchServerConfig.LogValues(); - } + natPunchServerConfig.LogValues(); + } - private void SetupConfig() - { - // Hidden + private void SetupConfig() + { + // Hidden - AcceptedTOS = Config.Bind("Hidden", "Accepted TOS", false, - new ConfigDescription("Has accepted TOS", tags: new ConfigurationManagerAttributes() { Browsable = false })); + AcceptedTOS = Config.Bind("Hidden", "Accepted TOS", false, + new ConfigDescription("Has accepted TOS", tags: new ConfigurationManagerAttributes() { Browsable = false })); - // Advanced + // Advanced - OfficialVersion = Config.Bind("Advanced", "Official Version", false, - new ConfigDescription("Show official version instead of Fika version.", tags: new ConfigurationManagerAttributes() { IsAdvanced = true })); + OfficialVersion = Config.Bind("Advanced", "Official Version", false, + new ConfigDescription("Show official version instead of Fika version.", tags: new ConfigurationManagerAttributes() { IsAdvanced = true })); - DisableSPTAIPatches = Config.Bind("Advanced", "Disable SPT AI Patches", false, - new ConfigDescription("Disable SPT AI patches that are most likely redundant in Fika.", tags: new ConfigurationManagerAttributes { IsAdvanced = true })); + DisableSPTAIPatches = Config.Bind("Advanced", "Disable SPT AI Patches", false, + new ConfigDescription("Disable SPT AI patches that are most likely redundant in Fika.", tags: new ConfigurationManagerAttributes { IsAdvanced = true })); - // Coop + // 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 = 7 })); + 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 = 7 })); - AutoExtract = Config.Bind("Coop", "Auto Extract", false, - new ConfigDescription("Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", tags: new ConfigurationManagerAttributes() { Order = 6 })); + AutoExtract = Config.Bind("Coop", "Auto Extract", false, + new ConfigDescription("Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", tags: new ConfigurationManagerAttributes() { Order = 6 })); - ShowExtractMessage = Config.Bind("Coop", "Show Extract Message", true, - new ConfigDescription("Whether to show the extract message after dying/extracting.", tags: new ConfigurationManagerAttributes() { Order = 5 })); + ShowExtractMessage = Config.Bind("Coop", "Show Extract Message", true, + new ConfigDescription("Whether to show the extract message after dying/extracting.", tags: new ConfigurationManagerAttributes() { Order = 5 })); - ExtractKey = Config.Bind("Coop", "Extract Key", new KeyboardShortcut(KeyCode.F8), - new ConfigDescription("The key used to extract from the raid.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + ExtractKey = Config.Bind("Coop", "Extract Key", new KeyboardShortcut(KeyCode.F8), + new ConfigDescription("The key used to extract from the raid.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - EnableChat = Config.Bind("Coop", "Enable Chat", false, - new ConfigDescription("Toggle to enable chat in game. Cannot be change mid raid", tags: new ConfigurationManagerAttributes() { Order = 1 })); + EnableChat = Config.Bind("Coop", "Enable Chat", false, + new ConfigDescription("Toggle to enable chat in game. Cannot be change mid raid", tags: new ConfigurationManagerAttributes() { Order = 1 })); - ChatKey = Config.Bind("Coop", "Chat Key", new KeyboardShortcut(KeyCode.RightControl), - new ConfigDescription("The key used to open the chat window.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + ChatKey = Config.Bind("Coop", "Chat Key", new KeyboardShortcut(KeyCode.RightControl), + new ConfigDescription("The key used to open the chat window.", tags: new ConfigurationManagerAttributes() { Order = 0 })); - // Coop | Name Plates + // Coop | Name Plates - UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, - new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 13 })); + UseNamePlates = Config.Bind("Coop | Name Plates", "Show Player Name Plates", false, + new ConfigDescription("Toggle Health-Bars & Names.", tags: new ConfigurationManagerAttributes() { Order = 13 })); - HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, - new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 12 })); + HideHealthBar = Config.Bind("Coop | Name Plates", "Hide Health Bar", false, + new ConfigDescription("Completely hides the health bar.", tags: new ConfigurationManagerAttributes() { Order = 12 })); - 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 = 11 })); + 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 = 11 })); - ShowEffects = Config.Bind("Coop | Name Plates", "Show Effects", true, - new ConfigDescription("If status effects should be displayed below the health bar.", tags: new ConfigurationManagerAttributes() { Order = 10 })); + ShowEffects = Config.Bind("Coop | Name Plates", "Show Effects", true, + new ConfigDescription("If status effects should be displayed below the health bar.", tags: new ConfigurationManagerAttributes() { Order = 10 })); - 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 = 9 })); + 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 = 9 })); - 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 = 8 })); + 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 = 8 })); - 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 = 7, IsAdvanced = true })); + 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 = 7, 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 = 6 })); + 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 = 6 })); - NamePlateScale = Config.Bind("Coop | Name Plates", "Name Plate Scale", 0.22f, - new ConfigDescription("Size of the name plates", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 5 })); + NamePlateScale = Config.Bind("Coop | Name Plates", "Name Plate Scale", 0.22f, + new ConfigDescription("Size of the name plates", new AcceptableValueRange(0.05f, 1f), new ConfigurationManagerAttributes() { Order = 5 })); - OpacityInADS = Config.Bind("Coop | Name Plates", "Opacity in ADS", 0.75f, - new ConfigDescription("The opacity of the name plates when aiming down sights.", new AcceptableValueRange(0.1f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); + OpacityInADS = Config.Bind("Coop | Name Plates", "Opacity in ADS", 0.75f, + new ConfigDescription("The opacity of the name plates when aiming down sights.", new AcceptableValueRange(0.1f, 1f), new ConfigurationManagerAttributes() { Order = 4 })); - MaxDistanceToShow = Config.Bind("Coop | Name Plates", "Max Distance to Show", 500f, - new ConfigDescription("The maximum distance at which name plates will become invisible, starts to fade at half the input value.", new AcceptableValueRange(10f, 1000f), new ConfigurationManagerAttributes() { Order = 3 })); + MaxDistanceToShow = Config.Bind("Coop | Name Plates", "Max Distance to Show", 500f, + new ConfigDescription("The maximum distance at which name plates will become invisible, starts to fade at half the input value.", new AcceptableValueRange(10f, 1000f), new ConfigurationManagerAttributes() { Order = 3 })); - MinimumOpacity = Config.Bind("Coop | Name Plates", "Minimum Opacity", 0.1f, - new ConfigDescription("The minimum opacity of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); + MinimumOpacity = Config.Bind("Coop | Name Plates", "Minimum Opacity", 0.1f, + new ConfigDescription("The minimum opacity of the name plates.", new AcceptableValueRange(0.0f, 1f), new ConfigurationManagerAttributes() { Order = 2 })); - 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 = 1 })); + 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 = 1 })); - UseOcclusion = Config.Bind("Coop | Name Plates", "Use Occlusion", false, - new ConfigDescription("Use occlusion to hide the name plate when the player is out of sight.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + UseOcclusion = Config.Bind("Coop | Name Plates", "Use Occlusion", false, + new ConfigDescription("Use occlusion to hide the name plate when the player is out of sight.", tags: new ConfigurationManagerAttributes() { Order = 0 })); - // Coop | Quest Sharing + // Coop | Quest Sharing - QuestTypesToShareAndReceive = Config.Bind("Coop | Quest Sharing", "Quest Types", EQuestSharingTypes.All, - new ConfigDescription("Which quest types to receive and send. PlaceBeacon is both markers and items.", tags: new ConfigurationManagerAttributes() { Order = 2 })); + QuestTypesToShareAndReceive = Config.Bind("Coop | Quest Sharing", "Quest Types", EQuestSharingTypes.All, + new ConfigDescription("Which quest types to receive and send. PlaceBeacon is both markers and items.", tags: new ConfigurationManagerAttributes() { Order = 2 })); - QuestSharingNotifications = Config.Bind("Coop | Quest Sharing", "Show Notifications", true, - new ConfigDescription("If a notification should be shown when quest progress is shared with out.", tags: new ConfigurationManagerAttributes() { Order = 1 })); + QuestSharingNotifications = Config.Bind("Coop | Quest Sharing", "Show Notifications", true, + new ConfigDescription("If a notification should be shown when quest progress is shared with out.", tags: new ConfigurationManagerAttributes() { Order = 1 })); - EasyKillConditions = Config.Bind("Coop | Quest Sharing", "Easy Kill Conditions", false, - new ConfigDescription("Enables easy kill conditions. When this is used, any time a friendly player kills something, it treats it as if you killed it for your quests as long as all conditions are met.\nThis can be inconsistent and does not always work.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + EasyKillConditions = Config.Bind("Coop | Quest Sharing", "Easy Kill Conditions", false, + new ConfigDescription("Enables easy kill conditions. When this is used, any time a friendly player kills something, it treats it as if you killed it for your quests as long as all conditions are met.\nThis can be inconsistent and does not always work.", tags: new ConfigurationManagerAttributes() { Order = 0 })); - // Coop | Custom + // 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 = 9 })); + 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 })); + 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 })); + 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 })); - 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 })); + 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 })); - PingTime = Config.Bind("Coop | Custom", "Ping Time", 3, - new ConfigDescription("How long pings should be displayed.", new AcceptableValueRange(2, 10), 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 })); - 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 })); + 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 })); - 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 })); + 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 })); - 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 })); + 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 })); - 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 })); + 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 })); - PingSound = Config.Bind("Coop | Custom", "Ping Sound", EPingSound.SubQuestComplete, - new ConfigDescription("The audio that plays on ping")); + 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 })); + PingSound = Config.Bind("Coop | Custom", "Ping Sound", EPingSound.SubQuestComplete, + new ConfigDescription("The audio that plays on ping")); - // Coop | Debug + // Coop | Debug - FreeCamButton = Config.Bind("Coop | Debug", "Free Camera Button", new KeyboardShortcut(KeyCode.F9), - "Button used to toggle free camera."); + FreeCamButton = Config.Bind("Coop | Debug", "Free Camera Button", new KeyboardShortcut(KeyCode.F9), + "Button used to toggle free camera."); - AZERTYMode = Config.Bind("Coop | Debug", "AZERTY Mode", false, - "If free camera should use AZERTY keys for input."); + 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."); + KeybindOverlay = Config.Bind("Coop | Debug", "Keybind Overlay", true, + "If an overlay with all free cam keybinds should show."); - // Performance + // 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 = 3 })); + 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 = 3 })); - 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 = 2 })); + 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 = 2 })); - 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 = 1 })); + 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 = 1 })); - DynamicAIIgnoreSnipers = Config.Bind("Performance", "Dynamic AI - Ignore Snipers", true, - new ConfigDescription("Whether Dynamic AI should ignore sniper scavs.", tags: new ConfigurationManagerAttributes() { Order = 0 })); + DynamicAIIgnoreSnipers = Config.Bind("Performance", "Dynamic AI - Ignore Snipers", true, + new ConfigDescription("Whether Dynamic AI should ignore sniper scavs.", tags: new ConfigurationManagerAttributes() { Order = 0 })); - //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 + // Performance | Max Bots - EnforcedSpawnLimits = Config.Bind("Performance | Max Bots", "Enforced Spawn Limits", false, - new ConfigDescription("Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", tags: new ConfigurationManagerAttributes() { Order = 14 })); + EnforcedSpawnLimits = Config.Bind("Performance | Max Bots", "Enforced Spawn Limits", false, + new ConfigDescription("Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", tags: new ConfigurationManagerAttributes() { Order = 14 })); - DespawnFurthest = Config.Bind("Performance | Max Bots", "Despawn Furthest", false, - new ConfigDescription("When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", tags: new ConfigurationManagerAttributes() { Order = 13 })); + DespawnFurthest = Config.Bind("Performance | Max Bots", "Despawn Furthest", false, + new ConfigDescription("When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", tags: new ConfigurationManagerAttributes() { Order = 13 })); - DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, - new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 3000f), new ConfigurationManagerAttributes() { Order = 12 })); + DespawnMinimumDistance = Config.Bind("Performance | Max Bots", "Despawn Minimum Distance", 200.0f, + new ConfigDescription("Don't despawn bots within this distance.", new AcceptableValueRange(50f, 3000f), new ConfigurationManagerAttributes() { Order = 12 })); - MaxBotsFactory = Config.Bind("Performance | Max Bots", "Max Bots Factory", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Factory. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 11 })); + MaxBotsFactory = Config.Bind("Performance | Max Bots", "Max Bots Factory", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Factory. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 11 })); - MaxBotsCustoms = Config.Bind("Performance | Max Bots", "Max Bots Customs", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Customs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 10 })); + MaxBotsCustoms = Config.Bind("Performance | Max Bots", "Max Bots Customs", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Customs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 10 })); - MaxBotsInterchange = Config.Bind("Performance | Max Bots", "Max Bots Interchange", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Interchange. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 8 })); + MaxBotsInterchange = Config.Bind("Performance | Max Bots", "Max Bots Interchange", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Interchange. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 8 })); - MaxBotsReserve = Config.Bind("Performance | Max Bots", "Max Bots Reserve", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Reserve. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 7 })); + MaxBotsReserve = Config.Bind("Performance | Max Bots", "Max Bots Reserve", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Reserve. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 7 })); - MaxBotsWoods = Config.Bind("Performance | Max Bots", "Max Bots Woods", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Woods. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 6 })); + MaxBotsWoods = Config.Bind("Performance | Max Bots", "Max Bots Woods", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Woods. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 6 })); - MaxBotsShoreline = Config.Bind("Performance | Max Bots", "Max Bots Shoreline", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Shoreline. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 5 })); + MaxBotsShoreline = Config.Bind("Performance | Max Bots", "Max Bots Shoreline", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Shoreline. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 5 })); - MaxBotsStreets = Config.Bind("Performance | Max Bots", "Max Bots Streets", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Streets of Tarkov. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 4 })); + MaxBotsStreets = Config.Bind("Performance | Max Bots", "Max Bots Streets", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Streets of Tarkov. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 4 })); - MaxBotsGroundZero = Config.Bind("Performance | Max Bots", "Max Bots Ground Zero", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Ground Zero. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 3 })); + MaxBotsGroundZero = Config.Bind("Performance | Max Bots", "Max Bots Ground Zero", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Ground Zero. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 3 })); - MaxBotsLabs = Config.Bind("Performance | Max Bots", "Max Bots Labs", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Labs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 2 })); + MaxBotsLabs = Config.Bind("Performance | Max Bots", "Max Bots Labs", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Labs. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 2 })); - MaxBotsLighthouse = Config.Bind("Performance | Max Bots", "Max Bots Lighthouse", 0, - new ConfigDescription("Max amount of bots that can be active at the same time on Lighthouse. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 1 })); + MaxBotsLighthouse = Config.Bind("Performance | Max Bots", "Max Bots Lighthouse", 0, + new ConfigDescription("Max amount of bots that can be active at the same time on Lighthouse. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", new AcceptableValueRange(0, 50), new ConfigurationManagerAttributes() { Order = 1 })); - // Network + // 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 = 8 })); + 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 = 7 })); - 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 = 6 })); - 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 = 5 })); - 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 = 4 })); - 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 = 3 })); - 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 SPT server. UPnP, Force IP and Force Bind IP are disabled with this mode.", 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 SPT 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 })); + 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 + // 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.05f, 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.05f, 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.05f, 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 = []; - ips.Add("Disabled"); - ips.Add("0.0.0.0"); - - try - { - foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces()) - { - foreach (UnicastIPAddressInformation ip in networkInterface.GetIPProperties().UnicastAddresses) - { - if (!ip.IsDnsEligible) - { - continue; - } - - if (ip.Address.AddressFamily == AddressFamily.InterNetwork) - { - string stringIp = ip.Address.ToString(); - if (stringIp != "127.0.0.1") - { - ips.Add(stringIp); - } - } - } - } - - LocalIPs = ips.Skip(1).ToArray(); - return [.. ips]; - } - catch (Exception) - { - return [.. ips]; - } - } - - private void DisableSPTPatches() - { - Chainloader.PluginInfos.TryGetValue("com.SPT.core", out PluginInfo pluginInfo); - - bool OlderSPTVersion = false; - - if (pluginInfo.Metadata.Version < Version.Parse("3.9.5")) - { - FikaLogger.LogWarning("Older SPT version found!"); - OlderSPTVersion = true; - } - - // Disable these as they interfere with Fika - new BotDifficultyPatch().Disable(); - new AirdropPatch().Disable(); - new AirdropFlarePatch().Disable(); - new VersionLabelPatch().Disable(); - new EmptyInfilFixPatch().Disable(); - new OfflineSpawnPointPatch().Disable(); - new BotTemplateLimitPatch().Disable(); - new OfflineRaidSettingsMenuPatch().Disable(); - new AddEnemyToAllGroupsInBotZonePatch().Disable(); - new MaxBotPatch().Disable(); - new LabsKeycardRemovalPatch().Disable(); // We handle this locally instead - new AmmoUsedCounterPatch().Disable(); - new ArmorDamageCounterPatch().Disable(); - new DogtagPatch().Disable(); - new OfflineSaveProfilePatch().Disable(); // We handle this with our own exit manager - new ScavRepAdjustmentPatch().Disable(); - new DisablePvEPatch().Disable(); - - new AddEnemyToAllGroupsInBotZonePatch().Disable(); - - Assembly sptCustomAssembly = typeof(IsEnemyPatch).Assembly; - - if (OlderSPTVersion) - { - Type botCallForHelpCallBotPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotCallForHelpCallBotPatch"); - ModulePatch botCallForHelpCallBotPatch = (ModulePatch)Activator.CreateInstance(botCallForHelpCallBotPatchType); - botCallForHelpCallBotPatch.Disable(); - } - - if (DisableSPTAIPatches.Value) - { - new BotEnemyTargetPatch().Disable(); - new IsEnemyPatch().Disable(); - - if (!OlderSPTVersion) - { - new BotOwnerDisposePatch().Disable(); - new BotCalledDataTryCallPatch().Disable(); - new BotSelfEnemyPatch().Disable(); - } - else - { - Type botOwnerDisposePatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotOwnerDisposePatch"); - ModulePatch botOwnerDisposePatch = (ModulePatch)Activator.CreateInstance(botOwnerDisposePatchType); - botOwnerDisposePatch.Disable(); - - Type botCalledDataTryCallPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotCalledDataTryCallPatch"); - ModulePatch botCalledDataTryCallPatch = (ModulePatch)Activator.CreateInstance(botCalledDataTryCallPatchType); - botCalledDataTryCallPatch.Disable(); - - Type botSelfEnemyPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotSelfEnemyPatch"); - ModulePatch botSelfEnemyPatch = (ModulePatch)Activator.CreateInstance(botSelfEnemyPatchType); - botSelfEnemyPatch.Disable(); - } - } - - new BTRInteractionPatch().Disable(); - new BTRExtractPassengersPatch().Disable(); - new BTRPatch().Disable(); - } - - private void EnableOverridePatches() - { - new BotDifficultyPatch_Override().Enable(); - new ScavProfileLoad_Override().Enable(); - new MaxBotPatch_Override().Enable(); - new BotTemplateLimitPatch_Override().Enable(); - new OfflineRaidSettingsMenuPatch_Override().Enable(); - new AddEnemyToAllGroupsInBotZonePatch_Override().Enable(); - new AirdropBox_Patch().Enable(); - new FikaAirdropFlare_Patch().Enable(); - } - - public enum EDynamicAIRates - { - Low, - Medium, - High - } - - public enum EPingSound - { - SubQuestComplete, - InsuranceInsured, - ButtonClick, - ButtonHover, - InsuranceItemInsured, - MenuButtonBottom, - ErrorMessage, - InspectWindow, - InspectWindowClose, - MenuEscape, - } - - [Flags] - public enum EQuestSharingTypes - { - Kills = 1, - Item = 2, - Location = 4, - PlaceBeacon = 8, - - All = Kills | Item | Location | PlaceBeacon - } - } + 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 = []; + ips.Add("Disabled"); + ips.Add("0.0.0.0"); + + try + { + foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces()) + { + foreach (UnicastIPAddressInformation ip in networkInterface.GetIPProperties().UnicastAddresses) + { + if (!ip.IsDnsEligible) + { + continue; + } + + if (ip.Address.AddressFamily == AddressFamily.InterNetwork) + { + string stringIp = ip.Address.ToString(); + if (stringIp != "127.0.0.1") + { + ips.Add(stringIp); + } + } + } + } + + LocalIPs = ips.Skip(1).ToArray(); + return [.. ips]; + } + catch (Exception) + { + return [.. ips]; + } + } + + private void DisableSPTPatches() + { + Chainloader.PluginInfos.TryGetValue("com.SPT.core", out PluginInfo pluginInfo); + + bool OlderSPTVersion = false; + + if (pluginInfo.Metadata.Version < Version.Parse("3.9.5")) + { + FikaLogger.LogWarning("Older SPT version found!"); + OlderSPTVersion = true; + } + + // Disable these as they interfere with Fika + new BotDifficultyPatch().Disable(); + new AirdropPatch().Disable(); + new AirdropFlarePatch().Disable(); + new VersionLabelPatch().Disable(); + new EmptyInfilFixPatch().Disable(); + new OfflineSpawnPointPatch().Disable(); + new BotTemplateLimitPatch().Disable(); + new OfflineRaidSettingsMenuPatch().Disable(); + new AddEnemyToAllGroupsInBotZonePatch().Disable(); + new MaxBotPatch().Disable(); + new LabsKeycardRemovalPatch().Disable(); // We handle this locally instead + new AmmoUsedCounterPatch().Disable(); + new ArmorDamageCounterPatch().Disable(); + new DogtagPatch().Disable(); + new OfflineSaveProfilePatch().Disable(); // We handle this with our own exit manager + new ScavRepAdjustmentPatch().Disable(); + new DisablePvEPatch().Disable(); + + new AddEnemyToAllGroupsInBotZonePatch().Disable(); + + Assembly sptCustomAssembly = typeof(IsEnemyPatch).Assembly; + + if (OlderSPTVersion) + { + Type botCallForHelpCallBotPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotCallForHelpCallBotPatch"); + ModulePatch botCallForHelpCallBotPatch = (ModulePatch)Activator.CreateInstance(botCallForHelpCallBotPatchType); + botCallForHelpCallBotPatch.Disable(); + } + + if (DisableSPTAIPatches.Value) + { + new BotEnemyTargetPatch().Disable(); + new IsEnemyPatch().Disable(); + + if (!OlderSPTVersion) + { + new BotOwnerDisposePatch().Disable(); + new BotCalledDataTryCallPatch().Disable(); + new BotSelfEnemyPatch().Disable(); + } + else + { + Type botOwnerDisposePatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotOwnerDisposePatch"); + ModulePatch botOwnerDisposePatch = (ModulePatch)Activator.CreateInstance(botOwnerDisposePatchType); + botOwnerDisposePatch.Disable(); + + Type botCalledDataTryCallPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotCalledDataTryCallPatch"); + ModulePatch botCalledDataTryCallPatch = (ModulePatch)Activator.CreateInstance(botCalledDataTryCallPatchType); + botCalledDataTryCallPatch.Disable(); + + Type botSelfEnemyPatchType = sptCustomAssembly.GetType("SPT.Custom.Patches.BotSelfEnemyPatch"); + ModulePatch botSelfEnemyPatch = (ModulePatch)Activator.CreateInstance(botSelfEnemyPatchType); + botSelfEnemyPatch.Disable(); + } + } + + new BTRInteractionPatch().Disable(); + new BTRExtractPassengersPatch().Disable(); + new BTRPatch().Disable(); + } + + private void EnableOverridePatches() + { + new BotDifficultyPatch_Override().Enable(); + new ScavProfileLoad_Override().Enable(); + new MaxBotPatch_Override().Enable(); + new BotTemplateLimitPatch_Override().Enable(); + new OfflineRaidSettingsMenuPatch_Override().Enable(); + new AddEnemyToAllGroupsInBotZonePatch_Override().Enable(); + new AirdropBox_Patch().Enable(); + new FikaAirdropFlare_Patch().Enable(); + } + + public enum EDynamicAIRates + { + Low, + Medium, + High + } + + public enum EPingSound + { + SubQuestComplete, + InsuranceInsured, + ButtonClick, + ButtonHover, + InsuranceItemInsured, + MenuButtonBottom, + ErrorMessage, + InspectWindow, + InspectWindowClose, + MenuEscape, + } + + [Flags] + public enum EQuestSharingTypes + { + Kills = 1, + Item = 2, + Location = 4, + PlaceBeacon = 8, + + All = Kills | Item | Location | PlaceBeacon + } + } } \ No newline at end of file diff --git a/Fika.Core/Modding/Events/AbstractGameCreatedEvent.cs b/Fika.Core/Modding/Events/AbstractGameCreatedEvent.cs index 0e17f9c0..61bf742f 100644 --- a/Fika.Core/Modding/Events/AbstractGameCreatedEvent.cs +++ b/Fika.Core/Modding/Events/AbstractGameCreatedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class AbstractGameCreatedEvent : FikaEvent - { - public AbstractGame Game { get; } + public class AbstractGameCreatedEvent : FikaEvent + { + public AbstractGame Game { get; } - internal AbstractGameCreatedEvent(AbstractGame game) - { - this.Game = game; - } - } + internal AbstractGameCreatedEvent(AbstractGame game) + { + this.Game = game; + } + } } diff --git a/Fika.Core/Modding/Events/FikaClientCreatedEvent.cs b/Fika.Core/Modding/Events/FikaClientCreatedEvent.cs index 40b716dd..24bce82f 100644 --- a/Fika.Core/Modding/Events/FikaClientCreatedEvent.cs +++ b/Fika.Core/Modding/Events/FikaClientCreatedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class FikaClientCreatedEvent : FikaEvent - { - public FikaClient Client { get; } + public class FikaClientCreatedEvent : FikaEvent + { + public FikaClient Client { get; } - internal FikaClientCreatedEvent(FikaClient client) - { - this.Client = client; - } - } + internal FikaClientCreatedEvent(FikaClient client) + { + this.Client = client; + } + } } diff --git a/Fika.Core/Modding/Events/FikaClientDestroyedEvent.cs b/Fika.Core/Modding/Events/FikaClientDestroyedEvent.cs index a0df4f1b..2fbcf7ad 100644 --- a/Fika.Core/Modding/Events/FikaClientDestroyedEvent.cs +++ b/Fika.Core/Modding/Events/FikaClientDestroyedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class FikaClientDestroyedEvent : FikaEvent - { - public FikaClient Client { get; } + public class FikaClientDestroyedEvent : FikaEvent + { + public FikaClient Client { get; } - internal FikaClientDestroyedEvent(FikaClient client) - { - Client = client; - } - } + internal FikaClientDestroyedEvent(FikaClient client) + { + Client = client; + } + } } diff --git a/Fika.Core/Modding/Events/FikaEvent.cs b/Fika.Core/Modding/Events/FikaEvent.cs index 79f7540c..9463f7c6 100644 --- a/Fika.Core/Modding/Events/FikaEvent.cs +++ b/Fika.Core/Modding/Events/FikaEvent.cs @@ -1,6 +1,6 @@ namespace Fika.Core.Modding.Events { - public abstract class FikaEvent - { - } + public abstract class FikaEvent + { + } } diff --git a/Fika.Core/Modding/Events/FikaGameCreatedEvent.cs b/Fika.Core/Modding/Events/FikaGameCreatedEvent.cs index 61f86826..947508b2 100644 --- a/Fika.Core/Modding/Events/FikaGameCreatedEvent.cs +++ b/Fika.Core/Modding/Events/FikaGameCreatedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class FikaGameCreatedEvent : FikaEvent - { - public IFikaGame Game { get; } + public class FikaGameCreatedEvent : FikaEvent + { + public IFikaGame Game { get; } - internal FikaGameCreatedEvent(IFikaGame game) - { - this.Game = game; - } - } + internal FikaGameCreatedEvent(IFikaGame game) + { + this.Game = game; + } + } } diff --git a/Fika.Core/Modding/Events/FikaServerCreatedEvent.cs b/Fika.Core/Modding/Events/FikaServerCreatedEvent.cs index 3f117de8..fe2b9105 100644 --- a/Fika.Core/Modding/Events/FikaServerCreatedEvent.cs +++ b/Fika.Core/Modding/Events/FikaServerCreatedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class FikaServerCreatedEvent : FikaEvent - { - public FikaServer Server { get; } + public class FikaServerCreatedEvent : FikaEvent + { + public FikaServer Server { get; } - internal FikaServerCreatedEvent(FikaServer server) - { - Server = server; - } - } + internal FikaServerCreatedEvent(FikaServer server) + { + Server = server; + } + } } diff --git a/Fika.Core/Modding/Events/FikaServerDestroyedEvent.cs b/Fika.Core/Modding/Events/FikaServerDestroyedEvent.cs index 98821d1a..0c244c16 100644 --- a/Fika.Core/Modding/Events/FikaServerDestroyedEvent.cs +++ b/Fika.Core/Modding/Events/FikaServerDestroyedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class FikaServerDestroyedEvent : FikaEvent - { - public FikaServer Server { get; } + public class FikaServerDestroyedEvent : FikaEvent + { + public FikaServer Server { get; } - internal FikaServerDestroyedEvent(FikaServer server) - { - Server = server; - } - } + internal FikaServerDestroyedEvent(FikaServer server) + { + Server = server; + } + } } diff --git a/Fika.Core/Modding/Events/GameWorldStartedEvent.cs b/Fika.Core/Modding/Events/GameWorldStartedEvent.cs index 6bae1e67..34906e40 100644 --- a/Fika.Core/Modding/Events/GameWorldStartedEvent.cs +++ b/Fika.Core/Modding/Events/GameWorldStartedEvent.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Modding.Events { - public class GameWorldStartedEvent : FikaEvent - { - public GameWorld GameWorld { get; } + public class GameWorldStartedEvent : FikaEvent + { + public GameWorld GameWorld { get; } - internal GameWorldStartedEvent(GameWorld gameWorld) - { - this.GameWorld = gameWorld; - } - } + internal GameWorldStartedEvent(GameWorld gameWorld) + { + this.GameWorld = gameWorld; + } + } } diff --git a/Fika.Core/Modding/FikaEventDispatcher.cs b/Fika.Core/Modding/FikaEventDispatcher.cs index 71bf923b..65496c41 100644 --- a/Fika.Core/Modding/FikaEventDispatcher.cs +++ b/Fika.Core/Modding/FikaEventDispatcher.cs @@ -3,38 +3,38 @@ namespace Fika.Core.Modding { - public static class FikaEventDispatcher - { - public delegate void FikaEventHandler(FikaEvent e); + public static class FikaEventDispatcher + { + public delegate void FikaEventHandler(FikaEvent e); - // I'm leaving this here but consumers should definitely subscribe to individual events - public static event FikaEventHandler OnFikaEvent; + // I'm leaving this here but consumers should definitely subscribe to individual events + public static event FikaEventHandler OnFikaEvent; - internal static void DispatchEvent(TEvent e) where TEvent : FikaEvent - { - OnFikaEvent?.Invoke(e); - } + internal static void DispatchEvent(TEvent e) where TEvent : FikaEvent + { + OnFikaEvent?.Invoke(e); + } - public static void SubscribeEvent(Action callback) where TEvent : FikaEvent - { - OnFikaEvent += e => - { - if (e is TEvent specificEvent) - { - callback?.Invoke(specificEvent); - } - }; - } + public static void SubscribeEvent(Action callback) where TEvent : FikaEvent + { + OnFikaEvent += e => + { + if (e is TEvent specificEvent) + { + callback?.Invoke(specificEvent); + } + }; + } - public static void UnsubscribeEvent(Action callback) where TEvent : FikaEvent - { - OnFikaEvent -= e => - { - if (e is TEvent specificEvent) - { - callback?.Invoke(specificEvent); - } - }; - } - } + public static void UnsubscribeEvent(Action callback) where TEvent : FikaEvent + { + OnFikaEvent -= e => + { + if (e is TEvent specificEvent) + { + callback?.Invoke(specificEvent); + } + }; + } + } } diff --git a/Fika.Core/Models/BotDifficulties.cs b/Fika.Core/Models/BotDifficulties.cs index 08b8caa9..f55f6851 100644 --- a/Fika.Core/Models/BotDifficulties.cs +++ b/Fika.Core/Models/BotDifficulties.cs @@ -5,46 +5,46 @@ namespace Fika.Core.Models { - public class BotDifficulties : Dictionary - { - [JsonIgnore] - private CoreBotSettingsClass CoreSettings; - - public BotDifficulties() - { - string coreString = RequestHandler.GetJson("/singleplayer/settings/bot/difficulty/core/core"); - CoreSettings = JsonConvert.DeserializeObject(coreString); - } - - public BotSettingsComponents GetComponent(BotDifficulty botDifficulty, WildSpawnType role) - { - FikaPlugin.Instance.FikaLogger.LogInfo($"Retrieving data for: {role}, difficulty: {botDifficulty}"); - if (TryGetValue(role.ToString().ToLower(), out RoleData value)) - { - if (value.TryGetValue(botDifficulty.ToString().ToLower(), out BotSettingsComponents botSettingsComponents)) - { - return botSettingsComponents; - } - } - - FikaPlugin.Instance.FikaLogger.LogError($"Unable to retrieve difficulty settings for: {role}, difficulty: {botDifficulty}"); - return null; - } - - public CoreBotSettingsClass GetCoreSettings() - { - FikaPlugin.Instance.FikaLogger.LogInfo("Retrieving Core settings"); - if (CoreSettings != null) - { - return CoreSettings; - } - - return null; - } - - public class RoleData : Dictionary - { - - } - } + public class BotDifficulties : Dictionary + { + [JsonIgnore] + private CoreBotSettingsClass CoreSettings; + + public BotDifficulties() + { + string coreString = RequestHandler.GetJson("/singleplayer/settings/bot/difficulty/core/core"); + CoreSettings = JsonConvert.DeserializeObject(coreString); + } + + public BotSettingsComponents GetComponent(BotDifficulty botDifficulty, WildSpawnType role) + { + FikaPlugin.Instance.FikaLogger.LogInfo($"Retrieving data for: {role}, difficulty: {botDifficulty}"); + if (TryGetValue(role.ToString().ToLower(), out RoleData value)) + { + if (value.TryGetValue(botDifficulty.ToString().ToLower(), out BotSettingsComponents botSettingsComponents)) + { + return botSettingsComponents; + } + } + + FikaPlugin.Instance.FikaLogger.LogError($"Unable to retrieve difficulty settings for: {role}, difficulty: {botDifficulty}"); + return null; + } + + public CoreBotSettingsClass GetCoreSettings() + { + FikaPlugin.Instance.FikaLogger.LogInfo("Retrieving Core settings"); + if (CoreSettings != null) + { + return CoreSettings; + } + + return null; + } + + public class RoleData : Dictionary + { + + } + } } diff --git a/Fika.Core/Models/ClientConfigModel.cs b/Fika.Core/Models/ClientConfigModel.cs index 90b8c2de..a3ef7e26 100644 --- a/Fika.Core/Models/ClientConfigModel.cs +++ b/Fika.Core/Models/ClientConfigModel.cs @@ -4,74 +4,74 @@ namespace Fika.Core.UI.Models { - [DataContract] - public struct ClientConfigModel - { - [DataMember(Name = "useBtr")] - public bool UseBTR; + [DataContract] + public struct ClientConfigModel + { + [DataMember(Name = "useBtr")] + public bool UseBTR; - [DataMember(Name = "friendlyFire")] - public bool FriendlyFire; + [DataMember(Name = "friendlyFire")] + public bool FriendlyFire; - [DataMember(Name = "dynamicVExfils")] - public bool DynamicVExfils; + [DataMember(Name = "dynamicVExfils")] + public bool DynamicVExfils; - [DataMember(Name = "allowFreeCam")] - public bool AllowFreeCam; + [DataMember(Name = "allowFreeCam")] + public bool AllowFreeCam; - [DataMember(Name = "allowItemSending")] - public bool AllowItemSending; + [DataMember(Name = "allowItemSending")] + public bool AllowItemSending; - [DataMember(Name = "blacklistedItems")] - public string[] BlacklistedItems; + [DataMember(Name = "blacklistedItems")] + public string[] BlacklistedItems; - [DataMember(Name = "forceSaveOnDeath")] - public bool ForceSaveOnDeath; + [DataMember(Name = "forceSaveOnDeath")] + public bool ForceSaveOnDeath; - [DataMember(Name = "useInertia")] - public bool UseInertia; + [DataMember(Name = "useInertia")] + public bool UseInertia; - [DataMember(Name = "sharedQuestProgression")] - public bool SharedQuestProgression; + [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 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 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); - } - } - } + 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); + } + } + } } \ No newline at end of file diff --git a/Fika.Core/Models/NatPunchServerConfigModel.cs b/Fika.Core/Models/NatPunchServerConfigModel.cs index 0a237059..082453cb 100644 --- a/Fika.Core/Models/NatPunchServerConfigModel.cs +++ b/Fika.Core/Models/NatPunchServerConfigModel.cs @@ -4,49 +4,49 @@ namespace Fika.Core.UI.Models { - [DataContract] - public struct NatPunchServerConfigModel - { - [DataMember(Name = "enable")] - public bool Enable; + [DataContract] + public struct NatPunchServerConfigModel + { + [DataMember(Name = "enable")] + public bool Enable; - [DataMember(Name = "port")] - public int Port; + [DataMember(Name = "port")] + public int Port; - [DataMember(Name = "natIntroduceAmount")] - public int NatIntroduceAmount; + [DataMember(Name = "natIntroduceAmount")] + public int NatIntroduceAmount; - public NatPunchServerConfigModel(bool enable, int port, int natIntroduceAmount) - { - Enable = enable; - Port = port; - NatIntroduceAmount = 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); - } - } - } + 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 e3a64d22..2833dd35 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -36,989 +36,989 @@ namespace Fika.Core.Networking { - public class FikaClient : MonoBehaviour, INetEventListener - { - public NetDataWriter Writer => dataWriter; - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public NetPacketProcessor packetProcessor = new(); - public int Ping = 0; - public int ServerFPS = 0; - public int ConnectedClients = 0; - public int ReadyClients = 0; - public bool HostReady = false; - public bool ReconnectDone = false; - public NetManager NetClient - { - get - { - return netClient; - } - } - public NetPeer ServerConnection { get; private set; } - public bool SpawnPointsReceived { get; private set; } = false; - public bool Started - { - get - { - if (netClient == null) - { - return false; - } - return netClient.IsRunning; - } - } - - private NetManager netClient; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); - private NetDataWriter dataWriter = new(); - private FikaChat fikaChat; - private string myProfileId; - - public async void Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - myProfileId = FikaBackendUtils.Profile.ProfileId; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); - packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); - - netClient = new NetManager(this) - { - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - NatPunchEnabled = false, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - MaxConnectAttempts = 5, - ReconnectDelay = 1 * 1000 - }; - - await NetManagerUtils.CreateCoopHandler(); - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - - if (FikaBackendUtils.IsHostNatPunch) - { - NetManagerUtils.DestroyPingingClient(); - } - - netClient.Start(FikaBackendUtils.LocalPort); - - string ip = FikaBackendUtils.RemoteIp; - int port = FikaBackendUtils.RemotePort; - - 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"); - }; - - while (ServerConnection.ConnectionState != ConnectionState.Connected) - { + public class FikaClient : MonoBehaviour, INetEventListener + { + public NetDataWriter Writer => dataWriter; + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public NetPacketProcessor packetProcessor = new(); + public int Ping = 0; + public int ServerFPS = 0; + public int ConnectedClients = 0; + public int ReadyClients = 0; + public bool HostReady = false; + public bool ReconnectDone = false; + public NetManager NetClient + { + get + { + return netClient; + } + } + public NetPeer ServerConnection { get; private set; } + public bool SpawnPointsReceived { get; private set; } = false; + public bool Started + { + get + { + if (netClient == null) + { + return false; + } + return netClient.IsRunning; + } + } + + private NetManager netClient; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); + private NetDataWriter dataWriter = new(); + private FikaChat fikaChat; + private string myProfileId; + + public async void Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + myProfileId = FikaBackendUtils.Profile.ProfileId; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAirdropLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); + packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHalloweenEventPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnStatisticsPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); + + netClient = new NetManager(this) + { + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + NatPunchEnabled = false, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + MaxConnectAttempts = 5, + ReconnectDelay = 1 * 1000 + }; + + await NetManagerUtils.CreateCoopHandler(); + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + + if (FikaBackendUtils.IsHostNatPunch) + { + NetManagerUtils.DestroyPingingClient(); + } + + netClient.Start(FikaBackendUtils.LocalPort); + + string ip = FikaBackendUtils.RemoteIp; + int port = FikaBackendUtils.RemotePort; + + 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"); + }; + + while (ServerConnection.ConnectionState != ConnectionState.Connected) + { #if DEBUG - FikaPlugin.Instance.FikaLogger.LogWarning("FikaClient was not able to connect in time!"); + FikaPlugin.Instance.FikaLogger.LogWarning("FikaClient was not able to connect in time!"); #endif - await Task.Delay(1 * 6000); - ServerConnection = netClient.Connect(ip, port, "fika.core"); - } - - FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); - } - - private void OnReconnectPacketReceived(ReconnectPacket packet) - { - if (!packet.IsRequest) - { - switch (packet.Type) - { - case ReconnectPacket.EReconnectDataType.Throwable: - Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); - break; - case ReconnectPacket.EReconnectDataType.Interactives: - { - WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; - Dictionary netIdDictionary = []; - { - foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) - { - netIdDictionary.Add(data.NetId, data); - } - } - foreach (WorldInteractiveObject item in worldInteractiveObjects) - { - if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) - { - item.SetInitialSyncState(value); - } - } - break; - } - case ReconnectPacket.EReconnectDataType.LampControllers: - { - Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) - .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) - .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); - - foreach (KeyValuePair lampState in packet.LampStates) - { - if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) - { - if (lampController.LampState != (Turnable.EState)lampState.Value) - { - lampController.Switch((Turnable.EState)lampState.Value); - } - } - } - break; - } - case ReconnectPacket.EReconnectDataType.Windows: - { - Dictionary windowBreakerStates = packet.WindowBreakerStates; - foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) - .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) - { - if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) - { - try - { - DamageInfo damageInfo = default; - damageInfo.HitPoint = hitPosition; - windowBreaker.MakeHit(in damageInfo, true); - } - catch (Exception ex) - { - logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); - } - } - } - break; - } - case ReconnectPacket.EReconnectDataType.OwnCharacter: - coopHandler.LocalGameInstance.Profile_0 = packet.Profile; - coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; - FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; - break; - case ReconnectPacket.EReconnectDataType.Finished: - ReconnectDone = true; - break; - default: - break; - } - } - } - - private void OnWorldLootPacketReceived(WorldLootPacket packet) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); - coopGame.LootItems = lootItems; - coopGame.HasReceivedLoot = true; - } - } - - private void OnStatisticsPacketReceived(ThrowablePacket packet) - { - GClass724 grenades = Singleton.Instance.Grenades; - if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) - { - throwable.ApplyNetPacket(packet.Data); - } - } - - private void OnStatisticsPacketReceived(StatisticsPacket packet) - { - ServerFPS = packet.ServerFPS; - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet) - { - if (!packet.IsRequest) - { - World world = Singleton.Instance.World_0; - if (world.Interactables == null) - { - world.RegisterNetworkInteractionObjects(packet.Interactables); - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - coopGame.InteractablesInitialized = true; - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet) - { - CoopGame coopGame = (CoopGame)Singleton.Instance; - if (coopGame != null) - { - if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) - { - coopGame.SpawnId = packet.Name; - } - } - else - { - logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; - } - } - - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) - { - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - 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) - { - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } - - private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) - { - player.HandleCallbackFromServer(in packet); - } - } - - private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) - { - Dictionary newPlayers = Players; - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (player.ProfileId != packet.ProfileId) - { - FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); - for (int i = 0; i < Players.Count; i++) - { - KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); - Players.Remove(playerToReorganize.Key); - Players[packet.NetId] = playerToReorganize.Value; - } - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); - string allPlayers = ""; - 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); - } - FikaPlugin.Instance.FikaLogger.LogError(allPlayers); - } - } - - 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 (KeyValuePair player in Players) - { - if (player.Value == MyPlayer) - { - i = player.Key; - - break; - } - } - - if (i == -1) - { - FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); - return; - } - - Players.Remove(i); - Players[packet.NetId] = MyPlayer; - } - - private void OnSendCharacterPacketReceived(SendCharacterPacket packet) - { - if (coopHandler == null) - { - return; - } - - if (packet.PlayerInfo.Profile.ProfileId != myProfileId) - { - coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); - } - } - - private void OnBorderZonePacketReceived(BorderZonePacket packet) - { - if (Singleton.Instantiated) - { - BorderZone[] borderZones = Singleton.Instance.BorderZones; - if (borderZones != null && borderZones.Length > 0) - { - foreach (BorderZone borderZone in borderZones) - { - if (borderZone.Id == packet.ZoneId) - { - List players = Singleton.Instance.RegisteredPlayers; - foreach (IPlayer player in players) - { - if (player.ProfileId == packet.ProfileId) - { - IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); - borderZone.ProcessIncomingPacket(playerBridge, true); - } - } - } - } - } - } - } - - private void OnMinePacketReceived(MinePacket packet) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - player.ProcessInteractWithBTR(packet); - } - } - - private void OnBTRPacketReceived(BTRPacket packet) - { - if (coopHandler.clientBTR != null) - { - coopHandler.clientBTR.btrPackets.Enqueue(packet); - } - } - - private void OnWeatherPacketReceived(WeatherPacket packet) - { - if (!packet.IsRequest) - { - if (WeatherController.Instance != null) - { - WeatherController.Instance.method_0(packet.WeatherClasses); - } - else - { - logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); - } - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) - { - if (!packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - CoopGame coopGame = coopHandler.LocalGameInstance; - - CarExtraction carExtraction = FindObjectOfType(); - - foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) - { - ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); - if (point != null || point != default) - { - if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) - { - point.Enable(); - point.Status = exfilPoint.Value; - } - else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) - { - point.Disable(); - point.Status = exfilPoint.Value; - - if (carExtraction != null) - { - if (carExtraction.Subscribee == point) - { - carExtraction.Play(true); - } - } - } - coopGame.UpdateExfiltrationUi(point, false, true); - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); - } - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) - { - foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) - { - ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); - if (scavPoint != null || scavPoint != default) - { - if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) - { - scavPoint.Enable(); - scavPoint.EligibleIds.Add(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, true); - } - else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) - { - scavPoint.Disable(); - scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); - scavPoint.Status = scavExfilPoint.Value; - coopGame.UpdateExfilPointFromServer(scavPoint, false); - } - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); - } - } - - ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); - coopGame.ResetExfilPointsFromServer(points); - } - - SpawnPointsReceived = true; - } - else - { - logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet) - { - switch (packet.PacketType) - { - case EPackageType.ClientExtract: - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); - - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - break; - case EPackageType.Ping: - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - break; - case EPackageType.TrainSync: - { - Locomotive locomotive = FindObjectOfType(); - if (locomotive != null) - { - DateTime depart = new(packet.DepartureTime); - Traverse.Create(locomotive).Field("_depart").SetValue(depart); - } - else - { - logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); - } - } - break; - case EPackageType.ExfilCountdown: - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - break; - case EPackageType.TraderServiceNotification: - { - if (coopHandler.clientBTR) - { - coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); - } - } - break; - case EPackageType.DisposeBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) - { - if (!botToDispose.gameObject.activeSelf) - { - botToDispose.gameObject.SetActive(true); - } - - if (coopHandler.Players.Remove(packet.BotNetId)) - { - botToDispose.Dispose(); - AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); - logger.LogInfo("Disposing bot: " + packet.BotNetId); - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); - } - } - else - { - logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); - } - } - break; - case EPackageType.RemoveAirdropManager: - { - if (Singleton.Instance != null) - { - Destroy(Singleton.Instance); - } - } - break; - case EPackageType.EnableBot: - { - if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) - { - if (!botToEnable.gameObject.activeSelf) - { + await Task.Delay(1 * 6000); + ServerConnection = netClient.Connect(ip, port, "fika.core"); + } + + FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); + } + + private void OnReconnectPacketReceived(ReconnectPacket packet) + { + if (!packet.IsRequest) + { + switch (packet.Type) + { + case ReconnectPacket.EReconnectDataType.Throwable: + Singleton.Instance.OnSmokeGrenadesDeserialized(packet.ThrowableData); + break; + case ReconnectPacket.EReconnectDataType.Interactives: + { + WorldInteractiveObject[] worldInteractiveObjects = Traverse.Create(Singleton.Instance.World_0).Field("worldInteractiveObject_0").Value; + Dictionary netIdDictionary = []; + { + foreach (WorldInteractiveObject.GStruct384 data in packet.InteractivesData) + { + netIdDictionary.Add(data.NetId, data); + } + } + foreach (WorldInteractiveObject item in worldInteractiveObjects) + { + if (netIdDictionary.TryGetValue(item.NetId, out WorldInteractiveObject.GStruct384 value)) + { + item.SetInitialSyncState(value); + } + } + break; + } + case ReconnectPacket.EReconnectDataType.LampControllers: + { + Dictionary lampControllerDictionary = LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_0)) + .ToDictionary(new Func(ClientWorld.Class1231.class1231_0.method_1)); + + foreach (KeyValuePair lampState in packet.LampStates) + { + if (lampControllerDictionary.TryGetValue(lampState.Key, out LampController lampController)) + { + if (lampController.LampState != (Turnable.EState)lampState.Value) + { + lampController.Switch((Turnable.EState)lampState.Value); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.Windows: + { + Dictionary windowBreakerStates = packet.WindowBreakerStates; + foreach (WindowBreaker windowBreaker in LocationScene.GetAllObjects(true) + .Where(new Func(ClientWorld.Class1231.class1231_0.method_2))) + { + if (windowBreakerStates.TryGetValue(windowBreaker.NetId, out Vector3 hitPosition)) + { + try + { + DamageInfo damageInfo = default; + damageInfo.HitPoint = hitPosition; + windowBreaker.MakeHit(in damageInfo, true); + } + catch (Exception ex) + { + logger.LogError("OnReconnectPacketReceived: Exception caught while setting up WindowBreakers: " + ex.Message); + } + } + } + break; + } + case ReconnectPacket.EReconnectDataType.OwnCharacter: + coopHandler.LocalGameInstance.Profile_0 = packet.Profile; + coopHandler.LocalGameInstance.Profile_0.Health = packet.ProfileHealthClass; + FikaBackendUtils.ReconnectPosition = packet.PlayerPosition; + break; + case ReconnectPacket.EReconnectDataType.Finished: + ReconnectDone = true; + break; + default: + break; + } + } + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + GClass1211 lootItems = SimpleZlib.Decompress(packet.Data).ParseJsonTo(); + coopGame.LootItems = lootItems; + coopGame.HasReceivedLoot = true; + } + } + + private void OnStatisticsPacketReceived(ThrowablePacket packet) + { + GClass724 grenades = Singleton.Instance.Grenades; + if (grenades.TryGetByKey(packet.Data.Id, out Throwable throwable)) + { + throwable.ApplyNetPacket(packet.Data); + } + } + + private void OnStatisticsPacketReceived(StatisticsPacket packet) + { + ServerFPS = packet.ServerFPS; + } + + private void OnInteractableInitPacketReceived(InteractableInitPacket packet) + { + if (!packet.IsRequest) + { + World world = Singleton.Instance.World_0; + if (world.Interactables == null) + { + world.RegisterNetworkInteractionObjects(packet.Interactables); + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + coopGame.InteractablesInitialized = true; + } + } + } + } + + private void OnSpawnPointPacketReceived(SpawnpointPacket packet) + { + CoopGame coopGame = (CoopGame)Singleton.Instance; + if (coopGame != null) + { + if (!packet.IsRequest && !string.IsNullOrEmpty(packet.Name)) + { + coopGame.SpawnId = packet.Name; + } + } + else + { + logger.LogError("OnSpawnPointPacketReceived: CoopGame was null upon receiving packet!"); ; + } + } + + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet) + { + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + + 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) + { + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player) && player.IsYourPlayer) + { + player.HandleCallbackFromServer(in packet); + } + } + + private void OnSyncNetIdPacketReceived(SyncNetIdPacket packet) + { + Dictionary newPlayers = Players; + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (player.ProfileId != packet.ProfileId) + { + FikaPlugin.Instance.FikaLogger.LogWarning($"OnSyncNetIdPacketReceived: {packet.ProfileId} had the wrong NetId: {Players[packet.NetId].NetId}, should be {packet.NetId}"); + for (int i = 0; i < Players.Count; i++) + { + KeyValuePair playerToReorganize = Players.Where(x => x.Value.ProfileId == packet.ProfileId).First(); + Players.Remove(playerToReorganize.Key); + Players[packet.NetId] = playerToReorganize.Value; + } + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError($"OnSyncNetIdPacketReceived: Could not find NetId {packet.NetId} in player list!"); + string allPlayers = ""; + 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); + } + FikaPlugin.Instance.FikaLogger.LogError(allPlayers); + } + } + + 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 (KeyValuePair player in Players) + { + if (player.Value == MyPlayer) + { + i = player.Key; + + break; + } + } + + if (i == -1) + { + FikaPlugin.Instance.FikaLogger.LogError("OnAssignNetIdPacketReceived: Could not find own player among players list"); + return; + } + + Players.Remove(i); + Players[packet.NetId] = MyPlayer; + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet) + { + if (coopHandler == null) + { + return; + } + + if (packet.PlayerInfo.Profile.ProfileId != myProfileId) + { + coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.netId, packet.IsAlive, packet.IsAI); + } + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet) + { + if (Singleton.Instantiated) + { + BorderZone[] borderZones = Singleton.Instance.BorderZones; + if (borderZones != null && borderZones.Length > 0) + { + foreach (BorderZone borderZone in borderZones) + { + if (borderZone.Id == packet.ZoneId) + { + List players = Singleton.Instance.RegisteredPlayers; + foreach (IPlayer player in players) + { + if (player.ProfileId == packet.ProfileId) + { + IPlayerOwner playerBridge = Singleton.Instance.GetAlivePlayerBridgeByProfileID(player.ProfileId); + borderZone.ProcessIncomingPacket(playerBridge, true); + } + } + } + } + } + } + } + + private void OnMinePacketReceived(MinePacket packet) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + player.ProcessInteractWithBTR(packet); + } + } + + private void OnBTRPacketReceived(BTRPacket packet) + { + if (coopHandler.clientBTR != null) + { + coopHandler.clientBTR.btrPackets.Enqueue(packet); + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet) + { + if (!packet.IsRequest) + { + if (WeatherController.Instance != null) + { + WeatherController.Instance.method_0(packet.WeatherClasses); + } + else + { + logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); + } + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) + { + if (!packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + CoopGame coopGame = coopHandler.LocalGameInstance; + + CarExtraction carExtraction = FindObjectOfType(); + + foreach (KeyValuePair exfilPoint in packet.ExfiltrationPoints) + { + ExfiltrationPoint point = exfilController.ExfiltrationPoints.Where(x => x.Settings.Name == exfilPoint.Key).FirstOrDefault(); + if (point != null || point != default) + { + if (point.Status != exfilPoint.Value && (exfilPoint.Value == EExfiltrationStatus.RegularMode || exfilPoint.Value == EExfiltrationStatus.UncompleteRequirements)) + { + point.Enable(); + point.Status = exfilPoint.Value; + } + else if (point.Status != exfilPoint.Value && exfilPoint.Value == EExfiltrationStatus.NotPresent || exfilPoint.Value == EExfiltrationStatus.Pending) + { + point.Disable(); + point.Status = exfilPoint.Value; + + if (carExtraction != null) + { + if (carExtraction.Subscribee == point) + { + carExtraction.Play(true); + } + } + } + coopGame.UpdateExfiltrationUi(point, false, true); + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); + } + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null && packet.HasScavExfils) + { + foreach (KeyValuePair scavExfilPoint in packet.ScavExfiltrationPoints) + { + ScavExfiltrationPoint scavPoint = exfilController.ScavExfiltrationPoints.Where(x => x.Settings.Name == scavExfilPoint.Key).FirstOrDefault(); + if (scavPoint != null || scavPoint != default) + { + if (scavPoint.Status != scavExfilPoint.Value && scavExfilPoint.Value == EExfiltrationStatus.RegularMode) + { + scavPoint.Enable(); + scavPoint.EligibleIds.Add(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, true); + } + else if (scavPoint.Status != scavExfilPoint.Value && (scavExfilPoint.Value == EExfiltrationStatus.NotPresent || scavExfilPoint.Value == EExfiltrationStatus.Pending)) + { + scavPoint.Disable(); + scavPoint.EligibleIds.Remove(MyPlayer.ProfileId); + scavPoint.Status = scavExfilPoint.Value; + coopGame.UpdateExfilPointFromServer(scavPoint, false); + } + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); + } + } + + ExfiltrationPoint[] points = exfilController.ScavExfiltrationPoints.Where(x => x.Status == EExfiltrationStatus.RegularMode).ToArray(); + coopGame.ResetExfilPointsFromServer(points); + } + + SpawnPointsReceived = true; + } + else + { + logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet) + { + switch (packet.PacketType) + { + case EPackageType.ClientExtract: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + break; + case EPackageType.Ping: + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + break; + case EPackageType.TrainSync: + { + Locomotive locomotive = FindObjectOfType(); + if (locomotive != null) + { + DateTime depart = new(packet.DepartureTime); + Traverse.Create(locomotive).Field("_depart").SetValue(depart); + } + else + { + logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); + } + } + break; + case EPackageType.ExfilCountdown: + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + break; + case EPackageType.TraderServiceNotification: + { + if (coopHandler.clientBTR) + { + coopHandler.clientBTR.DisplayNetworkNotification(packet.TraderServiceType); + } + } + break; + case EPackageType.DisposeBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToDispose)) + { + if (!botToDispose.gameObject.activeSelf) + { + botToDispose.gameObject.SetActive(true); + } + + if (coopHandler.Players.Remove(packet.BotNetId)) + { + botToDispose.Dispose(); + AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); + logger.LogInfo("Disposing bot: " + packet.BotNetId); + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + } + } + else + { + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + } + } + break; + case EPackageType.RemoveAirdropManager: + { + if (Singleton.Instance != null) + { + Destroy(Singleton.Instance); + } + } + break; + case EPackageType.EnableBot: + { + if (coopHandler.Players.TryGetValue(packet.BotNetId, out CoopPlayer botToEnable)) + { + if (!botToEnable.gameObject.activeSelf) + { #if DEBUG - logger.LogWarning("Enabling " + packet.BotNetId); + logger.LogWarning("Enabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(true); - } - else - { - 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 (botToEnable.gameObject.activeSelf) - { + botToEnable.gameObject.SetActive(true); + } + else + { + 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 (botToEnable.gameObject.activeSelf) + { #if DEBUG - logger.LogWarning("Disabling " + packet.BotNetId); + logger.LogWarning("Disabling " + packet.BotNetId); #endif - botToEnable.gameObject.SetActive(false); - } - else - { - logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); - } - } - } - break; - case EPackageType.ClearEffects: - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - if (playerToApply is ObservedCoopPlayer observedPlayer) - { - observedPlayer.HealthBar.ClearEffects(); - } - } - } - break; - } - } - - private void OnHealthSyncPacketReceived(HealthSyncPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - } - - private void OnAirdropLootPacketReceived(AirdropLootPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.ReceiveBuildLootContainer(packet); - } - else - { - logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); - } - } - - private void OnAirdropPacketReceived(AirdropPacket packet) - { - if (Singleton.Instance != null) - { - Singleton.Instance.AirdropParameters = new() - { - Config = packet.Config, - AirdropAvailable = packet.AirdropAvailable, - PlaneSpawned = packet.PlaneSpawned, - BoxSpawned = packet.BoxSpawned, - DistanceTraveled = packet.DistanceTraveled, - DistanceToTravel = packet.DistanceToTravel, - DistanceToDrop = packet.DistanceToDrop, - Timer = packet.Timer, - DropHeight = packet.DropHeight, - TimeToStart = packet.TimeToStart, - RandomAirdropPoint = packet.BoxPoint, - SpawnPoint = packet.SpawnPoint, - LookPoint = packet.LookPoint - }; - } - else - { - logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); - } - } - - private void OnInformationPacketReceived(InformationPacket packet) - { - if (!packet.IsRequest) - { - ConnectedClients = packet.NumberOfPlayers; - ReadyClients = packet.ReadyPlayers; - HostReady = packet.HostReady; - } - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) - { - if (coopHandler == null) - { - return; - } - - if (!packet.IsRequest) - { - 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); - } - } - else if (packet.IsRequest) - { - logger.LogInfo($"Received CharacterRequest from server, send my Profile."); - AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = MyPlayer.Profile - }, - IsAlive = MyPlayer.ActiveHealthController.IsAlive, - IsAI = MyPlayer.IsAI, - Position = MyPlayer.Transform.position, - NetId = MyPlayer.NetId - }; - dataWriter.Reset(); - SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); - } - } - - private void OnInventoryPacketReceived(InventoryPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); - } - } - - private void OnDamagePacketReceived(DamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); - } - } - - private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); - } - } - - private void OnFirearmPacketReceived(WeaponPacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - } - - private void OnGameTimerPacketReceived(GameTimerPacket packet) - { - TimeSpan sessionTime = new(packet.Tick); - - CoopGame coopGame = coopHandler.LocalGameInstance; - - GameTimerClass gameTimer = coopGame.GameTimer; - if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) - { - TimeSpan timeRemain = gameTimer.PastTime + sessionTime; - - gameTimer.ChangeSessionTime(timeRemain); - - Traverse timerPanel = Traverse.Create(coopGame.GameUi.TimerPanel); - timerPanel.Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds)); - - MainTimerPanel mainTimerPanel = timerPanel.Field("_mainTimerPanel").Value; - if (mainTimerPanel != null) - { - Traverse.Create(mainTimerPanel).Field("dateTime_0").Value = gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds); - mainTimerPanel.UpdateTimer(); - } - - Traverse.Create(gameTimer).Field("nullable_0").Value = new DateTime(packet.StartTime); - } - } - - private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) - { - HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; - - if (controller == null) - { - return; - } - - switch (packet.PacketType) - { - case EHalloweenPacketType.Summon: - controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); - break; - case EHalloweenPacketType.Sync: - controller.SetEventState(packet.EventState, false); - break; - case EHalloweenPacketType.Exit: - controller.method_3(packet.Exit); - break; - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - } - - protected void Update() - { - netClient?.PollEvents(); - - /*if (_netClient.FirstPeer == null) + botToEnable.gameObject.SetActive(false); + } + else + { + logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); + } + } + } + break; + case EPackageType.ClearEffects: + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + if (playerToApply is ObservedCoopPlayer observedPlayer) + { + observedPlayer.HealthBar.ClearEffects(); + } + } + } + break; + } + } + + private void OnHealthSyncPacketReceived(HealthSyncPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + } + + private void OnAirdropLootPacketReceived(AirdropLootPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.ReceiveBuildLootContainer(packet); + } + else + { + logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); + } + } + + private void OnAirdropPacketReceived(AirdropPacket packet) + { + if (Singleton.Instance != null) + { + Singleton.Instance.AirdropParameters = new() + { + Config = packet.Config, + AirdropAvailable = packet.AirdropAvailable, + PlaneSpawned = packet.PlaneSpawned, + BoxSpawned = packet.BoxSpawned, + DistanceTraveled = packet.DistanceTraveled, + DistanceToTravel = packet.DistanceToTravel, + DistanceToDrop = packet.DistanceToDrop, + Timer = packet.Timer, + DropHeight = packet.DropHeight, + TimeToStart = packet.TimeToStart, + RandomAirdropPoint = packet.BoxPoint, + SpawnPoint = packet.SpawnPoint, + LookPoint = packet.LookPoint + }; + } + else + { + logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); + } + } + + private void OnInformationPacketReceived(InformationPacket packet) + { + if (!packet.IsRequest) + { + ConnectedClients = packet.NumberOfPlayers; + ReadyClients = packet.ReadyPlayers; + HostReady = packet.HostReady; + } + } + + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet) + { + if (coopHandler == null) + { + return; + } + + if (!packet.IsRequest) + { + 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); + } + } + else if (packet.IsRequest) + { + logger.LogInfo($"Received CharacterRequest from server, send my Profile."); + AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = MyPlayer.Profile + }, + IsAlive = MyPlayer.ActiveHealthController.IsAlive, + IsAI = MyPlayer.IsAI, + Position = MyPlayer.Transform.position, + NetId = MyPlayer.NetId + }; + dataWriter.Reset(); + SendData(dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + } + + private void OnInventoryPacketReceived(InventoryPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.InventoryPackets?.Enqueue(packet); + } + } + + private void OnDamagePacketReceived(DamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.DamagePackets?.Enqueue(packet); + } + } + + private void OnArmorDamagePacketReceived(ArmorDamagePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.ArmorDamagePackets?.Enqueue(packet); + } + } + + private void OnFirearmPacketReceived(WeaponPacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + } + + private void OnGameTimerPacketReceived(GameTimerPacket packet) + { + TimeSpan sessionTime = new(packet.Tick); + + CoopGame coopGame = coopHandler.LocalGameInstance; + + GameTimerClass gameTimer = coopGame.GameTimer; + if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue) + { + TimeSpan timeRemain = gameTimer.PastTime + sessionTime; + + gameTimer.ChangeSessionTime(timeRemain); + + Traverse timerPanel = Traverse.Create(coopGame.GameUi.TimerPanel); + timerPanel.Field("dateTime_0").SetValue(gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds)); + + MainTimerPanel mainTimerPanel = timerPanel.Field("_mainTimerPanel").Value; + if (mainTimerPanel != null) + { + Traverse.Create(mainTimerPanel).Field("dateTime_0").Value = gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds); + mainTimerPanel.UpdateTimer(); + } + + Traverse.Create(gameTimer).Field("nullable_0").Value = new DateTime(packet.StartTime); + } + } + + private void OnHalloweenEventPacketReceived(HalloweenEventPacket packet) + { + HalloweenEventControllerClass controller = HalloweenEventControllerClass.Instance; + + if (controller == null) + { + return; + } + + switch (packet.PacketType) + { + case EHalloweenPacketType.Summon: + controller.method_5(new EFT.GlobalEvents.HalloweenSummonStartedEvent() { PointPosition = packet.SummonPosition }); + break; + case EHalloweenPacketType.Sync: + controller.SetEventState(packet.EventState, false); + break; + case EHalloweenPacketType.Exit: + controller.method_3(packet.Exit); + break; + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + } + + protected void Update() + { + netClient?.PollEvents(); + + /*if (_netClient.FirstPeer == null) { _netClient.SendBroadcast([1], Port); }*/ - } - - protected void OnDestroy() - { - netClient?.Stop(); - - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); - } - - public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netClient.FirstPeer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogInfo("[CLIENT] We received error " + socketErrorCode); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - packetProcessor.ReadAllPackets(reader, peer); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) - { - logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); - netClient.Connect(remoteEndPoint, "fika.core"); - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - Ping = latency; - NetworkGameSession.RTT = peer.RoundTripTime; - NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; - } - - public void OnConnectionRequest(ConnectionRequest request) - { - - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); - if (disconnectInfo.Reason is DisconnectReason.Timeout) - { - NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); - Destroy(MyPlayer.PacketReceiver); - MyPlayer.PacketSender.DestroyThis(); - Destroy(this); - Singleton.Release(this); - } - } - } + } + + protected void OnDestroy() + { + netClient?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); + } + + public void SendData(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netClient.FirstPeer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Connected to server on port {peer.Port}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.Friend); + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogInfo("[CLIENT] We received error " + socketErrorCode); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + packetProcessor.ReadAllPackets(reader, peer); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.BasicMessage && netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) + { + logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); + netClient.Connect(remoteEndPoint, "fika.core"); + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + Ping = latency; + NetworkGameSession.RTT = peer.RoundTripTime; + NetworkGameSession.LossPercent = (int)NetClient.Statistics.PacketLossPercent; + } + + public void OnConnectionRequest(ConnectionRequest request) + { + + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); + if (disconnectInfo.Reason is DisconnectReason.Timeout) + { + NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); + Destroy(MyPlayer.PacketReceiver); + MyPlayer.PacketSender.DestroyThis(); + Destroy(this); + Singleton.Release(this); + } + } + } } diff --git a/Fika.Core/Networking/FikaPingingClient.cs b/Fika.Core/Networking/FikaPingingClient.cs index 72c21e10..de80f469 100644 --- a/Fika.Core/Networking/FikaPingingClient.cs +++ b/Fika.Core/Networking/FikaPingingClient.cs @@ -12,200 +12,200 @@ namespace Fika.Core.Networking { - public class FikaPingingClient : MonoBehaviour, INetEventListener, INatPunchListener - { - public NetManager NetClient; - 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(string serverId) - { - NetClient = new(this) - { - UnconnectedMessagesEnabled = true, - NatPunchEnabled = true - }; - - GetHostRequest body = new(serverId); - GetHostResponse result = FikaRequestHandler.GetHost(body); - - FikaBackendUtils.IsHostNatPunch = result.NatPunch; - FikaBackendUtils.IsDedicatedGame = result.IsDedicated; - - NetClient.Start(); - - if (FikaBackendUtils.IsHostNatPunch) - { - 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; - - if (string.IsNullOrEmpty(ip)) - { - _logger.LogError("IP was empty when pinging!"); - return false; - } - - 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 void PingEndPoint(string message) - { - NetDataWriter writer = new(); - writer.Put(message); - - if (remoteEndPoint != null) - { - NetClient.SendUnconnectedMessage(writer, remoteEndPoint); - } - if (localEndPoint != null) - { - NetClient.SendUnconnectedMessage(writer, localEndPoint); - } - } - - 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(); - - yield return new WaitForSeconds(1.0f); - } - } - - public void OnConnectionRequest(ConnectionRequest request) - { - // Do nothing - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketError) - { - // Do nothing - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - // Do nothing - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - // Do nothing - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (reader.TryGetString(out string result)) - { - switch (result) - { - 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 - { - _logger.LogError("Could not parse string"); - } - } - - public void OnPeerConnected(NetPeer peer) - { - // Do nothing - } - - 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); - } - }); - } - } + public class FikaPingingClient : MonoBehaviour, INetEventListener, INatPunchListener + { + public NetManager NetClient; + 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(string serverId) + { + NetClient = new(this) + { + UnconnectedMessagesEnabled = true, + NatPunchEnabled = true + }; + + GetHostRequest body = new(serverId); + GetHostResponse result = FikaRequestHandler.GetHost(body); + + FikaBackendUtils.IsHostNatPunch = result.NatPunch; + FikaBackendUtils.IsDedicatedGame = result.IsDedicated; + + NetClient.Start(); + + if (FikaBackendUtils.IsHostNatPunch) + { + 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; + + if (string.IsNullOrEmpty(ip)) + { + _logger.LogError("IP was empty when pinging!"); + return false; + } + + 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 void PingEndPoint(string message) + { + NetDataWriter writer = new(); + writer.Put(message); + + if (remoteEndPoint != null) + { + NetClient.SendUnconnectedMessage(writer, remoteEndPoint); + } + if (localEndPoint != null) + { + NetClient.SendUnconnectedMessage(writer, localEndPoint); + } + } + + 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(); + + yield return new WaitForSeconds(1.0f); + } + } + + public void OnConnectionRequest(ConnectionRequest request) + { + // Do nothing + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketError) + { + // Do nothing + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + // Do nothing + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + // Do nothing + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (reader.TryGetString(out string result)) + { + switch (result) + { + 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 + { + _logger.LogError("Could not parse string"); + } + } + + public void OnPeerConnected(NetPeer peer) + { + // Do nothing + } + + 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 f762c8f9..26a4c55b 100644 --- a/Fika.Core/Networking/FikaSerialization.cs +++ b/Fika.Core/Networking/FikaSerialization.cs @@ -11,11 +11,11 @@ namespace Fika.Core.Networking { - public class FikaSerialization - { - // Do not use and do not remove + public class FikaSerialization + { + // Do not use and do not remove - /*public class AddressUtils + /*public class AddressUtils { public static void SerializeGridItemAddressDescriptor(NetDataWriter writer, GridItemAddressDescriptorClass gridItemAddressDescriptor) { @@ -208,7 +208,7 @@ public static GClass1527 DeserializeStackSlotItemAddressDescriptor(NetDataReader } }*/ - /*public class PlayerUtils + /*public class PlayerUtils { public static void SerializeProfile(NetDataWriter writer, Profile profile) { @@ -249,934 +249,934 @@ public static Inventory DeserializeInventory(byte[] inventoryBytes) } }*/ - public struct PlayerInfoPacket(Profile profile) - { - public Profile Profile = profile; - - public static void Serialize(NetDataWriter writer, PlayerInfoPacket packet) - { - byte[] profileBytes = SimpleZlib.CompressToBytes(packet.Profile.ToJson(), 4, null); - writer.PutByteArray(profileBytes); - } - - public static PlayerInfoPacket Deserialize(NetDataReader reader) - { - byte[] profileBytes = reader.GetByteArray(); - PlayerInfoPacket packet = new(SimpleZlib.Decompress(profileBytes, null).ParseJsonTo()); - return packet; - } - } - - public struct LightStatesPacket - { - public int Amount; - public FirearmLightStateStruct[] LightStates; - public static LightStatesPacket Deserialize(NetDataReader reader) - { - LightStatesPacket packet = new() - { - Amount = reader.GetInt() - }; - if (packet.Amount > 0) - { - packet.LightStates = new FirearmLightStateStruct[packet.Amount]; - for (int i = 0; i < packet.Amount; i++) - { - packet.LightStates[i] = new() - { - Id = reader.GetString(), - IsActive = reader.GetBool(), - LightMode = reader.GetInt() - }; - } - } - return packet; - } - - public static void Serialize(NetDataWriter writer, LightStatesPacket packet) - { - writer.Put(packet.Amount); - if (packet.Amount > 0) - { - for (int i = 0; i < packet.Amount; i++) - { - writer.Put(packet.LightStates[i].Id); - writer.Put(packet.LightStates[i].IsActive); - writer.Put(packet.LightStates[i].LightMode); - } - } - } - } - - public struct HeadLightsPacket - { - public int Amount; - public bool IsSilent; - public FirearmLightStateStruct[] LightStates; - public static HeadLightsPacket Deserialize(NetDataReader reader) - { - HeadLightsPacket packet = new() - { - Amount = reader.GetInt(), - IsSilent = reader.GetBool() - }; - if (packet.Amount > 0) - { - packet.LightStates = new FirearmLightStateStruct[packet.Amount]; - for (int i = 0; i < packet.Amount; i++) - { - packet.LightStates[i] = new() - { - Id = reader.GetString(), - IsActive = reader.GetBool(), - LightMode = reader.GetInt() - }; - } - } - return packet; - } - - public static void Serialize(NetDataWriter writer, HeadLightsPacket packet) - { - writer.Put(packet.Amount); - writer.Put(packet.IsSilent); - if (packet.Amount > 0) - { - for (int i = 0; i < packet.Amount; i++) - { - writer.Put(packet.LightStates[i].Id); - writer.Put(packet.LightStates[i].IsActive); - writer.Put(packet.LightStates[i].LightMode); - } - } - } - } - - public struct ScopeStatesPacket - { - public int Amount; - public FirearmScopeStateStruct[] FirearmScopeStateStruct; - public static ScopeStatesPacket Deserialize(NetDataReader reader) - { - ScopeStatesPacket packet = new(); - packet.Amount = reader.GetInt(); - if (packet.Amount > 0) - { - packet.FirearmScopeStateStruct = new FirearmScopeStateStruct[packet.Amount]; - for (int i = 0; i < packet.Amount; i++) - { - packet.FirearmScopeStateStruct[i] = new() - { - Id = reader.GetString(), - ScopeMode = reader.GetInt(), - ScopeIndexInsideSight = reader.GetInt(), - ScopeCalibrationIndex = reader.GetInt() - }; - } - } - return packet; - } - - public static void Serialize(NetDataWriter writer, ScopeStatesPacket packet) - { - writer.Put(packet.Amount); - if (packet.Amount > 0) - { - for (int i = 0; i < packet.Amount; i++) - { - writer.Put(packet.FirearmScopeStateStruct[i].Id); - writer.Put(packet.FirearmScopeStateStruct[i].ScopeMode); - writer.Put(packet.FirearmScopeStateStruct[i].ScopeIndexInsideSight); - writer.Put(packet.FirearmScopeStateStruct[i].ScopeCalibrationIndex); - } - } - } - } - - public struct ReloadMagPacket - { - public bool Reload; - public string MagId; - public byte[] LocationDescription; - - public static ReloadMagPacket Deserialize(NetDataReader reader) - { - ReloadMagPacket packet = new() - { - Reload = reader.GetBool() - }; - if (packet.Reload) - { - packet.MagId = reader.GetString(); - packet.LocationDescription = reader.GetByteArray(); - } - return packet; - } - public static void Serialize(NetDataWriter writer, ReloadMagPacket packet) - { - writer.Put(packet.Reload); - if (packet.Reload) - { - writer.Put(packet.MagId); - writer.PutByteArray(packet.LocationDescription); - } - } - } - - public struct QuickReloadMagPacket - { - public bool Reload; - public string MagId; - - public static QuickReloadMagPacket Deserialize(NetDataReader reader) - { - QuickReloadMagPacket packet = new() - { - Reload = reader.GetBool() - }; - if (packet.Reload) - { - packet.MagId = reader.GetString(); - } - return packet; - } - - public static void Serialize(NetDataWriter writer, QuickReloadMagPacket packet) - { - writer.Put(packet.Reload); - if (packet.Reload) - { - writer.Put(packet.MagId); - } - } - } - - public struct ReloadWithAmmoPacket - { - public bool Reload; - public EReloadWithAmmoStatus Status; - public int AmmoLoadedToMag; - public string[] AmmoIds; - - public enum EReloadWithAmmoStatus - { - None = 0, - StartReload, - EndReload, - AbortReload - } - - public static ReloadWithAmmoPacket Deserialize(NetDataReader reader) - { - ReloadWithAmmoPacket packet = new() - { - Reload = reader.GetBool() - }; - if (packet.Reload) - { - packet.Status = (EReloadWithAmmoStatus)reader.GetInt(); - if (packet.Status == EReloadWithAmmoStatus.StartReload) - { - packet.AmmoIds = reader.GetStringArray(); - } - if (packet.Status is EReloadWithAmmoStatus.EndReload or EReloadWithAmmoStatus.AbortReload) - { - packet.AmmoLoadedToMag = reader.GetInt(); - } - } - return packet; - } - - public static void Serialize(NetDataWriter writer, ReloadWithAmmoPacket packet) - { - writer.Put(packet.Reload); - if (packet.Reload) - { - writer.Put((int)packet.Status); - if (packet.Status == EReloadWithAmmoStatus.StartReload) - { - writer.PutArray(packet.AmmoIds); - } - if (packet.AmmoLoadedToMag > 0) - { - writer.Put(packet.AmmoLoadedToMag); - } - } - } - } - - public struct CylinderMagPacket - { - public bool Changed; - public int CamoraIndex; - public bool HammerClosed; - - public static CylinderMagPacket Deserialize(NetDataReader reader) - { - CylinderMagPacket packet = new(); - packet.Changed = reader.GetBool(); - if (packet.Changed) - { - packet.CamoraIndex = reader.GetInt(); - packet.HammerClosed = reader.GetBool(); - } - return packet; - } - - public static void Serialize(NetDataWriter writer, CylinderMagPacket packet) - { - writer.Put(packet.Changed); - if (packet.Changed) - { - writer.Put(packet.CamoraIndex); - writer.Put(packet.HammerClosed); - } - } - } - - public struct ReloadLauncherPacket - { - public bool Reload; - public string[] AmmoIds; - - public static ReloadLauncherPacket Deserialize(NetDataReader reader) - { - ReloadLauncherPacket packet = new(); - packet.Reload = reader.GetBool(); - if (packet.Reload) - { - packet.AmmoIds = reader.GetStringArray(); - } - return packet; - } - - public static void Serialize(NetDataWriter writer, ReloadLauncherPacket packet) - { - writer.Put(packet.Reload); - if (packet.Reload) - { - writer.PutArray(packet.AmmoIds); - } - } - } - - public struct ReloadBarrelsPacket - { - public bool Reload; - public string[] AmmoIds; - public byte[] LocationDescription; - - public static ReloadBarrelsPacket Deserialize(NetDataReader reader) - { - ReloadBarrelsPacket packet = new() - { - Reload = reader.GetBool() - }; - if (packet.Reload) - { - packet.AmmoIds = reader.GetStringArray(); - packet.LocationDescription = reader.GetByteArray(); - } - return packet; - } - public static void Serialize(NetDataWriter writer, ReloadBarrelsPacket packet) - { - writer.Put(packet.Reload); - if (packet.Reload) - { - writer.PutArray(packet.AmmoIds); - writer.PutByteArray(packet.LocationDescription); - } - } - } - - public struct GrenadePacket() - { - public GrenadePacketType PacketType; - public enum GrenadePacketType - { - None, - ExamineWeapon, - HighThrow, - LowThrow, - PullRingForHighThrow, - PullRingForLowThrow - }; - public bool HasGrenade = false; - public Quaternion GrenadeRotation; - public Vector3 GrenadePosition; - public Vector3 ThrowForce; - public bool LowThrow; - - public static GrenadePacket Deserialize(NetDataReader reader) - { - GrenadePacket packet = new() - { - PacketType = (GrenadePacketType)reader.GetInt(), - HasGrenade = reader.GetBool() - }; - if (packet.HasGrenade) - { - packet.GrenadeRotation = reader.GetQuaternion(); - packet.GrenadePosition = reader.GetVector3(); - packet.ThrowForce = reader.GetVector3(); - packet.LowThrow = reader.GetBool(); - } - return packet; - } - public static void Serialize(NetDataWriter writer, GrenadePacket packet) - { - writer.Put((int)packet.PacketType); - writer.Put(packet.HasGrenade); - if (packet.HasGrenade) - { - writer.Put(packet.GrenadeRotation); - writer.Put(packet.GrenadePosition); - writer.Put(packet.ThrowForce); - writer.Put(packet.LowThrow); - } - } - } - - public struct ItemControllerExecutePacket - { - public uint CallbackId; - public byte[] OperationBytes; - public static ItemControllerExecutePacket Deserialize(NetDataReader reader) - { - ItemControllerExecutePacket packet = new() - { - CallbackId = reader.GetUInt(), - OperationBytes = reader.GetByteArray(), - }; - return packet; - } - public static void Serialize(NetDataWriter writer, ItemControllerExecutePacket packet) - { - writer.Put(packet.CallbackId); - writer.PutByteArray(packet.OperationBytes); - } - } - - public struct WorldInteractionPacket - { - public string InteractiveId; - public EInteractionType InteractionType; - public EInteractionStage InteractionStage; - public string ItemId; - - public static WorldInteractionPacket Deserialize(NetDataReader reader) - { - WorldInteractionPacket packet = new() - { - InteractiveId = reader.GetString(), - InteractionType = (EInteractionType)reader.GetByte(), - InteractionStage = (EInteractionStage)reader.GetByte(), - }; - if (packet.InteractionType == EInteractionType.Unlock) - packet.ItemId = reader.GetString(); - - return packet; - } - - public static void Serialize(NetDataWriter writer, WorldInteractionPacket packet) - { - writer.Put(packet.InteractiveId); - writer.Put((byte)packet.InteractionType); - writer.Put((byte)packet.InteractionStage); - if (packet.InteractionType == EInteractionType.Unlock) - writer.Put(packet.ItemId); - } - } - - public struct ContainerInteractionPacket - { - public string InteractiveId; - public EInteractionType InteractionType; - - public static ContainerInteractionPacket Deserialize(NetDataReader reader) - { - ContainerInteractionPacket packet = new() - { - InteractiveId = reader.GetString(), - InteractionType = (EInteractionType)reader.GetInt() - }; - return packet; - } - public static void Serialize(NetDataWriter writer, ContainerInteractionPacket packet) - { - writer.Put(packet.InteractiveId); - writer.Put((int)packet.InteractionType); - } - } - - public struct ProceedPacket() - { - public EProceedType ProceedType; - public string ItemId = ""; - public string ItemTemplateId = ""; - public float Amount = 0f; - public int AnimationVariant = 0; - public bool Scheduled = false; - public EBodyPart BodyPart = EBodyPart.Common; - - public static ProceedPacket Deserialize(NetDataReader reader) - { - return new ProceedPacket - { - ProceedType = (EProceedType)reader.GetInt(), - ItemId = reader.GetString(), - ItemTemplateId = reader.GetString(), - Amount = reader.GetFloat(), - AnimationVariant = reader.GetInt(), - Scheduled = reader.GetBool(), - BodyPart = (EBodyPart)reader.GetInt() - }; - } - public static void Serialize(NetDataWriter writer, ProceedPacket packet) - { - writer.Put((int)packet.ProceedType); - writer.Put(packet.ItemId); - writer.Put(packet.ItemTemplateId); - writer.Put(packet.Amount); - writer.Put(packet.AnimationVariant); - writer.Put(packet.Scheduled); - writer.Put((int)packet.BodyPart); - } - - } - - public struct DropPacket - { - public bool FastDrop; - public bool HasItemId; - public string ItemId; - - public static DropPacket Deserialize(NetDataReader reader) - { - DropPacket packet = new() - { - FastDrop = reader.GetBool(), - HasItemId = reader.GetBool() - }; - if (packet.HasItemId) - packet.ItemId = reader.GetString(); - - return packet; - } - public static void Serialize(NetDataWriter writer, DropPacket packet) - { - writer.Put(packet.FastDrop); - writer.Put(packet.HasItemId); - if (packet.HasItemId) - writer.Put(packet.ItemId); - } - } - - public struct StationaryPacket - { - public EStationaryCommand Command; - public string Id; - public enum EStationaryCommand : byte - { - Occupy, - Leave, - Denied - } - - public static StationaryPacket Deserialize(NetDataReader reader) - { - StationaryPacket packet = new() - { - Command = (EStationaryCommand)reader.GetByte() - }; - - if (packet.Command == EStationaryCommand.Occupy) - packet.Id = reader.GetString(); - - return packet; - } - public static void Serialize(NetDataWriter writer, StationaryPacket packet) - { - writer.Put((byte)packet.Command); - if (packet.Command == EStationaryCommand.Occupy && !string.IsNullOrEmpty(packet.Id)) - writer.Put(packet.Id); - } - } - - public struct KnifePacket() - { - public bool Examine = false; - public bool Kick = false; - public bool AltKick = false; - public bool BreakCombo = false; - public static KnifePacket Deserialize(NetDataReader reader) - { - return new KnifePacket() - { - Examine = reader.GetBool(), - Kick = reader.GetBool(), - AltKick = reader.GetBool(), - BreakCombo = reader.GetBool() - }; - } - public static void Serialize(NetDataWriter writer, KnifePacket packet) - { - writer.Put(packet.Examine); - writer.Put(packet.Kick); - writer.Put(packet.AltKick); - writer.Put(packet.BreakCombo); - } - } - - public struct ShotInfoPacket() - { - - public EShotType ShotType = EShotType.Unknown; - public int AmmoAfterShot = 0; - public Vector3 ShotPosition = Vector3.zero; - public Vector3 ShotDirection = Vector3.zero; - public Vector3 FireportPosition = Vector3.zero; - public int ChamberIndex = 0; - public float Overheat = 0f; - public bool UnderbarrelShot = false; - public string AmmoTemplate; - public float LastShotOverheat; - public float LastShotTime; - public bool SlideOnOverheatReached; - - public static ShotInfoPacket Deserialize(NetDataReader reader) - { - ShotInfoPacket packet = new() - { - ShotType = (EShotType)reader.GetInt(), - AmmoAfterShot = reader.GetInt(), - ShotPosition = reader.GetVector3(), - ShotDirection = reader.GetVector3(), - FireportPosition = reader.GetVector3(), - ChamberIndex = reader.GetInt(), - Overheat = reader.GetFloat(), - UnderbarrelShot = reader.GetBool(), - AmmoTemplate = reader.GetString(), - LastShotOverheat = reader.GetFloat(), - LastShotTime = reader.GetFloat(), - SlideOnOverheatReached = reader.GetBool() - }; - - return packet; - } - public static void Serialize(NetDataWriter writer, ShotInfoPacket packet) - { - writer.Put((int)packet.ShotType); - writer.Put(packet.AmmoAfterShot); - writer.Put(packet.ShotPosition); - writer.Put(packet.ShotDirection); - writer.Put(packet.FireportPosition); - writer.Put(packet.ChamberIndex); - writer.Put(packet.Overheat); - writer.Put(packet.UnderbarrelShot); - writer.Put(packet.AmmoTemplate); - writer.Put(packet.LastShotOverheat); - writer.Put(packet.LastShotTime); - writer.Put(packet.SlideOnOverheatReached); - } - } - - public struct SearchPacket - { - public bool IsStop; - public string ItemId; - public int OperationId; - - public static SearchPacket Deserialize(NetDataReader reader) - { - SearchPacket packet = new() - { - IsStop = reader.GetBool(), - ItemId = reader.GetString(), - OperationId = reader.GetInt() - }; - return packet; - } - - public static void Serialize(NetDataWriter writer, SearchPacket packet) - { - writer.Put(packet.IsStop); - writer.Put(packet.ItemId); - writer.Put(packet.OperationId); - } - } - - public struct WeatherClassPacket - { - public float AtmospherePressure; - public float Cloudness; - public float GlobalFogDensity; - public float GlobalFogHeight; - public float LyingWater; - public Vector2 MainWindDirection; - public Vector2 MainWindPosition; - public float Rain; - public float RainRandomness; - public float ScaterringFogDensity; - public float ScaterringFogHeight; - public float Temperature; - public long Time; - public Vector2 TopWindDirection; - public Vector2 TopWindPosition; - public float Turbulence; - public float Wind; - public int WindDirection; - - public static WeatherClassPacket Deserialize(NetDataReader reader) - { - return new WeatherClassPacket() - { - AtmospherePressure = reader.GetFloat(), - Cloudness = reader.GetFloat(), - GlobalFogDensity = reader.GetFloat(), - GlobalFogHeight = reader.GetFloat(), - LyingWater = reader.GetFloat(), - MainWindDirection = reader.GetVector2(), - MainWindPosition = reader.GetVector2(), - Rain = reader.GetFloat(), - RainRandomness = reader.GetFloat(), - ScaterringFogDensity = reader.GetFloat(), - ScaterringFogHeight = reader.GetFloat(), - Temperature = reader.GetFloat(), - Time = reader.GetLong(), - TopWindDirection = reader.GetVector2(), - TopWindPosition = reader.GetVector2(), - Turbulence = reader.GetFloat(), - Wind = reader.GetFloat(), - WindDirection = reader.GetInt() - }; - } - - public static void Serialize(NetDataWriter writer, WeatherClass weatherClass) - { - writer.Put(weatherClass.AtmospherePressure); - writer.Put(weatherClass.Cloudness); - writer.Put(weatherClass.GlobalFogDensity); - writer.Put(weatherClass.GlobalFogHeight); - writer.Put(weatherClass.LyingWater); - writer.Put(weatherClass.MainWindDirection); - writer.Put(weatherClass.MainWindPosition); - writer.Put(weatherClass.Rain); - writer.Put(weatherClass.RainRandomness); - writer.Put(weatherClass.ScaterringFogDensity); - writer.Put(weatherClass.ScaterringFogHeight); - writer.Put(weatherClass.Temperature); - writer.Put(weatherClass.Time); - writer.Put(weatherClass.TopWindDirection); - writer.Put(weatherClass.TopWindPosition); - writer.Put(weatherClass.Turbulence); - writer.Put(weatherClass.Wind); - writer.Put(weatherClass.WindDirection); - } - } - - public struct FlareShotPacket - { - public Vector3 ShotPosition; - public Vector3 ShotForward; - public string AmmoTemplateId; - - public static FlareShotPacket Deserialize(NetDataReader reader) - { - return new FlareShotPacket() - { - ShotPosition = reader.GetVector3(), - ShotForward = reader.GetVector3(), - AmmoTemplateId = reader.GetString() - }; - } - - public static void Serialize(NetDataWriter writer, FlareShotPacket packet) - { - writer.Put(packet.ShotPosition); - writer.Put(packet.ShotForward); - writer.Put(packet.AmmoTemplateId); - } - } - - public struct VaultPacket - { - public EVaultingStrategy VaultingStrategy; - public Vector3 VaultingPoint; - public float VaultingHeight; - public float VaultingLength; - public float VaultingSpeed; - public float BehindObstacleHeight; - public float AbsoluteForwardVelocity; - - public static VaultPacket Deserialize(NetDataReader reader) - { - return new VaultPacket() - { - VaultingStrategy = (EVaultingStrategy)reader.GetInt(), - VaultingPoint = reader.GetVector3(), - VaultingHeight = reader.GetFloat(), - VaultingLength = reader.GetFloat(), - VaultingSpeed = reader.GetFloat(), - BehindObstacleHeight = reader.GetFloat(), - AbsoluteForwardVelocity = reader.GetFloat() - }; - } - - public static void Serialize(NetDataWriter writer, VaultPacket packet) - { - writer.Put((int)packet.VaultingStrategy); - writer.Put(packet.VaultingPoint); - writer.Put(packet.VaultingHeight); - writer.Put(packet.VaultingLength); - writer.Put(packet.VaultingSpeed); - writer.Put(packet.BehindObstacleHeight); - writer.Put(packet.AbsoluteForwardVelocity); - } - } - - public class BTRDataPacketUtils - { - public static BTRDataPacket Deserialize(NetDataReader reader) - { - return new() - { - position = reader.GetVector3(), - BtrBotId = reader.GetInt(), - MoveSpeed = reader.GetFloat(), - moveDirection = reader.GetByte(), - timeToEndPause = reader.GetFloat(), - currentSpeed = reader.GetFloat(), - RightSlot1State = reader.GetByte(), - RightSlot0State = reader.GetByte(), - RightSideState = reader.GetByte(), - LeftSlot1State = reader.GetByte(), - LeftSlot0State = reader.GetByte(), - LeftSideState = reader.GetByte(), - RouteState = reader.GetByte(), - State = reader.GetByte(), - gunsBlockRotation = reader.GetFloat(), - turretRotation = reader.GetFloat(), - rotation = reader.GetQuaternion() - }; - } - - public static void Serialize(NetDataWriter writer, BTRDataPacket packet) - { - writer.Put(packet.position); - writer.Put(packet.BtrBotId); - writer.Put(packet.MoveSpeed); - writer.Put(packet.moveDirection); - writer.Put(packet.timeToEndPause); - writer.Put(packet.currentSpeed); - writer.Put(packet.RightSlot1State); - writer.Put(packet.RightSlot0State); - writer.Put(packet.RightSideState); - writer.Put(packet.LeftSlot1State); - writer.Put(packet.LeftSlot0State); - writer.Put(packet.LeftSideState); - writer.Put(packet.RouteState); - writer.Put(packet.State); - writer.Put(packet.gunsBlockRotation); - writer.Put(packet.turretRotation); - writer.Put(packet.rotation); - } - } - - public struct RagdollPacket - { - public EBodyPartColliderType BodyPartColliderType; - public Vector3 Direction; - public Vector3 Point; - public float Force; - public Vector3 OverallVelocity; - - public static RagdollPacket Deserialize(NetDataReader reader) - { - return new RagdollPacket() - { - BodyPartColliderType = (EBodyPartColliderType)reader.GetInt(), - Direction = reader.GetVector3(), - Point = reader.GetVector3(), - Force = reader.GetFloat(), - OverallVelocity = reader.GetVector3() - }; - } - - public static void Serialize(NetDataWriter writer, RagdollPacket packet) - { - writer.Put((int)packet.BodyPartColliderType); - writer.Put(packet.Direction); - writer.Put(packet.Point); - writer.Put(packet.Force); - writer.Put(packet.OverallVelocity); - } - } - - public struct DeathInfoPacket - { - public string AccountId; - public string ProfileId; - public string Nickname; - public string KillerAccountId; - public string KillerProfileId; - public string KillerName; - public EPlayerSide Side; - public int Level; - public DateTime Time; - public string Status; - public string WeaponName; - public string GroupId; - - public static DeathInfoPacket Deserialize(NetDataReader reader) - { - return new() - { - AccountId = reader.GetString(), - ProfileId = reader.GetString(), - Nickname = reader.GetString(), - KillerAccountId = reader.GetString(), - KillerProfileId = reader.GetString(), - KillerName = reader.GetString(), - Side = (EPlayerSide)reader.GetInt(), - Level = reader.GetInt(), - Time = reader.GetDateTime(), - Status = reader.GetString(), - WeaponName = reader.GetString(), - GroupId = reader.GetString() - }; - } - - public static void Serialize(NetDataWriter writer, DeathInfoPacket packet) - { - writer.Put(packet.AccountId); - writer.Put(packet.ProfileId); - writer.Put(packet.Nickname); - writer.Put(packet.KillerAccountId); - writer.Put(packet.KillerProfileId); - writer.Put(packet.KillerName); - writer.Put((int)packet.Side); - writer.Put(packet.Level); - writer.Put(packet.Time); - writer.Put(packet.Status); - writer.Put(packet.WeaponName); - writer.Put(packet.GroupId); - } - } - - public enum EProceedType - { - EmptyHands, - FoodClass, - GrenadeClass, - MedsClass, - QuickGrenadeThrow, - QuickKnifeKick, - QuickUse, - Weapon, - Knife, - TryProceed - } - } + public struct PlayerInfoPacket(Profile profile) + { + public Profile Profile = profile; + + public static void Serialize(NetDataWriter writer, PlayerInfoPacket packet) + { + byte[] profileBytes = SimpleZlib.CompressToBytes(packet.Profile.ToJson(), 4, null); + writer.PutByteArray(profileBytes); + } + + public static PlayerInfoPacket Deserialize(NetDataReader reader) + { + byte[] profileBytes = reader.GetByteArray(); + PlayerInfoPacket packet = new(SimpleZlib.Decompress(profileBytes, null).ParseJsonTo()); + return packet; + } + } + + public struct LightStatesPacket + { + public int Amount; + public FirearmLightStateStruct[] LightStates; + public static LightStatesPacket Deserialize(NetDataReader reader) + { + LightStatesPacket packet = new() + { + Amount = reader.GetInt() + }; + if (packet.Amount > 0) + { + packet.LightStates = new FirearmLightStateStruct[packet.Amount]; + for (int i = 0; i < packet.Amount; i++) + { + packet.LightStates[i] = new() + { + Id = reader.GetString(), + IsActive = reader.GetBool(), + LightMode = reader.GetInt() + }; + } + } + return packet; + } + + public static void Serialize(NetDataWriter writer, LightStatesPacket packet) + { + writer.Put(packet.Amount); + if (packet.Amount > 0) + { + for (int i = 0; i < packet.Amount; i++) + { + writer.Put(packet.LightStates[i].Id); + writer.Put(packet.LightStates[i].IsActive); + writer.Put(packet.LightStates[i].LightMode); + } + } + } + } + + public struct HeadLightsPacket + { + public int Amount; + public bool IsSilent; + public FirearmLightStateStruct[] LightStates; + public static HeadLightsPacket Deserialize(NetDataReader reader) + { + HeadLightsPacket packet = new() + { + Amount = reader.GetInt(), + IsSilent = reader.GetBool() + }; + if (packet.Amount > 0) + { + packet.LightStates = new FirearmLightStateStruct[packet.Amount]; + for (int i = 0; i < packet.Amount; i++) + { + packet.LightStates[i] = new() + { + Id = reader.GetString(), + IsActive = reader.GetBool(), + LightMode = reader.GetInt() + }; + } + } + return packet; + } + + public static void Serialize(NetDataWriter writer, HeadLightsPacket packet) + { + writer.Put(packet.Amount); + writer.Put(packet.IsSilent); + if (packet.Amount > 0) + { + for (int i = 0; i < packet.Amount; i++) + { + writer.Put(packet.LightStates[i].Id); + writer.Put(packet.LightStates[i].IsActive); + writer.Put(packet.LightStates[i].LightMode); + } + } + } + } + + public struct ScopeStatesPacket + { + public int Amount; + public FirearmScopeStateStruct[] FirearmScopeStateStruct; + public static ScopeStatesPacket Deserialize(NetDataReader reader) + { + ScopeStatesPacket packet = new(); + packet.Amount = reader.GetInt(); + if (packet.Amount > 0) + { + packet.FirearmScopeStateStruct = new FirearmScopeStateStruct[packet.Amount]; + for (int i = 0; i < packet.Amount; i++) + { + packet.FirearmScopeStateStruct[i] = new() + { + Id = reader.GetString(), + ScopeMode = reader.GetInt(), + ScopeIndexInsideSight = reader.GetInt(), + ScopeCalibrationIndex = reader.GetInt() + }; + } + } + return packet; + } + + public static void Serialize(NetDataWriter writer, ScopeStatesPacket packet) + { + writer.Put(packet.Amount); + if (packet.Amount > 0) + { + for (int i = 0; i < packet.Amount; i++) + { + writer.Put(packet.FirearmScopeStateStruct[i].Id); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeMode); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeIndexInsideSight); + writer.Put(packet.FirearmScopeStateStruct[i].ScopeCalibrationIndex); + } + } + } + } + + public struct ReloadMagPacket + { + public bool Reload; + public string MagId; + public byte[] LocationDescription; + + public static ReloadMagPacket Deserialize(NetDataReader reader) + { + ReloadMagPacket packet = new() + { + Reload = reader.GetBool() + }; + if (packet.Reload) + { + packet.MagId = reader.GetString(); + packet.LocationDescription = reader.GetByteArray(); + } + return packet; + } + public static void Serialize(NetDataWriter writer, ReloadMagPacket packet) + { + writer.Put(packet.Reload); + if (packet.Reload) + { + writer.Put(packet.MagId); + writer.PutByteArray(packet.LocationDescription); + } + } + } + + public struct QuickReloadMagPacket + { + public bool Reload; + public string MagId; + + public static QuickReloadMagPacket Deserialize(NetDataReader reader) + { + QuickReloadMagPacket packet = new() + { + Reload = reader.GetBool() + }; + if (packet.Reload) + { + packet.MagId = reader.GetString(); + } + return packet; + } + + public static void Serialize(NetDataWriter writer, QuickReloadMagPacket packet) + { + writer.Put(packet.Reload); + if (packet.Reload) + { + writer.Put(packet.MagId); + } + } + } + + public struct ReloadWithAmmoPacket + { + public bool Reload; + public EReloadWithAmmoStatus Status; + public int AmmoLoadedToMag; + public string[] AmmoIds; + + public enum EReloadWithAmmoStatus + { + None = 0, + StartReload, + EndReload, + AbortReload + } + + public static ReloadWithAmmoPacket Deserialize(NetDataReader reader) + { + ReloadWithAmmoPacket packet = new() + { + Reload = reader.GetBool() + }; + if (packet.Reload) + { + packet.Status = (EReloadWithAmmoStatus)reader.GetInt(); + if (packet.Status == EReloadWithAmmoStatus.StartReload) + { + packet.AmmoIds = reader.GetStringArray(); + } + if (packet.Status is EReloadWithAmmoStatus.EndReload or EReloadWithAmmoStatus.AbortReload) + { + packet.AmmoLoadedToMag = reader.GetInt(); + } + } + return packet; + } + + public static void Serialize(NetDataWriter writer, ReloadWithAmmoPacket packet) + { + writer.Put(packet.Reload); + if (packet.Reload) + { + writer.Put((int)packet.Status); + if (packet.Status == EReloadWithAmmoStatus.StartReload) + { + writer.PutArray(packet.AmmoIds); + } + if (packet.AmmoLoadedToMag > 0) + { + writer.Put(packet.AmmoLoadedToMag); + } + } + } + } + + public struct CylinderMagPacket + { + public bool Changed; + public int CamoraIndex; + public bool HammerClosed; + + public static CylinderMagPacket Deserialize(NetDataReader reader) + { + CylinderMagPacket packet = new(); + packet.Changed = reader.GetBool(); + if (packet.Changed) + { + packet.CamoraIndex = reader.GetInt(); + packet.HammerClosed = reader.GetBool(); + } + return packet; + } + + public static void Serialize(NetDataWriter writer, CylinderMagPacket packet) + { + writer.Put(packet.Changed); + if (packet.Changed) + { + writer.Put(packet.CamoraIndex); + writer.Put(packet.HammerClosed); + } + } + } + + public struct ReloadLauncherPacket + { + public bool Reload; + public string[] AmmoIds; + + public static ReloadLauncherPacket Deserialize(NetDataReader reader) + { + ReloadLauncherPacket packet = new(); + packet.Reload = reader.GetBool(); + if (packet.Reload) + { + packet.AmmoIds = reader.GetStringArray(); + } + return packet; + } + + public static void Serialize(NetDataWriter writer, ReloadLauncherPacket packet) + { + writer.Put(packet.Reload); + if (packet.Reload) + { + writer.PutArray(packet.AmmoIds); + } + } + } + + public struct ReloadBarrelsPacket + { + public bool Reload; + public string[] AmmoIds; + public byte[] LocationDescription; + + public static ReloadBarrelsPacket Deserialize(NetDataReader reader) + { + ReloadBarrelsPacket packet = new() + { + Reload = reader.GetBool() + }; + if (packet.Reload) + { + packet.AmmoIds = reader.GetStringArray(); + packet.LocationDescription = reader.GetByteArray(); + } + return packet; + } + public static void Serialize(NetDataWriter writer, ReloadBarrelsPacket packet) + { + writer.Put(packet.Reload); + if (packet.Reload) + { + writer.PutArray(packet.AmmoIds); + writer.PutByteArray(packet.LocationDescription); + } + } + } + + public struct GrenadePacket() + { + public GrenadePacketType PacketType; + public enum GrenadePacketType + { + None, + ExamineWeapon, + HighThrow, + LowThrow, + PullRingForHighThrow, + PullRingForLowThrow + }; + public bool HasGrenade = false; + public Quaternion GrenadeRotation; + public Vector3 GrenadePosition; + public Vector3 ThrowForce; + public bool LowThrow; + + public static GrenadePacket Deserialize(NetDataReader reader) + { + GrenadePacket packet = new() + { + PacketType = (GrenadePacketType)reader.GetInt(), + HasGrenade = reader.GetBool() + }; + if (packet.HasGrenade) + { + packet.GrenadeRotation = reader.GetQuaternion(); + packet.GrenadePosition = reader.GetVector3(); + packet.ThrowForce = reader.GetVector3(); + packet.LowThrow = reader.GetBool(); + } + return packet; + } + public static void Serialize(NetDataWriter writer, GrenadePacket packet) + { + writer.Put((int)packet.PacketType); + writer.Put(packet.HasGrenade); + if (packet.HasGrenade) + { + writer.Put(packet.GrenadeRotation); + writer.Put(packet.GrenadePosition); + writer.Put(packet.ThrowForce); + writer.Put(packet.LowThrow); + } + } + } + + public struct ItemControllerExecutePacket + { + public uint CallbackId; + public byte[] OperationBytes; + public static ItemControllerExecutePacket Deserialize(NetDataReader reader) + { + ItemControllerExecutePacket packet = new() + { + CallbackId = reader.GetUInt(), + OperationBytes = reader.GetByteArray(), + }; + return packet; + } + public static void Serialize(NetDataWriter writer, ItemControllerExecutePacket packet) + { + writer.Put(packet.CallbackId); + writer.PutByteArray(packet.OperationBytes); + } + } + + public struct WorldInteractionPacket + { + public string InteractiveId; + public EInteractionType InteractionType; + public EInteractionStage InteractionStage; + public string ItemId; + + public static WorldInteractionPacket Deserialize(NetDataReader reader) + { + WorldInteractionPacket packet = new() + { + InteractiveId = reader.GetString(), + InteractionType = (EInteractionType)reader.GetByte(), + InteractionStage = (EInteractionStage)reader.GetByte(), + }; + if (packet.InteractionType == EInteractionType.Unlock) + packet.ItemId = reader.GetString(); + + return packet; + } + + public static void Serialize(NetDataWriter writer, WorldInteractionPacket packet) + { + writer.Put(packet.InteractiveId); + writer.Put((byte)packet.InteractionType); + writer.Put((byte)packet.InteractionStage); + if (packet.InteractionType == EInteractionType.Unlock) + writer.Put(packet.ItemId); + } + } + + public struct ContainerInteractionPacket + { + public string InteractiveId; + public EInteractionType InteractionType; + + public static ContainerInteractionPacket Deserialize(NetDataReader reader) + { + ContainerInteractionPacket packet = new() + { + InteractiveId = reader.GetString(), + InteractionType = (EInteractionType)reader.GetInt() + }; + return packet; + } + public static void Serialize(NetDataWriter writer, ContainerInteractionPacket packet) + { + writer.Put(packet.InteractiveId); + writer.Put((int)packet.InteractionType); + } + } + + public struct ProceedPacket() + { + public EProceedType ProceedType; + public string ItemId = ""; + public string ItemTemplateId = ""; + public float Amount = 0f; + public int AnimationVariant = 0; + public bool Scheduled = false; + public EBodyPart BodyPart = EBodyPart.Common; + + public static ProceedPacket Deserialize(NetDataReader reader) + { + return new ProceedPacket + { + ProceedType = (EProceedType)reader.GetInt(), + ItemId = reader.GetString(), + ItemTemplateId = reader.GetString(), + Amount = reader.GetFloat(), + AnimationVariant = reader.GetInt(), + Scheduled = reader.GetBool(), + BodyPart = (EBodyPart)reader.GetInt() + }; + } + public static void Serialize(NetDataWriter writer, ProceedPacket packet) + { + writer.Put((int)packet.ProceedType); + writer.Put(packet.ItemId); + writer.Put(packet.ItemTemplateId); + writer.Put(packet.Amount); + writer.Put(packet.AnimationVariant); + writer.Put(packet.Scheduled); + writer.Put((int)packet.BodyPart); + } + + } + + public struct DropPacket + { + public bool FastDrop; + public bool HasItemId; + public string ItemId; + + public static DropPacket Deserialize(NetDataReader reader) + { + DropPacket packet = new() + { + FastDrop = reader.GetBool(), + HasItemId = reader.GetBool() + }; + if (packet.HasItemId) + packet.ItemId = reader.GetString(); + + return packet; + } + public static void Serialize(NetDataWriter writer, DropPacket packet) + { + writer.Put(packet.FastDrop); + writer.Put(packet.HasItemId); + if (packet.HasItemId) + writer.Put(packet.ItemId); + } + } + + public struct StationaryPacket + { + public EStationaryCommand Command; + public string Id; + public enum EStationaryCommand : byte + { + Occupy, + Leave, + Denied + } + + public static StationaryPacket Deserialize(NetDataReader reader) + { + StationaryPacket packet = new() + { + Command = (EStationaryCommand)reader.GetByte() + }; + + if (packet.Command == EStationaryCommand.Occupy) + packet.Id = reader.GetString(); + + return packet; + } + public static void Serialize(NetDataWriter writer, StationaryPacket packet) + { + writer.Put((byte)packet.Command); + if (packet.Command == EStationaryCommand.Occupy && !string.IsNullOrEmpty(packet.Id)) + writer.Put(packet.Id); + } + } + + public struct KnifePacket() + { + public bool Examine = false; + public bool Kick = false; + public bool AltKick = false; + public bool BreakCombo = false; + public static KnifePacket Deserialize(NetDataReader reader) + { + return new KnifePacket() + { + Examine = reader.GetBool(), + Kick = reader.GetBool(), + AltKick = reader.GetBool(), + BreakCombo = reader.GetBool() + }; + } + public static void Serialize(NetDataWriter writer, KnifePacket packet) + { + writer.Put(packet.Examine); + writer.Put(packet.Kick); + writer.Put(packet.AltKick); + writer.Put(packet.BreakCombo); + } + } + + public struct ShotInfoPacket() + { + + public EShotType ShotType = EShotType.Unknown; + public int AmmoAfterShot = 0; + public Vector3 ShotPosition = Vector3.zero; + public Vector3 ShotDirection = Vector3.zero; + public Vector3 FireportPosition = Vector3.zero; + public int ChamberIndex = 0; + public float Overheat = 0f; + public bool UnderbarrelShot = false; + public string AmmoTemplate; + public float LastShotOverheat; + public float LastShotTime; + public bool SlideOnOverheatReached; + + public static ShotInfoPacket Deserialize(NetDataReader reader) + { + ShotInfoPacket packet = new() + { + ShotType = (EShotType)reader.GetInt(), + AmmoAfterShot = reader.GetInt(), + ShotPosition = reader.GetVector3(), + ShotDirection = reader.GetVector3(), + FireportPosition = reader.GetVector3(), + ChamberIndex = reader.GetInt(), + Overheat = reader.GetFloat(), + UnderbarrelShot = reader.GetBool(), + AmmoTemplate = reader.GetString(), + LastShotOverheat = reader.GetFloat(), + LastShotTime = reader.GetFloat(), + SlideOnOverheatReached = reader.GetBool() + }; + + return packet; + } + public static void Serialize(NetDataWriter writer, ShotInfoPacket packet) + { + writer.Put((int)packet.ShotType); + writer.Put(packet.AmmoAfterShot); + writer.Put(packet.ShotPosition); + writer.Put(packet.ShotDirection); + writer.Put(packet.FireportPosition); + writer.Put(packet.ChamberIndex); + writer.Put(packet.Overheat); + writer.Put(packet.UnderbarrelShot); + writer.Put(packet.AmmoTemplate); + writer.Put(packet.LastShotOverheat); + writer.Put(packet.LastShotTime); + writer.Put(packet.SlideOnOverheatReached); + } + } + + public struct SearchPacket + { + public bool IsStop; + public string ItemId; + public int OperationId; + + public static SearchPacket Deserialize(NetDataReader reader) + { + SearchPacket packet = new() + { + IsStop = reader.GetBool(), + ItemId = reader.GetString(), + OperationId = reader.GetInt() + }; + return packet; + } + + public static void Serialize(NetDataWriter writer, SearchPacket packet) + { + writer.Put(packet.IsStop); + writer.Put(packet.ItemId); + writer.Put(packet.OperationId); + } + } + + public struct WeatherClassPacket + { + public float AtmospherePressure; + public float Cloudness; + public float GlobalFogDensity; + public float GlobalFogHeight; + public float LyingWater; + public Vector2 MainWindDirection; + public Vector2 MainWindPosition; + public float Rain; + public float RainRandomness; + public float ScaterringFogDensity; + public float ScaterringFogHeight; + public float Temperature; + public long Time; + public Vector2 TopWindDirection; + public Vector2 TopWindPosition; + public float Turbulence; + public float Wind; + public int WindDirection; + + public static WeatherClassPacket Deserialize(NetDataReader reader) + { + return new WeatherClassPacket() + { + AtmospherePressure = reader.GetFloat(), + Cloudness = reader.GetFloat(), + GlobalFogDensity = reader.GetFloat(), + GlobalFogHeight = reader.GetFloat(), + LyingWater = reader.GetFloat(), + MainWindDirection = reader.GetVector2(), + MainWindPosition = reader.GetVector2(), + Rain = reader.GetFloat(), + RainRandomness = reader.GetFloat(), + ScaterringFogDensity = reader.GetFloat(), + ScaterringFogHeight = reader.GetFloat(), + Temperature = reader.GetFloat(), + Time = reader.GetLong(), + TopWindDirection = reader.GetVector2(), + TopWindPosition = reader.GetVector2(), + Turbulence = reader.GetFloat(), + Wind = reader.GetFloat(), + WindDirection = reader.GetInt() + }; + } + + public static void Serialize(NetDataWriter writer, WeatherClass weatherClass) + { + writer.Put(weatherClass.AtmospherePressure); + writer.Put(weatherClass.Cloudness); + writer.Put(weatherClass.GlobalFogDensity); + writer.Put(weatherClass.GlobalFogHeight); + writer.Put(weatherClass.LyingWater); + writer.Put(weatherClass.MainWindDirection); + writer.Put(weatherClass.MainWindPosition); + writer.Put(weatherClass.Rain); + writer.Put(weatherClass.RainRandomness); + writer.Put(weatherClass.ScaterringFogDensity); + writer.Put(weatherClass.ScaterringFogHeight); + writer.Put(weatherClass.Temperature); + writer.Put(weatherClass.Time); + writer.Put(weatherClass.TopWindDirection); + writer.Put(weatherClass.TopWindPosition); + writer.Put(weatherClass.Turbulence); + writer.Put(weatherClass.Wind); + writer.Put(weatherClass.WindDirection); + } + } + + public struct FlareShotPacket + { + public Vector3 ShotPosition; + public Vector3 ShotForward; + public string AmmoTemplateId; + + public static FlareShotPacket Deserialize(NetDataReader reader) + { + return new FlareShotPacket() + { + ShotPosition = reader.GetVector3(), + ShotForward = reader.GetVector3(), + AmmoTemplateId = reader.GetString() + }; + } + + public static void Serialize(NetDataWriter writer, FlareShotPacket packet) + { + writer.Put(packet.ShotPosition); + writer.Put(packet.ShotForward); + writer.Put(packet.AmmoTemplateId); + } + } + + public struct VaultPacket + { + public EVaultingStrategy VaultingStrategy; + public Vector3 VaultingPoint; + public float VaultingHeight; + public float VaultingLength; + public float VaultingSpeed; + public float BehindObstacleHeight; + public float AbsoluteForwardVelocity; + + public static VaultPacket Deserialize(NetDataReader reader) + { + return new VaultPacket() + { + VaultingStrategy = (EVaultingStrategy)reader.GetInt(), + VaultingPoint = reader.GetVector3(), + VaultingHeight = reader.GetFloat(), + VaultingLength = reader.GetFloat(), + VaultingSpeed = reader.GetFloat(), + BehindObstacleHeight = reader.GetFloat(), + AbsoluteForwardVelocity = reader.GetFloat() + }; + } + + public static void Serialize(NetDataWriter writer, VaultPacket packet) + { + writer.Put((int)packet.VaultingStrategy); + writer.Put(packet.VaultingPoint); + writer.Put(packet.VaultingHeight); + writer.Put(packet.VaultingLength); + writer.Put(packet.VaultingSpeed); + writer.Put(packet.BehindObstacleHeight); + writer.Put(packet.AbsoluteForwardVelocity); + } + } + + public class BTRDataPacketUtils + { + public static BTRDataPacket Deserialize(NetDataReader reader) + { + return new() + { + position = reader.GetVector3(), + BtrBotId = reader.GetInt(), + MoveSpeed = reader.GetFloat(), + moveDirection = reader.GetByte(), + timeToEndPause = reader.GetFloat(), + currentSpeed = reader.GetFloat(), + RightSlot1State = reader.GetByte(), + RightSlot0State = reader.GetByte(), + RightSideState = reader.GetByte(), + LeftSlot1State = reader.GetByte(), + LeftSlot0State = reader.GetByte(), + LeftSideState = reader.GetByte(), + RouteState = reader.GetByte(), + State = reader.GetByte(), + gunsBlockRotation = reader.GetFloat(), + turretRotation = reader.GetFloat(), + rotation = reader.GetQuaternion() + }; + } + + public static void Serialize(NetDataWriter writer, BTRDataPacket packet) + { + writer.Put(packet.position); + writer.Put(packet.BtrBotId); + writer.Put(packet.MoveSpeed); + writer.Put(packet.moveDirection); + writer.Put(packet.timeToEndPause); + writer.Put(packet.currentSpeed); + writer.Put(packet.RightSlot1State); + writer.Put(packet.RightSlot0State); + writer.Put(packet.RightSideState); + writer.Put(packet.LeftSlot1State); + writer.Put(packet.LeftSlot0State); + writer.Put(packet.LeftSideState); + writer.Put(packet.RouteState); + writer.Put(packet.State); + writer.Put(packet.gunsBlockRotation); + writer.Put(packet.turretRotation); + writer.Put(packet.rotation); + } + } + + public struct RagdollPacket + { + public EBodyPartColliderType BodyPartColliderType; + public Vector3 Direction; + public Vector3 Point; + public float Force; + public Vector3 OverallVelocity; + + public static RagdollPacket Deserialize(NetDataReader reader) + { + return new RagdollPacket() + { + BodyPartColliderType = (EBodyPartColliderType)reader.GetInt(), + Direction = reader.GetVector3(), + Point = reader.GetVector3(), + Force = reader.GetFloat(), + OverallVelocity = reader.GetVector3() + }; + } + + public static void Serialize(NetDataWriter writer, RagdollPacket packet) + { + writer.Put((int)packet.BodyPartColliderType); + writer.Put(packet.Direction); + writer.Put(packet.Point); + writer.Put(packet.Force); + writer.Put(packet.OverallVelocity); + } + } + + public struct DeathInfoPacket + { + public string AccountId; + public string ProfileId; + public string Nickname; + public string KillerAccountId; + public string KillerProfileId; + public string KillerName; + public EPlayerSide Side; + public int Level; + public DateTime Time; + public string Status; + public string WeaponName; + public string GroupId; + + public static DeathInfoPacket Deserialize(NetDataReader reader) + { + return new() + { + AccountId = reader.GetString(), + ProfileId = reader.GetString(), + Nickname = reader.GetString(), + KillerAccountId = reader.GetString(), + KillerProfileId = reader.GetString(), + KillerName = reader.GetString(), + Side = (EPlayerSide)reader.GetInt(), + Level = reader.GetInt(), + Time = reader.GetDateTime(), + Status = reader.GetString(), + WeaponName = reader.GetString(), + GroupId = reader.GetString() + }; + } + + public static void Serialize(NetDataWriter writer, DeathInfoPacket packet) + { + writer.Put(packet.AccountId); + writer.Put(packet.ProfileId); + writer.Put(packet.Nickname); + writer.Put(packet.KillerAccountId); + writer.Put(packet.KillerProfileId); + writer.Put(packet.KillerName); + writer.Put((int)packet.Side); + writer.Put(packet.Level); + writer.Put(packet.Time); + writer.Put(packet.Status); + writer.Put(packet.WeaponName); + writer.Put(packet.GroupId); + } + } + + public enum EProceedType + { + EmptyHands, + FoodClass, + GrenadeClass, + MedsClass, + QuickGrenadeThrow, + QuickKnifeKick, + QuickUse, + Weapon, + Knife, + TryProceed + } + } } diff --git a/Fika.Core/Networking/FikaSerializationExtensions.cs b/Fika.Core/Networking/FikaSerializationExtensions.cs index fd130a3f..5fcc3b99 100644 --- a/Fika.Core/Networking/FikaSerializationExtensions.cs +++ b/Fika.Core/Networking/FikaSerializationExtensions.cs @@ -13,342 +13,342 @@ namespace Fika.Core.Networking { - /// - /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika - /// - public static class FikaSerializationExtensions - { - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector3 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - writer.Put(vector.z); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Vector3 GetVector3(this NetDataReader reader) - { - return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Vector2 vector) - { - writer.Put(vector.x); - writer.Put(vector.y); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Vector2 GetVector2(this NetDataReader reader) - { - return new Vector2(reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Quaternion quaternion) - { - writer.Put(quaternion.x); - writer.Put(quaternion.y); - writer.Put(quaternion.z); - writer.Put(quaternion.w); - } - - /// - /// Deserializes a - /// - /// - /// A - public static Quaternion GetQuaternion(this NetDataReader reader) - { - return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, Color color) - { - writer.Put(color.r); - writer.Put(color.g); - writer.Put(color.b); - writer.Put(color.a); - } - - /// - /// Deserializes a - /// - /// - /// A /returns> - public static Color GetColor(this NetDataReader reader) - { - return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); - } - - /// - /// Serializes a (Physical) struct - /// - /// - /// - public static void Put(this NetDataWriter writer, GStruct36 physical) - { - writer.Put(physical.StaminaExhausted); - writer.Put(physical.OxygenExhausted); - writer.Put(physical.HandsExhausted); - } - - /// - /// Deserializes a (Physical) struct - /// - /// - /// A (Physical) - public static GStruct36 GetPhysical(this NetDataReader reader) - { - return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; - } - - /// - /// Serialize a array - /// - /// - /// - public static void PutByteArray(this NetDataWriter writer, byte[] bytes) - { - writer.Put(bytes.Length); - if (bytes.Length > 0) - { - writer.Put(bytes); - } - } - - /// - /// Deserializes a array - /// - /// - /// A array - public static byte[] GetByteArray(this NetDataReader reader) - { - int length = reader.GetInt(); - if (length > 0) - { - byte[] bytes = new byte[length]; - reader.GetBytes(bytes, length); - return bytes; - } - return Array.Empty(); - } - - /// - /// Serializes a - /// - /// - /// - public static void Put(this NetDataWriter writer, DateTime dateTime) - { - writer.Put(dateTime.ToOADate()); - } - - /// - /// Deserializes a - /// - /// - /// A - public static DateTime GetDateTime(this NetDataReader reader) - { - return DateTime.FromOADate(reader.GetDouble()); - } - - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// The to serialize - public static void PutAirdropItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); - } - - /// - /// Same as , however this one is specifically for airdrops to handle bundle loading - /// - /// - /// An - public static Item GetAirdropItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); - - ContainerCollection[] containerCollections = [item as ContainerCollection]; - ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() - .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); - - return item; - } - - /// - /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. - /// - /// - /// The to serialize - public static void PutItem(this NetDataWriter writer, Item item) - { - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.Write(GClass1535.SerializeItem(item)); - writer.PutByteArray(memoryStream.ToArray()); - } - - /// - /// Gets a serialized - /// - /// - /// An - public static Item GetItem(this NetDataReader reader) - { - using MemoryStream memoryStream = new(reader.GetByteArray()); - using BinaryReader binaryReader = new(memoryStream); - - return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); - } - - public static void PutThrowableData(this NetDataWriter writer, List throwables) - { - writer.Put(throwables.Count); - foreach (GStruct35 data in throwables) - { - writer.Put(data.Id); - writer.Put(data.Position); - writer.Put(data.Template); - writer.Put(data.Time); - writer.Put(data.Orientation); - writer.Put(data.PlatformId); - } - } - - public static List GetThrowableData(this NetDataReader reader) - { - int amount = reader.GetInt(); - List throwables = new(amount); - for (int i = 0; i < amount; i++) - { - GStruct35 data = new() - { - Id = reader.GetString(), - Position = reader.GetVector3(), - Template = reader.GetString(), - Time = reader.GetInt(), - Orientation = reader.GetQuaternion(), - PlatformId = reader.GetShort() - }; - throwables.Add(data); - } - - return throwables; - } - - public static void PutInteractivesStates(this NetDataWriter writer, List interactiveObjectsData) - { - writer.Put(interactiveObjectsData.Count); - for (int i = 0; i < interactiveObjectsData.Count; i++) - { - writer.Put(interactiveObjectsData[i].NetId); - writer.Put(interactiveObjectsData[i].State); - writer.Put(interactiveObjectsData[i].IsBroken); - } - } - - public static List GetInteractivesStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - List interactivesStates = new(amount); - for (int i = 0; i < amount; i++) - { - WorldInteractiveObject.GStruct384 data = new() - { - NetId = reader.GetInt(), - State = reader.GetByte(), - IsBroken = reader.GetBool() - }; - interactivesStates.Add(data); - } - - return interactivesStates; - } - - public static void PutLampStates(this NetDataWriter writer, Dictionary lampStates) - { - int amount = lampStates.Count; - writer.Put(amount); - foreach (KeyValuePair lampState in lampStates) - { - writer.Put(lampState.Key); - writer.Put(lampState.Value); - } - } - - public static Dictionary GetLampStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { - states.Add(reader.GetInt(), reader.GetByte()); - } - - return states; - } - - public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) - { - int amount = windowBreakerStates.Count; - writer.Put(amount); - foreach (KeyValuePair windowBreakerState in windowBreakerStates) - { - writer.Put(windowBreakerState.Key); - writer.Put(windowBreakerState.Value); - } - } - - public static Dictionary GetWindowBreakerStates(this NetDataReader reader) - { - int amount = reader.GetInt(); - Dictionary states = new(amount); - for (int i = 0; i < amount; i++) - { - states.Add(reader.GetInt(), reader.GetVector3()); - } - - return states; - } - } + /// + /// Serialization extensions for Unity/EFT classes to ease writing of packets in Fika + /// + public static class FikaSerializationExtensions + { + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector3 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + writer.Put(vector.z); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Vector3 GetVector3(this NetDataReader reader) + { + return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Vector2 vector) + { + writer.Put(vector.x); + writer.Put(vector.y); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Vector2 GetVector2(this NetDataReader reader) + { + return new Vector2(reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Quaternion quaternion) + { + writer.Put(quaternion.x); + writer.Put(quaternion.y); + writer.Put(quaternion.z); + writer.Put(quaternion.w); + } + + /// + /// Deserializes a + /// + /// + /// A + public static Quaternion GetQuaternion(this NetDataReader reader) + { + return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, Color color) + { + writer.Put(color.r); + writer.Put(color.g); + writer.Put(color.b); + writer.Put(color.a); + } + + /// + /// Deserializes a + /// + /// + /// A /returns> + public static Color GetColor(this NetDataReader reader) + { + return new Color(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); + } + + /// + /// Serializes a (Physical) struct + /// + /// + /// + public static void Put(this NetDataWriter writer, GStruct36 physical) + { + writer.Put(physical.StaminaExhausted); + writer.Put(physical.OxygenExhausted); + writer.Put(physical.HandsExhausted); + } + + /// + /// Deserializes a (Physical) struct + /// + /// + /// A (Physical) + public static GStruct36 GetPhysical(this NetDataReader reader) + { + return new GStruct36() { StaminaExhausted = reader.GetBool(), OxygenExhausted = reader.GetBool(), HandsExhausted = reader.GetBool() }; + } + + /// + /// Serialize a array + /// + /// + /// + public static void PutByteArray(this NetDataWriter writer, byte[] bytes) + { + writer.Put(bytes.Length); + if (bytes.Length > 0) + { + writer.Put(bytes); + } + } + + /// + /// Deserializes a array + /// + /// + /// A array + public static byte[] GetByteArray(this NetDataReader reader) + { + int length = reader.GetInt(); + if (length > 0) + { + byte[] bytes = new byte[length]; + reader.GetBytes(bytes, length); + return bytes; + } + return Array.Empty(); + } + + /// + /// Serializes a + /// + /// + /// + public static void Put(this NetDataWriter writer, DateTime dateTime) + { + writer.Put(dateTime.ToOADate()); + } + + /// + /// Deserializes a + /// + /// + /// A + public static DateTime GetDateTime(this NetDataReader reader) + { + return DateTime.FromOADate(reader.GetDouble()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// The to serialize + public static void PutAirdropItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Same as , however this one is specifically for airdrops to handle bundle loading + /// + /// + /// An + public static Item GetAirdropItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); + + Item item = GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + + ContainerCollection[] containerCollections = [item as ContainerCollection]; + ResourceKey[] resourceKeys = containerCollections.GetAllItemsFromCollections() + .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); + + return item; + } + + /// + /// This write and serializes an , which can be cast to different types of inherited classes. Casting should be handled inside packet for consistency. + /// + /// + /// The to serialize + public static void PutItem(this NetDataWriter writer, Item item) + { + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.Write(GClass1535.SerializeItem(item)); + writer.PutByteArray(memoryStream.ToArray()); + } + + /// + /// Gets a serialized + /// + /// + /// An + public static Item GetItem(this NetDataReader reader) + { + using MemoryStream memoryStream = new(reader.GetByteArray()); + using BinaryReader binaryReader = new(memoryStream); + + return GClass1535.DeserializeItem(Singleton.Instance, [], binaryReader.ReadEFTItemDescriptor()); + } + + public static void PutThrowableData(this NetDataWriter writer, List throwables) + { + writer.Put(throwables.Count); + foreach (GStruct35 data in throwables) + { + writer.Put(data.Id); + writer.Put(data.Position); + writer.Put(data.Template); + writer.Put(data.Time); + writer.Put(data.Orientation); + writer.Put(data.PlatformId); + } + } + + public static List GetThrowableData(this NetDataReader reader) + { + int amount = reader.GetInt(); + List throwables = new(amount); + for (int i = 0; i < amount; i++) + { + GStruct35 data = new() + { + Id = reader.GetString(), + Position = reader.GetVector3(), + Template = reader.GetString(), + Time = reader.GetInt(), + Orientation = reader.GetQuaternion(), + PlatformId = reader.GetShort() + }; + throwables.Add(data); + } + + return throwables; + } + + public static void PutInteractivesStates(this NetDataWriter writer, List interactiveObjectsData) + { + writer.Put(interactiveObjectsData.Count); + for (int i = 0; i < interactiveObjectsData.Count; i++) + { + writer.Put(interactiveObjectsData[i].NetId); + writer.Put(interactiveObjectsData[i].State); + writer.Put(interactiveObjectsData[i].IsBroken); + } + } + + public static List GetInteractivesStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + List interactivesStates = new(amount); + for (int i = 0; i < amount; i++) + { + WorldInteractiveObject.GStruct384 data = new() + { + NetId = reader.GetInt(), + State = reader.GetByte(), + IsBroken = reader.GetBool() + }; + interactivesStates.Add(data); + } + + return interactivesStates; + } + + public static void PutLampStates(this NetDataWriter writer, Dictionary lampStates) + { + int amount = lampStates.Count; + writer.Put(amount); + foreach (KeyValuePair lampState in lampStates) + { + writer.Put(lampState.Key); + writer.Put(lampState.Value); + } + } + + public static Dictionary GetLampStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetByte()); + } + + return states; + } + + public static void PutWindowBreakerStates(this NetDataWriter writer, Dictionary windowBreakerStates) + { + int amount = windowBreakerStates.Count; + writer.Put(amount); + foreach (KeyValuePair windowBreakerState in windowBreakerStates) + { + writer.Put(windowBreakerState.Key); + writer.Put(windowBreakerState.Value); + } + } + + public static Dictionary GetWindowBreakerStates(this NetDataReader reader) + { + int amount = reader.GetInt(); + Dictionary states = new(amount); + for (int i = 0; i < amount; i++) + { + states.Add(reader.GetInt(), reader.GetVector3()); + } + + return states; + } + } } diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index dd6a2a64..2132d7da 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -40,879 +40,879 @@ namespace Fika.Core.Networking { - public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener - { - public NetPacketProcessor packetProcessor = new(); - public CoopPlayer MyPlayer; - public Dictionary Players => coopHandler.Players; - public List PlayersMissing = []; - public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); - public int ReadyClients = 0; - public NetManager NetServer - { - get - { - return netServer; - } - } - public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); - public bool hasHadPeer = false; - public bool Started - { - get - { - if (netServer == null) - { - return false; - } - return netServer.IsRunning; - } - } - - private NetManager netServer; - public NetDataWriter Writer => dataWriter; - private readonly NetDataWriter dataWriter = new(); - private int Port => FikaPlugin.UDPPort.Value; - private CoopHandler coopHandler; - private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); - private int currentNetId; - private FikaChat fikaChat; - private CancellationTokenSource natIntroduceRoutineCts; - private int statisticsCounter = 0; - - public async Task Init() - { - NetworkGameSession.RTT = 0; - NetworkGameSession.LossPercent = 0; - - // Start at 1 to avoid having 0 and making us think it's working when it's not - currentNetId = 1; - - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); - packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); - packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); - packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); - packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); - packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); - packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); - packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); - packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); - packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); - packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); - packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); - packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); - packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); - packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); - - netServer = new NetManager(this) - { - BroadcastReceiveEnabled = true, - UnconnectedMessagesEnabled = true, - UpdateTime = 15, - AutoRecycle = true, - IPv6Enabled = false, - DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, - UseNativeSockets = FikaPlugin.NativeSockets.Value, - EnableStatistics = true, - NatPunchEnabled = true - }; - - if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) - { - bool upnpFailed = false; - - 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(); - - 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) - { - Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); - } - } - else if (FikaPlugin.ForceIP.Value != "") - { - MyExternalIP = FikaPlugin.ForceIP.Value; - } - else - { - try - { - HttpClient client = new(); - string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); - MyExternalIP = ipAdress.Replace("\n", ""); - client.Dispose(); - } - catch (Exception) - { - Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); - } - } - - if (FikaPlugin.UseNatPunching.Value) - { - 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 - { - if (FikaPlugin.ForceBindIP.Value != "Disabled") - { - netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); - } - else - { - netServer.Start(Port); - } - } - - logger.LogInfo("Started Fika Server"); - - NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - - string[] Ips = []; - - foreach (string ip in FikaPlugin.Instance.LocalIPs) - { - if (ValidateLocalIP(ip)) - { - 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, FikaBackendUtils.IsDedicatedGame); - FikaRequestHandler.UpdateSetHost(body); - - FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); - } - - private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (packet.InitialRequest) - { - NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); - foreach (CoopPlayer player in coopHandler.HumanPlayers) - { - if (player.ProfileId == packet.ProfileId && player is ObservedCoopPlayer observedCoopPlayer) - { - ReconnectPacket ownCharacterPacket = new(false) - { - Type = EReconnectDataType.OwnCharacter, - Profile = observedCoopPlayer.Profile, - ProfileHealthClass = observedCoopPlayer.NetworkHealthController.Store(), - PlayerPosition = observedCoopPlayer.Position - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref ownCharacterPacket, DeliveryMethod.ReliableOrdered); - - observedCoopPlayer.HealthBar.ClearEffects(); - GenericPacket clearEffectsPacket = new(EPackageType.ClearEffects) - { - NetId = observedCoopPlayer.NetId - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref clearEffectsPacket, DeliveryMethod.ReliableUnordered, peer); - } - } - - return; - } - - GameWorld gameWorld = Singleton.Instance; - Traverse worldTraverse = Traverse.Create(gameWorld.World_0); - - GClass724.GStruct43 grenades = gameWorld.Grenades.GetValuesEnumerator(); - List smokeData = []; - foreach (Throwable item in grenades) - { - if (item is SmokeGrenade smokeGrenade) - { - smokeData.Add(smokeGrenade.NetworkData); - } - } - - if (smokeData.Count > 0) - { - ReconnectPacket throwablePacket = new(false) - { - Type = EReconnectDataType.Throwable, - ThrowableData = smokeData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref throwablePacket, DeliveryMethod.ReliableOrdered); - } - - List interactivesData = []; - WorldInteractiveObject[] worldInteractiveObjects = worldTraverse.Field("worldInteractiveObject_0").Value; - foreach (WorldInteractiveObject interactiveObject in worldInteractiveObjects) - { - if ((interactiveObject.DoorState != interactiveObject.InitialDoorState && interactiveObject.DoorState != EDoorState.Interacting) - || (interactiveObject is Door door && door.IsBroken)) - { - interactivesData.Add(interactiveObject.GetStatusInfo(true)); - } - } - - if (interactivesData.Count > 0) - { - ReconnectPacket interactivePacket = new(false) - { - Type = EReconnectDataType.Interactives, - InteractivesData = interactivesData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref interactivePacket, DeliveryMethod.ReliableOrdered); - } - - IEnumerable lampControllers = LocationScene.GetAllObjects(false); - Dictionary lampStates = []; - foreach (LampController controller in lampControllers) - { - lampStates.Add(controller.NetId, (byte)controller.LampState); - } - - if (lampStates.Count > 0) - { - ReconnectPacket lampPacket = new(false) - { - Type = EReconnectDataType.LampControllers, - LampStates = lampStates - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref lampPacket, DeliveryMethod.ReliableOrdered); - } - - GClass724.GStruct43 windows = gameWorld.Windows.GetValuesEnumerator(); - Dictionary windowData = []; - foreach (WindowBreaker window in windows) - { - if (window.AvailableToSync && window.IsDamaged) - { - windowData.Add(window.NetId, window.FirstHitPosition.Value); - } - } - - if (windowData.Count > 0) - { - ReconnectPacket windowPacket = new(false) - { - Type = EReconnectDataType.Windows, - WindowBreakerStates = windowData - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); - } - - foreach (CoopPlayer player in coopHandler.Players.Values) - { - SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), - player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref characterPacket, DeliveryMethod.ReliableOrdered); - } - - foreach (CoopPlayer player in coopHandler.HumanPlayers) - { - if (player.ProfileId == packet.ProfileId) - { - AssignNetIdPacket assignPacket = new() - { - NetId = player.NetId - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref assignPacket, DeliveryMethod.ReliableOrdered); - } - } - - ReconnectPacket finishPacket = new(false) - { - Type = EReconnectDataType.Finished - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); - } - } - - private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - WorldLootPacket response = new(false) - { - Data = coopGame.GetHostLootItems() - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - - private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (Singleton.Instantiated) - { - World world = Singleton.Instance.World_0; - if (world.Interactables != null) - { - InteractableInitPacket response = new(false) - { - Interactables = world.Interactables - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - } - - private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) - { - if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) - { - if (packet.IsRequest) - { - SpawnpointPacket response = new(false) - { - Name = coopGame.GetSpawnpointName() - }; - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); - } - } - } - - private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) - { - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - if (MyPlayer.HealthController.IsAlive) - { - if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) - { - sharedQuestController.ReceiveQuestDropItemPacket(ref packet); - } - } - } - - private bool ValidateLocalIP(string LocalIP) - { - if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) - { - return true; - } - - //Check for RFC1918's 20 bit block. - int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); - - if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) - { - return true; - } - - return false; - } - - 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() - { - int netId = currentNetId; - currentNetId++; - - return netId; - } - - public void SetupGameVariables(CoopPlayer coopPlayer) - { - coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); - MyPlayer = coopPlayer; - if (FikaPlugin.EnableChat.Value) - { - fikaChat = gameObject.AddComponent(); - } - } - - private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - 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); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); - - AssignNetIdPacket assignNetIdPacket = new() - { - NetId = netId - }; - - dataWriter.Reset(); - packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); - peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); - } - - private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) - { - // This shouldn't happen - } - - private void OnMinePacketReceived(MinePacket packet, NetPeer peer) - { - if (Singleton.Instance.MineManager != null) - { - NetworkGame.Class1407 mineSeeker = new() - { - minePosition = packet.MinePositon - }; - MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); - if (mineDirectional == null) - { - return; - } - mineDirectional.Explosion(); - } - } - - private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); - } - } - - private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) - { - if (coopHandler.serverBTR != null) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) - { - if (coopHandler.serverBTR.CanPlayerEnter(player)) - { - coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); - } - else - { - BTRInteractionPacket newPacket = new(packet.NetId) - { - HasInteractPacket = false - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); - } - } - } - } - - private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (FikaBackendUtils.Nodes != null) - { - WeatherPacket weatherPacket2 = new() - { - IsRequest = false, - HasData = true, - Amount = FikaBackendUtils.Nodes.Length, - WeatherClasses = FikaBackendUtils.Nodes - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); - }; - } - } - - private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) - { - if (packet.IsRequest) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - if (exfilController.ExfiltrationPoints == null) - return; - - ExfiltrationPacket exfilPacket = new(false) - { - ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, - ExfiltrationPoints = [] - }; - - foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) - { - exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); - } - - if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) - { - exfilPacket.HasScavExfils = true; - exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; - exfilPacket.ScavExfiltrationPoints = []; - - foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) - { - exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); - } - } - - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); - } - } - } - - private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) - { - if (packet.PacketType == EPackageType.ClientExtract) - { - if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - coopHandler.Players.Remove(packet.NetId); - coopHandler.HumanPlayers.Remove(playerToApply); - if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - coopHandler.ExtractedPlayers.Add(packet.NetId); - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.ExtractedPlayers.Add(packet.NetId); - coopGame.ClearHostAI(playerToApply); - - if (FikaPlugin.ShowNotifications.Value) - { - string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; - NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", - EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); - } - } - - playerToApply.Dispose(); - AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); - } - } - else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) - { - PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); - } - else if (packet.PacketType == EPackageType.LoadBot) - { - CoopGame coopGame = coopHandler.LocalGameInstance; - coopGame.IncreaseLoadedPlayers(packet.BotNetId); - - return; - } - else if (packet.PacketType == EPackageType.ExfilCountdown) - { - if (ExfiltrationControllerClass.Instance != null) - { - ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; - - ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); - if (exfilPoint != null) - { - CoopGame game = coopHandler.LocalGameInstance; - exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; - - if (exfilPoint.Status != EExfiltrationStatus.Countdown) - { - exfilPoint.Status = EExfiltrationStatus.Countdown; - } - } - } - } - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) - { - ReadyClients += packet.ReadyPlayers; - - InformationPacket respondPackage = new(false) - { - NumberOfPlayers = netServer.ConnectedPeersCount, - ReadyPlayers = ReadyClients, - HostReady = coopHandler != null && coopHandler.LocalGameInstance.Status == GameStatus.Started - }; - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); - } - - private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) - { - if (coopHandler == null) - { - return; - } - - if (packet.IsRequest) - { - foreach (CoopPlayer player in coopHandler.Players.Values) - { - if (player.ProfileId == packet.ProfileId) - { - continue; - } - - if (packet.Characters.Contains(player.ProfileId)) - { - continue; - } - - AllCharacterRequestPacket requestPacket = new(player.ProfileId) - { - IsRequest = false, - PlayerInfo = new() - { - Profile = player.Profile - }, - IsAlive = player.HealthController.IsAlive, - IsAI = player is CoopBot, - Position = player.Transform.position, - NetId = player.NetId - }; - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); - } - } - if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) - { - PlayersMissing.Add(packet.ProfileId); - 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)) - { - 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); - PlayersMissing.Remove(packet.ProfileId); - } - } - } - - private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); - using BinaryReader binaryReader = new(memoryStream); - try - { - GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); - - InventoryOperationHandler opHandler = new() - { - opResult = result, - operationId = packet.ItemControllerExecutePacket.CallbackId, - netId = playerToApply.NetId, - peer = peer, - server = this - }; - - OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); - SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); - - // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. - // Unknown what problems this might cause so far. - if (result.Value is UnloadOperationClass unloadOperation) - { - if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) - { - Item item = internalSplitOperation.To.Item; - if (item != null) - { - if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) - { - item.Id = internalSplitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - } - - // TODO: Same as above. - if (result.Value is SplitOperationClass splitOperation) - { - Item item = splitOperation.To.Item; - if (item != null) - { - if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) - { - item.Id = splitOperation.CloneId; - } - else - { - FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); - } - } - else - { - FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); - } - } - - /*// Fix for folding not replicating + public class FikaServer : MonoBehaviour, INetEventListener, INetLogger, INatPunchListener + { + public NetPacketProcessor packetProcessor = new(); + public CoopPlayer MyPlayer; + public Dictionary Players => coopHandler.Players; + public List PlayersMissing = []; + public string MyExternalIP { get; private set; } = NetUtils.GetLocalIp(LocalAddrType.IPv4); + public int ReadyClients = 0; + public NetManager NetServer + { + get + { + return netServer; + } + } + public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); + public bool hasHadPeer = false; + public bool Started + { + get + { + if (netServer == null) + { + return false; + } + return netServer.IsRunning; + } + } + + private NetManager netServer; + public NetDataWriter Writer => dataWriter; + private readonly NetDataWriter dataWriter = new(); + private int Port => FikaPlugin.UDPPort.Value; + private CoopHandler coopHandler; + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); + private int currentNetId; + private FikaChat fikaChat; + private CancellationTokenSource natIntroduceRoutineCts; + private int statisticsCounter = 0; + + public async Task Init() + { + NetworkGameSession.RTT = 0; + NetworkGameSession.LossPercent = 0; + + // Start at 1 to avoid having 0 and making us think it's working when it's not + currentNetId = 1; + + packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); + packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); + packetProcessor.SubscribeNetSerializable(OnDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnArmorDamagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnInventoryPacketReceived); + packetProcessor.SubscribeNetSerializable(OnCommonPlayerPacketReceived); + packetProcessor.SubscribeNetSerializable(OnAllCharacterRequestPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInformationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnHealthSyncPacketReceived); + packetProcessor.SubscribeNetSerializable(OnGenericPacketReceived); + packetProcessor.SubscribeNetSerializable(OnExfiltrationPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWeatherPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRInteractionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnBTRServicePacketReceived); + packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); + packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); + packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestConditionPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnQuestDropItemPacketReceived); + packetProcessor.SubscribeNetSerializable(OnSpawnPointPacketReceived); + packetProcessor.SubscribeNetSerializable(OnInteractableInitPacketReceived); + packetProcessor.SubscribeNetSerializable(OnWorldLootPacketReceived); + packetProcessor.SubscribeNetSerializable(OnReconnectPacketReceived); + + netServer = new NetManager(this) + { + BroadcastReceiveEnabled = true, + UnconnectedMessagesEnabled = true, + UpdateTime = 15, + AutoRecycle = true, + IPv6Enabled = false, + DisconnectTimeout = FikaPlugin.ConnectionTimeout.Value * 1000, + UseNativeSockets = FikaPlugin.NativeSockets.Value, + EnableStatistics = true, + NatPunchEnabled = true + }; + + if (FikaPlugin.UseUPnP.Value && !FikaPlugin.UseNatPunching.Value) + { + bool upnpFailed = false; + + 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(); + + 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) + { + Singleton.Instance.ShowErrorScreen("Network Error", "UPnP mapping failed. Make sure the selected port is not already open!"); + } + } + else if (FikaPlugin.ForceIP.Value != "") + { + MyExternalIP = FikaPlugin.ForceIP.Value; + } + else + { + try + { + HttpClient client = new(); + string ipAdress = await client.GetStringAsync("https://ipv4.icanhazip.com/"); + MyExternalIP = ipAdress.Replace("\n", ""); + client.Dispose(); + } + catch (Exception) + { + Singleton.Instance.ShowErrorScreen("Network Error", "Error when trying to receive IP automatically."); + } + } + + if (FikaPlugin.UseNatPunching.Value) + { + 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 + { + if (FikaPlugin.ForceBindIP.Value != "Disabled") + { + netServer.Start(FikaPlugin.ForceBindIP.Value, "", Port); + } + else + { + netServer.Start(Port); + } + } + + logger.LogInfo("Started Fika Server"); + + NotificationManagerClass.DisplayMessageNotification($"Server started on port {netServer.LocalPort}.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + + string[] Ips = []; + + foreach (string ip in FikaPlugin.Instance.LocalIPs) + { + if (ValidateLocalIP(ip)) + { + 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, FikaBackendUtils.IsDedicatedGame); + FikaRequestHandler.UpdateSetHost(body); + + FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); + } + + private void OnReconnectPacketReceived(ReconnectPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (packet.InitialRequest) + { + NotificationManagerClass.DisplayMessageNotification("Reconnect requested, expect lag...", iconType: EFT.Communications.ENotificationIconType.Alert); + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId && player is ObservedCoopPlayer observedCoopPlayer) + { + ReconnectPacket ownCharacterPacket = new(false) + { + Type = EReconnectDataType.OwnCharacter, + Profile = observedCoopPlayer.Profile, + ProfileHealthClass = observedCoopPlayer.NetworkHealthController.Store(), + PlayerPosition = observedCoopPlayer.Position + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref ownCharacterPacket, DeliveryMethod.ReliableOrdered); + + observedCoopPlayer.HealthBar.ClearEffects(); + GenericPacket clearEffectsPacket = new(EPackageType.ClearEffects) + { + NetId = observedCoopPlayer.NetId + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref clearEffectsPacket, DeliveryMethod.ReliableUnordered, peer); + } + } + + return; + } + + GameWorld gameWorld = Singleton.Instance; + Traverse worldTraverse = Traverse.Create(gameWorld.World_0); + + GClass724.GStruct43 grenades = gameWorld.Grenades.GetValuesEnumerator(); + List smokeData = []; + foreach (Throwable item in grenades) + { + if (item is SmokeGrenade smokeGrenade) + { + smokeData.Add(smokeGrenade.NetworkData); + } + } + + if (smokeData.Count > 0) + { + ReconnectPacket throwablePacket = new(false) + { + Type = EReconnectDataType.Throwable, + ThrowableData = smokeData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref throwablePacket, DeliveryMethod.ReliableOrdered); + } + + List interactivesData = []; + WorldInteractiveObject[] worldInteractiveObjects = worldTraverse.Field("worldInteractiveObject_0").Value; + foreach (WorldInteractiveObject interactiveObject in worldInteractiveObjects) + { + if ((interactiveObject.DoorState != interactiveObject.InitialDoorState && interactiveObject.DoorState != EDoorState.Interacting) + || (interactiveObject is Door door && door.IsBroken)) + { + interactivesData.Add(interactiveObject.GetStatusInfo(true)); + } + } + + if (interactivesData.Count > 0) + { + ReconnectPacket interactivePacket = new(false) + { + Type = EReconnectDataType.Interactives, + InteractivesData = interactivesData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref interactivePacket, DeliveryMethod.ReliableOrdered); + } + + IEnumerable lampControllers = LocationScene.GetAllObjects(false); + Dictionary lampStates = []; + foreach (LampController controller in lampControllers) + { + lampStates.Add(controller.NetId, (byte)controller.LampState); + } + + if (lampStates.Count > 0) + { + ReconnectPacket lampPacket = new(false) + { + Type = EReconnectDataType.LampControllers, + LampStates = lampStates + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref lampPacket, DeliveryMethod.ReliableOrdered); + } + + GClass724.GStruct43 windows = gameWorld.Windows.GetValuesEnumerator(); + Dictionary windowData = []; + foreach (WindowBreaker window in windows) + { + if (window.AvailableToSync && window.IsDamaged) + { + windowData.Add(window.NetId, window.FirstHitPosition.Value); + } + } + + if (windowData.Count > 0) + { + ReconnectPacket windowPacket = new(false) + { + Type = EReconnectDataType.Windows, + WindowBreakerStates = windowData + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref windowPacket, DeliveryMethod.ReliableOrdered); + } + + foreach (CoopPlayer player in coopHandler.Players.Values) + { + SendCharacterPacket characterPacket = new(new FikaSerialization.PlayerInfoPacket(player.Profile), + player.HealthController.IsAlive, player.IsAI, player.Position, player.NetId); + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref characterPacket, DeliveryMethod.ReliableOrdered); + } + + foreach (CoopPlayer player in coopHandler.HumanPlayers) + { + if (player.ProfileId == packet.ProfileId) + { + AssignNetIdPacket assignPacket = new() + { + NetId = player.NetId + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref assignPacket, DeliveryMethod.ReliableOrdered); + } + } + + ReconnectPacket finishPacket = new(false) + { + Type = EReconnectDataType.Finished + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref finishPacket, DeliveryMethod.ReliableOrdered); + } + } + + private void OnWorldLootPacketReceived(WorldLootPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + WorldLootPacket response = new(false) + { + Data = coopGame.GetHostLootItems() + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + + private void OnInteractableInitPacketReceived(InteractableInitPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (Singleton.Instantiated) + { + World world = Singleton.Instance.World_0; + if (world.Interactables != null) + { + InteractableInitPacket response = new(false) + { + Interactables = world.Interactables + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + } + + private void OnSpawnPointPacketReceived(SpawnpointPacket packet, NetPeer peer) + { + if (Singleton.Instance != null && Singleton.Instance is CoopGame coopGame) + { + if (packet.IsRequest) + { + SpawnpointPacket response = new(false) + { + Name = coopGame.GetSpawnpointName() + }; + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref response, DeliveryMethod.ReliableUnordered); + } + } + } + + private void OnQuestDropItemPacketReceived(QuestDropItemPacket packet, NetPeer peer) + { + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + if (MyPlayer.HealthController.IsAlive) + { + if (MyPlayer.AbstractQuestControllerClass is CoopClientSharedQuestController sharedQuestController) + { + sharedQuestController.ReceiveQuestDropItemPacket(ref packet); + } + } + } + + private bool ValidateLocalIP(string LocalIP) + { + if (LocalIP.StartsWith("192.168") || LocalIP.StartsWith("10")) + { + return true; + } + + //Check for RFC1918's 20 bit block. + int[] ip = Array.ConvertAll(LocalIP.Split('.'), int.Parse); + + if (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) + { + return true; + } + + return false; + } + + 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() + { + int netId = currentNetId; + currentNetId++; + + return netId; + } + + public void SetupGameVariables(CoopPlayer coopPlayer) + { + coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); + MyPlayer = coopPlayer; + if (FikaPlugin.EnableChat.Value) + { + fikaChat = gameObject.AddComponent(); + } + } + + private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + 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); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + + AssignNetIdPacket assignNetIdPacket = new() + { + NetId = netId + }; + + dataWriter.Reset(); + packetProcessor.WriteNetSerializable(dataWriter, ref assignNetIdPacket); + peer.Send(dataWriter, DeliveryMethod.ReliableUnordered); + } + + private void OnBorderZonePacketReceived(BorderZonePacket packet, NetPeer peer) + { + // This shouldn't happen + } + + private void OnMinePacketReceived(MinePacket packet, NetPeer peer) + { + if (Singleton.Instance.MineManager != null) + { + NetworkGame.Class1407 mineSeeker = new() + { + minePosition = packet.MinePositon + }; + MineDirectional mineDirectional = Singleton.Instance.MineManager.Mines.FirstOrDefault(new Func(mineSeeker.method_0)); + if (mineDirectional == null) + { + return; + } + mineDirectional.Explosion(); + } + } + + private void OnBTRServicePacketReceived(BTRServicePacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + coopHandler.serverBTR.NetworkBtrTraderServicePurchased(packet); + } + } + + private void OnBTRInteractionPacketReceived(BTRInteractionPacket packet, NetPeer peer) + { + if (coopHandler.serverBTR != null) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer player)) + { + if (coopHandler.serverBTR.CanPlayerEnter(player)) + { + coopHandler.serverBTR.HostObservedInteraction(player, packet.InteractPacket); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered); + } + else + { + BTRInteractionPacket newPacket = new(packet.NetId) + { + HasInteractPacket = false + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref newPacket, DeliveryMethod.ReliableOrdered); + } + } + } + } + + private void OnWeatherPacketReceived(WeatherPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (FikaBackendUtils.Nodes != null) + { + WeatherPacket weatherPacket2 = new() + { + IsRequest = false, + HasData = true, + Amount = FikaBackendUtils.Nodes.Length, + WeatherClasses = FikaBackendUtils.Nodes + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref weatherPacket2, DeliveryMethod.ReliableOrdered); + }; + } + } + + private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer peer) + { + if (packet.IsRequest) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + if (exfilController.ExfiltrationPoints == null) + return; + + ExfiltrationPacket exfilPacket = new(false) + { + ExfiltrationAmount = exfilController.ExfiltrationPoints.Length, + ExfiltrationPoints = [] + }; + + foreach (ExfiltrationPoint exfilPoint in exfilController.ExfiltrationPoints) + { + exfilPacket.ExfiltrationPoints.Add(exfilPoint.Settings.Name, exfilPoint.Status); + } + + if (MyPlayer.Side == EPlayerSide.Savage && exfilController.ScavExfiltrationPoints != null) + { + exfilPacket.HasScavExfils = true; + exfilPacket.ScavExfiltrationAmount = exfilController.ScavExfiltrationPoints.Length; + exfilPacket.ScavExfiltrationPoints = []; + + foreach (ScavExfiltrationPoint scavExfilPoint in exfilController.ScavExfiltrationPoints) + { + exfilPacket.ScavExfiltrationPoints.Add(scavExfilPoint.Settings.Name, scavExfilPoint.Status); + } + } + + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref exfilPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + } + } + } + + private void OnGenericPacketReceived(GenericPacket packet, NetPeer peer) + { + if (packet.PacketType == EPackageType.ClientExtract) + { + if (coopHandler.Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + coopHandler.Players.Remove(packet.NetId); + coopHandler.HumanPlayers.Remove(playerToApply); + if (!coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + coopHandler.ExtractedPlayers.Add(packet.NetId); + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.ExtractedPlayers.Add(packet.NetId); + coopGame.ClearHostAI(playerToApply); + + if (FikaPlugin.ShowNotifications.Value) + { + string nickname = !string.IsNullOrEmpty(playerToApply.Profile.Info.MainProfileNickname) ? playerToApply.Profile.Info.MainProfileNickname : playerToApply.Profile.Nickname; + NotificationManagerClass.DisplayMessageNotification($"Group member {ColorizeText(Colors.GREEN, nickname)} has extracted.", + EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); + } + } + + playerToApply.Dispose(); + AssetPoolObject.ReturnToPool(playerToApply.gameObject, true); + } + } + else if (packet.PacketType == EPackageType.Ping && FikaPlugin.UsePingSystem.Value) + { + PingFactory.ReceivePing(packet.PingLocation, packet.PingType, packet.PingColor, packet.Nickname, packet.LocaleId); + } + else if (packet.PacketType == EPackageType.LoadBot) + { + CoopGame coopGame = coopHandler.LocalGameInstance; + coopGame.IncreaseLoadedPlayers(packet.BotNetId); + + return; + } + else if (packet.PacketType == EPackageType.ExfilCountdown) + { + if (ExfiltrationControllerClass.Instance != null) + { + ExfiltrationControllerClass exfilController = ExfiltrationControllerClass.Instance; + + ExfiltrationPoint exfilPoint = exfilController.ExfiltrationPoints.FirstOrDefault(x => x.Settings.Name == packet.ExfilName); + if (exfilPoint != null) + { + CoopGame game = coopHandler.LocalGameInstance; + exfilPoint.ExfiltrationStartTime = game != null ? game.PastTime : packet.ExfilStartTime; + + if (exfilPoint.Status != EExfiltrationStatus.Countdown) + { + exfilPoint.Status = EExfiltrationStatus.Countdown; + } + } + } + } + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnHealthSyncPacketReceived(HealthSyncPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.HealthSyncPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInformationPacketReceived(InformationPacket packet, NetPeer peer) + { + ReadyClients += packet.ReadyPlayers; + + InformationPacket respondPackage = new(false) + { + NumberOfPlayers = netServer.ConnectedPeersCount, + ReadyPlayers = ReadyClients, + HostReady = coopHandler != null && coopHandler.LocalGameInstance.Status == GameStatus.Started + }; + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref respondPackage, DeliveryMethod.ReliableOrdered); + } + + private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packet, NetPeer peer) + { + if (coopHandler == null) + { + return; + } + + if (packet.IsRequest) + { + foreach (CoopPlayer player in coopHandler.Players.Values) + { + if (player.ProfileId == packet.ProfileId) + { + continue; + } + + if (packet.Characters.Contains(player.ProfileId)) + { + continue; + } + + AllCharacterRequestPacket requestPacket = new(player.ProfileId) + { + IsRequest = false, + PlayerInfo = new() + { + Profile = player.Profile + }, + IsAlive = player.HealthController.IsAlive, + IsAI = player is CoopBot, + Position = player.Transform.position, + NetId = player.NetId + }; + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); + } + } + if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) + { + PlayersMissing.Add(packet.ProfileId); + 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)) + { + 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); + PlayersMissing.Remove(packet.ProfileId); + } + } + } + + private void OnCommonPlayerPacketReceived(CommonPlayerPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.CommonPlayerPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + using MemoryStream memoryStream = new(packet.ItemControllerExecutePacket.OperationBytes); + using BinaryReader binaryReader = new(memoryStream); + try + { + GStruct411 result = playerToApply.ToInventoryOperation(binaryReader.ReadPolymorph()); + + InventoryOperationHandler opHandler = new() + { + opResult = result, + operationId = packet.ItemControllerExecutePacket.CallbackId, + netId = playerToApply.NetId, + peer = peer, + server = this + }; + + OperationCallbackPacket operationCallbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Started); + SendDataToPeer(peer, new(), ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + opHandler.opResult.Value.vmethod_0(new Callback(opHandler.HandleResult), false); + + // TODO: Hacky workaround to fix errors due to each client generating new IDs. Might need to find a more 'elegant' solution later. + // Unknown what problems this might cause so far. + if (result.Value is UnloadOperationClass unloadOperation) + { + if (unloadOperation.InternalOperation is SplitOperationClass internalSplitOperation) + { + Item item = internalSplitOperation.To.Item; + if (item != null) + { + if (item.Id != internalSplitOperation.CloneId && item.TemplateId == internalSplitOperation.Item.TemplateId) + { + item.Id = internalSplitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {internalSplitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + } + + // TODO: Same as above. + if (result.Value is SplitOperationClass splitOperation) + { + Item item = splitOperation.To.Item; + if (item != null) + { + if (item.Id != splitOperation.CloneId && item.TemplateId == splitOperation.Item.TemplateId) + { + item.Id = splitOperation.CloneId; + } + else + { + FikaPlugin.Instance.FikaLogger.LogWarning($"Matching failed: ItemID: {item.Id}, SplitOperationItemID: {splitOperation.To.Item.Id}"); + } + } + else + { + FikaPlugin.Instance.FikaLogger.LogError("Split: Item was null"); + } + } + + /*// Fix for folding not replicating if (result.Value is GClass2858 foldOperation) { if (playerToApply.HandsController is CoopObservedFirearmController observedFirearmController) @@ -923,324 +923,324 @@ private void OnInventoryPacketReceived(InventoryPacket packet, NetPeer peer) } } }*/ - } - catch (Exception exception) - { - FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); - OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); - SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); - } - } - } - - private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - 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(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) - { - if (!packet.IsRequest) - return; - - CoopGame game = coopHandler.LocalGameInstance; - if (game != null) - { - GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks, game.GameTimer.StartDateTime.Value.Ticks); - dataWriter.Reset(); - SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); - } - else - { - logger.LogError("OnGameTimerPacketReceived: Game was null!"); - } - } - - private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) - { - if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) - { - playerToApply.PacketReceiver.NewState = packet; - } - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); - } - - protected void Update() - { - netServer?.PollEvents(); - netServer?.NatPunchModule?.PollEvents(); - - statisticsCounter++; - if (statisticsCounter > 600) - { - statisticsCounter = 0; - SendStatisticsPacket(); - } - } - - private void SendStatisticsPacket() - { - int fps = (int)(1f / Time.unscaledDeltaTime); - StatisticsPacket packet = new(fps); - - dataWriter.Reset(); - SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); - } - - protected void OnDestroy() - { - netServer?.Stop(); - - if (fikaChat != null) - { - Destroy(fikaChat); - } - - FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); - } - - public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable - { - if (peerToExclude != null) - { - if (NetServer.ConnectedPeersCount > 1) - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod, peerToExclude); - } - } - else - { - packetProcessor.WriteNetSerializable(writer, ref packet); - netServer.SendToAll(writer, deliveryMethod); - } - } - - public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable - { - packetProcessor.WriteNetSerializable(writer, ref packet); - peer.Send(writer, deliveryMethod); - } - - public void OnPeerConnected(NetPeer peer) - { - NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); - logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); - - hasHadPeer = true; - } - - public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) - { - logger.LogError("[SERVER] error " + socketErrorCode); - } - - public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) - { - if (messageType == UnconnectedMessageType.Broadcast) - { - logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); - NetDataWriter resp = new(); - resp.Put(1); - netServer.SendUnconnectedMessage(resp, remoteEndPoint); - } - else - { - if (reader.TryGetString(out string data)) - { - NetDataWriter resp; - - switch (data) - { - 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 - { - logger.LogError("PingingRequest: Could not parse string"); - } - } - } - - public void OnNetworkLatencyUpdate(NetPeer peer, int latency) - { - } - - public void OnConnectionRequest(ConnectionRequest request) - { - request.AcceptIfKey("fika.core"); - } - - public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) - { - 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) - { - timeSinceLastPeerDisconnected = DateTime.Now; - } - - if (FikaBackendUtils.IsDedicatedGame) - { - if (netServer.ConnectedPeersCount == 0) - { - foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) - { - if (profile is null) - { - continue; - } - - if (profile.ProfileId == RequestHandler.SessionId) - { - foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) - { - bodyPartHealth.Effects.Clear(); - bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; - } - } - } - - // End the raid - Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, - Singleton.Instance.MyExitStatus, - Singleton.Instance.MyExitLocation, 0); - } - } - } - - public void WriteNet(NetLogLevel level, string str, params object[] args) - { - Debug.LogFormat(str, args); - } - - public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) - { - 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; - public uint operationId; - public int netId; - public NetPeer peer; - public FikaServer server; - - internal void HandleResult(IResult result) - { - NetDataWriter writer = new(); - OperationCallbackPacket operationCallbackPacket; - - if (!result.Succeed) - { - FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); - operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - - return; - } - - InventoryPacket packet = new(netId) - { - HasItemControllerExecutePacket = true - }; - - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new(memoryStream); - binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); - byte[] opBytes = memoryStream.ToArray(); - packet.ItemControllerExecutePacket = new() - { - CallbackId = operationId, - OperationBytes = opBytes - }; - - server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); - - operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); - writer.Reset(); - server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); - } - } - } + } + catch (Exception exception) + { + FikaPlugin.Instance.FikaLogger.LogError($"ItemControllerExecutePacket::Exception thrown: {exception}"); + OperationCallbackPacket callbackPacket = new(playerToApply.NetId, packet.ItemControllerExecutePacket.CallbackId, EOperationStatus.Failed); + SendDataToAll(new(), ref callbackPacket, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + } + } + + private void OnDamagePacketReceived(DamagePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + 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(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnFirearmPacketReceived(WeaponPacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.FirearmPackets?.Enqueue(packet); + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) + { + if (!packet.IsRequest) + return; + + CoopGame game = coopHandler.LocalGameInstance; + if (game != null) + { + GameTimerPacket gameTimerPacket = new(false, (game.GameTimer.SessionTime - game.GameTimer.PastTime).Value.Ticks, game.GameTimer.StartDateTime.Value.Ticks); + dataWriter.Reset(); + SendDataToPeer(peer, dataWriter, ref gameTimerPacket, DeliveryMethod.ReliableOrdered); + } + else + { + logger.LogError("OnGameTimerPacketReceived: Game was null!"); + } + } + + private void OnPlayerStatePacketReceived(PlayerStatePacket packet, NetPeer peer) + { + if (Players.TryGetValue(packet.NetId, out CoopPlayer playerToApply)) + { + playerToApply.PacketReceiver.NewState = packet; + } + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableOrdered, peer); + } + + protected void Update() + { + netServer?.PollEvents(); + netServer?.NatPunchModule?.PollEvents(); + + statisticsCounter++; + if (statisticsCounter > 600) + { + statisticsCounter = 0; + SendStatisticsPacket(); + } + } + + private void SendStatisticsPacket() + { + int fps = (int)(1f / Time.unscaledDeltaTime); + StatisticsPacket packet = new(fps); + + dataWriter.Reset(); + SendDataToAll(dataWriter, ref packet, DeliveryMethod.ReliableUnordered); + } + + protected void OnDestroy() + { + netServer?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + + FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); + } + + public void SendDataToAll(NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod, NetPeer peerToExclude = null) where T : INetSerializable + { + if (peerToExclude != null) + { + if (NetServer.ConnectedPeersCount > 1) + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod, peerToExclude); + } + } + else + { + packetProcessor.WriteNetSerializable(writer, ref packet); + netServer.SendToAll(writer, deliveryMethod); + } + } + + public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, DeliveryMethod deliveryMethod) where T : INetSerializable + { + packetProcessor.WriteNetSerializable(writer, ref packet); + peer.Send(writer, deliveryMethod); + } + + public void OnPeerConnected(NetPeer peer) + { + NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); + logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); + + hasHadPeer = true; + } + + public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + logger.LogError("[SERVER] error " + socketErrorCode); + } + + public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (messageType == UnconnectedMessageType.Broadcast) + { + logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); + NetDataWriter resp = new(); + resp.Put(1); + netServer.SendUnconnectedMessage(resp, remoteEndPoint); + } + else + { + if (reader.TryGetString(out string data)) + { + NetDataWriter resp; + + switch (data) + { + 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 + { + logger.LogError("PingingRequest: Could not parse string"); + } + } + } + + public void OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + } + + public void OnConnectionRequest(ConnectionRequest request) + { + request.AcceptIfKey("fika.core"); + } + + public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + 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) + { + timeSinceLastPeerDisconnected = DateTime.Now; + } + + if (FikaBackendUtils.IsDedicatedGame) + { + if (netServer.ConnectedPeersCount == 0) + { + foreach (Profile profile in Singleton>.Instance.Session.AllProfiles) + { + if (profile is null) + { + continue; + } + + if (profile.ProfileId == RequestHandler.SessionId) + { + foreach (Profile.ProfileHealthClass.GClass1770 bodyPartHealth in profile.Health.BodyParts.Values) + { + bodyPartHealth.Effects.Clear(); + bodyPartHealth.Health.Current = bodyPartHealth.Health.Maximum; + } + } + } + + // End the raid + Singleton.Instance.Stop(Singleton.Instance.MainPlayer.ProfileId, + Singleton.Instance.MyExitStatus, + Singleton.Instance.MyExitLocation, 0); + } + } + } + + public void WriteNet(NetLogLevel level, string str, params object[] args) + { + Debug.LogFormat(str, args); + } + + public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + 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; + public uint operationId; + public int netId; + public NetPeer peer; + public FikaServer server; + + internal void HandleResult(IResult result) + { + NetDataWriter writer = new(); + OperationCallbackPacket operationCallbackPacket; + + if (!result.Succeed) + { + FikaPlugin.Instance.FikaLogger.LogError($"Error in operation: {result.Error ?? "An unknown error has occured"}"); + operationCallbackPacket = new(netId, operationId, EOperationStatus.Failed, result.Error ?? "An unknown error has occured"); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + + return; + } + + InventoryPacket packet = new(netId) + { + HasItemControllerExecutePacket = true + }; + + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new(memoryStream); + binaryWriter.WritePolymorph(FromObjectAbstractClass.FromInventoryOperation(opResult.Value, false)); + byte[] opBytes = memoryStream.ToArray(); + packet.ItemControllerExecutePacket = new() + { + CallbackId = operationId, + OperationBytes = opBytes + }; + + server.SendDataToAll(writer, ref packet, DeliveryMethod.ReliableOrdered, peer); + + operationCallbackPacket = new(netId, operationId, EOperationStatus.Finished); + writer.Reset(); + server.SendDataToPeer(peer, writer, ref operationCallbackPacket, DeliveryMethod.ReliableOrdered); + } + } + } } diff --git a/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs b/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs index 01c10bd4..790a3be9 100644 --- a/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs +++ b/Fika.Core/Networking/Fuyu.Platform.Common/Http/FuyuClient.cs @@ -11,149 +11,149 @@ namespace Fuyu.Platform.Common.Http { - // NOTE: Don't dispose this, keep a reference for the lifetime of the - // application. - public class FuyuClient : IDisposable - { - protected HttpClient Httpv; - protected string Address; - protected string Cookie; - protected int Retries; - - public FuyuClient(string address, string sessionId = "", int retries = 3) - { - Address = address; - Cookie = $"PHPSESSID={sessionId}"; - Retries = retries; - - var handler = new HttpClientHandler - { - // set cookies in header instead - UseCookies = false - }; - - Httpv = new HttpClient(handler); - } - - protected HttpRequestMessage GetNewRequest(HttpMethod method, string path) - { - return new HttpRequestMessage() - { - Method = method, - RequestUri = new Uri(Address + path), - Headers = { - { "Cookie", Cookie } - } - }; - } - - protected async Task SendAsync(HttpMethod method, string path, byte[] data, bool zipped = true) - { - HttpResponseMessage response = null; - - using (var request = GetNewRequest(method, path)) - { - if (data != null) - { - // add payload to request - if (zipped) - { - data = Zlib.Compress(data, ZlibCompression.Maximum); - } - - request.Content = new ByteArrayContent(data); - } - - // send request - response = await Httpv.SendAsync(request); - } - - if (!response.IsSuccessStatusCode) - { - // response error - throw new Exception($"Code {response.StatusCode}"); - } - - using (var ms = new MemoryStream()) - { - using (var stream = await response.Content.ReadAsStreamAsync()) - { - // grap response payload - await stream.CopyToAsync(ms); - var body = ms.ToArray(); - - if (Zlib.IsCompressed(body)) - { - body = Zlib.Decompress(body); - } - - if (body == null) - { - // payload doesn't contains data - var code = response.StatusCode.ToString(); - body = Encoding.UTF8.GetBytes(code); - } - - return body; - } - } - } - - protected async Task SendWithRetriesAsync(HttpMethod method, string path, byte[] data, bool zipped = true) - { - var error = new Exception("Internal error"); - - // NOTE: <= is intentional. 0 is send, 1/2/3 is retry - for (var i = 0; i <= Retries; ++i) - { - try - { - return await SendAsync(method, path, data, zipped); - } - catch (Exception ex) - { - error = ex; - } - } - - throw error; - } - - public async Task GetAsync(string path) - { - return await SendWithRetriesAsync(HttpMethod.Get, path, null); - } - - public byte[] Get(string path) - { - return Task.Run(() => GetAsync(path)).Result; - } - - public async Task PostAsync(string path, byte[] data, bool zipped = true) - { - return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped); - } - - public byte[] Post(string path, byte[] data, bool zipped = true) - { - return Task.Run(() => PostAsync(path, data, zipped)).Result; - } - - // NOTE: returns status code as bytes - public async Task PutAsync(string path, byte[] data, bool zipped = true) - { - return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped); - } - - // NOTE: returns status code as bytes - public byte[] Put(string path, byte[] data, bool zipped = true) - { - return Task.Run(() => PutAsync(path, data, zipped)).Result; - } - - public void Dispose() - { - Httpv.Dispose(); - } - } + // NOTE: Don't dispose this, keep a reference for the lifetime of the + // application. + public class FuyuClient : IDisposable + { + protected HttpClient Httpv; + protected string Address; + protected string Cookie; + protected int Retries; + + public FuyuClient(string address, string sessionId = "", int retries = 3) + { + Address = address; + Cookie = $"PHPSESSID={sessionId}"; + Retries = retries; + + var handler = new HttpClientHandler + { + // set cookies in header instead + UseCookies = false + }; + + Httpv = new HttpClient(handler); + } + + protected HttpRequestMessage GetNewRequest(HttpMethod method, string path) + { + return new HttpRequestMessage() + { + Method = method, + RequestUri = new Uri(Address + path), + Headers = { + { "Cookie", Cookie } + } + }; + } + + protected async Task SendAsync(HttpMethod method, string path, byte[] data, bool zipped = true) + { + HttpResponseMessage response = null; + + using (var request = GetNewRequest(method, path)) + { + if (data != null) + { + // add payload to request + if (zipped) + { + data = Zlib.Compress(data, ZlibCompression.Maximum); + } + + request.Content = new ByteArrayContent(data); + } + + // send request + response = await Httpv.SendAsync(request); + } + + if (!response.IsSuccessStatusCode) + { + // response error + throw new Exception($"Code {response.StatusCode}"); + } + + using (var ms = new MemoryStream()) + { + using (var stream = await response.Content.ReadAsStreamAsync()) + { + // grap response payload + await stream.CopyToAsync(ms); + var body = ms.ToArray(); + + if (Zlib.IsCompressed(body)) + { + body = Zlib.Decompress(body); + } + + if (body == null) + { + // payload doesn't contains data + var code = response.StatusCode.ToString(); + body = Encoding.UTF8.GetBytes(code); + } + + return body; + } + } + } + + protected async Task SendWithRetriesAsync(HttpMethod method, string path, byte[] data, bool zipped = true) + { + var error = new Exception("Internal error"); + + // NOTE: <= is intentional. 0 is send, 1/2/3 is retry + for (var i = 0; i <= Retries; ++i) + { + try + { + return await SendAsync(method, path, data, zipped); + } + catch (Exception ex) + { + error = ex; + } + } + + throw error; + } + + public async Task GetAsync(string path) + { + return await SendWithRetriesAsync(HttpMethod.Get, path, null); + } + + public byte[] Get(string path) + { + return Task.Run(() => GetAsync(path)).Result; + } + + public async Task PostAsync(string path, byte[] data, bool zipped = true) + { + return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped); + } + + public byte[] Post(string path, byte[] data, bool zipped = true) + { + return Task.Run(() => PostAsync(path, data, zipped)).Result; + } + + // NOTE: returns status code as bytes + public async Task PutAsync(string path, byte[] data, bool zipped = true) + { + return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped); + } + + // NOTE: returns status code as bytes + public byte[] Put(string path, byte[] data, bool zipped = true) + { + return Task.Run(() => PutAsync(path, data, zipped)).Result; + } + + public void Dispose() + { + Httpv.Dispose(); + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Http/FikaRequestHandler.cs b/Fika.Core/Networking/Http/FikaRequestHandler.cs index 6349b314..f20a2994 100644 --- a/Fika.Core/Networking/Http/FikaRequestHandler.cs +++ b/Fika.Core/Networking/Http/FikaRequestHandler.cs @@ -13,169 +13,169 @@ namespace Fika.Core.Networking.Http { - public static class FikaRequestHandler - { - private static readonly FuyuClient _httpClient; - - static FikaRequestHandler() - { - _httpClient = new FuyuClient(RequestHandler.Host, RequestHandler.SessionId); - } - - private static byte[] EncodeBody(T o) - { - string serialized = JsonConvert.SerializeObject(o); - return Encoding.UTF8.GetBytes(serialized); - } - - private static T DecodeBody(byte[] data) - { - string json = Encoding.UTF8.GetString(data); - return JsonConvert.DeserializeObject(json); - } - - private static async Task GetJsonAsync(string path) - { - byte[] response = await _httpClient.GetAsync(path); - return DecodeBody(response); - } - - private static T GetJson(string path) - { - return Task.Run(() => GetJsonAsync(path)).Result; - } - - private static async Task PostJsonAsync(string path, T1 o) - { - byte[] data = EncodeBody(o); - byte[] response = await _httpClient.PostAsync(path, data); - return DecodeBody(response); - } - - private static T2 PostJson(string path, T1 o) - { - return Task.Run(() => PostJsonAsync(path, o)).Result; - } - - private static async Task PutJsonAsync(string path, T o) - { - byte[] data = EncodeBody(o); - return await _httpClient.PutAsync(path, data); - } - - private static byte[] PutJson(string path, T o) - { - return Task.Run(() => PutJsonAsync(path, o)).Result; - } - - public static BotDifficulties GetBotDifficulties() - { - return GetJson("/singleplayer/settings/bot/difficulties/"); - } - - 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); - } - - public static async Task UpdateSetStatus(SetStatusModel data) - { - await PutJsonAsync("/fika/update/setstatus", data); - } - - public static async Task UpdateSpawnPoint(UpdateSpawnPointRequest data) - { - await PutJsonAsync("/fika/update/spawnpoint", data); - } - - public static async Task UpdatePlayerSpawn(PlayerSpawnRequest data) - { - await PutJsonAsync("/fika/update/playerspawn", data); - } - - public static void UpdateSetHost(SetHostRequest data) - { - PutJson("/fika/update/sethost", data); - } - - public static void RaidLeave(PlayerLeftRequest data) - { - PutJson("/fika/raid/leave", data); - } - - public static CreateMatch RaidJoin(MatchJoinRequest data) - { - return PostJson("/fika/raid/join", data); - } - - public static void UpdateAddPlayer(AddPlayerRequest data) - { - PutJson("/fika/update/addplayer", data); - } - - public static async Task RaidCreate(CreateMatch data) - { - await PutJsonAsync("/fika/raid/create", data); - } - - public static GetHostResponse GetHost(GetHostRequest data) - { - return PostJson("/fika/raid/gethost", data); - } - - public static LobbyEntry[] LocationRaids(RaidSettings data) - { - return PostJson("/fika/location/raids", data); - } - - public static Dictionary AvailableReceivers(AvailableReceiversRequest data) - { - return PostJson>("/fika/senditem/availablereceivers", data); - } - - public static async Task GetRaidSettings(RaidSettingsRequest data) - { - return await PostJsonAsync("/fika/raid/getsettings", data); - } - - public static async Task GetProfile() - { - return await GetJsonAsync("/fika/profile/download"); - } - - public static async Task StartDedicated(StartDedicatedRequest request) - { - return await PostJsonAsync("/fika/raid/dedicated/start", request); - } - - public static async Task SetDedicatedStatus(SetDedicatedStatusRequest request) - { - return await PostJsonAsync("/fika/raid/dedicated/status", request); - } - - public static GetDedicatedStatusResponse GetDedicatedStatus() - { - return GetJson("/fika/raid/dedicated/getstatus"); - } - - public static async Task RegisterPlayer(RegisterPlayerRequest request) - { - await PutJsonAsync("/fika/raid/registerPlayer", request); - } - - public static async Task PlayerDied(AddPlayerRequest request) - { - await PutJsonAsync("/fika/update/playerdied", request); - } - } + public static class FikaRequestHandler + { + private static readonly FuyuClient _httpClient; + + static FikaRequestHandler() + { + _httpClient = new FuyuClient(RequestHandler.Host, RequestHandler.SessionId); + } + + private static byte[] EncodeBody(T o) + { + string serialized = JsonConvert.SerializeObject(o); + return Encoding.UTF8.GetBytes(serialized); + } + + private static T DecodeBody(byte[] data) + { + string json = Encoding.UTF8.GetString(data); + return JsonConvert.DeserializeObject(json); + } + + private static async Task GetJsonAsync(string path) + { + byte[] response = await _httpClient.GetAsync(path); + return DecodeBody(response); + } + + private static T GetJson(string path) + { + return Task.Run(() => GetJsonAsync(path)).Result; + } + + private static async Task PostJsonAsync(string path, T1 o) + { + byte[] data = EncodeBody(o); + byte[] response = await _httpClient.PostAsync(path, data); + return DecodeBody(response); + } + + private static T2 PostJson(string path, T1 o) + { + return Task.Run(() => PostJsonAsync(path, o)).Result; + } + + private static async Task PutJsonAsync(string path, T o) + { + byte[] data = EncodeBody(o); + return await _httpClient.PutAsync(path, data); + } + + private static byte[] PutJson(string path, T o) + { + return Task.Run(() => PutJsonAsync(path, o)).Result; + } + + public static BotDifficulties GetBotDifficulties() + { + return GetJson("/singleplayer/settings/bot/difficulties/"); + } + + 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); + } + + public static async Task UpdateSetStatus(SetStatusModel data) + { + await PutJsonAsync("/fika/update/setstatus", data); + } + + public static async Task UpdateSpawnPoint(UpdateSpawnPointRequest data) + { + await PutJsonAsync("/fika/update/spawnpoint", data); + } + + public static async Task UpdatePlayerSpawn(PlayerSpawnRequest data) + { + await PutJsonAsync("/fika/update/playerspawn", data); + } + + public static void UpdateSetHost(SetHostRequest data) + { + PutJson("/fika/update/sethost", data); + } + + public static void RaidLeave(PlayerLeftRequest data) + { + PutJson("/fika/raid/leave", data); + } + + public static CreateMatch RaidJoin(MatchJoinRequest data) + { + return PostJson("/fika/raid/join", data); + } + + public static void UpdateAddPlayer(AddPlayerRequest data) + { + PutJson("/fika/update/addplayer", data); + } + + public static async Task RaidCreate(CreateMatch data) + { + await PutJsonAsync("/fika/raid/create", data); + } + + public static GetHostResponse GetHost(GetHostRequest data) + { + return PostJson("/fika/raid/gethost", data); + } + + public static LobbyEntry[] LocationRaids(RaidSettings data) + { + return PostJson("/fika/location/raids", data); + } + + public static Dictionary AvailableReceivers(AvailableReceiversRequest data) + { + return PostJson>("/fika/senditem/availablereceivers", data); + } + + public static async Task GetRaidSettings(RaidSettingsRequest data) + { + return await PostJsonAsync("/fika/raid/getsettings", data); + } + + public static async Task GetProfile() + { + return await GetJsonAsync("/fika/profile/download"); + } + + public static async Task StartDedicated(StartDedicatedRequest request) + { + return await PostJsonAsync("/fika/raid/dedicated/start", request); + } + + public static async Task SetDedicatedStatus(SetDedicatedStatusRequest request) + { + return await PostJsonAsync("/fika/raid/dedicated/status", request); + } + + public static GetDedicatedStatusResponse GetDedicatedStatus() + { + return GetJson("/fika/raid/dedicated/getstatus"); + } + + public static async Task RegisterPlayer(RegisterPlayerRequest request) + { + await PutJsonAsync("/fika/raid/registerPlayer", request); + } + + public static async Task PlayerDied(AddPlayerRequest request) + { + await PutJsonAsync("/fika/update/playerdied", request); + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/AddPlayerRequest.cs b/Fika.Core/Networking/Models/AddPlayerRequest.cs index 2fea0ced..88e504c7 100644 --- a/Fika.Core/Networking/Models/AddPlayerRequest.cs +++ b/Fika.Core/Networking/Models/AddPlayerRequest.cs @@ -2,19 +2,19 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct AddPlayerRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct AddPlayerRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "profileId")] - public string ProfileId; + [DataMember(Name = "profileId")] + public string ProfileId; - public AddPlayerRequest(string serverId, string profileId) - { - ServerId = serverId; - ProfileId = profileId; - } - } + public AddPlayerRequest(string serverId, string profileId) + { + ServerId = serverId; + ProfileId = profileId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/CreateMatchRequest.cs b/Fika.Core/Networking/Models/CreateMatchRequest.cs index 1c3cde97..cb0eb971 100644 --- a/Fika.Core/Networking/Models/CreateMatchRequest.cs +++ b/Fika.Core/Networking/Models/CreateMatchRequest.cs @@ -6,51 +6,51 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct CreateMatch - { - [DataMember(Name = "raidCode")] - public string RaidCode; - - [DataMember(Name = "serverId")] - public string ServerId; - - [DataMember(Name = "hostUsername")] - public string HostUsername; - - [DataMember(Name = "timestamp")] - public long Timestamp; - - [DataMember(Name = "settings")] - public RaidSettings Settings; - - [DataMember(Name = "expectedNumberOfPlayers")] - public int ExpectedNumberOfPlayers; - - [DataMember(Name = "gameVersion")] - public string GameVersion; - - [DataMember(Name = "fikaVersion")] - public Version FikaVersion; - - [DataMember(Name = "side")] - public ESideType Side; - - [DataMember(Name = "time")] - public 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; - Settings = settings; - ExpectedNumberOfPlayers = expectedNumberOfPlayers; - GameVersion = FikaPlugin.EFTVersionMajor; - FikaVersion = Assembly.GetExecutingAssembly().GetName().Version; - Side = side; - Time = time; - } - } + [DataContract] + public struct CreateMatch + { + [DataMember(Name = "raidCode")] + public string RaidCode; + + [DataMember(Name = "serverId")] + public string ServerId; + + [DataMember(Name = "hostUsername")] + public string HostUsername; + + [DataMember(Name = "timestamp")] + public long Timestamp; + + [DataMember(Name = "settings")] + public RaidSettings Settings; + + [DataMember(Name = "expectedNumberOfPlayers")] + public int ExpectedNumberOfPlayers; + + [DataMember(Name = "gameVersion")] + public string GameVersion; + + [DataMember(Name = "fikaVersion")] + public Version FikaVersion; + + [DataMember(Name = "side")] + public ESideType Side; + + [DataMember(Name = "time")] + public 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; + Settings = settings; + ExpectedNumberOfPlayers = expectedNumberOfPlayers; + GameVersion = FikaPlugin.EFTVersionMajor; + FikaVersion = Assembly.GetExecutingAssembly().GetName().Version; + Side = side; + Time = time; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/Dedicated/DedicatedStatus.cs b/Fika.Core/Networking/Models/Dedicated/DedicatedStatus.cs index 25e46951..0069feef 100644 --- a/Fika.Core/Networking/Models/Dedicated/DedicatedStatus.cs +++ b/Fika.Core/Networking/Models/Dedicated/DedicatedStatus.cs @@ -2,6 +2,6 @@ public enum DedicatedStatus { - READY = 1, - IN_RAID = 2 + READY = 1, + IN_RAID = 2 } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/Dedicated/GetDedicatedStatusResponse.cs b/Fika.Core/Networking/Models/Dedicated/GetDedicatedStatusResponse.cs index 5dd466a4..264b3884 100644 --- a/Fika.Core/Networking/Models/Dedicated/GetDedicatedStatusResponse.cs +++ b/Fika.Core/Networking/Models/Dedicated/GetDedicatedStatusResponse.cs @@ -2,10 +2,10 @@ namespace Fika.Core.Networking.Models.Dedicated { - [DataContract] - public struct GetDedicatedStatusResponse(bool available) - { - [DataMember(Name = "available")] - public bool Available = available; - } + [DataContract] + public struct GetDedicatedStatusResponse(bool available) + { + [DataMember(Name = "available")] + public bool Available = available; + } } diff --git a/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusRequest.cs b/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusRequest.cs index 2f152f31..33dd9c82 100644 --- a/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusRequest.cs +++ b/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusRequest.cs @@ -2,19 +2,19 @@ namespace Fika.Core.Networking.Models.Dedicated { - [DataContract] - public struct SetDedicatedStatusRequest - { - [DataMember(Name = "sessionId")] - public string SessionId { get; set; } + [DataContract] + public struct SetDedicatedStatusRequest + { + [DataMember(Name = "sessionId")] + public string SessionId { get; set; } - [DataMember(Name = "status")] - public DedicatedStatus Status { get; set; } + [DataMember(Name = "status")] + public DedicatedStatus Status { get; set; } - public SetDedicatedStatusRequest(string sessionId, DedicatedStatus status) - { - SessionId = sessionId; - Status = status; - } - } + public SetDedicatedStatusRequest(string sessionId, DedicatedStatus status) + { + SessionId = sessionId; + Status = status; + } + } } diff --git a/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusResponse.cs b/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusResponse.cs index 0c0d501a..6b0ef329 100644 --- a/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusResponse.cs +++ b/Fika.Core/Networking/Models/Dedicated/SetDedicatedStatusResponse.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Networking.Models.Dedicated { - [DataContract] - public struct SetDedicatedStatusResponse - { - [DataMember(Name = "sessionId")] - public string SessionId { get; set; } + [DataContract] + public struct SetDedicatedStatusResponse + { + [DataMember(Name = "sessionId")] + public string SessionId { get; set; } - [DataMember(Name = "status")] - public DedicatedStatus Status { get; set; } - } + [DataMember(Name = "status")] + public DedicatedStatus Status { get; set; } + } } diff --git a/Fika.Core/Networking/Models/Dedicated/StartDedicatedRequest.cs b/Fika.Core/Networking/Models/Dedicated/StartDedicatedRequest.cs index a63ea0a1..d80baeca 100644 --- a/Fika.Core/Networking/Models/Dedicated/StartDedicatedRequest.cs +++ b/Fika.Core/Networking/Models/Dedicated/StartDedicatedRequest.cs @@ -5,37 +5,37 @@ namespace Fika.Core.Networking.Models.Dedicated { - [DataContract] - public struct StartDedicatedRequest - { - [DataMember(Name = "expectedNumberOfPlayers")] - public int ExpectedNumPlayers { get; set; } + [DataContract] + public struct StartDedicatedRequest + { + [DataMember(Name = "expectedNumberOfPlayers")] + public int ExpectedNumPlayers { get; set; } - [DataMember(Name = "time")] - public EDateTime Time { get; set; } + [DataMember(Name = "time")] + public EDateTime Time { get; set; } - [DataMember(Name = "locationId")] - public string LocationId { readonly get; set; } + [DataMember(Name = "locationId")] + public string LocationId { readonly get; set; } - [DataMember(Name = "spawnPlace")] - public EPlayersSpawnPlace SpawnPlace { readonly get; set; } + [DataMember(Name = "spawnPlace")] + public EPlayersSpawnPlace SpawnPlace { readonly get; set; } - [DataMember(Name = "metabolismDisabled")] - public bool MetabolismDisabled { readonly get; set; } + [DataMember(Name = "metabolismDisabled")] + public bool MetabolismDisabled { readonly get; set; } - [DataMember(Name = "timeAndWeatherSettings")] - public TimeAndWeatherSettings TimeAndWeatherSettings { readonly get; set; } + [DataMember(Name = "timeAndWeatherSettings")] + public TimeAndWeatherSettings TimeAndWeatherSettings { readonly get; set; } - [DataMember(Name = "botSettings")] - public BotControllerSettings BotSettings { readonly get; set; } + [DataMember(Name = "botSettings")] + public BotControllerSettings BotSettings { readonly get; set; } - [DataMember(Name = "wavesSettings")] - public WavesSettings WavesSettings { readonly get; set; } + [DataMember(Name = "wavesSettings")] + public WavesSettings WavesSettings { readonly get; set; } - [DataMember(Name = "side")] - public ESideType Side { readonly get; set; } + [DataMember(Name = "side")] + public ESideType Side { readonly get; set; } - [DataMember(Name = "customWeather")] - public bool CustomWeather { readonly get; set; } - } + [DataMember(Name = "customWeather")] + public bool CustomWeather { readonly get; set; } + } } diff --git a/Fika.Core/Networking/Models/Dedicated/StartDedicatedResponse.cs b/Fika.Core/Networking/Models/Dedicated/StartDedicatedResponse.cs index a1323b71..a10b4ee3 100644 --- a/Fika.Core/Networking/Models/Dedicated/StartDedicatedResponse.cs +++ b/Fika.Core/Networking/Models/Dedicated/StartDedicatedResponse.cs @@ -2,13 +2,13 @@ namespace Fika.Core.Networking.Models.Dedicated { - [DataContract] - public struct StartDedicatedResponse - { - [DataMember(Name = "matchId")] - public string MatchId { get; set; } + [DataContract] + public struct StartDedicatedResponse + { + [DataMember(Name = "matchId")] + public string MatchId { get; set; } - [DataMember(Name = "error")] - public string Error { get; set; } - } + [DataMember(Name = "error")] + public string Error { get; set; } + } } diff --git a/Fika.Core/Networking/Models/GetHostRequest.cs b/Fika.Core/Networking/Models/GetHostRequest.cs index 8ce16366..2f7d201d 100644 --- a/Fika.Core/Networking/Models/GetHostRequest.cs +++ b/Fika.Core/Networking/Models/GetHostRequest.cs @@ -2,15 +2,15 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct GetHostRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct GetHostRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - public GetHostRequest(string serverId) - { - ServerId = serverId; - } - } + public GetHostRequest(string serverId) + { + ServerId = serverId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/GetHostResponse.cs b/Fika.Core/Networking/Models/GetHostResponse.cs index 513a1915..5556d292 100644 --- a/Fika.Core/Networking/Models/GetHostResponse.cs +++ b/Fika.Core/Networking/Models/GetHostResponse.cs @@ -2,27 +2,27 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct GetHostResponse - { - [DataMember(Name = "ips")] - public string[] Ips; + [DataContract] + public struct GetHostResponse + { + [DataMember(Name = "ips")] + public string[] Ips; - [DataMember(Name = "port")] - public int Port; + [DataMember(Name = "port")] + public int Port; - [DataMember(Name = "natPunch")] - public bool NatPunch; + [DataMember(Name = "natPunch")] + public bool NatPunch; - [DataMember(Name = "isDedicated")] - public bool IsDedicated; + [DataMember(Name = "isDedicated")] + public bool IsDedicated; - public GetHostResponse(string[] ips, int port, bool natPunch, bool isDedicated) - { - Ips = ips; - Port = port; - NatPunch = natPunch; - IsDedicated = isDedicated; - } - } + public GetHostResponse(string[] ips, int port, bool natPunch, bool isDedicated) + { + Ips = ips; + Port = port; + NatPunch = natPunch; + IsDedicated = isDedicated; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/GetHostStunRequest.cs b/Fika.Core/Networking/Models/GetHostStunRequest.cs index fe6c621e..e7a2f721 100644 --- a/Fika.Core/Networking/Models/GetHostStunRequest.cs +++ b/Fika.Core/Networking/Models/GetHostStunRequest.cs @@ -2,31 +2,31 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct GetHostStunRequest - { - [DataMember(Name = "requestType")] - public string RequestType; + [DataContract] + public struct GetHostStunRequest + { + [DataMember(Name = "requestType")] + public string RequestType; - [DataMember(Name = "sessionId")] - public string SessionId; + [DataMember(Name = "sessionId")] + public string SessionId; - [DataMember(Name = "serverId")] - public string ServerId; + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "stunIp")] - public string StunIp; + [DataMember(Name = "stunIp")] + public string StunIp; - [DataMember(Name = "stunPort")] - public int StunPort; + [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; - } - } + 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 index da74a9fb..d7ac0c7c 100644 --- a/Fika.Core/Networking/Models/GetHostStunResponse.cs +++ b/Fika.Core/Networking/Models/GetHostStunResponse.cs @@ -3,23 +3,23 @@ [DataContract] public struct GetHostStunResponse { - [DataMember(Name = "requestType")] - public string RequestType; + [DataMember(Name = "requestType")] + public string RequestType; - [DataMember(Name = "sessionId")] - public string SessionId; + [DataMember(Name = "sessionId")] + public string SessionId; - [DataMember(Name = "StunIp")] - public string StunIp; + [DataMember(Name = "StunIp")] + public string StunIp; - [DataMember(Name = "StunPort")] - public int StunPort; + [DataMember(Name = "StunPort")] + public int StunPort; - public GetHostStunResponse(string sessionId, string stunIp, int stunPort) - { - RequestType = GetType().Name; - SessionId = sessionId; - StunIp = stunIp; - StunPort = 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/MatchJoinRequest.cs b/Fika.Core/Networking/Models/MatchJoinRequest.cs index 82778d7b..d43e572c 100644 --- a/Fika.Core/Networking/Models/MatchJoinRequest.cs +++ b/Fika.Core/Networking/Models/MatchJoinRequest.cs @@ -2,19 +2,19 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct MatchJoinRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct MatchJoinRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "profileId")] - public string ProfileId; + [DataMember(Name = "profileId")] + public string ProfileId; - public MatchJoinRequest(string serverId, string profileId) - { - ServerId = serverId; - ProfileId = profileId; - } - } + public MatchJoinRequest(string serverId, string profileId) + { + ServerId = serverId; + ProfileId = profileId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/MatchJoinResponse.cs b/Fika.Core/Networking/Models/MatchJoinResponse.cs index c0382cd6..7014be08 100644 --- a/Fika.Core/Networking/Models/MatchJoinResponse.cs +++ b/Fika.Core/Networking/Models/MatchJoinResponse.cs @@ -2,19 +2,19 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct MatchJoinResponse - { - [DataMember(Name = "gameVersion")] - public string GameVersion; + [DataContract] + public struct MatchJoinResponse + { + [DataMember(Name = "gameVersion")] + public string GameVersion; - [DataMember(Name = "fikaVersion")] - public string FikaVersion; + [DataMember(Name = "fikaVersion")] + public string FikaVersion; - public MatchJoinResponse(string gameVersion, string fikaVersion) - { - GameVersion = gameVersion; - FikaVersion = fikaVersion; - } - } + public MatchJoinResponse(string gameVersion, string fikaVersion) + { + GameVersion = gameVersion; + FikaVersion = fikaVersion; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/ModValidationResponse.cs b/Fika.Core/Networking/Models/ModValidationResponse.cs index f5fba64c..8d681c60 100644 --- a/Fika.Core/Networking/Models/ModValidationResponse.cs +++ b/Fika.Core/Networking/Models/ModValidationResponse.cs @@ -2,16 +2,16 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct ModValidationResponse - { - [DataMember(Name = "forbidden")] - public string[] Forbidden; + [DataContract] + public struct ModValidationResponse + { + [DataMember(Name = "forbidden")] + public string[] Forbidden; - [DataMember(Name = "missingRequired")] - public string[] MissingRequired; + [DataMember(Name = "missingRequired")] + public string[] MissingRequired; - [DataMember(Name = "hashMismatch")] - public string[] HashMismatch; - } + [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 45add8ba..6dba3c72 100644 --- a/Fika.Core/Networking/Models/PingRequest.cs +++ b/Fika.Core/Networking/Models/PingRequest.cs @@ -3,15 +3,15 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct PingRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct PingRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - public PingRequest() - { - ServerId = FikaBackendUtils.GetGroupId(); - } - } + public PingRequest() + { + ServerId = FikaBackendUtils.GetGroupId(); + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/PlayerLeftRequest.cs b/Fika.Core/Networking/Models/PlayerLeftRequest.cs index 1122e310..278ad581 100644 --- a/Fika.Core/Networking/Models/PlayerLeftRequest.cs +++ b/Fika.Core/Networking/Models/PlayerLeftRequest.cs @@ -3,19 +3,19 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct PlayerLeftRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct PlayerLeftRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "profileId")] - public string ProfileId; + [DataMember(Name = "profileId")] + public string ProfileId; - public PlayerLeftRequest(string profileId) - { - ServerId = CoopHandler.GetServerId(); - ProfileId = profileId; - } - } + public PlayerLeftRequest(string profileId) + { + ServerId = CoopHandler.GetServerId(); + ProfileId = profileId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/PlayerSpawnRequest.cs b/Fika.Core/Networking/Models/PlayerSpawnRequest.cs index 151abcaa..150839db 100644 --- a/Fika.Core/Networking/Models/PlayerSpawnRequest.cs +++ b/Fika.Core/Networking/Models/PlayerSpawnRequest.cs @@ -3,23 +3,23 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct PlayerSpawnRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct PlayerSpawnRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "profileId")] - public string ProfileId; + [DataMember(Name = "profileId")] + public string ProfileId; - [DataMember(Name = "groupId")] - public string GroupId; + [DataMember(Name = "groupId")] + public string GroupId; - public PlayerSpawnRequest(string profileId, string groupId) - { - ServerId = CoopHandler.GetServerId(); - ProfileId = profileId; - GroupId = groupId; - } - } + public PlayerSpawnRequest(string profileId, string groupId) + { + ServerId = CoopHandler.GetServerId(); + ProfileId = profileId; + GroupId = groupId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/RaidSettingsRequest.cs b/Fika.Core/Networking/Models/RaidSettingsRequest.cs index b6df6a28..602ece07 100644 --- a/Fika.Core/Networking/Models/RaidSettingsRequest.cs +++ b/Fika.Core/Networking/Models/RaidSettingsRequest.cs @@ -3,15 +3,15 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct RaidSettingsRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct RaidSettingsRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - public RaidSettingsRequest() - { - ServerId = CoopHandler.GetServerId(); - } - } + public RaidSettingsRequest() + { + ServerId = CoopHandler.GetServerId(); + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/RaidSettingsResponse.cs b/Fika.Core/Networking/Models/RaidSettingsResponse.cs index bb627ba5..d9d2d0f8 100644 --- a/Fika.Core/Networking/Models/RaidSettingsResponse.cs +++ b/Fika.Core/Networking/Models/RaidSettingsResponse.cs @@ -2,12 +2,12 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct RaidSettingsResponse(bool metabolismDisabled, string playersSpawnPlace) - { - [DataMember(Name = "metabolismDisabled")] - public bool MetabolismDisabled = metabolismDisabled; - [DataMember(Name = "playersSpawnPlace")] - public string PlayersSpawnPlace = playersSpawnPlace; - } + [DataContract] + public struct RaidSettingsResponse(bool metabolismDisabled, string playersSpawnPlace) + { + [DataMember(Name = "metabolismDisabled")] + public bool MetabolismDisabled = metabolismDisabled; + [DataMember(Name = "playersSpawnPlace")] + public string PlayersSpawnPlace = playersSpawnPlace; + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/RegisterPlayerRequest.cs b/Fika.Core/Networking/Models/RegisterPlayerRequest.cs index 9f38292b..80848ef4 100644 --- a/Fika.Core/Networking/Models/RegisterPlayerRequest.cs +++ b/Fika.Core/Networking/Models/RegisterPlayerRequest.cs @@ -2,23 +2,23 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct RegisterPlayerRequest - { - [DataMember(Name = "crc")] - public int Crc; + [DataContract] + public struct RegisterPlayerRequest + { + [DataMember(Name = "crc")] + public int Crc; - [DataMember(Name = "locationId")] - public string LocationId; + [DataMember(Name = "locationId")] + public string LocationId; - [DataMember(Name = "variantId")] - public int VariantId; + [DataMember(Name = "variantId")] + public int VariantId; - public RegisterPlayerRequest(int crc, string locationId, int variantId) - { - Crc = crc; - LocationId = locationId; - VariantId = variantId; - } - } + public RegisterPlayerRequest(int crc, string locationId, int variantId) + { + Crc = crc; + LocationId = locationId; + VariantId = variantId; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/SetHostRequest.cs b/Fika.Core/Networking/Models/SetHostRequest.cs index c8876aaa..c9222b6b 100644 --- a/Fika.Core/Networking/Models/SetHostRequest.cs +++ b/Fika.Core/Networking/Models/SetHostRequest.cs @@ -3,31 +3,31 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct SetHostRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct SetHostRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "ips")] - public string[] Ips; + [DataMember(Name = "ips")] + public string[] Ips; - [DataMember(Name = "port")] - public int Port; + [DataMember(Name = "port")] + public int Port; - [DataMember(Name = "natPunch")] - public bool NatPunch; + [DataMember(Name = "natPunch")] + public bool NatPunch; - [DataMember(Name = "isDedicated")] - public bool IsDedicated; + [DataMember(Name = "isDedicated")] + public bool IsDedicated; - public SetHostRequest(string[] ips, int port, bool natPunch, bool isDedicated) - { - ServerId = CoopHandler.GetServerId(); - Ips = ips; - Port = port; - NatPunch = natPunch; - IsDedicated = isDedicated; - } - } + public SetHostRequest(string[] ips, int port, bool natPunch, bool isDedicated) + { + ServerId = CoopHandler.GetServerId(); + Ips = ips; + Port = port; + NatPunch = natPunch; + IsDedicated = isDedicated; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/SetStatusModel.cs b/Fika.Core/Networking/Models/SetStatusModel.cs index 7809033e..6a2fd140 100644 --- a/Fika.Core/Networking/Models/SetStatusModel.cs +++ b/Fika.Core/Networking/Models/SetStatusModel.cs @@ -3,19 +3,19 @@ namespace Fika.Core.UI.Models { - [DataContract] - public struct SetStatusModel - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct SetStatusModel + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "status")] - public ELobbyStatus Status; + [DataMember(Name = "status")] + public ELobbyStatus Status; - public SetStatusModel(string serverId, ELobbyStatus status) - { - ServerId = serverId; - Status = status; - } - } + public SetStatusModel(string serverId, ELobbyStatus status) + { + ServerId = serverId; + Status = status; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Models/UpdateSpawnPointRequest.cs b/Fika.Core/Networking/Models/UpdateSpawnPointRequest.cs index 9bdbe07e..c661a85c 100644 --- a/Fika.Core/Networking/Models/UpdateSpawnPointRequest.cs +++ b/Fika.Core/Networking/Models/UpdateSpawnPointRequest.cs @@ -3,19 +3,19 @@ namespace Fika.Core.Networking.Http.Models { - [DataContract] - public struct UpdateSpawnPointRequest - { - [DataMember(Name = "serverId")] - public string ServerId; + [DataContract] + public struct UpdateSpawnPointRequest + { + [DataMember(Name = "serverId")] + public string ServerId; - [DataMember(Name = "name")] - public string Name; + [DataMember(Name = "name")] + public string Name; - public UpdateSpawnPointRequest(string name) - { - ServerId = CoopHandler.GetServerId(); - Name = name; - } - } + public UpdateSpawnPointRequest(string name) + { + ServerId = CoopHandler.GetServerId(); + Name = name; + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs index d2ebd63d..f29acf27 100644 --- a/Fika.Core/Networking/Packets/Backend/InformationPacket.cs +++ b/Fika.Core/Networking/Packets/Backend/InformationPacket.cs @@ -4,27 +4,27 @@ namespace Fika.Core.Networking { - public struct InformationPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public int NumberOfPlayers = 0; - public int ReadyPlayers = 0; - public bool HostReady = false; + public struct InformationPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public int NumberOfPlayers = 0; + public int ReadyPlayers = 0; + public bool HostReady = false; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - NumberOfPlayers = reader.GetInt(); - ReadyPlayers = reader.GetInt(); - HostReady = reader.GetBool(); - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + NumberOfPlayers = reader.GetInt(); + ReadyPlayers = reader.GetInt(); + HostReady = reader.GetBool(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(NumberOfPlayers); - writer.Put(ReadyPlayers); - writer.Put(HostReady); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(NumberOfPlayers); + writer.Put(ReadyPlayers); + writer.Put(HostReady); + } + } } diff --git a/Fika.Core/Networking/Packets/Backend/StatisticsPacket.cs b/Fika.Core/Networking/Packets/Backend/StatisticsPacket.cs index 8c4e749d..e11e4b2c 100644 --- a/Fika.Core/Networking/Packets/Backend/StatisticsPacket.cs +++ b/Fika.Core/Networking/Packets/Backend/StatisticsPacket.cs @@ -2,18 +2,18 @@ namespace Fika.Core.Networking { - public struct StatisticsPacket(int serverFps) : INetSerializable - { - public int ServerFPS = serverFps; + public struct StatisticsPacket(int serverFps) : INetSerializable + { + public int ServerFPS = serverFps; - public void Deserialize(NetDataReader reader) - { - ServerFPS = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + ServerFPS = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(ServerFPS); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(ServerFPS); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/AllCharacterRequestPacket.cs b/Fika.Core/Networking/Packets/Communication/AllCharacterRequestPacket.cs index db431408..0a43c6d7 100644 --- a/Fika.Core/Networking/Packets/Communication/AllCharacterRequestPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/AllCharacterRequestPacket.cs @@ -6,54 +6,54 @@ namespace Fika.Core.Networking { - public struct AllCharacterRequestPacket(string profileId) : INetSerializable - { - public bool IsRequest = true; - public string ProfileId = profileId; - public bool HasCharacters = false; - public string[] Characters; - public PlayerInfoPacket PlayerInfo; - public bool IsAlive = true; - public bool IsAI = false; - public Vector3 Position; - public int NetId; + public struct AllCharacterRequestPacket(string profileId) : INetSerializable + { + public bool IsRequest = true; + public string ProfileId = profileId; + public bool HasCharacters = false; + public string[] Characters; + public PlayerInfoPacket PlayerInfo; + public bool IsAlive = true; + public bool IsAI = false; + public Vector3 Position; + public int NetId; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - ProfileId = reader.GetString(); - HasCharacters = reader.GetBool(); - if (HasCharacters) - { - Characters = reader.GetStringArray(); - } - if (!IsRequest) - { - PlayerInfo = PlayerInfoPacket.Deserialize(reader); - } - IsAlive = reader.GetBool(); - IsAI = reader.GetBool(); - Position = reader.GetVector3(); - NetId = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + ProfileId = reader.GetString(); + HasCharacters = reader.GetBool(); + if (HasCharacters) + { + Characters = reader.GetStringArray(); + } + if (!IsRequest) + { + PlayerInfo = PlayerInfoPacket.Deserialize(reader); + } + IsAlive = reader.GetBool(); + IsAI = reader.GetBool(); + Position = reader.GetVector3(); + NetId = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(ProfileId); - writer.Put(HasCharacters); - if (HasCharacters) - { - writer.PutArray(Characters); - } - if (!IsRequest) - { - PlayerInfoPacket.Serialize(writer, PlayerInfo); - } - writer.Put(IsAlive); - writer.Put(IsAI); - writer.Put(Position); - writer.Put(NetId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(ProfileId); + writer.Put(HasCharacters); + if (HasCharacters) + { + writer.PutArray(Characters); + } + if (!IsRequest) + { + PlayerInfoPacket.Serialize(writer, PlayerInfo); + } + writer.Put(IsAlive); + writer.Put(IsAI); + writer.Put(Position); + writer.Put(NetId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/AssignNetIdPacket.cs b/Fika.Core/Networking/Packets/Communication/AssignNetIdPacket.cs index e5483a3a..e841c8c9 100644 --- a/Fika.Core/Networking/Packets/Communication/AssignNetIdPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/AssignNetIdPacket.cs @@ -2,18 +2,18 @@ namespace Fika.Core.Networking { - public struct AssignNetIdPacket : INetSerializable - { - public int NetId; + public struct AssignNetIdPacket : INetSerializable + { + public int NetId; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs index 783acc36..3d35b645 100644 --- a/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/QuestConditionPacket.cs @@ -2,24 +2,24 @@ namespace Fika.Core.Networking { - public struct QuestConditionPacket(string nickname, string id, string sourceId) : INetSerializable - { - public string Nickname = nickname; - public string Id = id; - public string SourceId = sourceId; + 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 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); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(Id); + writer.Put(SourceId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs index 76347b38..07df34d6 100644 --- a/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/QuestDropItemPacket.cs @@ -2,24 +2,24 @@ namespace Fika.Core.Networking { - public struct QuestDropItemPacket(string nickname, string itemId, string zoneId) : INetSerializable - { - public string Nickname = nickname; - public string ItemId = itemId; - public string ZoneId = zoneId; + public struct QuestDropItemPacket(string nickname, string itemId, string zoneId) : INetSerializable + { + public string Nickname = nickname; + public string ItemId = itemId; + public string ZoneId = zoneId; - public void Deserialize(NetDataReader reader) - { - Nickname = reader.GetString(); - ItemId = reader.GetString(); - ZoneId = reader.GetString(); - } + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + ItemId = reader.GetString(); + ZoneId = reader.GetString(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(Nickname); - writer.Put(ItemId); - writer.Put(ZoneId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(ItemId); + writer.Put(ZoneId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs index c8c59893..6aeaf2a8 100644 --- a/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/QuestItemPacket.cs @@ -2,21 +2,21 @@ namespace Fika.Core.Networking { - public struct QuestItemPacket(string nickname, string itemId) : INetSerializable - { - public string Nickname = nickname; - public string ItemId = itemId; + 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 Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + ItemId = reader.GetString(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(Nickname); - writer.Put(ItemId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(ItemId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/SendCharacterPacket.cs b/Fika.Core/Networking/Packets/Communication/SendCharacterPacket.cs index 6c741bb7..f1b7d0b5 100644 --- a/Fika.Core/Networking/Packets/Communication/SendCharacterPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/SendCharacterPacket.cs @@ -4,30 +4,30 @@ namespace Fika.Core.Networking { - public struct SendCharacterPacket(PlayerInfoPacket playerInfoPacket, bool isAlive, bool isAi, Vector3 position, int netId) : INetSerializable - { - public PlayerInfoPacket PlayerInfo = playerInfoPacket; - public bool IsAlive = isAlive; - public bool IsAI = isAi; - public Vector3 Position = position; - public int netId = netId; + public struct SendCharacterPacket(PlayerInfoPacket playerInfoPacket, bool isAlive, bool isAi, Vector3 position, int netId) : INetSerializable + { + public PlayerInfoPacket PlayerInfo = playerInfoPacket; + public bool IsAlive = isAlive; + public bool IsAI = isAi; + public Vector3 Position = position; + public int netId = netId; - public void Deserialize(NetDataReader reader) - { - PlayerInfo = PlayerInfoPacket.Deserialize(reader); - IsAlive = reader.GetBool(); - IsAI = reader.GetBool(); - Position = reader.GetVector3(); - netId = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + PlayerInfo = PlayerInfoPacket.Deserialize(reader); + IsAlive = reader.GetBool(); + IsAI = reader.GetBool(); + Position = reader.GetVector3(); + netId = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - PlayerInfoPacket.Serialize(writer, PlayerInfo); - writer.Put(IsAlive); - writer.Put(IsAI); - writer.Put(Position); - writer.Put(netId); - } - } + public void Serialize(NetDataWriter writer) + { + PlayerInfoPacket.Serialize(writer, PlayerInfo); + writer.Put(IsAlive); + writer.Put(IsAI); + writer.Put(Position); + writer.Put(netId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/SpawnpointPacket.cs b/Fika.Core/Networking/Packets/Communication/SpawnpointPacket.cs index 57a1a7e1..4e98415a 100644 --- a/Fika.Core/Networking/Packets/Communication/SpawnpointPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/SpawnpointPacket.cs @@ -2,21 +2,21 @@ namespace Fika.Core.Networking { - public struct SpawnpointPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public string Name; + public struct SpawnpointPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public string Name; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - Name = reader.GetString(); - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + Name = reader.GetString(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(Name); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(Name); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/SyncNetIdPacket.cs b/Fika.Core/Networking/Packets/Communication/SyncNetIdPacket.cs index 3f9590b9..aa0aca2f 100644 --- a/Fika.Core/Networking/Packets/Communication/SyncNetIdPacket.cs +++ b/Fika.Core/Networking/Packets/Communication/SyncNetIdPacket.cs @@ -2,21 +2,21 @@ namespace Fika.Core.Networking { - public struct SyncNetIdPacket(string profileId, int netId) : INetSerializable - { - public string ProfileId = profileId; - public int NetId = netId; + public struct SyncNetIdPacket(string profileId, int netId) : INetSerializable + { + public string ProfileId = profileId; + public int NetId = netId; - public void Deserialize(NetDataReader reader) - { - ProfileId = reader.GetString(); - NetId = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + ProfileId = reader.GetString(); + NetId = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(ProfileId); - writer.Put(NetId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(ProfileId); + writer.Put(NetId); + } + } } diff --git a/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs index bb65386d..ed306561 100644 --- a/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs +++ b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs @@ -2,21 +2,21 @@ namespace Fika.Core.Networking { - public struct TextMessagePacket(string nickname, string message) : INetSerializable - { - public string Nickname = nickname; - public string Message = message; + 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 Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + Message = reader.GetString(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(Nickname); - writer.Put(Message); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(Message); + } + } } diff --git a/Fika.Core/Networking/Packets/FirearmController/WeaponPacket.cs b/Fika.Core/Networking/Packets/FirearmController/WeaponPacket.cs index 7a841088..f1b92f3a 100644 --- a/Fika.Core/Networking/Packets/FirearmController/WeaponPacket.cs +++ b/Fika.Core/Networking/Packets/FirearmController/WeaponPacket.cs @@ -6,195 +6,195 @@ namespace Fika.Core.Networking { - public struct WeaponPacket(int netId) : INetSerializable - { - public int NetId = netId; - public bool HasShotInfo = false; - public ShotInfoPacket ShotInfoPacket; - public bool ChangeFireMode = false; - public Weapon.EFireMode FireMode; - public bool ToggleAim = false; - public int AimingIndex; - public bool ExamineWeapon = false; - public bool CheckAmmo = false; - public bool CheckChamber = false; - public bool CheckFireMode = false; - public bool ToggleTacticalCombo = false; - public LightStatesPacket LightStatesPacket; - public bool ChangeSightMode = false; - public ScopeStatesPacket ScopeStatesPacket; - public bool ToggleLauncher = false; - public EGesture Gesture = EGesture.None; - public bool EnableInventory = false; - public bool InventoryStatus = false; - public bool Loot = false; - public bool HasReloadMagPacket = false; - public ReloadMagPacket ReloadMagPacket; - public bool HasQuickReloadMagPacket = false; - public QuickReloadMagPacket QuickReloadMagPacket; - public bool HasReloadWithAmmoPacket = false; - public ReloadWithAmmoPacket ReloadWithAmmo; - public bool HasCylinderMagPacket = false; - public CylinderMagPacket CylinderMag; - public bool HasReloadLauncherPacket = false; - public ReloadLauncherPacket ReloadLauncher; - public bool HasReloadBarrelsPacket = false; - public ReloadBarrelsPacket ReloadBarrels; - public bool HasGrenadePacket = false; - public GrenadePacket GrenadePacket; - public bool CancelGrenade = false; - public bool HasCompassChange = false; - public bool CompassState; - public bool HasKnifePacket = false; - public KnifePacket KnifePacket; - public bool HasStanceChange = false; - public bool LeftStanceState; - public bool HasFlareShot = false; - public FlareShotPacket FlareShotPacket; - public bool ReloadBoltAction = false; - public bool HasRollCylinder = false; - public bool RollToZeroCamora = false; - public bool UnderbarrelSightingRangeUp = false; - public bool UnderbarrelSightingRangeDown = false; + public struct WeaponPacket(int netId) : INetSerializable + { + public int NetId = netId; + public bool HasShotInfo = false; + public ShotInfoPacket ShotInfoPacket; + public bool ChangeFireMode = false; + public Weapon.EFireMode FireMode; + public bool ToggleAim = false; + public int AimingIndex; + public bool ExamineWeapon = false; + public bool CheckAmmo = false; + public bool CheckChamber = false; + public bool CheckFireMode = false; + public bool ToggleTacticalCombo = false; + public LightStatesPacket LightStatesPacket; + public bool ChangeSightMode = false; + public ScopeStatesPacket ScopeStatesPacket; + public bool ToggleLauncher = false; + public EGesture Gesture = EGesture.None; + public bool EnableInventory = false; + public bool InventoryStatus = false; + public bool Loot = false; + public bool HasReloadMagPacket = false; + public ReloadMagPacket ReloadMagPacket; + public bool HasQuickReloadMagPacket = false; + public QuickReloadMagPacket QuickReloadMagPacket; + public bool HasReloadWithAmmoPacket = false; + public ReloadWithAmmoPacket ReloadWithAmmo; + public bool HasCylinderMagPacket = false; + public CylinderMagPacket CylinderMag; + public bool HasReloadLauncherPacket = false; + public ReloadLauncherPacket ReloadLauncher; + public bool HasReloadBarrelsPacket = false; + public ReloadBarrelsPacket ReloadBarrels; + public bool HasGrenadePacket = false; + public GrenadePacket GrenadePacket; + public bool CancelGrenade = false; + public bool HasCompassChange = false; + public bool CompassState; + public bool HasKnifePacket = false; + public KnifePacket KnifePacket; + public bool HasStanceChange = false; + public bool LeftStanceState; + public bool HasFlareShot = false; + public FlareShotPacket FlareShotPacket; + public bool ReloadBoltAction = false; + public bool HasRollCylinder = false; + public bool RollToZeroCamora = false; + public bool UnderbarrelSightingRangeUp = false; + public bool UnderbarrelSightingRangeDown = false; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - HasShotInfo = reader.GetBool(); - if (HasShotInfo) - ShotInfoPacket = ShotInfoPacket.Deserialize(reader); - ChangeFireMode = reader.GetBool(); - FireMode = (Weapon.EFireMode)reader.GetInt(); - ToggleAim = reader.GetBool(); - if (ToggleAim) - AimingIndex = reader.GetInt(); - ExamineWeapon = reader.GetBool(); - CheckAmmo = reader.GetBool(); - CheckChamber = reader.GetBool(); - CheckFireMode = reader.GetBool(); - ToggleTacticalCombo = reader.GetBool(); - if (ToggleTacticalCombo) - LightStatesPacket = LightStatesPacket.Deserialize(reader); - ChangeSightMode = reader.GetBool(); - if (ChangeSightMode) - ScopeStatesPacket = ScopeStatesPacket.Deserialize(reader); - ToggleLauncher = reader.GetBool(); - Gesture = (EGesture)reader.GetInt(); - EnableInventory = reader.GetBool(); - InventoryStatus = reader.GetBool(); - Loot = reader.GetBool(); - HasReloadMagPacket = reader.GetBool(); - if (HasReloadMagPacket) - { - ReloadMagPacket = ReloadMagPacket.Deserialize(reader); - } - HasQuickReloadMagPacket = reader.GetBool(); - if (HasQuickReloadMagPacket) - QuickReloadMagPacket = QuickReloadMagPacket.Deserialize(reader); - HasReloadWithAmmoPacket = reader.GetBool(); - if (HasReloadWithAmmoPacket) - ReloadWithAmmo = ReloadWithAmmoPacket.Deserialize(reader); - HasCylinderMagPacket = reader.GetBool(); - if (HasCylinderMagPacket) - CylinderMag = CylinderMagPacket.Deserialize(reader); - HasReloadLauncherPacket = reader.GetBool(); - if (HasReloadLauncherPacket) - ReloadLauncher = ReloadLauncherPacket.Deserialize(reader); - HasReloadBarrelsPacket = reader.GetBool(); - if (HasReloadBarrelsPacket) - { - ReloadBarrels = ReloadBarrelsPacket.Deserialize(reader); - } - HasGrenadePacket = reader.GetBool(); - if (HasGrenadePacket) - GrenadePacket = GrenadePacket.Deserialize(reader); - CancelGrenade = reader.GetBool(); - HasCompassChange = reader.GetBool(); - if (HasCompassChange) - CompassState = reader.GetBool(); - HasKnifePacket = reader.GetBool(); - if (HasKnifePacket) - KnifePacket = KnifePacket.Deserialize(reader); - HasStanceChange = reader.GetBool(); - if (HasStanceChange) - LeftStanceState = reader.GetBool(); - HasFlareShot = reader.GetBool(); - if (HasFlareShot) - FlareShotPacket = FlareShotPacket.Deserialize(reader); - ReloadBoltAction = reader.GetBool(); - HasRollCylinder = reader.GetBool(); - if (HasRollCylinder) - RollToZeroCamora = reader.GetBool(); - UnderbarrelSightingRangeUp = reader.GetBool(); - UnderbarrelSightingRangeDown = reader.GetBool(); - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + HasShotInfo = reader.GetBool(); + if (HasShotInfo) + ShotInfoPacket = ShotInfoPacket.Deserialize(reader); + ChangeFireMode = reader.GetBool(); + FireMode = (Weapon.EFireMode)reader.GetInt(); + ToggleAim = reader.GetBool(); + if (ToggleAim) + AimingIndex = reader.GetInt(); + ExamineWeapon = reader.GetBool(); + CheckAmmo = reader.GetBool(); + CheckChamber = reader.GetBool(); + CheckFireMode = reader.GetBool(); + ToggleTacticalCombo = reader.GetBool(); + if (ToggleTacticalCombo) + LightStatesPacket = LightStatesPacket.Deserialize(reader); + ChangeSightMode = reader.GetBool(); + if (ChangeSightMode) + ScopeStatesPacket = ScopeStatesPacket.Deserialize(reader); + ToggleLauncher = reader.GetBool(); + Gesture = (EGesture)reader.GetInt(); + EnableInventory = reader.GetBool(); + InventoryStatus = reader.GetBool(); + Loot = reader.GetBool(); + HasReloadMagPacket = reader.GetBool(); + if (HasReloadMagPacket) + { + ReloadMagPacket = ReloadMagPacket.Deserialize(reader); + } + HasQuickReloadMagPacket = reader.GetBool(); + if (HasQuickReloadMagPacket) + QuickReloadMagPacket = QuickReloadMagPacket.Deserialize(reader); + HasReloadWithAmmoPacket = reader.GetBool(); + if (HasReloadWithAmmoPacket) + ReloadWithAmmo = ReloadWithAmmoPacket.Deserialize(reader); + HasCylinderMagPacket = reader.GetBool(); + if (HasCylinderMagPacket) + CylinderMag = CylinderMagPacket.Deserialize(reader); + HasReloadLauncherPacket = reader.GetBool(); + if (HasReloadLauncherPacket) + ReloadLauncher = ReloadLauncherPacket.Deserialize(reader); + HasReloadBarrelsPacket = reader.GetBool(); + if (HasReloadBarrelsPacket) + { + ReloadBarrels = ReloadBarrelsPacket.Deserialize(reader); + } + HasGrenadePacket = reader.GetBool(); + if (HasGrenadePacket) + GrenadePacket = GrenadePacket.Deserialize(reader); + CancelGrenade = reader.GetBool(); + HasCompassChange = reader.GetBool(); + if (HasCompassChange) + CompassState = reader.GetBool(); + HasKnifePacket = reader.GetBool(); + if (HasKnifePacket) + KnifePacket = KnifePacket.Deserialize(reader); + HasStanceChange = reader.GetBool(); + if (HasStanceChange) + LeftStanceState = reader.GetBool(); + HasFlareShot = reader.GetBool(); + if (HasFlareShot) + FlareShotPacket = FlareShotPacket.Deserialize(reader); + ReloadBoltAction = reader.GetBool(); + HasRollCylinder = reader.GetBool(); + if (HasRollCylinder) + RollToZeroCamora = reader.GetBool(); + UnderbarrelSightingRangeUp = reader.GetBool(); + UnderbarrelSightingRangeDown = reader.GetBool(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put(HasShotInfo); - if (HasShotInfo) - ShotInfoPacket.Serialize(writer, ShotInfoPacket); - writer.Put(ChangeFireMode); - writer.Put((int)FireMode); - writer.Put(ToggleAim); - if (ToggleAim) - writer.Put(AimingIndex); - writer.Put(ExamineWeapon); - writer.Put(CheckAmmo); - writer.Put(CheckChamber); - writer.Put(CheckFireMode); - writer.Put(ToggleTacticalCombo); - if (ToggleTacticalCombo) - LightStatesPacket.Serialize(writer, LightStatesPacket); - writer.Put(ChangeSightMode); - if (ChangeSightMode) - ScopeStatesPacket.Serialize(writer, ScopeStatesPacket); - writer.Put(ToggleLauncher); - writer.Put((int)Gesture); - writer.Put(EnableInventory); - writer.Put(InventoryStatus); - writer.Put(Loot); - writer.Put(HasReloadMagPacket); - if (HasReloadMagPacket) - ReloadMagPacket.Serialize(writer, ReloadMagPacket); - writer.Put(HasQuickReloadMagPacket); - if (HasQuickReloadMagPacket) - QuickReloadMagPacket.Serialize(writer, QuickReloadMagPacket); - writer.Put(HasReloadWithAmmoPacket); - if (HasReloadWithAmmoPacket) - ReloadWithAmmoPacket.Serialize(writer, ReloadWithAmmo); - writer.Put(HasCylinderMagPacket); - if (HasCylinderMagPacket) - CylinderMagPacket.Serialize(writer, CylinderMag); - writer.Put(HasReloadLauncherPacket); - if (HasReloadLauncherPacket) - ReloadLauncherPacket.Serialize(writer, ReloadLauncher); - writer.Put(HasReloadBarrelsPacket); - if (HasReloadBarrelsPacket) - ReloadBarrelsPacket.Serialize(writer, ReloadBarrels); - writer.Put(HasGrenadePacket); - if (HasGrenadePacket) - GrenadePacket.Serialize(writer, GrenadePacket); - writer.Put(CancelGrenade); - writer.Put(HasCompassChange); - if (HasCompassChange) - writer.Put(CompassState); - writer.Put(HasKnifePacket); - if (HasKnifePacket) - KnifePacket.Serialize(writer, KnifePacket); - writer.Put(HasStanceChange); - if (HasStanceChange) - writer.Put(LeftStanceState); - writer.Put(HasFlareShot); - if (HasFlareShot) - FlareShotPacket.Serialize(writer, FlareShotPacket); - writer.Put(ReloadBoltAction); - writer.Put(HasRollCylinder); - if (HasRollCylinder) - writer.Put(RollToZeroCamora); - writer.Put(UnderbarrelSightingRangeUp); - writer.Put(UnderbarrelSightingRangeDown); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(HasShotInfo); + if (HasShotInfo) + ShotInfoPacket.Serialize(writer, ShotInfoPacket); + writer.Put(ChangeFireMode); + writer.Put((int)FireMode); + writer.Put(ToggleAim); + if (ToggleAim) + writer.Put(AimingIndex); + writer.Put(ExamineWeapon); + writer.Put(CheckAmmo); + writer.Put(CheckChamber); + writer.Put(CheckFireMode); + writer.Put(ToggleTacticalCombo); + if (ToggleTacticalCombo) + LightStatesPacket.Serialize(writer, LightStatesPacket); + writer.Put(ChangeSightMode); + if (ChangeSightMode) + ScopeStatesPacket.Serialize(writer, ScopeStatesPacket); + writer.Put(ToggleLauncher); + writer.Put((int)Gesture); + writer.Put(EnableInventory); + writer.Put(InventoryStatus); + writer.Put(Loot); + writer.Put(HasReloadMagPacket); + if (HasReloadMagPacket) + ReloadMagPacket.Serialize(writer, ReloadMagPacket); + writer.Put(HasQuickReloadMagPacket); + if (HasQuickReloadMagPacket) + QuickReloadMagPacket.Serialize(writer, QuickReloadMagPacket); + writer.Put(HasReloadWithAmmoPacket); + if (HasReloadWithAmmoPacket) + ReloadWithAmmoPacket.Serialize(writer, ReloadWithAmmo); + writer.Put(HasCylinderMagPacket); + if (HasCylinderMagPacket) + CylinderMagPacket.Serialize(writer, CylinderMag); + writer.Put(HasReloadLauncherPacket); + if (HasReloadLauncherPacket) + ReloadLauncherPacket.Serialize(writer, ReloadLauncher); + writer.Put(HasReloadBarrelsPacket); + if (HasReloadBarrelsPacket) + ReloadBarrelsPacket.Serialize(writer, ReloadBarrels); + writer.Put(HasGrenadePacket); + if (HasGrenadePacket) + GrenadePacket.Serialize(writer, GrenadePacket); + writer.Put(CancelGrenade); + writer.Put(HasCompassChange); + if (HasCompassChange) + writer.Put(CompassState); + writer.Put(HasKnifePacket); + if (HasKnifePacket) + KnifePacket.Serialize(writer, KnifePacket); + writer.Put(HasStanceChange); + if (HasStanceChange) + writer.Put(LeftStanceState); + writer.Put(HasFlareShot); + if (HasFlareShot) + FlareShotPacket.Serialize(writer, FlareShotPacket); + writer.Put(ReloadBoltAction); + writer.Put(HasRollCylinder); + if (HasRollCylinder) + writer.Put(RollToZeroCamora); + writer.Put(UnderbarrelSightingRangeUp); + writer.Put(UnderbarrelSightingRangeDown); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/AirdropLootPacket.cs b/Fika.Core/Networking/Packets/GameWorld/AirdropLootPacket.cs index 618292ae..dd27f2fa 100644 --- a/Fika.Core/Networking/Packets/GameWorld/AirdropLootPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/AirdropLootPacket.cs @@ -5,27 +5,27 @@ namespace Fika.Core.Networking { - public struct AirdropLootPacket(bool isRequest = false) : INetSerializable - { - public bool IsRequest = isRequest; - public string ContainerId; - public int ContainerNetId; - public Item RootItem; + public struct AirdropLootPacket(bool isRequest = false) : INetSerializable + { + public bool IsRequest = isRequest; + public string ContainerId; + public int ContainerNetId; + public Item RootItem; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - ContainerId = reader.GetString(); - ContainerNetId = reader.GetInt(); - RootItem = reader.GetAirdropItem(); - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + ContainerId = reader.GetString(); + ContainerNetId = reader.GetInt(); + RootItem = reader.GetAirdropItem(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(ContainerId); - writer.Put(ContainerNetId); - writer.PutAirdropItem(RootItem); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(ContainerId); + writer.Put(ContainerNetId); + writer.PutAirdropItem(RootItem); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs b/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs index c47befcd..5b78a23d 100644 --- a/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/AirdropPacket.cs @@ -7,56 +7,56 @@ namespace Fika.Core.Networking { - public struct AirdropPacket() : INetSerializable - { - 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 BoxPoint; - public Vector3 SpawnPoint; - public Vector3 LookPoint; + public struct AirdropPacket() : INetSerializable + { + 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 BoxPoint; + public Vector3 SpawnPoint; + public Vector3 LookPoint; - public void Deserialize(NetDataReader reader) - { - byte[] configBytes = reader.GetByteArray(); - Config = SimpleZlib.Decompress(configBytes, null).ParseJsonTo(); - AirdropAvailable = reader.GetBool(); - PlaneSpawned = reader.GetBool(); - BoxSpawned = reader.GetBool(); - DistanceTraveled = reader.GetFloat(); - DistanceToTravel = reader.GetFloat(); - DistanceToDrop = reader.GetFloat(); - Timer = reader.GetFloat(); - DropHeight = reader.GetInt(); - TimeToStart = reader.GetInt(); - BoxPoint = reader.GetVector3(); - SpawnPoint = reader.GetVector3(); - LookPoint = reader.GetVector3(); - } + public void Deserialize(NetDataReader reader) + { + byte[] configBytes = reader.GetByteArray(); + Config = SimpleZlib.Decompress(configBytes, null).ParseJsonTo(); + AirdropAvailable = reader.GetBool(); + PlaneSpawned = reader.GetBool(); + BoxSpawned = reader.GetBool(); + DistanceTraveled = reader.GetFloat(); + DistanceToTravel = reader.GetFloat(); + DistanceToDrop = reader.GetFloat(); + Timer = reader.GetFloat(); + DropHeight = reader.GetInt(); + TimeToStart = reader.GetInt(); + BoxPoint = reader.GetVector3(); + SpawnPoint = reader.GetVector3(); + LookPoint = reader.GetVector3(); + } - public void Serialize(NetDataWriter writer) - { - byte[] configBytes = SimpleZlib.CompressToBytes(Config.ToJson(), 4, null); - writer.PutByteArray(configBytes); - writer.Put(AirdropAvailable); - writer.Put(PlaneSpawned); - writer.Put(BoxSpawned); - writer.Put(DistanceTraveled); - writer.Put(DistanceToTravel); - writer.Put(DistanceToDrop); - writer.Put(Timer); - writer.Put(DropHeight); - writer.Put(TimeToStart); - writer.Put(BoxPoint); - writer.Put(SpawnPoint); - writer.Put(LookPoint); - } - } + public void Serialize(NetDataWriter writer) + { + byte[] configBytes = SimpleZlib.CompressToBytes(Config.ToJson(), 4, null); + writer.PutByteArray(configBytes); + writer.Put(AirdropAvailable); + writer.Put(PlaneSpawned); + writer.Put(BoxSpawned); + writer.Put(DistanceTraveled); + writer.Put(DistanceToTravel); + writer.Put(DistanceToDrop); + writer.Put(Timer); + writer.Put(DropHeight); + writer.Put(TimeToStart); + writer.Put(BoxPoint); + writer.Put(SpawnPoint); + writer.Put(LookPoint); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/AirdropUpdatePacket.cs b/Fika.Core/Networking/Packets/GameWorld/AirdropUpdatePacket.cs index 44a2bf1e..aa57f220 100644 --- a/Fika.Core/Networking/Packets/GameWorld/AirdropUpdatePacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/AirdropUpdatePacket.cs @@ -4,53 +4,53 @@ namespace Fika.Core.Networking.Packets.GameWorld { - public struct AirdropUpdatePacket : INetSerializable - { - public AirplaneDataPacketStruct Data; + public struct AirdropUpdatePacket : INetSerializable + { + public AirplaneDataPacketStruct Data; - public void Deserialize(NetDataReader reader) - { - Data = new() - { - ObjectId = reader.GetInt(), - Position = reader.GetVector3(), - Rotation = reader.GetVector3(), - ObjectType = (SynchronizableObjectType)reader.GetByte() - }; - if (Data.ObjectType == SynchronizableObjectType.AirDrop) - { - Data.PacketData.AirdropDataPacket.SignalFire = reader.GetBool(); - Data.PacketData.AirdropDataPacket.FallingStage = (EAirdropFallingStage)reader.GetByte(); - Data.PacketData.AirdropDataPacket.AirdropType = (EAirdropType)reader.GetByte(); - Data.PacketData.AirdropDataPacket.UniqueId = reader.GetInt(); - } - else - { - Data.PacketData.AirplaneDataPacket.AirplanePercent = reader.GetInt(); - } - Data.Outdated = reader.GetBool(); - Data.IsStatic = reader.GetBool(); - } + public void Deserialize(NetDataReader reader) + { + Data = new() + { + ObjectId = reader.GetInt(), + Position = reader.GetVector3(), + Rotation = reader.GetVector3(), + ObjectType = (SynchronizableObjectType)reader.GetByte() + }; + if (Data.ObjectType == SynchronizableObjectType.AirDrop) + { + Data.PacketData.AirdropDataPacket.SignalFire = reader.GetBool(); + Data.PacketData.AirdropDataPacket.FallingStage = (EAirdropFallingStage)reader.GetByte(); + Data.PacketData.AirdropDataPacket.AirdropType = (EAirdropType)reader.GetByte(); + Data.PacketData.AirdropDataPacket.UniqueId = reader.GetInt(); + } + else + { + Data.PacketData.AirplaneDataPacket.AirplanePercent = reader.GetInt(); + } + Data.Outdated = reader.GetBool(); + Data.IsStatic = reader.GetBool(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(Data.ObjectId); - writer.Put(Data.Position); - writer.Put(Data.Rotation); - writer.Put((byte)Data.ObjectType); - if (Data.ObjectType == SynchronizableObjectType.AirDrop) - { - writer.Put(Data.PacketData.AirdropDataPacket.SignalFire); - writer.Put((byte)Data.PacketData.AirdropDataPacket.FallingStage); - writer.Put((byte)Data.PacketData.AirdropDataPacket.AirdropType); - writer.Put(Data.PacketData.AirdropDataPacket.UniqueId); - } - else - { - writer.Put(Data.PacketData.AirplaneDataPacket.AirplanePercent); - } - writer.Put(Data.Outdated); - writer.Put(Data.IsStatic); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Data.ObjectId); + writer.Put(Data.Position); + writer.Put(Data.Rotation); + writer.Put((byte)Data.ObjectType); + if (Data.ObjectType == SynchronizableObjectType.AirDrop) + { + writer.Put(Data.PacketData.AirdropDataPacket.SignalFire); + writer.Put((byte)Data.PacketData.AirdropDataPacket.FallingStage); + writer.Put((byte)Data.PacketData.AirdropDataPacket.AirdropType); + writer.Put(Data.PacketData.AirdropDataPacket.UniqueId); + } + else + { + writer.Put(Data.PacketData.AirplaneDataPacket.AirplanePercent); + } + writer.Put(Data.Outdated); + writer.Put(Data.IsStatic); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/BTRInteractionPacket.cs b/Fika.Core/Networking/Packets/GameWorld/BTRInteractionPacket.cs index 3fe06068..a29892fd 100644 --- a/Fika.Core/Networking/Packets/GameWorld/BTRInteractionPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/BTRInteractionPacket.cs @@ -3,41 +3,41 @@ namespace Fika.Core.Networking { - public struct BTRInteractionPacket(int netId) : INetSerializable - { - public int NetId = netId; - public bool HasInteractPacket = false; - public PlayerInteractPacket InteractPacket; + public struct BTRInteractionPacket(int netId) : INetSerializable + { + public int NetId = netId; + public bool HasInteractPacket = false; + public PlayerInteractPacket InteractPacket; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - HasInteractPacket = reader.GetBool(); - if (HasInteractPacket) - { - InteractPacket = new() - { - HasInteraction = reader.GetBool(), - InteractionType = (EInteractionType)reader.GetInt(), - SideId = reader.GetByte(), - SlotId = reader.GetByte(), - Fast = reader.GetBool() - }; - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + HasInteractPacket = reader.GetBool(); + if (HasInteractPacket) + { + InteractPacket = new() + { + HasInteraction = reader.GetBool(), + InteractionType = (EInteractionType)reader.GetInt(), + SideId = reader.GetByte(), + SlotId = reader.GetByte(), + Fast = reader.GetBool() + }; + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put(HasInteractPacket); - if (HasInteractPacket) - { - writer.Put(InteractPacket.HasInteraction); - writer.Put((int)InteractPacket.InteractionType); - writer.Put(InteractPacket.SideId); - writer.Put(InteractPacket.SlotId); - writer.Put(InteractPacket.Fast); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(HasInteractPacket); + if (HasInteractPacket) + { + writer.Put(InteractPacket.HasInteraction); + writer.Put((int)InteractPacket.InteractionType); + writer.Put(InteractPacket.SideId); + writer.Put(InteractPacket.SlotId); + writer.Put(InteractPacket.Fast); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/BTRPacket.cs b/Fika.Core/Networking/Packets/GameWorld/BTRPacket.cs index 525d913e..41e2bc85 100644 --- a/Fika.Core/Networking/Packets/GameWorld/BTRPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/BTRPacket.cs @@ -4,41 +4,41 @@ namespace Fika.Core.Networking { - public struct BTRPacket() : INetSerializable - { - public BTRDataPacket BTRDataPacket; - public bool HasBotProfileId = false; - public int BotNetId; - public bool HasShot = false; - public Vector3 ShotPosition; - public Vector3 ShotDirection; + public struct BTRPacket() : INetSerializable + { + public BTRDataPacket BTRDataPacket; + public bool HasBotProfileId = false; + public int BotNetId; + public bool HasShot = false; + public Vector3 ShotPosition; + public Vector3 ShotDirection; - public void Deserialize(NetDataReader reader) - { - BTRDataPacket = BTRDataPacketUtils.Deserialize(reader); - HasBotProfileId = reader.GetBool(); - if (HasBotProfileId) - BotNetId = reader.GetInt(); - HasShot = reader.GetBool(); - if (HasShot) - { - ShotPosition = reader.GetVector3(); - ShotDirection = reader.GetVector3(); - } - } + public void Deserialize(NetDataReader reader) + { + BTRDataPacket = BTRDataPacketUtils.Deserialize(reader); + HasBotProfileId = reader.GetBool(); + if (HasBotProfileId) + BotNetId = reader.GetInt(); + HasShot = reader.GetBool(); + if (HasShot) + { + ShotPosition = reader.GetVector3(); + ShotDirection = reader.GetVector3(); + } + } - public void Serialize(NetDataWriter writer) - { - BTRDataPacketUtils.Serialize(writer, BTRDataPacket); - writer.Put(HasBotProfileId); - if (HasBotProfileId) - writer.Put(BotNetId); - writer.Put(HasShot); - if (HasShot) - { - writer.Put(ShotPosition); - writer.Put(ShotDirection); - } - } - } + public void Serialize(NetDataWriter writer) + { + BTRDataPacketUtils.Serialize(writer, BTRDataPacket); + writer.Put(HasBotProfileId); + if (HasBotProfileId) + writer.Put(BotNetId); + writer.Put(HasShot); + if (HasShot) + { + writer.Put(ShotPosition); + writer.Put(ShotDirection); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/BTRServicePacket.cs b/Fika.Core/Networking/Packets/GameWorld/BTRServicePacket.cs index 422d5e7c..c35a101b 100644 --- a/Fika.Core/Networking/Packets/GameWorld/BTRServicePacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/BTRServicePacket.cs @@ -3,33 +3,33 @@ namespace Fika.Core.Networking { - public struct BTRServicePacket(string profileId) : INetSerializable - { - public string ProfileId; - public ETraderServiceType TraderServiceType; - public bool HasSubservice = false; - public string SubserviceId; + public struct BTRServicePacket(string profileId) : INetSerializable + { + public string ProfileId; + public ETraderServiceType TraderServiceType; + public bool HasSubservice = false; + public string SubserviceId; - public void Deserialize(NetDataReader reader) - { - ProfileId = reader.GetString(); - TraderServiceType = (ETraderServiceType)reader.GetInt(); - HasSubservice = reader.GetBool(); - if (HasSubservice) - { - SubserviceId = reader.GetString(); - } - } + public void Deserialize(NetDataReader reader) + { + ProfileId = reader.GetString(); + TraderServiceType = (ETraderServiceType)reader.GetInt(); + HasSubservice = reader.GetBool(); + if (HasSubservice) + { + SubserviceId = reader.GetString(); + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(profileId); - writer.Put((int)TraderServiceType); - writer.Put(HasSubservice); - if (HasSubservice) - { - writer.Put(SubserviceId); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(profileId); + writer.Put((int)TraderServiceType); + writer.Put(HasSubservice); + if (HasSubservice) + { + writer.Put(SubserviceId); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/BorderZonePacket.cs b/Fika.Core/Networking/Packets/GameWorld/BorderZonePacket.cs index 2e385177..22cbfd35 100644 --- a/Fika.Core/Networking/Packets/GameWorld/BorderZonePacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/BorderZonePacket.cs @@ -2,21 +2,21 @@ namespace Fika.Core.Networking { - internal struct BorderZonePacket : INetSerializable - { - public string ProfileId; - public int ZoneId; + internal struct BorderZonePacket : INetSerializable + { + public string ProfileId; + public int ZoneId; - public void Deserialize(NetDataReader reader) - { - ProfileId = reader.GetString(); - ZoneId = reader.GetInt(); - } + public void Deserialize(NetDataReader reader) + { + ProfileId = reader.GetString(); + ZoneId = reader.GetInt(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(ProfileId); - writer.Put(ZoneId); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(ProfileId); + writer.Put(ZoneId); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/ExfiltrationPacket.cs b/Fika.Core/Networking/Packets/GameWorld/ExfiltrationPacket.cs index e5abc8d6..428b1088 100644 --- a/Fika.Core/Networking/Packets/GameWorld/ExfiltrationPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/ExfiltrationPacket.cs @@ -7,61 +7,61 @@ namespace Fika.Core.Networking { - public struct ExfiltrationPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public int ExfiltrationAmount; - public Dictionary ExfiltrationPoints; - public bool HasScavExfils = false; - public int ScavExfiltrationAmount; - public Dictionary ScavExfiltrationPoints; + public struct ExfiltrationPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public int ExfiltrationAmount; + public Dictionary ExfiltrationPoints; + public bool HasScavExfils = false; + public int ScavExfiltrationAmount; + public Dictionary ScavExfiltrationPoints; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - if (!IsRequest) - { - ExfiltrationAmount = reader.GetInt(); - ExfiltrationPoints = []; - for (int i = 0; i < ExfiltrationAmount; i++) - { - ExfiltrationPoints.Add(reader.GetString(), (EExfiltrationStatus)reader.GetInt()); - } - HasScavExfils = reader.GetBool(); - if (HasScavExfils) - { - ScavExfiltrationAmount = reader.GetInt(); - ScavExfiltrationPoints = []; - for (int i = 0; i < ScavExfiltrationAmount; i++) - { - ScavExfiltrationPoints.Add(reader.GetString(), (EExfiltrationStatus)reader.GetInt()); - } - } - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + if (!IsRequest) + { + ExfiltrationAmount = reader.GetInt(); + ExfiltrationPoints = []; + for (int i = 0; i < ExfiltrationAmount; i++) + { + ExfiltrationPoints.Add(reader.GetString(), (EExfiltrationStatus)reader.GetInt()); + } + HasScavExfils = reader.GetBool(); + if (HasScavExfils) + { + ScavExfiltrationAmount = reader.GetInt(); + ScavExfiltrationPoints = []; + for (int i = 0; i < ScavExfiltrationAmount; i++) + { + ScavExfiltrationPoints.Add(reader.GetString(), (EExfiltrationStatus)reader.GetInt()); + } + } + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - if (!IsRequest) - { - writer.Put(ExfiltrationAmount); - for (int i = 0; i < ExfiltrationPoints.Count; i++) - { - writer.Put(ExfiltrationPoints.ElementAt(i).Key); - writer.Put((int)ExfiltrationPoints.ElementAt(i).Value); - } - writer.Put(HasScavExfils); - if (HasScavExfils) - { - writer.Put(ScavExfiltrationAmount); - for (int i = 0; i < ScavExfiltrationPoints.Count; i++) - { - writer.Put(ScavExfiltrationPoints.ElementAt(i).Key); - writer.Put((int)ScavExfiltrationPoints.ElementAt(i).Value); - } - } - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + if (!IsRequest) + { + writer.Put(ExfiltrationAmount); + for (int i = 0; i < ExfiltrationPoints.Count; i++) + { + writer.Put(ExfiltrationPoints.ElementAt(i).Key); + writer.Put((int)ExfiltrationPoints.ElementAt(i).Value); + } + writer.Put(HasScavExfils); + if (HasScavExfils) + { + writer.Put(ScavExfiltrationAmount); + for (int i = 0; i < ScavExfiltrationPoints.Count; i++) + { + writer.Put(ScavExfiltrationPoints.ElementAt(i).Key); + writer.Put((int)ScavExfiltrationPoints.ElementAt(i).Value); + } + } + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs index af39b9ce..8b60a0a8 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GameTimerPacket.cs @@ -4,24 +4,24 @@ namespace Fika.Core.Networking { - public struct GameTimerPacket(bool isRequest, long tick = 0, long startTime = 0) : INetSerializable - { - public bool IsRequest = isRequest; - public long Tick = tick; - public long StartTime = startTime; + public struct GameTimerPacket(bool isRequest, long tick = 0, long startTime = 0) : INetSerializable + { + public bool IsRequest = isRequest; + public long Tick = tick; + public long StartTime = startTime; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - Tick = reader.GetLong(); - StartTime = reader.GetLong(); - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + Tick = reader.GetLong(); + StartTime = reader.GetLong(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(Tick); - writer.Put(StartTime); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(Tick); + writer.Put(StartTime); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs index 27e8071c..399da636 100644 --- a/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/GenericPacket.cs @@ -7,102 +7,102 @@ namespace Fika.Core.Networking { - /// - /// Packet used for many different things to reduce packet bloat - /// - /// - public struct GenericPacket(EPackageType packageType) : INetSerializable - { - public int NetId; - public EPackageType PacketType = packageType; - public Vector3 PingLocation; - public PingFactory.EPingType PingType; - public Color PingColor = Color.white; - public string Nickname; - public string LocaleId; - public int BotNetId; - public long DepartureTime; - public string ExfilName; - public float ExfilStartTime; - public ETraderServiceType TraderServiceType; + /// + /// Packet used for many different things to reduce packet bloat + /// + /// + public struct GenericPacket(EPackageType packageType) : INetSerializable + { + public int NetId; + public EPackageType PacketType = packageType; + public Vector3 PingLocation; + public PingFactory.EPingType PingType; + public Color PingColor = Color.white; + public string Nickname; + public string LocaleId; + public int BotNetId; + public long DepartureTime; + public string ExfilName; + public float ExfilStartTime; + public ETraderServiceType TraderServiceType; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - PacketType = (EPackageType)reader.GetInt(); - switch (PacketType) - { - case EPackageType.Ping: - PingLocation = reader.GetVector3(); - PingType = (PingFactory.EPingType)reader.GetByte(); - PingColor = reader.GetColor(); - Nickname = reader.GetString(); - LocaleId = reader.GetString(); - break; - case EPackageType.TrainSync: - DepartureTime = reader.GetLong(); - break; - case EPackageType.ExfilCountdown: - ExfilName = reader.GetString(); - ExfilStartTime = reader.GetFloat(); - break; - case EPackageType.TraderServiceNotification: - TraderServiceType = (ETraderServiceType)reader.GetInt(); - break; - case EPackageType.LoadBot: - case EPackageType.DisposeBot: - case EPackageType.EnableBot: - case EPackageType.DisableBot: - BotNetId = reader.GetInt(); - break; - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + PacketType = (EPackageType)reader.GetInt(); + switch (PacketType) + { + case EPackageType.Ping: + PingLocation = reader.GetVector3(); + PingType = (PingFactory.EPingType)reader.GetByte(); + PingColor = reader.GetColor(); + Nickname = reader.GetString(); + LocaleId = reader.GetString(); + break; + case EPackageType.TrainSync: + DepartureTime = reader.GetLong(); + break; + case EPackageType.ExfilCountdown: + ExfilName = reader.GetString(); + ExfilStartTime = reader.GetFloat(); + break; + case EPackageType.TraderServiceNotification: + TraderServiceType = (ETraderServiceType)reader.GetInt(); + break; + case EPackageType.LoadBot: + case EPackageType.DisposeBot: + case EPackageType.EnableBot: + case EPackageType.DisableBot: + BotNetId = reader.GetInt(); + break; + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put((int)PacketType); - switch (PacketType) - { - case EPackageType.Ping: - writer.Put(PingLocation); - writer.Put((byte)PingType); - writer.Put(PingColor); - writer.Put(Nickname); - writer.Put(LocaleId); - break; - case EPackageType.TrainSync: - writer.Put(DepartureTime); - break; - case EPackageType.ExfilCountdown: - writer.Put(ExfilName); - writer.Put(ExfilStartTime); - break; - case EPackageType.TraderServiceNotification: - writer.Put((int)TraderServiceType); - break; - case EPackageType.LoadBot: - case EPackageType.DisposeBot: - case EPackageType.EnableBot: - case EPackageType.DisableBot: - writer.Put(BotNetId); - break; - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put((int)PacketType); + switch (PacketType) + { + case EPackageType.Ping: + writer.Put(PingLocation); + writer.Put((byte)PingType); + writer.Put(PingColor); + writer.Put(Nickname); + writer.Put(LocaleId); + break; + case EPackageType.TrainSync: + writer.Put(DepartureTime); + break; + case EPackageType.ExfilCountdown: + writer.Put(ExfilName); + writer.Put(ExfilStartTime); + break; + case EPackageType.TraderServiceNotification: + writer.Put((int)TraderServiceType); + break; + case EPackageType.LoadBot: + case EPackageType.DisposeBot: + case EPackageType.EnableBot: + case EPackageType.DisableBot: + writer.Put(BotNetId); + break; + } + } + } - public enum EPackageType - { - ClientExtract, - Ping, - TrainSync, - ExfilCountdown, - TraderServiceNotification, - LoadBot, - DisposeBot, - EnableBot, - DisableBot, - RemoveAirdropManager, - ClearEffects - } + public enum EPackageType + { + ClientExtract, + Ping, + TrainSync, + ExfilCountdown, + TraderServiceNotification, + LoadBot, + DisposeBot, + EnableBot, + DisableBot, + RemoveAirdropManager, + ClearEffects + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/HalloweenEventPacket.cs b/Fika.Core/Networking/Packets/GameWorld/HalloweenEventPacket.cs index d7292b96..d58381c8 100644 --- a/Fika.Core/Networking/Packets/GameWorld/HalloweenEventPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/HalloweenEventPacket.cs @@ -4,52 +4,52 @@ namespace Fika.Core.Networking { - public struct HalloweenEventPacket(EHalloweenPacketType packetType) : INetSerializable - { - public EHalloweenPacketType PacketType = packetType; - public Vector3 SummonPosition; - public EEventState EventState; - public string Exit; + public struct HalloweenEventPacket(EHalloweenPacketType packetType) : INetSerializable + { + public EHalloweenPacketType PacketType = packetType; + public Vector3 SummonPosition; + public EEventState EventState; + public string Exit; - public void Deserialize(NetDataReader reader) - { - PacketType = (EHalloweenPacketType)reader.GetInt(); - switch (PacketType) - { - case EHalloweenPacketType.Summon: - SummonPosition = reader.GetVector3(); - break; - case EHalloweenPacketType.Sync: - EventState = (EEventState)reader.GetInt(); - break; - case EHalloweenPacketType.Exit: - Exit = reader.GetString(); - break; - } - } + public void Deserialize(NetDataReader reader) + { + PacketType = (EHalloweenPacketType)reader.GetInt(); + switch (PacketType) + { + case EHalloweenPacketType.Summon: + SummonPosition = reader.GetVector3(); + break; + case EHalloweenPacketType.Sync: + EventState = (EEventState)reader.GetInt(); + break; + case EHalloweenPacketType.Exit: + Exit = reader.GetString(); + break; + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put((int)PacketType); - switch (PacketType) - { - case EHalloweenPacketType.Summon: - writer.Put(SummonPosition); - break; - case EHalloweenPacketType.Sync: - writer.Put((int)EventState); - break; - case EHalloweenPacketType.Exit: - writer.Put(Exit); - break; - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put((int)PacketType); + switch (PacketType) + { + case EHalloweenPacketType.Summon: + writer.Put(SummonPosition); + break; + case EHalloweenPacketType.Sync: + writer.Put((int)EventState); + break; + case EHalloweenPacketType.Exit: + writer.Put(Exit); + break; + } + } + } - public enum EHalloweenPacketType - { - Summon, - Sync, - Exit - } + public enum EHalloweenPacketType + { + Summon, + Sync, + Exit + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/InteractableInitPacket.cs b/Fika.Core/Networking/Packets/GameWorld/InteractableInitPacket.cs index 00a66c0d..58038440 100644 --- a/Fika.Core/Networking/Packets/GameWorld/InteractableInitPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/InteractableInitPacket.cs @@ -4,30 +4,30 @@ namespace Fika.Core.Networking { - public struct InteractableInitPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public byte[] RawData; - public IDictionary Interactables; + public struct InteractableInitPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public byte[] RawData; + public IDictionary Interactables; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - if (!IsRequest) - { - RawData = reader.GetByteArray(); - Interactables = SimpleZlib.Decompress(RawData, null).ParseJsonTo>(); - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + if (!IsRequest) + { + RawData = reader.GetByteArray(); + Interactables = SimpleZlib.Decompress(RawData, null).ParseJsonTo>(); + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - if (!IsRequest) - { - byte[] data = SimpleZlib.CompressToBytes(Interactables.ToJson([]), 6); - writer.PutByteArray(data); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + if (!IsRequest) + { + byte[] data = SimpleZlib.CompressToBytes(Interactables.ToJson([]), 6); + writer.PutByteArray(data); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/MinePacket.cs b/Fika.Core/Networking/Packets/GameWorld/MinePacket.cs index 9b432809..57a2b79d 100644 --- a/Fika.Core/Networking/Packets/GameWorld/MinePacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/MinePacket.cs @@ -3,17 +3,17 @@ namespace Fika.Core.Networking { - public struct MinePacket : INetSerializable - { - public Vector3 MinePositon; - public void Deserialize(NetDataReader reader) - { - MinePositon = reader.GetVector3(); - } + public struct MinePacket : INetSerializable + { + public Vector3 MinePositon; + public void Deserialize(NetDataReader reader) + { + MinePositon = reader.GetVector3(); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(MinePositon); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(MinePositon); + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs index 96111c51..fcc0d8cb 100644 --- a/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/ReconnectPacket.cs @@ -7,98 +7,98 @@ namespace Fika.Core.Networking.Packets.GameWorld { - public struct ReconnectPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public bool InitialRequest = false; - public EReconnectDataType Type; + public struct ReconnectPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public bool InitialRequest = false; + public EReconnectDataType Type; - public string ProfileId; - public Profile Profile; - public Profile.ProfileHealthClass ProfileHealthClass; - public Vector3 PlayerPosition; + public string ProfileId; + public Profile Profile; + public Profile.ProfileHealthClass ProfileHealthClass; + public Vector3 PlayerPosition; - public List ThrowableData; - public List InteractivesData; - public Dictionary LampStates; - public Dictionary WindowBreakerStates; + public List ThrowableData; + public List InteractivesData; + public Dictionary LampStates; + public Dictionary WindowBreakerStates; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - InitialRequest = reader.GetBool(); - ProfileId = reader.GetString(); - if (!IsRequest) - { - Type = (EReconnectDataType)reader.GetByte(); - switch (Type) - { - case EReconnectDataType.Throwable: - ThrowableData = reader.GetThrowableData(); - break; - case EReconnectDataType.Interactives: - InteractivesData = reader.GetInteractivesStates(); - break; - case EReconnectDataType.LampControllers: - LampStates = reader.GetLampStates(); - break; - case EReconnectDataType.Windows: - WindowBreakerStates = reader.GetWindowBreakerStates(); - break; - case EReconnectDataType.OwnCharacter: - Profile = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); - ProfileHealthClass = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); - PlayerPosition = reader.GetVector3(); - break; - case EReconnectDataType.Finished: - default: - break; - } - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + InitialRequest = reader.GetBool(); + ProfileId = reader.GetString(); + if (!IsRequest) + { + Type = (EReconnectDataType)reader.GetByte(); + switch (Type) + { + case EReconnectDataType.Throwable: + ThrowableData = reader.GetThrowableData(); + break; + case EReconnectDataType.Interactives: + InteractivesData = reader.GetInteractivesStates(); + break; + case EReconnectDataType.LampControllers: + LampStates = reader.GetLampStates(); + break; + case EReconnectDataType.Windows: + WindowBreakerStates = reader.GetWindowBreakerStates(); + break; + case EReconnectDataType.OwnCharacter: + Profile = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + ProfileHealthClass = SimpleZlib.Decompress(reader.GetByteArray()).ParseJsonTo(); + PlayerPosition = reader.GetVector3(); + break; + case EReconnectDataType.Finished: + default: + break; + } + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(InitialRequest); - writer.Put(ProfileId); - if (!IsRequest) - { - writer.Put((byte)Type); - switch (Type) - { - case EReconnectDataType.Throwable: - writer.PutThrowableData(ThrowableData); - break; - case EReconnectDataType.Interactives: - writer.PutInteractivesStates(InteractivesData); - break; - case EReconnectDataType.LampControllers: - writer.PutLampStates(LampStates); - break; - case EReconnectDataType.Windows: - writer.PutWindowBreakerStates(WindowBreakerStates); - break; - case EReconnectDataType.OwnCharacter: - writer.PutByteArray(SimpleZlib.CompressToBytes(Profile.ToJson(), 4)); - writer.PutByteArray(SimpleZlib.CompressToBytes(ProfileHealthClass.ToJson(), 4)); - writer.Put(PlayerPosition); - break; - case EReconnectDataType.Finished: - default: - break; - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(InitialRequest); + writer.Put(ProfileId); + if (!IsRequest) + { + writer.Put((byte)Type); + switch (Type) + { + case EReconnectDataType.Throwable: + writer.PutThrowableData(ThrowableData); + break; + case EReconnectDataType.Interactives: + writer.PutInteractivesStates(InteractivesData); + break; + case EReconnectDataType.LampControllers: + writer.PutLampStates(LampStates); + break; + case EReconnectDataType.Windows: + writer.PutWindowBreakerStates(WindowBreakerStates); + break; + case EReconnectDataType.OwnCharacter: + writer.PutByteArray(SimpleZlib.CompressToBytes(Profile.ToJson(), 4)); + writer.PutByteArray(SimpleZlib.CompressToBytes(ProfileHealthClass.ToJson(), 4)); + writer.Put(PlayerPosition); + break; + case EReconnectDataType.Finished: + default: + break; + } + } + } - public enum EReconnectDataType - { - Throwable, - Interactives, - LampControllers, - Windows, - OwnCharacter, - Finished - } - } + public enum EReconnectDataType + { + Throwable, + Interactives, + LampControllers, + Windows, + OwnCharacter, + Finished + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/ThrowablePacket.cs b/Fika.Core/Networking/Packets/GameWorld/ThrowablePacket.cs index 72d6957b..d5f5b129 100644 --- a/Fika.Core/Networking/Packets/GameWorld/ThrowablePacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/ThrowablePacket.cs @@ -2,42 +2,42 @@ namespace Fika.Core.Networking { - internal struct ThrowablePacket : INetSerializable - { - public GStruct128 Data; + internal struct ThrowablePacket : INetSerializable + { + public GStruct128 Data; - public void Deserialize(NetDataReader reader) - { - Data = new() - { - Id = reader.GetInt(), - Position = reader.GetVector3(), - Rotation = reader.GetQuaternion(), - CollisionNumber = reader.GetByte() - }; - if (!reader.GetBool()) - { - Data.Velocity = reader.GetVector3(); - Data.AngularVelocity = reader.GetVector3(); - } - else - { - Data.Done = true; - } - } + public void Deserialize(NetDataReader reader) + { + Data = new() + { + Id = reader.GetInt(), + Position = reader.GetVector3(), + Rotation = reader.GetQuaternion(), + CollisionNumber = reader.GetByte() + }; + if (!reader.GetBool()) + { + Data.Velocity = reader.GetVector3(); + Data.AngularVelocity = reader.GetVector3(); + } + else + { + Data.Done = true; + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(Data.Id); - writer.Put(Data.Position); - writer.Put(Data.Rotation); - writer.Put(Data.CollisionNumber); - writer.Put(Data.Done); - if (!Data.Done) - { - writer.Put(Data.Velocity); - writer.Put(Data.AngularVelocity); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(Data.Id); + writer.Put(Data.Position); + writer.Put(Data.Rotation); + writer.Put(Data.CollisionNumber); + writer.Put(Data.Done); + if (!Data.Done) + { + writer.Put(Data.Velocity); + writer.Put(Data.AngularVelocity); + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/WeatherPacket.cs b/Fika.Core/Networking/Packets/GameWorld/WeatherPacket.cs index 0da47829..5171936a 100644 --- a/Fika.Core/Networking/Packets/GameWorld/WeatherPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/WeatherPacket.cs @@ -5,61 +5,61 @@ namespace Fika.Core.Networking { - public struct WeatherPacket : INetSerializable - { - public bool IsRequest; - public bool HasData; - public int Amount; - public WeatherClass[] WeatherClasses; + public struct WeatherPacket : INetSerializable + { + public bool IsRequest; + public bool HasData; + public int Amount; + public WeatherClass[] WeatherClasses; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - HasData = reader.GetBool(); - if (HasData) - { - Amount = reader.GetInt(); - WeatherClasses = new WeatherClass[Amount]; - for (int i = 0; i < Amount; i++) - { - WeatherClassPacket weatherClassPacket = WeatherClassPacket.Deserialize(reader); - WeatherClasses[i] = new() - { - AtmospherePressure = weatherClassPacket.AtmospherePressure, - Cloudness = weatherClassPacket.Cloudness, - GlobalFogDensity = weatherClassPacket.GlobalFogDensity, - GlobalFogHeight = weatherClassPacket.GlobalFogHeight, - LyingWater = weatherClassPacket.LyingWater, - MainWindDirection = weatherClassPacket.MainWindDirection, - MainWindPosition = weatherClassPacket.MainWindPosition, - Rain = weatherClassPacket.Rain, - RainRandomness = weatherClassPacket.RainRandomness, - ScaterringFogDensity = weatherClassPacket.ScaterringFogDensity, - ScaterringFogHeight = weatherClassPacket.ScaterringFogDensity, - Temperature = weatherClassPacket.Temperature, - Time = weatherClassPacket.Time, - TopWindDirection = weatherClassPacket.TopWindDirection, - TopWindPosition = weatherClassPacket.TopWindPosition, - Turbulence = weatherClassPacket.Turbulence, - Wind = weatherClassPacket.Wind, - WindDirection = weatherClassPacket.WindDirection - }; - } - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + HasData = reader.GetBool(); + if (HasData) + { + Amount = reader.GetInt(); + WeatherClasses = new WeatherClass[Amount]; + for (int i = 0; i < Amount; i++) + { + WeatherClassPacket weatherClassPacket = WeatherClassPacket.Deserialize(reader); + WeatherClasses[i] = new() + { + AtmospherePressure = weatherClassPacket.AtmospherePressure, + Cloudness = weatherClassPacket.Cloudness, + GlobalFogDensity = weatherClassPacket.GlobalFogDensity, + GlobalFogHeight = weatherClassPacket.GlobalFogHeight, + LyingWater = weatherClassPacket.LyingWater, + MainWindDirection = weatherClassPacket.MainWindDirection, + MainWindPosition = weatherClassPacket.MainWindPosition, + Rain = weatherClassPacket.Rain, + RainRandomness = weatherClassPacket.RainRandomness, + ScaterringFogDensity = weatherClassPacket.ScaterringFogDensity, + ScaterringFogHeight = weatherClassPacket.ScaterringFogDensity, + Temperature = weatherClassPacket.Temperature, + Time = weatherClassPacket.Time, + TopWindDirection = weatherClassPacket.TopWindDirection, + TopWindPosition = weatherClassPacket.TopWindPosition, + Turbulence = weatherClassPacket.Turbulence, + Wind = weatherClassPacket.Wind, + WindDirection = weatherClassPacket.WindDirection + }; + } + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - writer.Put(HasData); - if (HasData) - { - writer.Put(Amount); - for (int i = 0; i < Amount; i++) - { - WeatherClassPacket.Serialize(writer, WeatherClasses[i]); - } - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + writer.Put(HasData); + if (HasData) + { + writer.Put(Amount); + for (int i = 0; i < Amount; i++) + { + WeatherClassPacket.Serialize(writer, WeatherClasses[i]); + } + } + } + } } diff --git a/Fika.Core/Networking/Packets/GameWorld/WorldLootPacket.cs b/Fika.Core/Networking/Packets/GameWorld/WorldLootPacket.cs index f483c799..18316397 100644 --- a/Fika.Core/Networking/Packets/GameWorld/WorldLootPacket.cs +++ b/Fika.Core/Networking/Packets/GameWorld/WorldLootPacket.cs @@ -2,27 +2,27 @@ namespace Fika.Core.Networking.Packets.GameWorld { - public struct WorldLootPacket(bool isRequest) : INetSerializable - { - public bool IsRequest = isRequest; - public byte[] Data; + public struct WorldLootPacket(bool isRequest) : INetSerializable + { + public bool IsRequest = isRequest; + public byte[] Data; - public void Deserialize(NetDataReader reader) - { - IsRequest = reader.GetBool(); - if (!IsRequest) - { - Data = reader.GetByteArray(); - } - } + public void Deserialize(NetDataReader reader) + { + IsRequest = reader.GetBool(); + if (!IsRequest) + { + Data = reader.GetByteArray(); + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(IsRequest); - if (!IsRequest) - { - writer.PutByteArray(Data); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(IsRequest); + if (!IsRequest) + { + writer.PutByteArray(Data); + } + } + } } diff --git a/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs b/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs index 3e7de4ff..e8fd7fd1 100644 --- a/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs +++ b/Fika.Core/Networking/Packets/Player/ArmorDamagePacket.cs @@ -2,24 +2,24 @@ namespace Fika.Core.Networking { - public struct ArmorDamagePacket(int netId) : INetSerializable - { - public int NetId = netId; - public string[] ItemIds; - public float[] Durabilities; + 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 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); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.PutArray(ItemIds); + writer.PutArray(Durabilities); + } + } } diff --git a/Fika.Core/Networking/Packets/Player/CommonPlayerPacket.cs b/Fika.Core/Networking/Packets/Player/CommonPlayerPacket.cs index f6208c76..417a25c1 100644 --- a/Fika.Core/Networking/Packets/Player/CommonPlayerPacket.cs +++ b/Fika.Core/Networking/Packets/Player/CommonPlayerPacket.cs @@ -3,98 +3,98 @@ namespace Fika.Core.Networking { - public struct CommonPlayerPacket(int netId) : INetSerializable - { - public int NetId = netId; - public EPhraseTrigger Phrase = EPhraseTrigger.PhraseNone; - public int PhraseIndex; - public bool HasWorldInteractionPacket = false; - public WorldInteractionPacket WorldInteractionPacket; - public bool HasContainerInteractionPacket = false; - public ContainerInteractionPacket ContainerInteractionPacket; - public bool HasProceedPacket = false; - public ProceedPacket ProceedPacket; - public bool HasHeadLightsPacket = false; - public HeadLightsPacket HeadLightsPacket; - public bool HasInventoryChanged = false; - public bool SetInventoryOpen; - public bool HasDrop = false; - public DropPacket DropPacket; - public bool HasStationaryPacket = false; - public StationaryPacket StationaryPacket; - public bool Pickup = false; - public int PickupAnimation; - public bool HasVaultPacket = false; - public VaultPacket VaultPacket; + public struct CommonPlayerPacket(int netId) : INetSerializable + { + public int NetId = netId; + public EPhraseTrigger Phrase = EPhraseTrigger.PhraseNone; + public int PhraseIndex; + public bool HasWorldInteractionPacket = false; + public WorldInteractionPacket WorldInteractionPacket; + public bool HasContainerInteractionPacket = false; + public ContainerInteractionPacket ContainerInteractionPacket; + public bool HasProceedPacket = false; + public ProceedPacket ProceedPacket; + public bool HasHeadLightsPacket = false; + public HeadLightsPacket HeadLightsPacket; + public bool HasInventoryChanged = false; + public bool SetInventoryOpen; + public bool HasDrop = false; + public DropPacket DropPacket; + public bool HasStationaryPacket = false; + public StationaryPacket StationaryPacket; + public bool Pickup = false; + public int PickupAnimation; + public bool HasVaultPacket = false; + public VaultPacket VaultPacket; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - Phrase = (EPhraseTrigger)reader.GetInt(); - if (Phrase != EPhraseTrigger.PhraseNone) - PhraseIndex = reader.GetInt(); - HasWorldInteractionPacket = reader.GetBool(); - if (HasWorldInteractionPacket) - WorldInteractionPacket = WorldInteractionPacket.Deserialize(reader); - HasContainerInteractionPacket = reader.GetBool(); - if (HasContainerInteractionPacket) - ContainerInteractionPacket = ContainerInteractionPacket.Deserialize(reader); - HasProceedPacket = reader.GetBool(); - if (HasProceedPacket) - ProceedPacket = ProceedPacket.Deserialize(reader); - HasHeadLightsPacket = reader.GetBool(); - if (HasHeadLightsPacket) - HeadLightsPacket = HeadLightsPacket.Deserialize(reader); - HasInventoryChanged = reader.GetBool(); - if (HasInventoryChanged) - SetInventoryOpen = reader.GetBool(); - HasDrop = reader.GetBool(); - if (HasDrop) - DropPacket = DropPacket.Deserialize(reader); - HasStationaryPacket = reader.GetBool(); - if (HasStationaryPacket) - StationaryPacket = StationaryPacket.Deserialize(reader); - Pickup = reader.GetBool(); - if (Pickup) - PickupAnimation = reader.GetInt(); - HasVaultPacket = reader.GetBool(); - if (HasVaultPacket) - VaultPacket = VaultPacket.Deserialize(reader); - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + Phrase = (EPhraseTrigger)reader.GetInt(); + if (Phrase != EPhraseTrigger.PhraseNone) + PhraseIndex = reader.GetInt(); + HasWorldInteractionPacket = reader.GetBool(); + if (HasWorldInteractionPacket) + WorldInteractionPacket = WorldInteractionPacket.Deserialize(reader); + HasContainerInteractionPacket = reader.GetBool(); + if (HasContainerInteractionPacket) + ContainerInteractionPacket = ContainerInteractionPacket.Deserialize(reader); + HasProceedPacket = reader.GetBool(); + if (HasProceedPacket) + ProceedPacket = ProceedPacket.Deserialize(reader); + HasHeadLightsPacket = reader.GetBool(); + if (HasHeadLightsPacket) + HeadLightsPacket = HeadLightsPacket.Deserialize(reader); + HasInventoryChanged = reader.GetBool(); + if (HasInventoryChanged) + SetInventoryOpen = reader.GetBool(); + HasDrop = reader.GetBool(); + if (HasDrop) + DropPacket = DropPacket.Deserialize(reader); + HasStationaryPacket = reader.GetBool(); + if (HasStationaryPacket) + StationaryPacket = StationaryPacket.Deserialize(reader); + Pickup = reader.GetBool(); + if (Pickup) + PickupAnimation = reader.GetInt(); + HasVaultPacket = reader.GetBool(); + if (HasVaultPacket) + VaultPacket = VaultPacket.Deserialize(reader); + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put((int)Phrase); - if (Phrase != EPhraseTrigger.PhraseNone) - writer.Put(PhraseIndex); - writer.Put(HasWorldInteractionPacket); - if (HasWorldInteractionPacket) - WorldInteractionPacket.Serialize(writer, WorldInteractionPacket); - writer.Put(HasContainerInteractionPacket); - if (HasContainerInteractionPacket) - ContainerInteractionPacket.Serialize(writer, ContainerInteractionPacket); - writer.Put(HasProceedPacket); - if (HasProceedPacket) - ProceedPacket.Serialize(writer, ProceedPacket); - writer.Put(HasHeadLightsPacket); - if (HasHeadLightsPacket) - HeadLightsPacket.Serialize(writer, HeadLightsPacket); - writer.Put(HasInventoryChanged); - if (HasInventoryChanged) - writer.Put(SetInventoryOpen); - writer.Put(HasDrop); - if (HasDrop) - DropPacket.Serialize(writer, DropPacket); - writer.Put(HasStationaryPacket); - if (HasStationaryPacket) - StationaryPacket.Serialize(writer, StationaryPacket); - writer.Put(Pickup); - if (Pickup) - writer.Put(PickupAnimation); - writer.Put(HasVaultPacket); - if (HasVaultPacket) - VaultPacket.Serialize(writer, VaultPacket); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put((int)Phrase); + if (Phrase != EPhraseTrigger.PhraseNone) + writer.Put(PhraseIndex); + writer.Put(HasWorldInteractionPacket); + if (HasWorldInteractionPacket) + WorldInteractionPacket.Serialize(writer, WorldInteractionPacket); + writer.Put(HasContainerInteractionPacket); + if (HasContainerInteractionPacket) + ContainerInteractionPacket.Serialize(writer, ContainerInteractionPacket); + writer.Put(HasProceedPacket); + if (HasProceedPacket) + ProceedPacket.Serialize(writer, ProceedPacket); + writer.Put(HasHeadLightsPacket); + if (HasHeadLightsPacket) + HeadLightsPacket.Serialize(writer, HeadLightsPacket); + writer.Put(HasInventoryChanged); + if (HasInventoryChanged) + writer.Put(SetInventoryOpen); + writer.Put(HasDrop); + if (HasDrop) + DropPacket.Serialize(writer, DropPacket); + writer.Put(HasStationaryPacket); + if (HasStationaryPacket) + StationaryPacket.Serialize(writer, StationaryPacket); + writer.Put(Pickup); + if (Pickup) + writer.Put(PickupAnimation); + writer.Put(HasVaultPacket); + if (HasVaultPacket) + VaultPacket.Serialize(writer, VaultPacket); + } + } } diff --git a/Fika.Core/Networking/Packets/Player/DamagePacket.cs b/Fika.Core/Networking/Packets/Player/DamagePacket.cs index 91e011b4..bfa72b91 100644 --- a/Fika.Core/Networking/Packets/Player/DamagePacket.cs +++ b/Fika.Core/Networking/Packets/Player/DamagePacket.cs @@ -5,72 +5,72 @@ namespace Fika.Core.Networking { - public struct DamagePacket(int netId) : INetSerializable - { - public int NetId = netId; - 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 struct DamagePacket(int netId) : INetSerializable + { + public int NetId = netId; + 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(); - 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 Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + 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); - 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); - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + 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 333afcc7..4ba79a02 100644 --- a/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs +++ b/Fika.Core/Networking/Packets/Player/HealthSyncPacket.cs @@ -4,433 +4,433 @@ namespace Fika.Core.Networking { - public struct HealthSyncPacket(int netId) : INetSerializable - { - public int NetId = netId; - public GStruct346 Packet; - public string KillerId; - public RagdollPacket RagdollPacket; - public EquipmentClass Equipment; - public string[] TriggerZones; + public struct HealthSyncPacket(int netId) : INetSerializable + { + public int NetId = netId; + public GStruct346 Packet; + public string KillerId; + public RagdollPacket RagdollPacket; + public EquipmentClass Equipment; + public string[] TriggerZones; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - GStruct346 packet = new() - { - SyncType = (GStruct346.ESyncType)reader.GetInt() - }; - switch (packet.SyncType) - { - case GStruct346.ESyncType.AddEffect: - { - packet.Data.AddEffect = new() - { - EffectId = reader.GetInt(), - Type = reader.GetByte(), - BodyPart = (EBodyPart)reader.GetInt(), - DelayTime = reader.GetFloat(), - BuildUpTime = reader.GetFloat(), - WorkTime = reader.GetFloat(), - ResidueTime = reader.GetFloat(), - Strength = reader.GetFloat(), - ExtraDataType = (GStruct346.GStruct347.EExtraDataType)reader.GetInt() - }; - switch (packet.Data.AddEffect.ExtraDataType) - { - case GStruct346.GStruct347.EExtraDataType.MedEffect: - { - packet.Data.AddEffect.ExtraData = new() - { - MedEffect = new() - { - ItemId = reader.GetString(), - Amount = reader.GetFloat() - } - }; - } - break; - case GStruct346.GStruct347.EExtraDataType.Stimulator: - { - packet.Data.AddEffect.ExtraData = new() - { - Stimulator = new() - { - BuffsName = reader.GetString() - } - }; - break; - } - } - break; - } - case GStruct346.ESyncType.RemoveEffect: - { - packet.Data.RemoveEffect = new() - { - EffectId = reader.GetInt() - }; - break; - } - case GStruct346.ESyncType.EffectNextState: - { - packet.Data.EffectNextState = new() - { - EffectId = reader.GetInt(), - StateTime = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.EffectStateTime: - { - packet.Data.EffectStateTime = new() - { - EffectId = reader.GetInt(), - RemainingStateTime = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.EffectStrength: - { - packet.Data.EffectStrength = new() - { - EffectId = reader.GetInt(), - Strength = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.EffectMedResource: - { - packet.Data.EffectMedResource = new() - { - EffectId = reader.GetInt(), - Resource = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.EffectStimulatorBuff: - { - packet.Data.EffectStimulatorBuff = new() - { - EffectId = reader.GetInt(), - BuffIndex = reader.GetInt(), - BuffActivate = reader.GetBool() - }; - if (packet.Data.EffectStimulatorBuff.BuffActivate) - { - packet.Data.EffectStimulatorBuff.BuffValue = reader.GetFloat(); - packet.Data.EffectStimulatorBuff.BuffDuration = reader.GetFloat(); - packet.Data.EffectStimulatorBuff.BuffDelay = reader.GetFloat(); - break; - } - break; - } - case GStruct346.ESyncType.IsAlive: - { - packet.Data.IsAlive = new() - { - IsAlive = reader.GetBool() - }; - if (!packet.Data.IsAlive.IsAlive) - { - packet.Data.IsAlive.DamageType = (EDamageType)reader.GetInt(); - KillerId = reader.GetString(); - RagdollPacket = RagdollPacket.Deserialize(reader); - Equipment = (EquipmentClass)reader.GetItem(); - TriggerZones = reader.GetStringArray(); - break; - } - break; - } - case GStruct346.ESyncType.BodyHealth: - { - packet.Data.BodyHealth = new() - { - BodyPart = (EBodyPart)reader.GetInt(), - Value = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.Energy: - { - packet.Data.Energy = new() - { - Value = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.Hydration: - { - packet.Data.Hydration = new() - { - Value = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.Temperature: - { - packet.Data.Temperature = new() - { - Value = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.DamageCoeff: - { - packet.Data.DamageCoeff = new() - { - DamageCoeff = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.ApplyDamage: - { - packet.Data.ApplyDamage = new() - { - BodyPart = (EBodyPart)reader.GetInt(), - Damage = reader.GetFloat(), - DamageType = (EDamageType)reader.GetInt() - }; - break; - } - case GStruct346.ESyncType.DestroyedBodyPart: - { - packet.Data.DestroyedBodyPart = new() - { - BodyPart = (EBodyPart)reader.GetInt(), - IsDestroyed = reader.GetBool() - }; - if (packet.Data.DestroyedBodyPart.IsDestroyed) - { - packet.Data.DestroyedBodyPart.DamageType = (EDamageType)reader.GetInt(); - break; - } - packet.Data.DestroyedBodyPart.HealthMaximum = reader.GetFloat(); - break; - } - case GStruct346.ESyncType.HealthRates: - { - packet.Data.HealthRates = new() - { - HealRate = reader.GetFloat(), - DamageRate = reader.GetFloat(), - DamageMultiplier = reader.GetFloat(), - Energy = reader.GetFloat(), - Hydration = reader.GetFloat(), - Temperature = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.HealerDone: - { - packet.Data.HealerDone = new() - { - EffectId = reader.GetInt() - }; - break; - } - case GStruct346.ESyncType.BurnEyes: - { - packet.Data.BurnEyes = new() - { - Position = reader.GetVector3(), - DistanceStrength = reader.GetFloat(), - NormalTime = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.Poison: - { - packet.Data.Poison = new() - { - Value = reader.GetFloat() - }; - break; - } - case GStruct346.ESyncType.StaminaCoeff: - { - packet.Data.StaminaCoeff = new() - { - StaminaCoeff = reader.GetFloat() - }; - return; - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + GStruct346 packet = new() + { + SyncType = (GStruct346.ESyncType)reader.GetInt() + }; + switch (packet.SyncType) + { + case GStruct346.ESyncType.AddEffect: + { + packet.Data.AddEffect = new() + { + EffectId = reader.GetInt(), + Type = reader.GetByte(), + BodyPart = (EBodyPart)reader.GetInt(), + DelayTime = reader.GetFloat(), + BuildUpTime = reader.GetFloat(), + WorkTime = reader.GetFloat(), + ResidueTime = reader.GetFloat(), + Strength = reader.GetFloat(), + ExtraDataType = (GStruct346.GStruct347.EExtraDataType)reader.GetInt() + }; + switch (packet.Data.AddEffect.ExtraDataType) + { + case GStruct346.GStruct347.EExtraDataType.MedEffect: + { + packet.Data.AddEffect.ExtraData = new() + { + MedEffect = new() + { + ItemId = reader.GetString(), + Amount = reader.GetFloat() + } + }; + } + break; + case GStruct346.GStruct347.EExtraDataType.Stimulator: + { + packet.Data.AddEffect.ExtraData = new() + { + Stimulator = new() + { + BuffsName = reader.GetString() + } + }; + break; + } + } + break; + } + case GStruct346.ESyncType.RemoveEffect: + { + packet.Data.RemoveEffect = new() + { + EffectId = reader.GetInt() + }; + break; + } + case GStruct346.ESyncType.EffectNextState: + { + packet.Data.EffectNextState = new() + { + EffectId = reader.GetInt(), + StateTime = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.EffectStateTime: + { + packet.Data.EffectStateTime = new() + { + EffectId = reader.GetInt(), + RemainingStateTime = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.EffectStrength: + { + packet.Data.EffectStrength = new() + { + EffectId = reader.GetInt(), + Strength = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.EffectMedResource: + { + packet.Data.EffectMedResource = new() + { + EffectId = reader.GetInt(), + Resource = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.EffectStimulatorBuff: + { + packet.Data.EffectStimulatorBuff = new() + { + EffectId = reader.GetInt(), + BuffIndex = reader.GetInt(), + BuffActivate = reader.GetBool() + }; + if (packet.Data.EffectStimulatorBuff.BuffActivate) + { + packet.Data.EffectStimulatorBuff.BuffValue = reader.GetFloat(); + packet.Data.EffectStimulatorBuff.BuffDuration = reader.GetFloat(); + packet.Data.EffectStimulatorBuff.BuffDelay = reader.GetFloat(); + break; + } + break; + } + case GStruct346.ESyncType.IsAlive: + { + packet.Data.IsAlive = new() + { + IsAlive = reader.GetBool() + }; + if (!packet.Data.IsAlive.IsAlive) + { + packet.Data.IsAlive.DamageType = (EDamageType)reader.GetInt(); + KillerId = reader.GetString(); + RagdollPacket = RagdollPacket.Deserialize(reader); + Equipment = (EquipmentClass)reader.GetItem(); + TriggerZones = reader.GetStringArray(); + break; + } + break; + } + case GStruct346.ESyncType.BodyHealth: + { + packet.Data.BodyHealth = new() + { + BodyPart = (EBodyPart)reader.GetInt(), + Value = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.Energy: + { + packet.Data.Energy = new() + { + Value = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.Hydration: + { + packet.Data.Hydration = new() + { + Value = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.Temperature: + { + packet.Data.Temperature = new() + { + Value = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.DamageCoeff: + { + packet.Data.DamageCoeff = new() + { + DamageCoeff = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.ApplyDamage: + { + packet.Data.ApplyDamage = new() + { + BodyPart = (EBodyPart)reader.GetInt(), + Damage = reader.GetFloat(), + DamageType = (EDamageType)reader.GetInt() + }; + break; + } + case GStruct346.ESyncType.DestroyedBodyPart: + { + packet.Data.DestroyedBodyPart = new() + { + BodyPart = (EBodyPart)reader.GetInt(), + IsDestroyed = reader.GetBool() + }; + if (packet.Data.DestroyedBodyPart.IsDestroyed) + { + packet.Data.DestroyedBodyPart.DamageType = (EDamageType)reader.GetInt(); + break; + } + packet.Data.DestroyedBodyPart.HealthMaximum = reader.GetFloat(); + break; + } + case GStruct346.ESyncType.HealthRates: + { + packet.Data.HealthRates = new() + { + HealRate = reader.GetFloat(), + DamageRate = reader.GetFloat(), + DamageMultiplier = reader.GetFloat(), + Energy = reader.GetFloat(), + Hydration = reader.GetFloat(), + Temperature = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.HealerDone: + { + packet.Data.HealerDone = new() + { + EffectId = reader.GetInt() + }; + break; + } + case GStruct346.ESyncType.BurnEyes: + { + packet.Data.BurnEyes = new() + { + Position = reader.GetVector3(), + DistanceStrength = reader.GetFloat(), + NormalTime = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.Poison: + { + packet.Data.Poison = new() + { + Value = reader.GetFloat() + }; + break; + } + case GStruct346.ESyncType.StaminaCoeff: + { + packet.Data.StaminaCoeff = new() + { + StaminaCoeff = reader.GetFloat() + }; + return; + } + } - Packet = packet; - } + Packet = packet; + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - GStruct346.GStruct365 packet = Packet.Data; - writer.Put((int)Packet.SyncType); - switch (Packet.SyncType) - { - case GStruct346.ESyncType.AddEffect: - { - writer.Put(packet.AddEffect.EffectId); - writer.Put(packet.AddEffect.Type); - writer.Put((int)packet.AddEffect.BodyPart); - writer.Put(packet.AddEffect.DelayTime); - writer.Put(packet.AddEffect.BuildUpTime); - writer.Put(packet.AddEffect.WorkTime); - writer.Put(packet.AddEffect.ResidueTime); - writer.Put(packet.AddEffect.Strength); - writer.Put((int)packet.AddEffect.ExtraDataType); - switch (packet.AddEffect.ExtraDataType) - { - case GStruct346.GStruct347.EExtraDataType.None: - break; - case GStruct346.GStruct347.EExtraDataType.MedEffect: - { - writer.Put(packet.AddEffect.ExtraData.MedEffect.ItemId); - writer.Put(packet.AddEffect.ExtraData.MedEffect.Amount); - break; - } - case GStruct346.GStruct347.EExtraDataType.Stimulator: - { - writer.Put(packet.AddEffect.ExtraData.Stimulator.BuffsName); - break; - } - } - break; - } - case GStruct346.ESyncType.RemoveEffect: - { - writer.Put(packet.RemoveEffect.EffectId); - break; - } - case GStruct346.ESyncType.EffectNextState: - { - writer.Put(packet.EffectNextState.EffectId); - writer.Put(packet.EffectNextState.StateTime); - break; - } - case GStruct346.ESyncType.EffectStateTime: - { - writer.Put(packet.EffectStateTime.EffectId); - writer.Put(packet.EffectStateTime.RemainingStateTime); - break; - } - case GStruct346.ESyncType.EffectStrength: - { - writer.Put(packet.EffectStrength.EffectId); - writer.Put(packet.EffectStrength.Strength); - break; - } - case GStruct346.ESyncType.EffectMedResource: - { - writer.Put(packet.EffectMedResource.EffectId); - writer.Put(packet.EffectMedResource.Resource); - break; - } - case GStruct346.ESyncType.EffectStimulatorBuff: - { - writer.Put(packet.EffectStimulatorBuff.EffectId); - writer.Put(packet.EffectStimulatorBuff.BuffIndex); - writer.Put(packet.EffectStimulatorBuff.BuffActivate); - if (packet.EffectStimulatorBuff.BuffActivate) - { - writer.Put(packet.EffectStimulatorBuff.BuffValue); - writer.Put(packet.EffectStimulatorBuff.BuffDuration); - writer.Put(packet.EffectStimulatorBuff.BuffDelay); - break; - } - break; - } - case GStruct346.ESyncType.IsAlive: - { - writer.Put(packet.IsAlive.IsAlive); - if (!packet.IsAlive.IsAlive) - { - writer.Put((int)packet.IsAlive.DamageType); - writer.Put(KillerId); - RagdollPacket.Serialize(writer, RagdollPacket); - writer.PutItem(Equipment); - writer.PutArray(TriggerZones); - break; - } - break; - } - case GStruct346.ESyncType.BodyHealth: - { - writer.Put((int)packet.BodyHealth.BodyPart); - writer.Put(packet.BodyHealth.Value); - break; - } - case GStruct346.ESyncType.Energy: - { - writer.Put(packet.Energy.Value); - break; - } - case GStruct346.ESyncType.Hydration: - { - writer.Put(packet.Hydration.Value); - break; - } - case GStruct346.ESyncType.Temperature: - { - writer.Put(packet.Temperature.Value); - break; - } - case GStruct346.ESyncType.DamageCoeff: - { - writer.Put(packet.DamageCoeff.DamageCoeff); - break; - } - case GStruct346.ESyncType.ApplyDamage: - { - writer.Put((int)packet.ApplyDamage.BodyPart); - writer.Put(packet.ApplyDamage.Damage); - writer.Put((int)packet.ApplyDamage.DamageType); - break; - } - case GStruct346.ESyncType.DestroyedBodyPart: - { - writer.Put((int)packet.DestroyedBodyPart.BodyPart); - writer.Put(packet.DestroyedBodyPart.IsDestroyed); - if (packet.DestroyedBodyPart.IsDestroyed) - { - writer.Put((int)packet.DestroyedBodyPart.DamageType); - break; - } - writer.Put(packet.DestroyedBodyPart.HealthMaximum); - break; - } - case GStruct346.ESyncType.HealthRates: - { - writer.Put(packet.HealthRates.HealRate); - writer.Put(packet.HealthRates.DamageRate); - writer.Put(packet.HealthRates.DamageMultiplier); - writer.Put(packet.HealthRates.Energy); - writer.Put(packet.HealthRates.Hydration); - writer.Put(packet.HealthRates.Temperature); - break; - } - case GStruct346.ESyncType.HealerDone: - { - writer.Put(packet.HealerDone.EffectId); - break; - } - case GStruct346.ESyncType.BurnEyes: - { - writer.Put(packet.BurnEyes.Position); - writer.Put(packet.BurnEyes.DistanceStrength); - writer.Put(packet.BurnEyes.NormalTime); - break; - } - case GStruct346.ESyncType.Poison: - { - writer.Put(packet.Poison.Value); - break; - } - case GStruct346.ESyncType.StaminaCoeff: - { - writer.Put(packet.StaminaCoeff.StaminaCoeff); - break; - } - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + GStruct346.GStruct365 packet = Packet.Data; + writer.Put((int)Packet.SyncType); + switch (Packet.SyncType) + { + case GStruct346.ESyncType.AddEffect: + { + writer.Put(packet.AddEffect.EffectId); + writer.Put(packet.AddEffect.Type); + writer.Put((int)packet.AddEffect.BodyPart); + writer.Put(packet.AddEffect.DelayTime); + writer.Put(packet.AddEffect.BuildUpTime); + writer.Put(packet.AddEffect.WorkTime); + writer.Put(packet.AddEffect.ResidueTime); + writer.Put(packet.AddEffect.Strength); + writer.Put((int)packet.AddEffect.ExtraDataType); + switch (packet.AddEffect.ExtraDataType) + { + case GStruct346.GStruct347.EExtraDataType.None: + break; + case GStruct346.GStruct347.EExtraDataType.MedEffect: + { + writer.Put(packet.AddEffect.ExtraData.MedEffect.ItemId); + writer.Put(packet.AddEffect.ExtraData.MedEffect.Amount); + break; + } + case GStruct346.GStruct347.EExtraDataType.Stimulator: + { + writer.Put(packet.AddEffect.ExtraData.Stimulator.BuffsName); + break; + } + } + break; + } + case GStruct346.ESyncType.RemoveEffect: + { + writer.Put(packet.RemoveEffect.EffectId); + break; + } + case GStruct346.ESyncType.EffectNextState: + { + writer.Put(packet.EffectNextState.EffectId); + writer.Put(packet.EffectNextState.StateTime); + break; + } + case GStruct346.ESyncType.EffectStateTime: + { + writer.Put(packet.EffectStateTime.EffectId); + writer.Put(packet.EffectStateTime.RemainingStateTime); + break; + } + case GStruct346.ESyncType.EffectStrength: + { + writer.Put(packet.EffectStrength.EffectId); + writer.Put(packet.EffectStrength.Strength); + break; + } + case GStruct346.ESyncType.EffectMedResource: + { + writer.Put(packet.EffectMedResource.EffectId); + writer.Put(packet.EffectMedResource.Resource); + break; + } + case GStruct346.ESyncType.EffectStimulatorBuff: + { + writer.Put(packet.EffectStimulatorBuff.EffectId); + writer.Put(packet.EffectStimulatorBuff.BuffIndex); + writer.Put(packet.EffectStimulatorBuff.BuffActivate); + if (packet.EffectStimulatorBuff.BuffActivate) + { + writer.Put(packet.EffectStimulatorBuff.BuffValue); + writer.Put(packet.EffectStimulatorBuff.BuffDuration); + writer.Put(packet.EffectStimulatorBuff.BuffDelay); + break; + } + break; + } + case GStruct346.ESyncType.IsAlive: + { + writer.Put(packet.IsAlive.IsAlive); + if (!packet.IsAlive.IsAlive) + { + writer.Put((int)packet.IsAlive.DamageType); + writer.Put(KillerId); + RagdollPacket.Serialize(writer, RagdollPacket); + writer.PutItem(Equipment); + writer.PutArray(TriggerZones); + break; + } + break; + } + case GStruct346.ESyncType.BodyHealth: + { + writer.Put((int)packet.BodyHealth.BodyPart); + writer.Put(packet.BodyHealth.Value); + break; + } + case GStruct346.ESyncType.Energy: + { + writer.Put(packet.Energy.Value); + break; + } + case GStruct346.ESyncType.Hydration: + { + writer.Put(packet.Hydration.Value); + break; + } + case GStruct346.ESyncType.Temperature: + { + writer.Put(packet.Temperature.Value); + break; + } + case GStruct346.ESyncType.DamageCoeff: + { + writer.Put(packet.DamageCoeff.DamageCoeff); + break; + } + case GStruct346.ESyncType.ApplyDamage: + { + writer.Put((int)packet.ApplyDamage.BodyPart); + writer.Put(packet.ApplyDamage.Damage); + writer.Put((int)packet.ApplyDamage.DamageType); + break; + } + case GStruct346.ESyncType.DestroyedBodyPart: + { + writer.Put((int)packet.DestroyedBodyPart.BodyPart); + writer.Put(packet.DestroyedBodyPart.IsDestroyed); + if (packet.DestroyedBodyPart.IsDestroyed) + { + writer.Put((int)packet.DestroyedBodyPart.DamageType); + break; + } + writer.Put(packet.DestroyedBodyPart.HealthMaximum); + break; + } + case GStruct346.ESyncType.HealthRates: + { + writer.Put(packet.HealthRates.HealRate); + writer.Put(packet.HealthRates.DamageRate); + writer.Put(packet.HealthRates.DamageMultiplier); + writer.Put(packet.HealthRates.Energy); + writer.Put(packet.HealthRates.Hydration); + writer.Put(packet.HealthRates.Temperature); + break; + } + case GStruct346.ESyncType.HealerDone: + { + writer.Put(packet.HealerDone.EffectId); + break; + } + case GStruct346.ESyncType.BurnEyes: + { + writer.Put(packet.BurnEyes.Position); + writer.Put(packet.BurnEyes.DistanceStrength); + writer.Put(packet.BurnEyes.NormalTime); + break; + } + case GStruct346.ESyncType.Poison: + { + writer.Put(packet.Poison.Value); + break; + } + case GStruct346.ESyncType.StaminaCoeff: + { + writer.Put(packet.StaminaCoeff.StaminaCoeff); + break; + } + } + } + } } diff --git a/Fika.Core/Networking/Packets/Player/InventoryPacket.cs b/Fika.Core/Networking/Packets/Player/InventoryPacket.cs index 975248d1..37c13480 100644 --- a/Fika.Core/Networking/Packets/Player/InventoryPacket.cs +++ b/Fika.Core/Networking/Packets/Player/InventoryPacket.cs @@ -3,34 +3,34 @@ namespace Fika.Core.Networking { - public struct InventoryPacket(int netId) : INetSerializable - { - public int NetId = netId; - public bool HasItemControllerExecutePacket = false; - public ItemControllerExecutePacket ItemControllerExecutePacket; - public bool HasSearchPacket = false; - public SearchPacket SearchPacket; + public struct InventoryPacket(int netId) : INetSerializable + { + public int NetId = netId; + public bool HasItemControllerExecutePacket = false; + public ItemControllerExecutePacket ItemControllerExecutePacket; + public bool HasSearchPacket = false; + public SearchPacket SearchPacket; - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put(HasItemControllerExecutePacket); - if (HasItemControllerExecutePacket) - ItemControllerExecutePacket.Serialize(writer, ItemControllerExecutePacket); - writer.Put(HasSearchPacket); - if (HasSearchPacket) - SearchPacket.Serialize(writer, SearchPacket); - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(HasItemControllerExecutePacket); + if (HasItemControllerExecutePacket) + ItemControllerExecutePacket.Serialize(writer, ItemControllerExecutePacket); + writer.Put(HasSearchPacket); + if (HasSearchPacket) + SearchPacket.Serialize(writer, SearchPacket); + } - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - HasItemControllerExecutePacket = reader.GetBool(); - if (HasItemControllerExecutePacket) - ItemControllerExecutePacket = ItemControllerExecutePacket.Deserialize(reader); - HasSearchPacket = reader.GetBool(); - if (HasSearchPacket) - SearchPacket = SearchPacket.Deserialize(reader); - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + HasItemControllerExecutePacket = reader.GetBool(); + if (HasItemControllerExecutePacket) + ItemControllerExecutePacket = ItemControllerExecutePacket.Deserialize(reader); + HasSearchPacket = reader.GetBool(); + if (HasSearchPacket) + SearchPacket = SearchPacket.Deserialize(reader); + } + } } diff --git a/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs b/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs index 3ad2b193..64a4f33a 100644 --- a/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs +++ b/Fika.Core/Networking/Packets/Player/OperationCallbackPacket.cs @@ -3,33 +3,33 @@ namespace Fika.Core.Networking { - public struct OperationCallbackPacket(int netId, uint callbackId, EOperationStatus operationStatus, string error = null) : INetSerializable - { - public int NetId = netId; - public uint CallbackId = callbackId; - public EOperationStatus OperationStatus = operationStatus; - public string Error = error; + public struct OperationCallbackPacket(int netId, uint callbackId, EOperationStatus operationStatus, string error = null) : INetSerializable + { + public int NetId = netId; + public uint CallbackId = callbackId; + public EOperationStatus OperationStatus = operationStatus; + public string Error = error; - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - CallbackId = reader.GetUInt(); - OperationStatus = (EOperationStatus)reader.GetInt(); - if (OperationStatus == EOperationStatus.Failed) - { - Error = reader.GetString(); - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + CallbackId = reader.GetUInt(); + OperationStatus = (EOperationStatus)reader.GetInt(); + if (OperationStatus == EOperationStatus.Failed) + { + Error = reader.GetString(); + } + } - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put(CallbackId); - writer.Put((int)OperationStatus); - if (OperationStatus == EOperationStatus.Failed) - { - writer.Put(Error); - } - } - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(CallbackId); + writer.Put((int)OperationStatus); + if (OperationStatus == EOperationStatus.Failed) + { + writer.Put(Error); + } + } + } } diff --git a/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs b/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs index 6b0f2fe3..a5caae04 100644 --- a/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs +++ b/Fika.Core/Networking/Packets/Player/PlayerStatePacket.cs @@ -4,85 +4,85 @@ 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, BasePhysicalClass.GStruct36 stamina, int blindfire, - float weaponOverlap, bool leftStanceDisabled, bool isGrounded, bool hasGround, ESurfaceSound surfaceSound, Vector3 surfaceNormal) : INetSerializable - { - public int NetId = netId; - public Vector3 Position = position; - public Vector2 Rotation = rotation; - public Vector3 HeadRotation = headRotation; - public Vector2 MovementDirection = movementDirection; - public EPlayerState State = state; - public float Tilt = tilt; - public int Step = step; - public int AnimatorStateIndex = animatorStateIndex; - public float CharacterMovementSpeed = characterMovementSpeed; - public bool IsProne = isProne; - public float PoseLevel = poseLevel; - public bool IsSprinting = isSprinting; - public BasePhysicalClass.GStruct36 Stamina = stamina; - public int Blindfire = blindfire; - public float WeaponOverlap = weaponOverlap; - public bool LeftStanceDisabled = leftStanceDisabled; - public bool IsGrounded = isGrounded; - public bool HasGround = hasGround; - public ESurfaceSound SurfaceSound = surfaceSound; - public Vector3 SurfaceNormal = surfaceNormal; + 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, BasePhysicalClass.GStruct36 stamina, int blindfire, + float weaponOverlap, bool leftStanceDisabled, bool isGrounded, bool hasGround, ESurfaceSound surfaceSound, Vector3 surfaceNormal) : INetSerializable + { + public int NetId = netId; + public Vector3 Position = position; + public Vector2 Rotation = rotation; + public Vector3 HeadRotation = headRotation; + public Vector2 MovementDirection = movementDirection; + public EPlayerState State = state; + public float Tilt = tilt; + public int Step = step; + public int AnimatorStateIndex = animatorStateIndex; + public float CharacterMovementSpeed = characterMovementSpeed; + public bool IsProne = isProne; + public float PoseLevel = poseLevel; + public bool IsSprinting = isSprinting; + public BasePhysicalClass.GStruct36 Stamina = stamina; + public int Blindfire = blindfire; + public float WeaponOverlap = weaponOverlap; + public bool LeftStanceDisabled = leftStanceDisabled; + public bool IsGrounded = isGrounded; + public bool HasGround = hasGround; + public ESurfaceSound SurfaceSound = surfaceSound; + public Vector3 SurfaceNormal = surfaceNormal; - public void Serialize(NetDataWriter writer) - { - writer.Put(NetId); - writer.Put(Position); - writer.Put(Rotation); - writer.Put(HeadRotation); - writer.Put(MovementDirection); - writer.Put((int)State); - //writer.Put(GClass1089.ScaleFloatToByte(Tilt, -5f, 5f)); - writer.Put(Tilt); - //writer.Put(GClass1089.ScaleIntToByte(Step, -1, 1)); - writer.Put(Step); - writer.Put(AnimatorStateIndex); - //writer.Put(GClass1089.ScaleFloatToByte(CharacterMovementSpeed, 0f, 1f)); - writer.Put(CharacterMovementSpeed); - writer.Put(IsProne); - //writer.Put(GClass1089.ScaleFloatToByte(PoseLevel, 0f, 1f)); - writer.Put(PoseLevel); - writer.Put(IsSprinting); - writer.Put(Stamina); - writer.Put(Blindfire); - writer.Put(WeaponOverlap); - writer.Put(LeftStanceDisabled); - writer.Put(IsGrounded); - writer.Put(HasGround); - writer.Put((int)SurfaceSound); - writer.Put(SurfaceNormal); - } + public void Serialize(NetDataWriter writer) + { + writer.Put(NetId); + writer.Put(Position); + writer.Put(Rotation); + writer.Put(HeadRotation); + writer.Put(MovementDirection); + writer.Put((int)State); + //writer.Put(GClass1089.ScaleFloatToByte(Tilt, -5f, 5f)); + writer.Put(Tilt); + //writer.Put(GClass1089.ScaleIntToByte(Step, -1, 1)); + writer.Put(Step); + writer.Put(AnimatorStateIndex); + //writer.Put(GClass1089.ScaleFloatToByte(CharacterMovementSpeed, 0f, 1f)); + writer.Put(CharacterMovementSpeed); + writer.Put(IsProne); + //writer.Put(GClass1089.ScaleFloatToByte(PoseLevel, 0f, 1f)); + writer.Put(PoseLevel); + writer.Put(IsSprinting); + writer.Put(Stamina); + writer.Put(Blindfire); + writer.Put(WeaponOverlap); + writer.Put(LeftStanceDisabled); + writer.Put(IsGrounded); + writer.Put(HasGround); + writer.Put((int)SurfaceSound); + writer.Put(SurfaceNormal); + } - public void Deserialize(NetDataReader reader) - { - NetId = reader.GetInt(); - Position = reader.GetVector3(); - Rotation = reader.GetVector2(); - HeadRotation = reader.GetVector3(); - MovementDirection = reader.GetVector2(); - State = (EPlayerState)reader.GetInt(); - Tilt = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), -5f, 5f); - Step = reader.GetInt(); //GClass1089.ScaleByteToInt(reader.GetByte(), -1, 1); - AnimatorStateIndex = reader.GetInt(); - CharacterMovementSpeed = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), 0f, 1f); - IsProne = reader.GetBool(); - PoseLevel = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), 0f, 1f); - IsSprinting = reader.GetBool(); - Stamina = reader.GetPhysical(); - Blindfire = reader.GetInt(); - WeaponOverlap = reader.GetFloat(); - LeftStanceDisabled = reader.GetBool(); - IsGrounded = reader.GetBool(); - HasGround = reader.GetBool(); - SurfaceSound = (ESurfaceSound)reader.GetInt(); - SurfaceNormal = reader.GetVector3(); - } - } + public void Deserialize(NetDataReader reader) + { + NetId = reader.GetInt(); + Position = reader.GetVector3(); + Rotation = reader.GetVector2(); + HeadRotation = reader.GetVector3(); + MovementDirection = reader.GetVector2(); + State = (EPlayerState)reader.GetInt(); + Tilt = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), -5f, 5f); + Step = reader.GetInt(); //GClass1089.ScaleByteToInt(reader.GetByte(), -1, 1); + AnimatorStateIndex = reader.GetInt(); + CharacterMovementSpeed = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), 0f, 1f); + IsProne = reader.GetBool(); + PoseLevel = reader.GetFloat(); //GClass1089.ScaleByteToFloat(reader.GetByte(), 0f, 1f); + IsSprinting = reader.GetBool(); + Stamina = reader.GetPhysical(); + Blindfire = reader.GetInt(); + WeaponOverlap = reader.GetFloat(); + LeftStanceDisabled = reader.GetBool(); + IsGrounded = reader.GetBool(); + HasGround = reader.GetBool(); + SurfaceSound = (ESurfaceSound)reader.GetInt(); + SurfaceNormal = reader.GetVector3(); + } + } } \ No newline at end of file diff --git a/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs b/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs index a93fa370..b5cc6518 100644 --- a/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs +++ b/Fika.Core/Networking/Websocket/DedicatedRaidWebSocketClient.cs @@ -14,120 +14,120 @@ namespace Fika.Core.Networking.Websocket { - public class DedicatedRaidWebSocketClient - { - private static ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.DedicatedWebSocket"); - - public string Host { get; set; } - public string Url { get; set; } - public string SessionId { get; set; } - public bool Connected - { - get - { - return _webSocket.ReadyState == WebSocketState.Open; - } - } - - private WebSocket _webSocket; - - public DedicatedRaidWebSocketClient() - { - Host = RequestHandler.Host.Replace("http", "ws"); - SessionId = RequestHandler.SessionId; - Url = $"{Host}/fika/dedicatedraidservice/{SessionId}?"; - - _webSocket = new WebSocket(Url) - { - WaitTime = TimeSpan.FromMinutes(1), - EmitOnPing = true - }; - - _webSocket.OnOpen += WebSocket_OnOpen; - _webSocket.OnError += WebSocket_OnError; - _webSocket.OnMessage += (sender, args) => - { - // Run the OnMessage event on main thread - MainThreadDispatcher.RunOnMainThread(() => - { - WebSocket_OnMessage(sender, args); - }); - }; - } - - private void WebSocket_OnError(object sender, ErrorEventArgs e) - { - logger.LogInfo($"WS error: {e.Message}"); - } - - public void Connect() - { - _webSocket.Connect(); - } - - public void Close() - { - _webSocket.Close(); - } - - private void WebSocket_OnOpen(object sender, EventArgs e) - { - logger.LogInfo("Connected to FikaDedicatedRaidWebSocket as client"); - } - - private void WebSocket_OnMessage(object sender, MessageEventArgs e) - { - if (e == null) - { - return; - } - - if (string.IsNullOrEmpty(e.Data)) - { - return; - } - - JObject jsonObject = JObject.Parse(e.Data); - - if (!jsonObject.ContainsKey("type")) - { - return; - } - - // TODO: Convert to bytes and use an enum... - string type = jsonObject.Value("type"); - - switch (type) - { - case "fikaDedicatedJoinMatch": - string matchId = jsonObject.Value("matchId"); - - GameObject matchmakerObject = MatchmakerAcceptScreen_Show_Patch.MatchmakerObject; - MatchMakerAcceptScreen matchMakerAcceptScreen = FikaBackendUtils.MatchMakerAcceptScreenInstance; - - if (!string.IsNullOrEmpty(matchId)) - { - TarkovApplication tarkovApplication = (TarkovApplication)Singleton>.Instance; - - tarkovApplication.StartCoroutine(MatchMakerUIScript.JoinMatch(tarkovApplication.Session.Profile.Id, matchId, null, () => - { - // Hide matchmaker UI - matchmakerObject.SetActive(false); - - // Matchmaker next screen (accept) - matchMakerAcceptScreen.method_22(); - }, false)); - } - else - { - PreloaderUI.Instance.ShowErrorScreen("Fika Dedicated Error", "Received fikaJoinMatch WS event but there was no matchId"); - matchmakerObject.SetActive(true); - } - - FikaPlugin.DedicatedRaidWebSocket.Close(); - - break; - } - } - } + public class DedicatedRaidWebSocketClient + { + private static ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.DedicatedWebSocket"); + + public string Host { get; set; } + public string Url { get; set; } + public string SessionId { get; set; } + public bool Connected + { + get + { + return _webSocket.ReadyState == WebSocketState.Open; + } + } + + private WebSocket _webSocket; + + public DedicatedRaidWebSocketClient() + { + Host = RequestHandler.Host.Replace("http", "ws"); + SessionId = RequestHandler.SessionId; + Url = $"{Host}/fika/dedicatedraidservice/{SessionId}?"; + + _webSocket = new WebSocket(Url) + { + WaitTime = TimeSpan.FromMinutes(1), + EmitOnPing = true + }; + + _webSocket.OnOpen += WebSocket_OnOpen; + _webSocket.OnError += WebSocket_OnError; + _webSocket.OnMessage += (sender, args) => + { + // Run the OnMessage event on main thread + MainThreadDispatcher.RunOnMainThread(() => + { + WebSocket_OnMessage(sender, args); + }); + }; + } + + private void WebSocket_OnError(object sender, ErrorEventArgs e) + { + logger.LogInfo($"WS error: {e.Message}"); + } + + public void Connect() + { + _webSocket.Connect(); + } + + public void Close() + { + _webSocket.Close(); + } + + private void WebSocket_OnOpen(object sender, EventArgs e) + { + logger.LogInfo("Connected to FikaDedicatedRaidWebSocket as client"); + } + + private void WebSocket_OnMessage(object sender, MessageEventArgs e) + { + if (e == null) + { + return; + } + + if (string.IsNullOrEmpty(e.Data)) + { + return; + } + + JObject jsonObject = JObject.Parse(e.Data); + + if (!jsonObject.ContainsKey("type")) + { + return; + } + + // TODO: Convert to bytes and use an enum... + string type = jsonObject.Value("type"); + + switch (type) + { + case "fikaDedicatedJoinMatch": + string matchId = jsonObject.Value("matchId"); + + GameObject matchmakerObject = MatchmakerAcceptScreen_Show_Patch.MatchmakerObject; + MatchMakerAcceptScreen matchMakerAcceptScreen = FikaBackendUtils.MatchMakerAcceptScreenInstance; + + if (!string.IsNullOrEmpty(matchId)) + { + TarkovApplication tarkovApplication = (TarkovApplication)Singleton>.Instance; + + tarkovApplication.StartCoroutine(MatchMakerUIScript.JoinMatch(tarkovApplication.Session.Profile.Id, matchId, null, () => + { + // Hide matchmaker UI + matchmakerObject.SetActive(false); + + // Matchmaker next screen (accept) + matchMakerAcceptScreen.method_22(); + }, false)); + } + else + { + PreloaderUI.Instance.ShowErrorScreen("Fika Dedicated Error", "Received fikaJoinMatch WS event but there was no matchId"); + matchmakerObject.SetActive(true); + } + + FikaPlugin.DedicatedRaidWebSocket.Close(); + + break; + } + } + } } diff --git a/Fika.Core/UI/Custom/ButtonHandler.cs b/Fika.Core/UI/Custom/ButtonHandler.cs index e20ce327..5ae979d2 100644 --- a/Fika.Core/UI/Custom/ButtonHandler.cs +++ b/Fika.Core/UI/Custom/ButtonHandler.cs @@ -7,57 +7,57 @@ public class ButtonHandler : MonoBehaviour, IPointerExitHandler, IPointerEnterHandler { - private Image handleImage; - private TextMeshProUGUI handleText; + private Image handleImage; + private TextMeshProUGUI handleText; - [SerializeField] - Sprite buttonSprite; + [SerializeField] + Sprite buttonSprite; - void Start() - { - handleImage = GetComponent(); - handleText = GetComponentInChildren(); - } + void Start() + { + handleImage = GetComponent(); + handleText = GetComponentInChildren(); + } - private IEnumerator FadeButton() - { - while (handleImage.color.a > 0) - { - yield return new WaitForFixedUpdate(); - handleImage.color = new Color(255, 255, 255, handleImage.color.a - 0.15f); - handleText.color = new Color(0, 0, 0, handleText.color.a - 0.15f); - } - while (handleText.color.a < 1) - { - yield return new WaitForFixedUpdate(); - handleText.color = new Color(255, 255, 255, handleText.color.a + 0.15f); - } - } + private IEnumerator FadeButton() + { + while (handleImage.color.a > 0) + { + yield return new WaitForFixedUpdate(); + handleImage.color = new Color(255, 255, 255, handleImage.color.a - 0.15f); + handleText.color = new Color(0, 0, 0, handleText.color.a - 0.15f); + } + while (handleText.color.a < 1) + { + yield return new WaitForFixedUpdate(); + handleText.color = new Color(255, 255, 255, handleText.color.a + 0.15f); + } + } - private IEnumerator ShowButton() - { - while (handleText.color.a > 0) - { - yield return new WaitForFixedUpdate(); - handleText.color = new Color(255, 255, 255, handleText.color.a - 0.15f); - } - while (handleImage.color.a < 1) - { - yield return new WaitForFixedUpdate(); - handleImage.color = new Color(255, 255, 255, handleImage.color.a + 0.15f); - handleText.color = new Color(0, 0, 0, handleText.color.a + 0.15f); - } - } + private IEnumerator ShowButton() + { + while (handleText.color.a > 0) + { + yield return new WaitForFixedUpdate(); + handleText.color = new Color(255, 255, 255, handleText.color.a - 0.15f); + } + while (handleImage.color.a < 1) + { + yield return new WaitForFixedUpdate(); + handleImage.color = new Color(255, 255, 255, handleImage.color.a + 0.15f); + handleText.color = new Color(0, 0, 0, handleText.color.a + 0.15f); + } + } - public void OnPointerEnter(PointerEventData eventData) - { - StopAllCoroutines(); - StartCoroutine(ShowButton()); - } + public void OnPointerEnter(PointerEventData eventData) + { + StopAllCoroutines(); + StartCoroutine(ShowButton()); + } - public void OnPointerExit(PointerEventData eventData) - { - StopAllCoroutines(); - StartCoroutine(FadeButton()); - } + public void OnPointerExit(PointerEventData eventData) + { + StopAllCoroutines(); + StartCoroutine(FadeButton()); + } } diff --git a/Fika.Core/UI/Custom/IntUpDown.cs b/Fika.Core/UI/Custom/IntUpDown.cs index 752ae6a5..b779998b 100644 --- a/Fika.Core/UI/Custom/IntUpDown.cs +++ b/Fika.Core/UI/Custom/IntUpDown.cs @@ -6,28 +6,28 @@ public class IntUpDown : MonoBehaviour { - [SerializeField] - Button increaseButton; - [SerializeField] - Button decreaseButton; - [SerializeField] - int AmountOfPlayers = 1; - [SerializeField] - TextMeshProUGUI AmountText; + [SerializeField] + Button increaseButton; + [SerializeField] + Button decreaseButton; + [SerializeField] + int AmountOfPlayers = 1; + [SerializeField] + TextMeshProUGUI AmountText; - public void IncreaseAmount() - { - AmountOfPlayers++; - AmountOfPlayers = Mathf.Clamp(AmountOfPlayers, 1, 32); + public void IncreaseAmount() + { + AmountOfPlayers++; + AmountOfPlayers = Mathf.Clamp(AmountOfPlayers, 1, 32); - AmountText.text = AmountOfPlayers.ToString(); - } + AmountText.text = AmountOfPlayers.ToString(); + } - public void DecreaseAmount() - { - AmountOfPlayers--; - AmountOfPlayers = Mathf.Clamp(AmountOfPlayers, 1, 32); + public void DecreaseAmount() + { + AmountOfPlayers--; + AmountOfPlayers = Mathf.Clamp(AmountOfPlayers, 1, 32); - AmountText.text = AmountOfPlayers.ToString(); - } + AmountText.text = AmountOfPlayers.ToString(); + } } \ No newline at end of file diff --git a/Fika.Core/UI/Custom/MatchMakerUI.cs b/Fika.Core/UI/Custom/MatchMakerUI.cs index c44772a1..46dc9d47 100644 --- a/Fika.Core/UI/Custom/MatchMakerUI.cs +++ b/Fika.Core/UI/Custom/MatchMakerUI.cs @@ -4,28 +4,28 @@ public class MatchMakerUI : MonoBehaviour { - [SerializeField] - public GameObject ServerBrowserPanel; - [SerializeField] - public Button RefreshButton; - [SerializeField] - public Button RaidGroupHostButton; - [SerializeField] - public GameObject RaidGroupDefaultToClone; - [SerializeField] - public GameObject PlayerAmountSelection; - [SerializeField] - public Button StartButton; - [SerializeField] - public Button CloseButton; - [SerializeField] - public TextMeshProUGUI PlayerAmountText; - [SerializeField] - public Toggle DedicatedToggle; - [SerializeField] - public GameObject LoadingScreen; - [SerializeField] - public Image LoadingImage; - [SerializeField] - public TextMeshProUGUI LoadingText; + [SerializeField] + public GameObject ServerBrowserPanel; + [SerializeField] + public Button RefreshButton; + [SerializeField] + public Button RaidGroupHostButton; + [SerializeField] + public GameObject RaidGroupDefaultToClone; + [SerializeField] + public GameObject PlayerAmountSelection; + [SerializeField] + public Button StartButton; + [SerializeField] + public Button CloseButton; + [SerializeField] + public TextMeshProUGUI PlayerAmountText; + [SerializeField] + public Toggle DedicatedToggle; + [SerializeField] + public GameObject LoadingScreen; + [SerializeField] + public Image LoadingImage; + [SerializeField] + public TextMeshProUGUI LoadingText; } diff --git a/Fika.Core/UI/Custom/MatchMakerUIScript.cs b/Fika.Core/UI/Custom/MatchMakerUIScript.cs index 86564817..5903c2d0 100644 --- a/Fika.Core/UI/Custom/MatchMakerUIScript.cs +++ b/Fika.Core/UI/Custom/MatchMakerUIScript.cs @@ -22,709 +22,709 @@ namespace Fika.Core.UI.Custom { - public class MatchMakerUIScript : MonoBehaviour - { - public MatchMakerUI fikaMatchMakerUi; - public RaidSettings RaidSettings { get; set; } - private LobbyEntry[] Matches { get; set; } - private List MatchesListObjects { get; set; } = []; - private bool stopQuery = false; - - public DefaultUIButton BackButton { get; internal set; } - public DefaultUIButton AcceptButton { get; internal set; } - public GameObject NewBackButton { get; internal set; } - - private string ProfileId => FikaBackendUtils.Profile.ProfileId; - private float lastRefreshed; - - private bool _started; - private Coroutine serverQueryRoutine; - private float loadingTextTick = 0f; - - protected void OnEnable() - { - if (_started) - { - stopQuery = false; - if (serverQueryRoutine == null) - { - serverQueryRoutine = StartCoroutine(ServerQuery()); - } - } - } - - protected void OnDisable() - { - stopQuery = true; - if (serverQueryRoutine != null) - { - StopCoroutine(serverQueryRoutine); - serverQueryRoutine = null; - } - } - - protected void Start() - { - CreateMatchMakerUI(); - serverQueryRoutine = StartCoroutine(ServerQuery()); - _started = true; - } - - protected void Update() - { - if (Input.GetKeyDown(KeyCode.Escape)) - { - DestroyThis(); - } - - if (stopQuery) - { - if (serverQueryRoutine != null) - { - StopCoroutine(serverQueryRoutine); - serverQueryRoutine = null; - } - } - - if (fikaMatchMakerUi.LoadingScreen.activeSelf) - { - fikaMatchMakerUi.LoadingImage.transform.Rotate(0, 0, 3f); - string text = fikaMatchMakerUi.LoadingText.text; - TextMeshProUGUI tmpText = fikaMatchMakerUi.LoadingText; - - loadingTextTick++; - - if (loadingTextTick > 30) - { - loadingTextTick = 0; - - text += "."; - if (text == "....") - { - text = "."; - } - tmpText.text = text; - } - } - } - - private void DestroyThis() - { - stopQuery = true; - if (serverQueryRoutine != null) - { - StopCoroutine(serverQueryRoutine); - serverQueryRoutine = null; - } - - Destroy(gameObject); - Destroy(fikaMatchMakerUi); - Destroy(this); - } - - protected void OnDestroy() - { - stopQuery = true; - if (NewBackButton != null) - { - Destroy(NewBackButton); - } - } - - private void CreateMatchMakerUI() - { - GetDedicatedStatusResponse response = FikaRequestHandler.GetDedicatedStatus(); - - GameObject matchMakerUiPrefab = InternalBundleLoader.Instance.GetAssetBundle("newmatchmakerui").LoadAsset("NewMatchMakerUI"); - GameObject uiGameObj = Instantiate(matchMakerUiPrefab); - fikaMatchMakerUi = uiGameObj.GetComponent(); - fikaMatchMakerUi.transform.parent = transform; - fikaMatchMakerUi.GetComponent().sortingOrder = 100; // Might wanna do this directly in the SDK later - - if (fikaMatchMakerUi.RaidGroupDefaultToClone.active) - { - fikaMatchMakerUi.RaidGroupDefaultToClone.SetActive(false); - } - - if (fikaMatchMakerUi.PlayerAmountSelection.active) - { - fikaMatchMakerUi.PlayerAmountSelection.SetActive(false); - } - - fikaMatchMakerUi.LoadingText.text = ""; - - fikaMatchMakerUi.DedicatedToggle.isOn = false; - fikaMatchMakerUi.DedicatedToggle.onValueChanged.AddListener((arg) => - { - Singleton.Instance.PlayUISound(EUISoundType.MenuCheckBox); - }); - - if (!response.Available) - { - fikaMatchMakerUi.DedicatedToggle.interactable = false; - TextMeshProUGUI dedicatedText = fikaMatchMakerUi.DedicatedToggle.gameObject.GetComponentInChildren(); - if (dedicatedText != null) - { - dedicatedText.color = new(1f, 1f, 1f, 0.5f); - } - - TooltipTextGetter dediTooltipTextGetter = new() - { - TooltipText = $"No dedicated clients are available." - }; - - HoverTooltipArea dediTooltipArea = fikaMatchMakerUi.DedicatedToggle.GetOrAddComponent(); - dediTooltipArea.enabled = true; - dediTooltipArea.SetMessageText(new Func(dediTooltipTextGetter.GetText)); - } - - TMP_Text matchmakerUiHostRaidText = fikaMatchMakerUi.RaidGroupHostButton.GetComponentInChildren(); - fikaMatchMakerUi.RaidGroupHostButton.onClick.AddListener(() => - { - Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); - if (!fikaMatchMakerUi.PlayerAmountSelection.active) - { - fikaMatchMakerUi.PlayerAmountSelection.SetActive(true); - } - else - { - fikaMatchMakerUi.PlayerAmountSelection.SetActive(false); - } - }); - - fikaMatchMakerUi.CloseButton.onClick.AddListener(() => - { - Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); - if (fikaMatchMakerUi.PlayerAmountSelection.active) - { - fikaMatchMakerUi.PlayerAmountSelection.SetActive(false); - } - }); - - //fikaMatchMakerUi.DedicatedToggle.isOn = false; - fikaMatchMakerUi.DedicatedToggle.onValueChanged.AddListener((arg) => - { - Singleton.Instance.PlayUISound(EUISoundType.MenuCheckBox); - }); - - fikaMatchMakerUi.StartButton.onClick.AddListener(async () => - { - ToggleLoading(true); - - TarkovApplication tarkovApplication = (TarkovApplication)Singleton>.Instance; - ISession session = tarkovApplication.Session; - - Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); - - if (!fikaMatchMakerUi.DedicatedToggle.isOn) - { - if (FikaPlugin.ForceIP.Value != "") - { - // We need to handle DNS entries as well - string ip = FikaPlugin.ForceIP.Value; - try - { - IPAddress[] dnsAddress = Dns.GetHostAddresses(FikaPlugin.ForceIP.Value); - if (dnsAddress.Length > 0) - { - ip = dnsAddress[0].ToString(); - } - } - catch - { - - } - - if (!IPAddress.TryParse(ip, out _)) - { - Singleton.Instance.ShowCriticalErrorScreen("ERROR FORCING IP", - $"'{ip}' is not a valid IP address to connect to! Check your 'Force IP' setting.", - ErrorScreen.EButtonType.OkButton, 10f, null, null); - - ToggleLoading(false); - return; - } - } - - if (FikaPlugin.ForceBindIP.Value != "Disabled") - { - if (!IPAddress.TryParse(FikaPlugin.ForceBindIP.Value, out _)) - { - Singleton.Instance.ShowCriticalErrorScreen("ERROR BINDING", - $"'{FikaPlugin.ForceBindIP.Value}' is not a valid IP address to bind to! Check your 'Force Bind IP' setting.", - ErrorScreen.EButtonType.OkButton, 10f, null, null); - - ToggleLoading(false); - return; - } - } - - FikaBackendUtils.HostExpectedNumberOfPlayers = int.Parse(fikaMatchMakerUi.PlayerAmountText.text); - await FikaBackendUtils.CreateMatch(FikaBackendUtils.Profile.ProfileId, FikaBackendUtils.PMCName, RaidSettings); - AcceptButton.OnClick.Invoke(); - DestroyThis(); - } - else - { - ToggleLoading(true); - - FikaPlugin.DedicatedRaidWebSocket ??= new DedicatedRaidWebSocketClient(); - - if (!FikaPlugin.DedicatedRaidWebSocket.Connected) - { - FikaPlugin.DedicatedRaidWebSocket.Connect(); - } - - RaidSettings raidSettings = Traverse.Create(tarkovApplication).Field("_raidSettings").Value; - - StartDedicatedRequest request = new() - { - ExpectedNumPlayers = int.Parse(fikaMatchMakerUi.PlayerAmountText.text), - Time = raidSettings.SelectedDateTime, - LocationId = raidSettings.SelectedLocation._Id, - SpawnPlace = raidSettings.PlayersSpawnPlace, - MetabolismDisabled = raidSettings.MetabolismDisabled, - BotSettings = raidSettings.BotSettings, - Side = raidSettings.Side, - TimeAndWeatherSettings = raidSettings.TimeAndWeatherSettings, - WavesSettings = raidSettings.WavesSettings, - CustomWeather = OfflineRaidSettingsMenuPatch_Override.UseCustomWeather - }; - - StartDedicatedResponse response = await FikaRequestHandler.StartDedicated(request); - - if (!string.IsNullOrEmpty(response.Error)) - { - PreloaderUI.Instance.ShowErrorScreen("Fika Dedicated Error", response.Error); - - ToggleLoading(false); - } - else - { - NotificationManagerClass.DisplaySingletonWarningNotification("Starting raid on dedicated client... please wait."); - } - } - }); - - fikaMatchMakerUi.RefreshButton.onClick.AddListener(ManualRefresh); - - TooltipTextGetter tooltipTextGetter = new() - { - TooltipText = "Refresh list of active raids" - }; - - HoverTooltipArea tooltipArea = fikaMatchMakerUi.RefreshButton.GetOrAddComponent(); - tooltipArea.enabled = true; - tooltipArea.SetMessageText(new Func(tooltipTextGetter.GetText)); - - AcceptButton.gameObject.SetActive(false); - AcceptButton.enabled = false; - AcceptButton.Interactable = false; - - NewBackButton = Instantiate(BackButton.gameObject, BackButton.transform.parent); - UnityEngine.Events.UnityEvent newEvent = new(); - newEvent.AddListener(() => - { - DestroyThis(); - BackButton.OnClick.Invoke(); - }); - DefaultUIButton newButtonComponent = NewBackButton.GetComponent(); - Traverse.Create(newButtonComponent).Field("OnClick").SetValue(newEvent); - - if (!NewBackButton.active) - { - NewBackButton.SetActive(true); - } - - BackButton.gameObject.SetActive(false); - } - - private void ToggleLoading(bool enabled) - { - fikaMatchMakerUi.RaidGroupHostButton.interactable = !enabled; - fikaMatchMakerUi.StartButton.interactable = !enabled; - fikaMatchMakerUi.ServerBrowserPanel.SetActive(!enabled); - - fikaMatchMakerUi.LoadingScreen.SetActive(enabled); - - if (enabled) - { - if (serverQueryRoutine != null) - { - StopCoroutine(serverQueryRoutine); - serverQueryRoutine = null; - } - } - else if (!enabled) - { - serverQueryRoutine = StartCoroutine(ServerQuery()); - } - } - - private void AutoRefresh() - { - Matches = FikaRequestHandler.LocationRaids(RaidSettings); - - lastRefreshed = Time.time; - - RefreshUI(); - } - - private void ManualRefresh() - { - Singleton.Instance.PlayUISound(EUISoundType.ButtonClick); - Matches = FikaRequestHandler.LocationRaids(RaidSettings); - - lastRefreshed = Time.time; - - RefreshUI(); - } - - public static IEnumerator JoinMatch(string profileId, string serverId, Button button, Action successCallback, bool reconnect) - { - if (button != null) - { - button.enabled = false; - } - - FikaBackendUtils.IsReconnect = reconnect; - - NotificationManagerClass.DisplayMessageNotification("Connecting to session...", iconType: EFT.Communications.ENotificationIconType.EntryPoint); - - NetManagerUtils.CreatePingingClient(); - - FikaPingingClient pingingClient = Singleton.Instance; - - if (pingingClient.Init(serverId)) - { - int attempts = 0; - bool success; - - FikaPlugin.Instance.FikaLogger.LogInfo("Attempting to connect to host session..."); - - do - { - attempts++; - - pingingClient.PingEndPoint("fika.hello"); - pingingClient.NetClient.PollEvents(); - success = pingingClient.Received; - - yield return new WaitForSeconds(0.1f); - } while (!success && attempts < 50); - - if (!success) - { - Singleton.Instance.ShowCriticalErrorScreen( - "ERROR CONNECTING", - "Unable to connect to the server. Make sure that all ports are open and that all settings are configured correctly.", - ErrorScreen.EButtonType.OkButton, 10f, null, null); - - FikaPlugin.Instance.FikaLogger.LogError("Unable to connect to the session!"); - - if (button != null) - { - button.enabled = true; - } - yield break; - } - } - else - { - Singleton.Instance.ShowCriticalErrorScreen( - "ERROR CONNECTING", - "Could not start the FikaPinger!", - ErrorScreen.EButtonType.OkButton, 10f, null, null); - yield break; - } - - if (FikaBackendUtils.JoinMatch(profileId, serverId, out CreateMatch result, out string errorMessage)) - { - FikaBackendUtils.SetGroupId(result.ServerId); - FikaBackendUtils.MatchingType = EMatchmakerType.GroupPlayer; - FikaBackendUtils.HostExpectedNumberOfPlayers = result.ExpectedNumberOfPlayers; - - AddPlayerRequest data = new(FikaBackendUtils.GetGroupId(), profileId); - FikaRequestHandler.UpdateAddPlayer(data); - - if (FikaBackendUtils.IsHostNatPunch) - { - pingingClient.StartKeepAliveRoutine(); - } - else - { - NetManagerUtils.DestroyPingingClient(); - } - - //DestroyThis(); - - //AcceptButton.OnClick.Invoke(); - - successCallback?.Invoke(); - } - else - { - NetManagerUtils.DestroyPingingClient(); - - Singleton.Instance.ShowCriticalErrorScreen("ERROR JOINING", errorMessage, ErrorScreen.EButtonType.OkButton, 15, null, null); - } - } - - private void RefreshUI() - { - if (Matches == null) - { - // not initialized - return; - } - - if (MatchesListObjects != null) - { - // cleanup old objects - foreach (GameObject match in MatchesListObjects) - { - Destroy(match); - } - } - - // create lobby listings - for (int i = 0; i < Matches.Length; ++i) - { - LobbyEntry entry = Matches[i]; - - if (entry.ServerId == ProfileId) - { - continue; - } - - // server object - GameObject server = Instantiate(fikaMatchMakerUi.RaidGroupDefaultToClone, fikaMatchMakerUi.RaidGroupDefaultToClone.transform.parent); - server.SetActive(true); - MatchesListObjects.Add(server); - - server.name = entry.ServerId; - - bool localPlayerInRaid = false; - bool localPlayerDead = false; - foreach (KeyValuePair player in entry.Players) - { - if (player.Key == ProfileId) - { - localPlayerInRaid = true; - localPlayerDead = player.Value; - } - } - - // player label - GameObject playerLabel = GameObject.Find("PlayerLabel"); - playerLabel.name = "PlayerLabel" + i; - playerLabel.GetComponentInChildren().text = entry.HostUsername; - - // players count label - GameObject playerCountLabel = GameObject.Find("PlayerCountLabel"); - playerCountLabel.name = "PlayerCountLabel" + i; - playerCountLabel.GetComponentInChildren().text = entry.PlayerCount.ToString(); - - // player join button - GameObject joinButton = GameObject.Find("JoinButton"); - joinButton.name = "JoinButton" + i; - Button button = joinButton.GetComponent