diff --git a/.github/workflows/SIT-CD.yml b/.github/workflows/SIT-CD.yml
index 81f9ded0d..d4d0bb795 100644
--- a/.github/workflows/SIT-CD.yml
+++ b/.github/workflows/SIT-CD.yml
@@ -3,7 +3,6 @@ name: SIT Build Release
on:
workflow_dispatch:
-
permissions:
contents: write
@@ -16,78 +15,48 @@ jobs:
matrix:
configuration: [Release]
- runs-on: windows-latest
+ runs-on: ubuntu-latest
env:
SolutionName: StayInTarkov
- CSProj: Source/StayInTarkov.csproj
+ ProjectClient: Source/StayInTarkov.csproj
+ ProjectPrePatcher: SIT.WildSpawnType.PrePatcher/SIT.WildSpawnType.PrePatcher.csproj
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
-
- # Install the .NET Core workload
- name: Install .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
- # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
- - name: Setup MSBuild.exe
- uses: microsoft/setup-msbuild@v1.0.2
-
- # Restore all projects
- name: dotnet Restore
run: dotnet restore
- # Publish the Launcher as Self-Contained Single-File
- - name: dotnet Publish
+ - name: dotnet build
run: |
mkdir ${{ env.SolutionName }}-${{ matrix.configuration }}
- dotnet build ${{ env.CSProj }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
+ dotnet build ${{ env.ProjectClient }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
+ dotnet build ${{ env.ProjectPrePatcher }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
- name: Get version from DLL
id: extract-version
run: |
- $version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("${{ env.SolutionName }}-${{ matrix.configuration }}\StayInTarkov.dll").ProductVersion
- echo "::set-output name=VERSION::$version"
-
-
- - name: Remove unnecessary files
- run: |
- del ${{ env.SolutionName }}-${{ matrix.configuration }}\StayInTarkov.pdb
-
- #- name: Display Version in Logs
- # run: echo "The extracted version is ${{ steps.extract-version.outputs.VERSION }}"
+ version=`monodis --assembly ${{ env.SolutionName }}-${{ matrix.configuration }}/StayInTarkov.dll | grep Version | awk '{print $2}'`
+ echo "VERSION=$version" >> "$GITHUB_ENV"
- name: Zip remaining files
run: |
- Compress-Archive -Path ${{ env.SolutionName }}-${{ matrix.configuration }} -DestinationPath ${{ env.SolutionName }}-${{ matrix.configuration }}.zip
-
+ cd ${{ env.SolutionName }}-${{ matrix.configuration }}
+ zip -r ../${{ env.SolutionName }}-${{ matrix.configuration }}.zip ./*.dll
-
- # Upload artifact unless its merge to master
- - name: Upload artifact
- uses: actions/upload-artifact@v3
- with:
- name: StayInTarkov-${{ matrix.configuration }}
- path: ${{ env.SolutionName }}-${{ matrix.configuration }}\
- if-no-files-found: error
-
- #- name: Set build date
- # run: |
- # $NOW=& Get-Date -format yyyy-MM-dd-HH-mm
- # echo "NOW=$NOW" >> $env:GITHUB_ENV
-
- # Create release as draft from the compressed file
- name: Create Release
uses: softprops/action-gh-release@v1
- if: ${{ matrix.configuration == 'Release' }}
with:
draft: true
generate_release_notes: true
files: ${{ env.SolutionName }}-${{ matrix.configuration }}.zip
- tag_name: StayInTarkov.Client-${{ steps.extract-version.outputs.VERSION }}
+ tag_name: StayInTarkov.Client-${{ env.VERSION }}
diff --git a/.github/workflows/SIT-CI-Linux.yml b/.github/workflows/SIT-CI-Linux.yml
index 7d69554f2..1a5467d37 100644
--- a/.github/workflows/SIT-CI-Linux.yml
+++ b/.github/workflows/SIT-CI-Linux.yml
@@ -28,16 +28,17 @@ jobs:
env:
SolutionName: StayInTarkov
- CSProj: Source/StayInTarkov.csproj
+ ProjectClient: Source/StayInTarkov.csproj
+ ProjectPrePatcher: SIT.WildSpawnType.PrePatcher/SIT.WildSpawnType.PrePatcher.csproj
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
@@ -49,12 +50,13 @@ jobs:
- name: dotnet Publish
run: |
mkdir ${{ env.SolutionName }}-${{ matrix.configuration }}
- dotnet build ${{ env.CSProj }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
+ dotnet build ${{ env.ProjectClient }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
+ dotnet build ${{ env.ProjectPrePatcher }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
# Remove unnecessary files
- name: Remove unnecessary files
run: |
- rm ${{ env.SolutionName }}-${{ matrix.configuration }}/StayInTarkov.pdb
+ rm ${{ env.SolutionName }}-${{ matrix.configuration }}/*.pdb
# Zip remaining files
- name: Zip remaining files
@@ -63,7 +65,7 @@ jobs:
# Upload artifact with GitHub commit SHA
- name: Upload artifact
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ env.SolutionName }}-${{ matrix.configuration }}-${{ github.sha }}
path: ${{ env.SolutionName }}-${{ matrix.configuration }}/
diff --git a/.github/workflows/SIT-CI.yml b/.github/workflows/SIT-CI.yml
deleted file mode 100644
index 1069d88ad..000000000
--- a/.github/workflows/SIT-CI.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-name: SIT CI Windows
-
-on:
- #pull_request:
- #types:
- #- opened
- #- synchronize
- #- reopened
- #paths-ignore:
- #- '.github/**'
- #- '*.md'
- #branches:
- #- "master"
-
- workflow_dispatch:
-
-
-jobs:
- Build-SIT:
-
- strategy:
- matrix:
- configuration: [Debug]
-
-
- runs-on: windows-latest
-
- env:
- SolutionName: StayInTarkov
- CSProj: Source/StayInTarkov.csproj
-
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
-# Install the .NET Core workload
- - name: Install .NET Core
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
-
- # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
- - name: Setup MSBuild.exe
- uses: microsoft/setup-msbuild@v1.0.2
-
-
- # Restore all projects
- - name: dotnet Restore
- run: dotnet restore
-
- # Build the project
- - name: dotnet Publish
- run: |
- mkdir ${{ env.SolutionName }}-${{ matrix.configuration }}
- dotnet build ${{ env.CSProj }} -c ${{ matrix.configuration }} -o ${{ env.SolutionName }}-${{ matrix.configuration }}
- # Remove unnecessary files
- - name: Remove unnecessary files
- run: |
- del ${{ env.SolutionName }}-${{ matrix.configuration }}\StayInTarkov.pdb
- # Zip remaining files
- - name: Zip remaining files
- run: |
- Compress-Archive -Path ${{ env.SolutionName }}-${{ matrix.configuration }} -DestinationPath ${{ env.SolutionName }}-${{ matrix.configuration }}.zip
- # Upload artifact with github commit SHA
- - name: Upload artifact
- uses: actions/upload-artifact@v3
- with:
- name: ${{ env.SolutionName }}-${{ matrix.configuration }}-${{ github.sha }}
- path: ${{ env.SolutionName }}-${{ matrix.configuration }}\
- if-no-files-found: error
diff --git a/.github/workflows/publish-wiki.yml b/.github/workflows/publish-wiki.yml
index 0a15b73f2..35dba07ef 100644
--- a/.github/workflows/publish-wiki.yml
+++ b/.github/workflows/publish-wiki.yml
@@ -15,5 +15,5 @@ jobs:
publish-wiki:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: Andrew-Chen-Wang/github-wiki-action@v4
diff --git a/.gitignore b/.gitignore
index 2fef73d69..3467fe93d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
#
[Bb]in/
[Dd]ebug/
+/vcpkg_installed
# User-specific files
*.rsuser
diff --git a/COMPILE.md b/COMPILE.md
index f0aef9c83..38abb7578 100644
--- a/COMPILE.md
+++ b/COMPILE.md
@@ -1,15 +1,22 @@
-## How to compile?
+## How to compile Master branch?
+1. Create Working Directory for all Tarkov Modding {EFT_WORK}
+2. Clone this {SIT_CORE} to a {SIT_CORE} directory inside {EFT_WORK}
+3. Open the .sln with Visual Studio 2022
+4. Rebuild Solution (This should download and install all nuget packages on compilation)
+
+## How to compile on Latest EFT?
1. Create Working Directory for all Tarkov Modding {EFT_WORK}
2. Clone this {SIT_CORE} to a {SIT_CORE} directory inside {EFT_WORK}
3. Copy your Live Tarkov Directory somewhere else {EFT_OFFLINE}
-4. Deobfuscate latest Assembly-CSharp in {EFT_OFFLINE} via [SIT.Launcher](https://github.com/paulov-t/SIT.Tarkov.Launcher). Ensure to close and restart Launcher after Deobfuscation.
-5. Copy all of {EFT_OFFLINE}\EscapeFromTarkov_Data\Managed assemblies to References {TARKOV.REF} in the folder of this project {EFT_WORK}
+4. Deobfuscate latest Assembly-CSharp in {EFT_OFFLINE} via [SIT.Launcher](https://github.com/paulov-t/SIT.Launcher.Classic). Ensure to close and restart Launcher after Deobfuscation.
+5. Copy {EFT_OFFLINE}\EscapeFromTarkov_Data\Managed\Assembly-CSharp to References {TARKOV.REF} in the folder of this project {EFT_WORK}
6. You will need BepInEx Nuget Feed installed on your PC by running the following command in a terminal.
```
dotnet new -i BepInEx.Templates --nuget-source https://nuget.bepinex.dev/v3/index.json
```
7. Open the .sln with Visual Studio 2022
-8. Rebuild Solution (This should download and install all nuget packages on compilation)
+8. Copy the `contents` of `_GlobalUsings.SITRemapperConfig.cs` into `GlobalUsings.cs` in this project. `!Manually remove bad remaps!`
+9. Rebuild Solution (This should download and install all nuget packages on compilation)
-## Which version of BepInEx is this built for?
+## Which version of BepInEx is this project compatible with?
Version 5
\ No newline at end of file
diff --git a/References/Assembly-CSharp.dll b/References/Assembly-CSharp.dll
index 45f31f5b3..7e9b21dcd 100644
Binary files a/References/Assembly-CSharp.dll and b/References/Assembly-CSharp.dll differ
diff --git a/SIT.WildSpawnType.PrePatcher/SIT.WildSpawnType.PrePatcher.csproj b/SIT.WildSpawnType.PrePatcher/SIT.WildSpawnType.PrePatcher.csproj
new file mode 100644
index 000000000..0f4f01015
--- /dev/null
+++ b/SIT.WildSpawnType.PrePatcher/SIT.WildSpawnType.PrePatcher.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net471
+ SIT.WildSpawnType.PrePatcher
+ Release
+
+
+
+ Stay In Tarkov
+ Copyright @ Stay In Tarkov 2024
+ Stay in Tarkov plugin for Escape From Tarkov
+
+
+
+
+
+
+
+
+
+ compile; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
\ No newline at end of file
diff --git a/SIT.WildSpawnType.PrePatcher/WildSpawnTypePrePatcher.cs b/SIT.WildSpawnType.PrePatcher/WildSpawnTypePrePatcher.cs
new file mode 100644
index 000000000..5ee24a04b
--- /dev/null
+++ b/SIT.WildSpawnType.PrePatcher/WildSpawnTypePrePatcher.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+using System.Linq;
+using Mono.Cecil;
+
+namespace StayInTarkov
+{
+ public static class WildSpawnTypePrePatcher
+ {
+ public static IEnumerable TargetDLLs { get; } = new[] { "Assembly-CSharp.dll" };
+
+ public static int sptUsecValue = 47;
+ public static int sptBearValue = 48;
+
+ public static void Patch(ref AssemblyDefinition assembly)
+ {
+ var botEnums = assembly.MainModule.GetType("EFT.WildSpawnType");
+
+ var sptUsec = new FieldDefinition("sptUsec",
+ FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault,
+ botEnums)
+ { Constant = sptUsecValue };
+
+ var sptBear = new FieldDefinition("sptBear",
+ FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault,
+ botEnums)
+ { Constant = sptBearValue };
+
+ if(!botEnums.Fields.Any(x => x.Name == "sptUsec"))
+ botEnums.Fields.Add(sptUsec);
+
+ if(!botEnums.Fields.Any(x => x.Name == "sptBear"))
+ botEnums.Fields.Add(sptBear);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/AI/BlockerErrorFixPatch.cs b/Source/AI/BlockerErrorFixPatch.cs
new file mode 100644
index 000000000..8ded2178c
--- /dev/null
+++ b/Source/AI/BlockerErrorFixPatch.cs
@@ -0,0 +1,34 @@
+//using EFT;
+//using HarmonyLib;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Reflection;
+//using System.Security.Cryptography.X509Certificates;
+//using System.Text;
+//using System.Threading.Tasks;
+
+//namespace StayInTarkov.AI
+//{
+// ///
+// /// Paulov: Stay in Tarkov causes an error with the phrase BLOCKER ERROR caused by spt WildSpawnTypes
+// /// This patch resolves this error by adding the spt WildSpawnTypes to the ServerBotSettingsClass
+// /// LICENSE: This patch is only for use in Stay In Tarkov
+// ///
+// public sealed class BlockerErrorFixPatch : ModulePatch
+// {
+// protected override MethodBase GetTargetMethod()
+// {
+// return AccessTools.GetDeclaredMethods(typeof(ServerBotSettingsClass)).First(x => x.Name == nameof(ServerBotSettingsClass.Init));
+// }
+
+// [PatchPrefix]
+// public static bool Prefix(Dictionary ___dictionary_0)
+// {
+// var enumValues = Enum.GetValues(typeof(WildSpawnType));
+// ___dictionary_0.Add((WildSpawnType)enumValues.GetValue(enumValues.Length - 2), new ServerBotSettingsValuesClass(false, false, true, "ScavRole/PmcBot"));
+// ___dictionary_0.Add((WildSpawnType)enumValues.GetValue(enumValues.Length - 1), new ServerBotSettingsValuesClass(false, false, true, "ScavRole/PmcBot"));
+// return true;
+// }
+// }
+//}
diff --git a/Source/AkiSupport/Airdrops/AirdropBox.cs b/Source/AkiSupport/Airdrops/AirdropBox.cs
index 51f56f61c..b39e3b5ad 100644
--- a/Source/AkiSupport/Airdrops/AirdropBox.cs
+++ b/Source/AkiSupport/Airdrops/AirdropBox.cs
@@ -2,16 +2,18 @@
using EFT.Airdrop;
using EFT.Interactive;
using EFT.SynchronizableObjects;
+using StayInTarkov.Coop.Matchmaker;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
+using UnityEngine.AI;
namespace StayInTarkov.AkiSupport.Airdrops
{
///
/// Created by: SPT-Aki team
- /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.Custom/Airdrops/AirdropBox.cs
+ /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Airdrops/AirdropBox.cs
///
public class AirdropBox : MonoBehaviour
{
@@ -20,6 +22,8 @@ public class AirdropBox : MonoBehaviour
private readonly int CROSSFADE = Shader.PropertyToID("_Crossfade");
private readonly int COLLISION = Animator.StringToHash("collision");
+ private static int AirdropContainerCount = 0;
+
public LootableContainer Container { get; set; }
private float fallSpeed;
private AirdropSynchronizableObject boxSync;
@@ -44,12 +48,16 @@ private BetterSource AudioSource
}
}
+ public Vector3 ClientSyncPosition { get; internal set; }
+
public static async Task Init(float crateFallSpeed)
{
var instance = (await LoadCrate()).AddComponent();
instance.soundsDictionary = await LoadSounds();
instance.Container = instance.GetComponentInChildren();
+ instance.Container.Id = $"AirdropBox_{AirdropContainerCount}";
+ AirdropContainerCount++;
instance.boxSync = instance.GetComponent();
instance.boxLogic = new AirdropLogicClass();
@@ -121,6 +129,19 @@ public IEnumerator DropCrate(Vector3 position)
OpenParachute();
while (RaycastBoxDistance(LayerMaskClass.TerrainLowPoly, out _))
{
+ if (SITMatchmaking.IsClient)
+ {
+
+ if (transform.position.x != this.ClientSyncPosition.x)
+ transform.position = ClientSyncPosition;
+
+ // only do this if the box is higher than the client sync
+ if (transform.position.y > this.ClientSyncPosition.y)
+ transform.position = ClientSyncPosition;
+
+ if (transform.position.z != this.ClientSyncPosition.z)
+ transform.position = ClientSyncPosition;
+ }
transform.Translate(Vector3.down * (Time.deltaTime * fallSpeed));
transform.Rotate(Vector3.up, Time.deltaTime * 6f);
yield return null;
@@ -147,6 +168,15 @@ private void OnBoxLand(out float clipLength)
Falloff = (int)surfaceSet.LandingSoundBank.Rolloff,
Volume = surfaceSet.LandingSoundBank.BaseVolume
});
+
+ AddNavMeshObstacle();
+ }
+
+ private void AddNavMeshObstacle()
+ {
+ var navMeshObstacle = this.GetOrAddComponent();
+ navMeshObstacle.size = boxSync.CollisionCollider.bounds.size;
+ navMeshObstacle.carving = true;
}
private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo)
diff --git a/Source/AkiSupport/Airdrops/Patches/AirdropFlarePatch.cs b/Source/AkiSupport/Airdrops/Patches/AirdropFlarePatch.cs
index e1c63b1b7..b0bed908d 100644
--- a/Source/AkiSupport/Airdrops/Patches/AirdropFlarePatch.cs
+++ b/Source/AkiSupport/Airdrops/Patches/AirdropFlarePatch.cs
@@ -20,18 +20,19 @@ public class AirdropFlarePatch : ModulePatch
protected override MethodBase GetTargetMethod()
{
- return ReflectionHelpers.GetMethodForType(typeof(FlareCartridge), nameof(FlareCartridge.Init), false, true);
+ return typeof(FlareCartridge).GetMethod(nameof(FlareCartridge.Init),
+ BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
[PatchPostfix]
- private static void PatchPostfix(FlareCartridgeSettings flareCartridgeSettings, IPlayer player, BulletClass flareCartridge, Weapon weapon)
+ private static void PatchPostfix(BulletClass flareCartridge)
{
var gameWorld = Singleton.Instance;
var points = LocationScene.GetAll().Any();
if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id))
{
- gameWorld.gameObject.AddComponent().isFlareDrop = true;
+ gameWorld.gameObject.AddComponent().IsFlareDrop = true;
}
}
}
diff --git a/Source/AkiSupport/Airdrops/Utils/AirdropUtil.cs b/Source/AkiSupport/Airdrops/Utils/AirdropUtil.cs
index 2008566ab..dccb2d2d4 100644
--- a/Source/AkiSupport/Airdrops/Utils/AirdropUtil.cs
+++ b/Source/AkiSupport/Airdrops/Utils/AirdropUtil.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using UnityEngine;
using Random = UnityEngine.Random;
@@ -15,9 +16,9 @@ namespace StayInTarkov.AkiSupport.Airdrops.Utils
{
public static class AirdropUtil
{
- public static AirdropConfigModel GetConfigFromServer()
+ public static async Task GetConfigFromServer()
{
- string json = AkiBackendCommunication.Instance.GetJson("/singleplayer/airdrop/config");
+ string json = await AkiBackendCommunication.Instance.GetJsonAsync("/singleplayer/airdrop/config");
return JsonConvert.DeserializeObject(json);
}
@@ -79,9 +80,9 @@ public static bool ShouldAirdropOccur(int dropChance, List airdrop
return airdropPoints.Count > 0 && Random.Range(0, 100) <= dropChance;
}
- public static AirdropParametersModel InitAirdropParams(GameWorld gameWorld, bool isFlare)
+ public static async Task InitAirdropParams(GameWorld gameWorld, bool isFlare)
{
- var serverConfig = GetConfigFromServer();
+ var serverConfig = await GetConfigFromServer();
if (serverConfig == null)
return new AirdropParametersModel() { Config = serverConfig, AirdropAvailable = false };
diff --git a/Source/AkiSupport/Airdrops/Utils/ItemFactoryUtil.cs b/Source/AkiSupport/Airdrops/Utils/ItemFactoryUtil.cs
index b7992bb8d..36d85e6e2 100644
--- a/Source/AkiSupport/Airdrops/Utils/ItemFactoryUtil.cs
+++ b/Source/AkiSupport/Airdrops/Utils/ItemFactoryUtil.cs
@@ -6,6 +6,7 @@
using Newtonsoft.Json;
using StayInTarkov.Networking;
using System.Linq;
+using System.Threading.Tasks;
using UnityEngine;
namespace Aki.Custom.Airdrops.Utils
@@ -60,9 +61,9 @@ public async void AddLoot(LootableContainer container, AirdropLootResultModel lo
}
}
- public AirdropLootResultModel GetLoot()
+ public async Task GetLoot()
{
- var json = AkiBackendCommunication.Instance.GetJson("/client/location/getAirdropLoot");
+ var json = await AkiBackendCommunication.Instance.GetJsonAsync("/client/location/getAirdropLoot");
var result = JsonConvert.DeserializeObject(json);
return result;
diff --git a/Source/AkiSupport/Custom/BotDifficultyPatch.cs b/Source/AkiSupport/Custom/BotDifficultyPatch.cs
index 99593118c..2b28cb82f 100644
--- a/Source/AkiSupport/Custom/BotDifficultyPatch.cs
+++ b/Source/AkiSupport/Custom/BotDifficultyPatch.cs
@@ -2,6 +2,7 @@
using StayInTarkov.Networking;
using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
namespace StayInTarkov.AkiSupport.Custom
{
@@ -23,7 +24,7 @@ protected override MethodBase GetTargetMethod()
[PatchPrefix]
private static bool PatchPrefix(ref string __result, BotDifficulty botDifficulty, WildSpawnType role)
{
- __result = AkiBackendCommunication.Instance.GetJson($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}");
+ __result = AkiBackendCommunication.Instance.GetJsonBLOCKING($"/singleplayer/settings/bot/difficulty/{role}/{botDifficulty}");
return string.IsNullOrWhiteSpace(__result);
}
}
diff --git a/Source/AkiSupport/Custom/CoreDifficultyPatch.cs b/Source/AkiSupport/Custom/CoreDifficultyPatch.cs
index 19a1721e7..2783fccde 100644
--- a/Source/AkiSupport/Custom/CoreDifficultyPatch.cs
+++ b/Source/AkiSupport/Custom/CoreDifficultyPatch.cs
@@ -22,7 +22,7 @@ protected override MethodBase GetTargetMethod()
[PatchPrefix]
private static bool PatchPrefix(ref string __result)
{
- __result = AkiBackendCommunication.Instance.GetJson("/singleplayer/settings/bot/difficulty/core/core");
+ __result = AkiBackendCommunication.Instance.GetJsonBLOCKING("/singleplayer/settings/bot/difficulty/core/core");
return string.IsNullOrWhiteSpace(__result);
}
}
diff --git a/Source/AkiSupport/Custom/CustomAI/AIBrainSpawnWeightAdjustment.cs b/Source/AkiSupport/Custom/CustomAI/AIBrainSpawnWeightAdjustment.cs
new file mode 100644
index 000000000..1f82e4f63
--- /dev/null
+++ b/Source/AkiSupport/Custom/CustomAI/AIBrainSpawnWeightAdjustment.cs
@@ -0,0 +1,176 @@
+using BepInEx.Logging;
+using EFT;
+using Newtonsoft.Json;
+using StayInTarkov.Networking;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StayInTarkov.AkiSupport.Custom.CustomAI
+{
+ ///
+ /// Created by: SPT-Aki
+ /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/CustomAI/AIBrainSpawnWeightAdjustment.cs
+ ///
+ public class AIBrainSpawnWeightAdjustment
+ {
+ private static AIBrains aiBrainsCache = null;
+ private static DateTime aiBrainCacheDate = new DateTime();
+ private static readonly Random random = new Random();
+ private readonly ManualLogSource logger;
+
+ public AIBrainSpawnWeightAdjustment(ManualLogSource logger)
+ {
+ this.logger = logger;
+ }
+
+ public WildSpawnType GetRandomisedPlayerScavType(BotOwner botOwner, string currentMapName)
+ {
+ // Get map brain weights from server and cache
+ if (aiBrainsCache == null || CacheIsStale())
+ {
+ ResetCacheDate();
+ HydrateCacheWithServerData();
+
+ if (!aiBrainsCache.playerScav.TryGetValue(currentMapName.ToLower(), out _))
+ {
+ throw new Exception($"Bots were refreshed from the server but the assault cache still doesnt contain data");
+ }
+ }
+
+ // Choose random weighted brain
+ var randomType = WeightedRandom(aiBrainsCache.playerScav[currentMapName.ToLower()].Keys.ToArray(), aiBrainsCache.playerScav[currentMapName.ToLower()].Values.ToArray());
+ if (Enum.TryParse(randomType, out WildSpawnType newAiType))
+ {
+ logger.LogWarning($"Updated player scav bot to use: {newAiType} brain");
+ return newAiType;
+ }
+ else
+ {
+ logger.LogWarning($"Updated player scav bot {botOwner.Profile.Info.Nickname}: {botOwner.Profile.Info.Settings.Role} to use: {newAiType} brain");
+
+ return newAiType;
+ }
+ }
+
+ public WildSpawnType GetAssaultScavWildSpawnType(BotOwner botOwner, string currentMapName)
+ {
+ // Get map brain weights from server and cache
+ if (aiBrainsCache == null || CacheIsStale())
+ {
+ ResetCacheDate();
+ HydrateCacheWithServerData();
+
+ if (!aiBrainsCache.assault.TryGetValue(currentMapName.ToLower(), out _))
+ {
+ throw new Exception($"Bots were refreshed from the server but the assault cache still doesnt contain data");
+ }
+ }
+
+ // Choose random weighted brain
+ var randomType = WeightedRandom(aiBrainsCache.assault[currentMapName.ToLower()].Keys.ToArray(), aiBrainsCache.assault[currentMapName.ToLower()].Values.ToArray());
+ if (Enum.TryParse(randomType, out WildSpawnType newAiType))
+ {
+ logger.LogWarning($"Updated assault bot to use: {newAiType} brain");
+ return newAiType;
+ }
+ else
+ {
+ logger.LogWarning($"Updated assault bot {botOwner.Profile.Info.Nickname}: {botOwner.Profile.Info.Settings.Role} to use: {newAiType} brain");
+
+ return newAiType;
+ }
+ }
+
+ public WildSpawnType GetPmcWildSpawnType(BotOwner botOwner_0, WildSpawnType pmcType, string currentMapName)
+ {
+ if (aiBrainsCache == null || !aiBrainsCache.pmc.TryGetValue(pmcType, out var botSettings) || CacheIsStale())
+ {
+ ResetCacheDate();
+ HydrateCacheWithServerData();
+
+ if (!aiBrainsCache.pmc.TryGetValue(pmcType, out botSettings))
+ {
+ throw new Exception($"Bots were refreshed from the server but the cache still doesnt contain an appropriate bot for type {botOwner_0.Profile.Info.Settings.Role}");
+ }
+ }
+
+ var mapSettings = botSettings[currentMapName.ToLower()];
+ var randomType = WeightedRandom(mapSettings.Keys.ToArray(), mapSettings.Values.ToArray());
+ if (Enum.TryParse(randomType, out WildSpawnType newAiType))
+ {
+ logger.LogWarning($"Updated spt bot {botOwner_0.Profile.Info.Nickname}: {botOwner_0.Profile.Info.Settings.Role} to use: {newAiType} brain");
+
+ return newAiType;
+ }
+ else
+ {
+ logger.LogError($"Couldnt not update spt bot {botOwner_0.Profile.Info.Nickname} to random type {randomType}, does not exist for WildSpawnType enum, defaulting to 'assault'");
+
+ return WildSpawnType.assault;
+ }
+ }
+
+ private void HydrateCacheWithServerData()
+ {
+ // Get weightings for PMCs from server and store in dict
+ var result = AkiBackendCommunication.Instance.GetJsonBLOCKING($"/singleplayer/settings/bot/getBotBehaviours/");
+ aiBrainsCache = JsonConvert.DeserializeObject(result);
+ logger.LogWarning($"Cached ai brain weights in client");
+ }
+
+ private void ResetCacheDate()
+ {
+ aiBrainCacheDate = DateTime.Now;
+ aiBrainsCache?.pmc?.Clear();
+ aiBrainsCache?.assault?.Clear();
+ aiBrainsCache?.playerScav?.Clear();
+ }
+
+ private static bool CacheIsStale()
+ {
+ TimeSpan cacheAge = DateTime.Now - aiBrainCacheDate;
+
+ return cacheAge.Minutes > 15;
+ }
+
+ public class AIBrains
+ {
+ public Dictionary>> pmc { get; set; }
+ public Dictionary> assault { get; set; }
+ public Dictionary> playerScav { get; set; }
+ }
+
+ ///
+ /// Choose a value from a choice of values with weightings
+ ///
+ ///
+ ///
+ ///
+ private string WeightedRandom(string[] botTypes, int[] weights)
+ {
+ var cumulativeWeights = new int[botTypes.Length];
+
+ for (int i = 0; i < weights.Length; i++)
+ {
+ cumulativeWeights[i] = weights[i] + (i == 0 ? 0 : cumulativeWeights[i - 1]);
+ }
+
+ var maxCumulativeWeight = cumulativeWeights[cumulativeWeights.Length - 1];
+ var randomNumber = maxCumulativeWeight * random.NextDouble();
+
+ for (var itemIndex = 0; itemIndex < botTypes.Length; itemIndex++)
+ {
+ if (cumulativeWeights[itemIndex] >= randomNumber)
+ {
+ return botTypes[itemIndex];
+ }
+ }
+
+ logger.LogError("failed to get random bot weighting, returned assault");
+
+ return "assault";
+ }
+ }
+
+}
diff --git a/Source/AkiSupport/Custom/CustomAI/AiHelpers.cs b/Source/AkiSupport/Custom/CustomAI/AiHelpers.cs
new file mode 100644
index 000000000..cabd0263c
--- /dev/null
+++ b/Source/AkiSupport/Custom/CustomAI/AiHelpers.cs
@@ -0,0 +1,56 @@
+using EFT;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StayInTarkov.AkiSupport.Custom.CustomAI
+{
+ ///
+ /// Created by: SPT-Aki
+ /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/CustomAI/AiHelpers.cs
+ ///
+ public static class AiHelpers
+ {
+ ///
+ /// Bot is a PMC when it has IsStreamerModeAvailable flagged and has a wildspawn type of 'sptBear' or 'sptUsec'
+ ///
+ /// Bots role
+ /// Bot details
+ ///
+ public static bool BotIsSptPmc(WildSpawnType botRoleToCheck, BotOwner ___botOwner_0)
+ {
+ if (___botOwner_0.Profile.Info.IsStreamerModeAvailable)
+ {
+ // PMCs can sometimes have thier role changed to 'assaultGroup' by the client, we need a alternate way to figure out if they're a spt pmc
+ return true;
+ }
+
+ return (int) botRoleToCheck == WildSpawnTypePrePatcher.sptBearValue || (int) botRoleToCheck == WildSpawnTypePrePatcher.sptUsecValue;
+ }
+
+ public static bool BotIsPlayerScav(WildSpawnType role, string nickname)
+ {
+ if (role == WildSpawnType.assault && nickname.Contains("("))
+ {
+ // Check bot is pscav by looking for the opening parentheses of their nickname e.g. scavname (pmc name)
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool BotIsNormalAssaultScav(WildSpawnType role, BotOwner ___botOwner_0)
+ {
+ // Is assault + no (
+ if (!___botOwner_0.Profile.Info.Nickname.Contains("(") && role == WildSpawnType.assault)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+}
diff --git a/Source/AkiSupport/Custom/CustomAI/PmcFoundInRaidEquipment.cs b/Source/AkiSupport/Custom/CustomAI/PmcFoundInRaidEquipment.cs
new file mode 100644
index 000000000..266204c3d
--- /dev/null
+++ b/Source/AkiSupport/Custom/CustomAI/PmcFoundInRaidEquipment.cs
@@ -0,0 +1,157 @@
+using BepInEx.Logging;
+using EFT.InventoryLogic;
+using EFT;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StayInTarkov.AkiSupport.Custom.CustomAI
+{
+ ///
+ /// Created by: SPT-Aki
+ /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/CustomAI/PmcFoundInRaidEquipment.cs
+ ///
+ public class PmcFoundInRaidEquipment
+ {
+ private static readonly string magazineId = "5448bc234bdc2d3c308b4569";
+ private static readonly string drugId = "5448f3a14bdc2d27728b4569";
+ private static readonly string mediKitItem = "5448f39d4bdc2d0a728b4568";
+ private static readonly string medicalItemId = "5448f3ac4bdc2dce718b4569";
+ private static readonly string injectorItemId = "5448f3a64bdc2d60728b456a";
+ private static readonly string throwableItemId = "543be6564bdc2df4348b4568";
+ private static readonly string ammoItemId = "5485a8684bdc2da71d8b4567";
+ private static readonly string weaponId = "5422acb9af1c889c16000029";
+ private static readonly string moneyId = "543be5dd4bdc2deb348b4569";
+ private static readonly string armorPlate = "644120aa86ffbe10ee032b6f";
+ private static readonly string builtInInserts = "65649eb40bf0ed77b8044453";
+ private static readonly List nonFiRItems = new List() { magazineId, drugId, mediKitItem, medicalItemId, injectorItemId, throwableItemId, ammoItemId, armorPlate, builtInInserts };
+
+ private static readonly string pistolId = "5447b5cf4bdc2d65278b4567";
+ private static readonly string smgId = "5447b5e04bdc2d62278b4567";
+ private static readonly string assaultRifleId = "5447b5f14bdc2d61278b4567";
+ private static readonly string assaultCarbineId = "5447b5fc4bdc2d87278b4567";
+ private static readonly string shotgunId = "5447b6094bdc2dc3278b4567";
+ private static readonly string marksmanRifleId = "5447b6194bdc2d67278b4567";
+ private static readonly string sniperRifleId = "5447b6254bdc2dc3278b4568";
+ private static readonly string machinegunId = "5447bed64bdc2d97278b4568";
+ private static readonly string grenadeLauncherId = "5447bedf4bdc2d87278b4568";
+ private static readonly string knifeId = "5447e1d04bdc2dff2f8b4567";
+
+ private static readonly List weaponTypeIds = new List() { pistolId, smgId, assaultRifleId, assaultCarbineId, shotgunId, marksmanRifleId, sniperRifleId, machinegunId, grenadeLauncherId, knifeId };
+ private static readonly List nonFiRPocketLoot = new List { moneyId, throwableItemId, ammoItemId, magazineId, medicalItemId, mediKitItem, injectorItemId, drugId };
+ private readonly ManualLogSource logger;
+
+ public PmcFoundInRaidEquipment(ManualLogSource logger)
+ {
+ this.logger = logger;
+ }
+
+ public void ConfigurePMCFindInRaidStatus(BotOwner ___botOwner_0)
+ {
+ // Must run before the container loot code, otherwise backpack loot is not FiR
+ MakeEquipmentNotFiR(___botOwner_0);
+
+ // Get inventory items that hold other items (backpack/rig/pockets/armor)
+ IReadOnlyList containerGear = ___botOwner_0.Profile.Inventory.Equipment.ContainerSlots;
+ var nonFiRRootItems = new List- ();
+ foreach (var container in containerGear)
+ {
+ var firstItem = container.Items.FirstOrDefault();
+ foreach (var item in container.ContainedItem.GetAllItems())
+ {
+ // Skip items that match container (array has itself as an item)
+ if (item.Id == firstItem?.Id)
+ {
+ //this.logger.LogError($"Skipping item {item.Id} {item.Name} as its same as container {container.FullId}");
+ continue;
+ }
+
+ // Don't add FiR to any item inside armor vest, e.g. plates/soft inserts
+ if (container.Name == "ArmorVest")
+ {
+ continue;
+ }
+
+ // Don't add FiR to any item inside head gear, e.g. soft inserts/nvg/lights
+ if (container.Name == "Headwear")
+ {
+ continue;
+ }
+
+ // Don't add FiR to tacvest items PMC usually brings into raid (meds/mags etc)
+ if (container.Name == "TacticalVest" && nonFiRItems.Any(item.Template._parent.Contains))
+ {
+ //this.logger.LogError($"Skipping item {item.Id} {item.Name} as its on the item type blacklist");
+ continue;
+ }
+
+ // Don't add FiR to weapons in backpack (server sometimes adds pre-made weapons to backpack to simulate PMCs looting bodies)
+ if (container.Name == "Backpack" && weaponTypeIds.Any(item.Template._parent.Contains))
+ {
+ // Add weapon root to list for later use
+ nonFiRRootItems.Add(item);
+ //this.logger.LogError($"Skipping item {item.Id} {item.Name} as its on the item type blacklist");
+ continue;
+ }
+
+ // Don't add FiR to grenades/mags/ammo/meds in pockets
+ if (container.Name == "Pockets" && nonFiRPocketLoot.Exists(item.Template._parent.Contains))
+ {
+ //this.logger.LogError($"Skipping item {item.Id} {item.Name} as its on the item type blacklist");
+ continue;
+ }
+
+ // Check for mods of weapons in backpacks
+ var isChildOfEquippedNonFiRItem = nonFiRRootItems.Any(nonFiRRootItem => item.IsChildOf(nonFiRRootItem));
+ if (isChildOfEquippedNonFiRItem)
+ {
+ continue;
+ }
+
+ //Logger.LogError($"flagging item FiR: {item.Id} {item.Name} _parent: {item.Template._parent}");
+ item.SpawnedInSession = true;
+ }
+ }
+
+ // Set dogtag as FiR
+ var dogtag = ___botOwner_0.Profile.Inventory.GetItemsInSlots(new EquipmentSlot[] { EquipmentSlot.Dogtag }).FirstOrDefault();
+ if (dogtag != null)
+ {
+ dogtag.SpawnedInSession = true;
+ }
+ }
+
+
+ private void MakeEquipmentNotFiR(BotOwner ___botOwner_0)
+ {
+ var additionalItems = ___botOwner_0.Profile.Inventory.GetItemsInSlots(new EquipmentSlot[]
+ { EquipmentSlot.Backpack,
+ EquipmentSlot.FirstPrimaryWeapon,
+ EquipmentSlot.SecondPrimaryWeapon,
+ EquipmentSlot.TacticalVest,
+ EquipmentSlot.ArmorVest,
+ EquipmentSlot.Scabbard,
+ EquipmentSlot.Eyewear,
+ EquipmentSlot.Headwear,
+ EquipmentSlot.Earpiece,
+ EquipmentSlot.ArmBand,
+ EquipmentSlot.FaceCover,
+ EquipmentSlot.Holster,
+ EquipmentSlot.SecuredContainer
+ });
+
+ foreach (var item in additionalItems)
+ {
+ // Some items are null, probably because bot doesnt have that particular slot on them
+ if (item == null)
+ {
+ continue;
+ }
+
+ //Logger.LogError($"flagging item FiR: {item.Id} {item.Name} _parent: {item.Template._parent}");
+ item.SpawnedInSession = false;
+ }
+ }
+
+ }
+
+}
diff --git a/Source/AkiSupport/Custom/CustomAiPatch.cs b/Source/AkiSupport/Custom/CustomAiPatch.cs
new file mode 100644
index 000000000..646026eb8
--- /dev/null
+++ b/Source/AkiSupport/Custom/CustomAiPatch.cs
@@ -0,0 +1,122 @@
+using Comfort.Common;
+using EFT;
+using HarmonyLib;
+using StayInTarkov.AkiSupport.Custom.CustomAI;
+using System;
+using System.Reflection;
+
+namespace StayInTarkov.AkiSupport.Custom
+{
+ ///
+ /// Created by: SPT-Aki
+ /// Link: https://dev.sp-tarkov.com/SPT/Modules/src/branch/master/project/SPT.Custom/Patches/CustomAiPatch.cs
+ ///
+ public class CustomAiPatch : ModulePatch
+ {
+ private static readonly PmcFoundInRaidEquipment pmcFoundInRaidEquipment = new PmcFoundInRaidEquipment(Logger);
+ private static readonly AIBrainSpawnWeightAdjustment aIBrainSpawnWeightAdjustment = new AIBrainSpawnWeightAdjustment(Logger);
+
+ protected override MethodBase GetTargetMethod()
+ {
+ return AccessTools.Method(typeof(StandartBotBrain), nameof(StandartBotBrain.Activate));
+ }
+
+ ///
+ /// Get a randomly picked wildspawntype from server and change PMC bot to use it, this ensures the bot is generated with that random type altering its behaviour
+ /// Postfix will adjust it back to original type
+ ///
+ /// state to save for postfix to use later
+ ///
+ /// botOwner_0 property
+ [PatchPrefix]
+ private static bool PatchPrefix(out WildSpawnType __state, StandartBotBrain __instance, BotOwner ___botOwner_0)
+ {
+ var player = Singleton.Instance.MainPlayer;
+ ___botOwner_0.Profile.Info.Settings.Role = FixAssaultGroupPmcsRole(___botOwner_0);
+ __state = ___botOwner_0.Profile.Info.Settings.Role; // Store original type in state param to allow access in PatchPostFix()
+ try
+ {
+ string currentMapName = GetCurrentMap();
+ if (AiHelpers.BotIsPlayerScav(__state, ___botOwner_0.Profile.Info.Nickname))
+ {
+ ___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetRandomisedPlayerScavType(___botOwner_0, currentMapName);
+
+ return true; // Do original
+ }
+
+ if (AiHelpers.BotIsNormalAssaultScav(__state, ___botOwner_0))
+ {
+ ___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetAssaultScavWildSpawnType(___botOwner_0, currentMapName);
+
+ return true; // Do original
+ }
+
+ if (AiHelpers.BotIsSptPmc(__state, ___botOwner_0))
+ {
+ // Bot has inventory equipment
+ if (___botOwner_0.Profile?.Inventory?.Equipment != null)
+ {
+ pmcFoundInRaidEquipment.ConfigurePMCFindInRaidStatus(___botOwner_0);
+ }
+
+ ___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetPmcWildSpawnType(___botOwner_0, ___botOwner_0.Profile.Info.Settings.Role, currentMapName);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError($"Error running CustomAiPatch PatchPrefix(): {ex.Message}");
+ Logger.LogError(ex.StackTrace);
+ }
+
+ return true; // Do original
+ }
+
+ ///
+ /// the client sometimes replaces PMC roles with 'assaultGroup', give PMCs their original role back (sptBear/sptUsec)
+ ///
+ /// WildSpawnType
+ private static WildSpawnType FixAssaultGroupPmcsRole(BotOwner botOwner)
+ {
+ if (botOwner.Profile.Info.IsStreamerModeAvailable && botOwner.Profile.Info.Settings.Role == WildSpawnType.assaultGroup)
+ {
+ Logger.LogError($"Broken PMC found: {botOwner.Profile.Nickname}, was {botOwner.Profile.Info.Settings.Role}");
+
+ // Its a PMC, figure out what the bot originally was and return it
+ return botOwner.Profile.Info.Side == EPlayerSide.Bear
+ ? (WildSpawnType) WildSpawnTypePrePatcher.sptBearValue
+ : (WildSpawnType) WildSpawnTypePrePatcher.sptUsecValue;
+ }
+
+ // Not broken pmc, return original role
+ return botOwner.Profile.Info.Settings.Role;
+ }
+
+ ///
+ /// Revert prefix change, get bots type back to what it was before changes
+ ///
+ /// Saved state from prefix patch
+ /// botOwner_0 property
+ [PatchPostfix]
+ private static void PatchPostFix(WildSpawnType __state, BotOwner ___botOwner_0)
+ {
+ if (AiHelpers.BotIsSptPmc(__state, ___botOwner_0))
+ {
+ // Set spt bot bot back to original type
+ ___botOwner_0.Profile.Info.Settings.Role = __state;
+ }
+ else if (AiHelpers.BotIsPlayerScav(__state, ___botOwner_0.Profile.Info.Nickname))
+ {
+ // Set pscav back to original type
+ ___botOwner_0.Profile.Info.Settings.Role = __state;
+ }
+ }
+
+ private static string GetCurrentMap()
+ {
+ var gameWorld = Singleton.Instance;
+
+ return gameWorld.MainPlayer.Location;
+ }
+ }
+
+}
diff --git a/Source/AkiSupport/Custom/PmcFirstAidPatch.cs b/Source/AkiSupport/Custom/PmcFirstAidPatch.cs
index 3986107b3..3b921139f 100644
--- a/Source/AkiSupport/Custom/PmcFirstAidPatch.cs
+++ b/Source/AkiSupport/Custom/PmcFirstAidPatch.cs
@@ -16,7 +16,7 @@ public class PmcFirstAidPatch : ModulePatch
protected override MethodBase GetTargetMethod()
{
- return ReflectionHelpers.GetMethodForType(typeof(FirstAid), methodName);
+ return ReflectionHelpers.GetMethodForType(typeof(GFirstAid), methodName);
}
[PatchPrefix]
diff --git a/Source/AkiSupport/Custom/QTEPatch.cs b/Source/AkiSupport/Custom/QTEPatch.cs
index 01fc243db..19eb3a5b7 100644
--- a/Source/AkiSupport/Custom/QTEPatch.cs
+++ b/Source/AkiSupport/Custom/QTEPatch.cs
@@ -17,7 +17,7 @@ public class QTEPatch : ModulePatch
[PatchPostfix]
private static void PatchPostfix(HideoutPlayerOwner __instance)
{
- AkiBackendCommunication.Instance.PostJson("/client/hideout/workout", new
+ AkiBackendCommunication.Instance.PostJsonBLOCKING("/client/hideout/workout", new
{
skills = __instance.HideoutPlayer.Skills,
effects = __instance.HideoutPlayer.HealthController.BodyPartEffects
diff --git a/Source/AkiSupport/Custom/SettingsLocationPatch.cs b/Source/AkiSupport/Custom/SettingsLocationPatch.cs
index 2482cfc3f..d2b1ecd9e 100644
--- a/Source/AkiSupport/Custom/SettingsLocationPatch.cs
+++ b/Source/AkiSupport/Custom/SettingsLocationPatch.cs
@@ -20,10 +20,10 @@ public static void Enable()
Directory.CreateDirectory(_sptPath);
// Screenshot
- FieldInfo DocumentsSettings = ReflectionHelpers.GetFieldFromType(typeof(SettingsManager), "string_0");
+ FieldInfo DocumentsSettings = ReflectionHelpers.GetFieldFromType(typeof(SharedGameSettingsClass), "string_0");
// Game Settings
- FieldInfo ApplicationDataSettings = ReflectionHelpers.GetFieldFromType(typeof(SettingsManager), "string_1");
+ FieldInfo ApplicationDataSettings = ReflectionHelpers.GetFieldFromType(typeof(SharedGameSettingsClass), "string_1");
// They are 'static' variables, not needed to give a instance.
DocumentsSettings.SetValue(null, _sptPath);
diff --git a/Source/AkiSupport/Singleplayer/AkiSingleplayerPlugin.cs b/Source/AkiSupport/Singleplayer/AkiSingleplayerPlugin.cs
index b01626ced..40553ba9c 100644
--- a/Source/AkiSupport/Singleplayer/AkiSingleplayerPlugin.cs
+++ b/Source/AkiSupport/Singleplayer/AkiSingleplayerPlugin.cs
@@ -13,7 +13,7 @@ namespace StayInTarkov.AkiSupport.Singleplayer
/// Credit SPT-Aki team
/// Paulov. I have removed a lot of unused patches
///
- [BepInPlugin("com.spt-aki.singleplayer", "AKI.Singleplayer", "1.0.0.0")]
+ [BepInPlugin("com.stayintarkov.aki.sp", "SIT.Aki.SPT", "1.0.0.0")]
class AkiSingleplayerPlugin : BaseUnityPlugin
{
public void Awake()
@@ -29,7 +29,6 @@ public void Awake()
new GetNewBotTemplatesPatch().Enable();
new DogtagPatch().Enable();
new PostRaidHealingPricePatch().Enable();
- new EndByTimerPatch().Enable();
new PostRaidHealScreenPatch().Enable();
new LighthouseBridgePatch().Enable();
new LighthouseTransmitterPatch().Enable();
diff --git a/Source/AkiSupport/Singleplayer/Patches/MainMenu/AmmoUsedCounterPatch.cs b/Source/AkiSupport/Singleplayer/Patches/MainMenu/AmmoUsedCounterPatch.cs
index 048181c23..58b670c1f 100644
--- a/Source/AkiSupport/Singleplayer/Patches/MainMenu/AmmoUsedCounterPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/MainMenu/AmmoUsedCounterPatch.cs
@@ -23,7 +23,7 @@ private static void PatchPostfix(Player __instance)
{
if (__instance.IsYourPlayer)
{
- __instance.Profile.EftStats.SessionCounters.AddLong(1L, ASessionCounterManager.AmmoUsed);
+ __instance.Profile.EftStats.SessionCounters.AddLong(1L, SessionCounterTypesAbstractClass.AmmoUsed);
}
}
}
diff --git a/Source/AkiSupport/Singleplayer/Patches/MainMenu/ArmorDamageCounterPatch.cs b/Source/AkiSupport/Singleplayer/Patches/MainMenu/ArmorDamageCounterPatch.cs
index 16f15ea13..3c1118340 100644
--- a/Source/AkiSupport/Singleplayer/Patches/MainMenu/ArmorDamageCounterPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/MainMenu/ArmorDamageCounterPatch.cs
@@ -52,7 +52,7 @@ private static void PatchPostfix(DamageInfo damageInfo)
if (template is AmmoTemplate bulletTemplate)
{
float absorbedDamage = (float)Math.Round(bulletTemplate.Damage - damageInfo.Damage);
- damageInfo.Player.iPlayer.Profile.EftStats.SessionCounters.AddFloat(absorbedDamage, ASessionCounterManager.CauseArmorDamage);
+ damageInfo.Player.iPlayer.Profile.EftStats.SessionCounters.AddFloat(absorbedDamage, SessionCounterTypesAbstractClass.CauseArmorDamage);
}
}
}
diff --git a/Source/AkiSupport/Singleplayer/Patches/Quests/EndByTimerPatch.cs b/Source/AkiSupport/Singleplayer/Patches/Quests/EndByTimerPatch.cs
deleted file mode 100644
index 6b19759db..000000000
--- a/Source/AkiSupport/Singleplayer/Patches/Quests/EndByTimerPatch.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using EFT;
-using System.Reflection;
-using HarmonyLib;
-using StayInTarkov.Networking;
-
-namespace StayInTarkov.AkiSupport.Singleplayer.Patches.Quests
-{
- ///
- /// Credit SPT-Aki team
- /// Link: https://dev.sp-tarkov.com/SPT-AKI/Modules/src/branch/master/project/Aki.SinglePlayer/Patches/Quests/EndByTimerPatch.cs
- /// Modified by: KWJimWails. Converted to use AkiBackendCommunication
- /// Having the raid timer reach zero results in a successful extract,
- /// this patch makes it so letting the time reach zero results in a MIA result
- ///
- public class EndByTimerPatch : ModulePatch
- {
- protected override MethodBase GetTargetMethod()
- {
- return AccessTools.Method(typeof(BaseLocalGame), nameof(BaseLocalGame.Stop));
- }
-
- // Unused, but left here in case patch breaks and finding the intended method is difficult
- private static bool IsStopRaidMethod(MethodInfo mi)
- {
- var parameters = mi.GetParameters();
- return (parameters.Length == 4
- && parameters[0].Name == "profileId"
- && parameters[1].Name == "exitStatus"
- && parameters[2].Name == "exitName"
- && parameters[3].Name == "delay"
- && parameters[0].ParameterType == typeof(string)
- && parameters[1].ParameterType == typeof(ExitStatus)
- && parameters[2].ParameterType == typeof(string)
- && parameters[3].ParameterType == typeof(float));
- }
-
- [PatchPrefix]
- private static bool PrefixPatch(ref ExitStatus exitStatus, ref string exitName)
- {
- var isParsed = bool.TryParse(AkiBackendCommunication.Instance.GetJson("/singleplayer/settings/raid/endstate"), out bool MIAOnRaidEnd);
- if (isParsed)
- {
- // No extract name and successful, its a MIA
- if (MIAOnRaidEnd && string.IsNullOrEmpty(exitName?.Trim()) && exitStatus == ExitStatus.Survived)
- {
- exitStatus = ExitStatus.MissingInAction;
- exitName = null;
- }
- }
- return true; // Do original
- }
- }
-}
diff --git a/Source/AkiSupport/Singleplayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs b/Source/AkiSupport/Singleplayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs
index 8ba3d449b..5312aead1 100644
--- a/Source/AkiSupport/Singleplayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs
@@ -43,7 +43,7 @@ private static void PatchPostfix()
}
var inventoryController = Traverse.Create(player).Field("_inventoryController").Value;
- ItemMovementHandler.Remove(accessCardItem, inventoryController, false, true);
+ InteractionsHandlerClass.Remove(accessCardItem, inventoryController, false, true);
}
}
}
\ No newline at end of file
diff --git a/Source/AkiSupport/Singleplayer/Patches/ScavMode/IsHostileToEverybodyPatch.cs b/Source/AkiSupport/Singleplayer/Patches/ScavMode/IsHostileToEverybodyPatch.cs
index 6098d40d1..d409c19aa 100644
--- a/Source/AkiSupport/Singleplayer/Patches/ScavMode/IsHostileToEverybodyPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/ScavMode/IsHostileToEverybodyPatch.cs
@@ -7,7 +7,7 @@ public class IsHostileToEverybodyPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
- var desiredType = typeof(ServerBotSettingsClass);
+ var desiredType = typeof(BotSettingsRepoClass);
var desiredMethod = desiredType.GetMethod("IsHostileToEverybody", BindingFlags.Public | BindingFlags.Static);
return desiredMethod;
@@ -16,7 +16,7 @@ protected override MethodBase GetTargetMethod()
[PatchPrefix]
private static bool PatchPrefix(WildSpawnType role, ref bool __result)
{
- if (role == WildSpawnType.sptUsec || role == WildSpawnType.sptBear)
+ if ((int)role == 47 || (int)role == 48)
{
__result = true;
return false;
diff --git a/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavLateStartPatch.cs b/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavLateStartPatch.cs
index 4276ee14b..595c276b5 100644
--- a/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavLateStartPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavLateStartPatch.cs
@@ -56,7 +56,7 @@ private static bool PatchPrefix(ref RaidSettings ____raidSettings)
// Create request and send to server, parse response
var request = new RaidTimeRequest(____raidSettings.Side, currentMapId);
- var json = AkiBackendCommunication.Instance.PostJson("/singleplayer/settings/getRaidTime", Json.Serialize(request));
+ var json = AkiBackendCommunication.Instance.PostJsonBLOCKING("/singleplayer/settings/getRaidTime", Json.Serialize(request));
var serverResult = Json.Deserialize(json);
// Capture the changes that will be made to the raid so they can be easily accessed by modders
diff --git a/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavSellAllRequestPatch.cs b/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavSellAllRequestPatch.cs
index 3dfe7b12d..42d2dd963 100644
--- a/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavSellAllRequestPatch.cs
+++ b/Source/AkiSupport/Singleplayer/Patches/ScavMode/ScavSellAllRequestPatch.cs
@@ -19,7 +19,7 @@ protected override MethodBase GetTargetMethod()
{
// We want to find a type that contains `SellAllFromSavage` but doesn't extend from `IBackendStatus`
//Type targetType = StayInTarkovHelperConstants.EftTypes.SingleCustom(IsTargetType);
- Type targetType = typeof(TradingBackend1);
+ Type targetType = typeof(TradingBackend);
Logger.LogDebug($"{this.GetType().Name} Type: {targetType?.Name}");
diff --git a/Source/AssemblyInfo.cs b/Source/AssemblyInfo.cs
index dedee262f..426e7e6e6 100644
--- a/Source/AssemblyInfo.cs
+++ b/Source/AssemblyInfo.cs
@@ -31,5 +31,5 @@
// Revision
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("1.10.*")]
+[assembly: AssemblyVersion("1.11.*")]
diff --git a/Source/Configuration/PluginConfigSettings.cs b/Source/Configuration/PluginConfigSettings.cs
index 44a2df702..3cc4c2c1e 100644
--- a/Source/Configuration/PluginConfigSettings.cs
+++ b/Source/Configuration/PluginConfigSettings.cs
@@ -3,6 +3,7 @@
using StayInTarkov.Networking;
using System;
using System.Net;
+using UnityEngine;
#nullable enable
@@ -38,37 +39,34 @@ public void GetSettings()
}
+ internal sealed class ConfigurationManagerAttributes
+ {
+ public bool? IsAdvanced = false;
+ public bool? ShowRangeAsPercent;
+ }
+
public class SITAdvancedSettings
{
public const string Advanced = "Advanced";
public ConfigFile Config { get; }
public ManualLogSource Logger { get; }
- public bool SETTING_EnableSITGC
- {
- get
- {
- return StayInTarkovPlugin.Instance.Config.Bind
- (Advanced, "EnableSITGC", false, new ConfigDescription("Enable SIT's own Garbage Collector")).Value;
- }
- }
-
- public uint SETTING_SITGCMemoryThreshold
- {
- get
- {
- return StayInTarkovPlugin.Instance.Config.Bind
- (Advanced, "SITGCMemoryThreshold", 90u, new ConfigDescription("SIT's Garbage Collector. System Memory % before SIT forces a Garbage Collection.")).Value;
- }
- }
+ public bool SETTING_EnableSITGC { get; set; }
+ public uint SETTING_SITGCMemoryThreshold { get; set; }
+ public uint SETTING_SITGCMemoryCheckTime { get; set; }
public SITAdvancedSettings(ManualLogSource logger, ConfigFile config)
{
Logger = logger;
Config = config;
- _ = SETTING_EnableSITGC;
- _ = SETTING_SITGCMemoryThreshold;
+ SETTING_EnableSITGC = StayInTarkovPlugin.Instance.Config.Bind
+ (Advanced, "EnableSITGC", false, new ConfigDescription("Enable SIT's own Garbage Collector")).Value;
+ SETTING_SITGCMemoryThreshold = StayInTarkovPlugin.Instance.Config.Bind
+ (Advanced, "SITGCMemoryThreshold", 90u, new ConfigDescription("System Memory usage % limit, above that, SIT forces the run of Garbage Collection.", new AcceptableValueRange(50u, 95u), new ConfigurationManagerAttributes { IsAdvanced = true, ShowRangeAsPercent = true })).Value;
+ SETTING_SITGCMemoryCheckTime = StayInTarkovPlugin.Instance.Config.Bind
+ (Advanced, "SITGCMemoryCheckTime", 300u, new ConfigDescription("Set how many seconds to wait between SIT's Garbage Collector checks.", new AcceptableValueRange(60u, 900u), new ConfigurationManagerAttributes { IsAdvanced = true })).Value;
+
}
}
@@ -99,6 +97,8 @@ public bool SETTING_DEBUGShowPlayerList
public bool SETTING_HeadshotsAlwaysKill { get; set; } = false;
public bool SETTING_ShowFeed { get; set; } = true;
public bool SETTING_ShowSITStatistics { get; set; } = true;
+ public ConfigEntry SETTING_PressToExtractKey { get; private set; }
+ public ConfigEntry SETTING_PressToForceExtractKey { get; private set; }
public bool ShowPing { get; set; } = true;
public int SITWebSocketPort { get; set; } = 6970;
public int SITNatHelperPort { get; set; } = 6971;
@@ -114,17 +114,19 @@ public bool SETTING_DEBUGShowPlayerList
public bool ArenaMode { get; set; } = false;
public bool EnableAISpawnWaveSystem { get; set; } = true;
+ public int BotTeleportDistance { get; set; } = 40;
+
public bool ForceHighPingMode { get; set; } = false;
public bool RunThroughOnServerStop { get; set; } = true;
public int WaitingTimeBeforeStart { get; private set; }
- public int BlackScreenOnDeathTime
+ public float BlackScreenOnDeathTime
{
get
{
return StayInTarkovPlugin.Instance.Config.Bind
- ("Coop", "BlackScreenOnDeathTime", 500, new ConfigDescription("How long to wait until your death waits to become a Free Camera")).Value;
+ ("Coop", "BlackScreenOnDeathTime", 5F, new ConfigDescription("How long to wait after death until you become a Free Camera")).Value;
}
}
@@ -143,6 +145,11 @@ public void GetSettings()
SETTING_ShowFeed = StayInTarkovPlugin.Instance.Config.Bind
("Coop", "ShowFeed", true, new ConfigDescription("Enable the feed on the bottom right of the screen which shows player/bot spawns, kills, etc.")).Value;
+ SETTING_PressToExtractKey = StayInTarkovPlugin.Instance.Config.Bind
+ ("Coop", "PressToExtractKey", new KeyboardShortcut(KeyCode.F8), new ConfigDescription("The key you need to press to leave the raid."));
+
+ SETTING_PressToForceExtractKey = StayInTarkovPlugin.Instance.Config.Bind
+ ("Coop", "PressToForceExtractKey", new KeyboardShortcut(KeyCode.F7), new ConfigDescription("The key you need to press to FORCE leave the raid."));
AllPlayersSpawnTogether = StayInTarkovPlugin.Instance.Config.Bind
("Coop", "AllPlayersSpawnTogether", true, new ConfigDescription("Whether to spawn all players in the same place")).Value;
@@ -159,6 +166,9 @@ public void GetSettings()
WaitingTimeBeforeStart = Config.Bind("Coop", "WaitingTimeBeforeStart", 120
, new ConfigDescription("Time in seconds to wait for players before starting the game automatically")).Value;
+ BotTeleportDistance = Config.Bind("Coop", "BotTeleportDistance", 40
+ , new ConfigDescription("Distance greater than X meters to teleport bots to player")).Value;
+
SETTING_ShowSITStatistics = StayInTarkovPlugin.Instance.Config.Bind
("Coop", "ShowSITStatistics", true, new ConfigDescription("Enable the SIT statistics on the top left of the screen which shows ping, player count, etc.")).Value;
diff --git a/Source/Coop/AI/BotDespawnPatch.cs b/Source/Coop/AI/BotDespawnPatch.cs
new file mode 100644
index 000000000..4d10716b6
--- /dev/null
+++ b/Source/Coop/AI/BotDespawnPatch.cs
@@ -0,0 +1,47 @@
+using EFT;
+using StayInTarkov.Coop.Components.CoopGameComponents;
+using StayInTarkov.Coop.Matchmaker;
+using StayInTarkov.Coop.NetworkPacket.AI;
+using StayInTarkov.Coop.NetworkPacket.Player.Health;
+using StayInTarkov.Networking;
+using System.Reflection;
+
+namespace StayInTarkov.Coop.AI
+{
+ ///
+ /// Goal: Despawning bots between Server and Client is broken, this patch aims to fix that by patching DestroyInfo to send a packet once it has ran.
+ ///
+ internal class BotDespawnPatch : ModulePatch
+ {
+ private static readonly string methodName = "DestroyInfo";
+
+ protected override MethodBase GetTargetMethod()
+ {
+ return typeof(BotsController).GetMethod(methodName);
+ }
+
+ //[PatchPrefix]
+ //private static bool PatchPrefix(EFT.Player player)
+ //{
+ // return false;
+ //}
+
+ [PatchPostfix]
+ public static void Postfix(EFT.Player player)
+ {
+ if (SITMatchmaking.IsClient)
+ return;
+
+ if (!SITGameComponent.TryGetCoopGameComponent(out var coopGameComponent))
+ return;
+
+ if (coopGameComponent.ProfileIdsAI.Contains(player.ProfileId))
+ coopGameComponent.ProfileIdsAI.Remove(player.ProfileId);
+
+ DespawnAIPacket packet = new();
+ packet.AIProfileId = player.ProfileId;
+
+ GameClient.SendData(packet.Serialize());
+ }
+ }
+}
diff --git a/Source/Coop/Airdrops/SITAirdropsManager.cs b/Source/Coop/Airdrops/SITAirdropsManager.cs
index 70cc619c4..61816ab35 100644
--- a/Source/Coop/Airdrops/SITAirdropsManager.cs
+++ b/Source/Coop/Airdrops/SITAirdropsManager.cs
@@ -3,19 +3,17 @@
using BepInEx.Logging;
using Comfort.Common;
using EFT;
-using EFT.Game.Spawning;
-using EFT.Interactive;
using StayInTarkov;
using StayInTarkov.AkiSupport.Airdrops;
using StayInTarkov.AkiSupport.Airdrops.Models;
using StayInTarkov.AkiSupport.Airdrops.Utils;
using StayInTarkov.Coop.Components.CoopGameComponents;
using StayInTarkov.Coop.Matchmaker;
-using StayInTarkov.Coop.NetworkPacket;
-using StayInTarkov.Coop.Web;
+using StayInTarkov.Coop.NetworkPacket.Airdrop;
using StayInTarkov.Networking;
+using System;
using System.Collections;
-using System.Collections.Generic;
+using System.Threading.Tasks;
using UnityEngine;
namespace Aki.Custom.Airdrops
@@ -28,16 +26,23 @@ namespace Aki.Custom.Airdrops
public class SITAirdropsManager : MonoBehaviour
{
private AirdropPlane airdropPlane;
- public AirdropBox AirdropBox { get; private set; }
private ItemFactoryUtil factory;
- public bool isFlareDrop;
+ private float distanceTravelled = 0;
+
+ private DateTime LastSyncTime { get; set; }
+ private ManualLogSource Logger { get; } = BepInEx.Logging.Logger.CreateLogSource(nameof(SITAirdropsManager));
+
+ public AirdropBox AirdropBox { get; private set; }
public AirdropParametersModel AirdropParameters { get; set; }
- private ManualLogSource Logger { get; set; }
+ public bool ClientPlaneSpawned { get; private set; }
+ public AirdropLootResultModel ClientAirdropLootResultModel { get; private set; }
+ public AirdropConfigModel ClientAirdropConfigModel { get; private set; }
+ public bool ClientLootBuilt { get; private set; }
+ public bool IsFlareDrop { get; set; }
void Awake()
{
- Logger = BepInEx.Logging.Logger.CreateLogSource("SITAirdropsManager");
Logger.LogInfo("Awake");
Singleton.Create(this);
}
@@ -67,7 +72,7 @@ public async void Start()
// The server will generate stuff ready for the packet
- AirdropParameters = AirdropUtil.InitAirdropParams(gameWorld, isFlareDrop);
+ AirdropParameters = await AirdropUtil.InitAirdropParams(gameWorld, IsFlareDrop);
if (!AirdropParameters.AirdropAvailable)
{
@@ -98,10 +103,9 @@ public async void Start()
SetDistanceToDrop();
- BuildLootContainer(AirdropParameters.Config);
+ await BuildLootContainer(AirdropParameters.Config);
StartCoroutine(SendParamsToClients());
-
}
public IEnumerator SendParamsToClients()
@@ -112,11 +116,11 @@ public IEnumerator SendParamsToClients()
yield return new WaitForSeconds(AirdropParameters.TimeToStart);
Logger.LogDebug("Sending Airdrop Params");
- var packet = new Dictionary();
- packet.Add("serverId", SITGameComponent.GetServerId());
- packet.Add("m", "AirdropPacket");
- packet.Add("model", AirdropParameters);
- GameClient.SendData(packet.SITToJson());
+ AirdropPacket airdropPacket = new()
+ {
+ AirdropParametersModelJson = AirdropParameters.SITToJson()
+ };
+ GameClient.SendData(airdropPacket.Serialize());
yield break;
}
@@ -147,16 +151,11 @@ public async void FixedUpdate()
factory.BuildContainer(AirdropBox.Container, ClientAirdropConfigModel, ClientAirdropLootResultModel.DropType);
factory.AddLoot(AirdropBox.Container, ClientAirdropLootResultModel);
- if (AirdropBox.Container != null)
+
+ if (AirdropBox.Container != null && SITGameComponent.TryGetCoopGameComponent(out SITGameComponent coopGameComponent))
{
- if (SITGameComponent.TryGetCoopGameComponent(out var coopGameComponent))
- {
- List oldInteractiveObjectList = new List(coopGameComponent.ListOfInteractiveObjects)
- {
- AirdropBox.Container
- };
- coopGameComponent.ListOfInteractiveObjects = [.. oldInteractiveObjectList];
- }
+ Logger.LogDebug($"Adding Airdrop box with id {AirdropBox.Container.Id}");
+ coopGameComponent.WorldnteractiveObjects.TryAdd(AirdropBox.Container.Id, AirdropBox.Container);
}
}
@@ -197,6 +196,20 @@ public async void FixedUpdate()
StartBox();
}
+ if (AirdropParameters.BoxSpawned && !SITMatchmaking.IsClient)
+ {
+ if (this.LastSyncTime < DateTime.Now.AddSeconds(-1))
+ {
+ this.LastSyncTime = DateTime.Now;
+
+ AirdropBoxPositionSyncPacket packet = new()
+ {
+ Position = AirdropBox.transform.position
+ };
+ GameClient.SendData(packet.Serialize());
+ }
+ }
+
if (distanceTravelled < AirdropParameters.DistanceToTravel)
{
distanceTravelled += Time.deltaTime * AirdropParameters.Config.PlaneSpeed;
@@ -210,8 +223,6 @@ public async void FixedUpdate()
}
}
- float distanceTravelled = 0;
-
private void StartPlane()
{
airdropPlane.gameObject.SetActive(true);
@@ -227,50 +238,36 @@ private void StartBox()
AirdropBox.StartCoroutine(AirdropBox.DropCrate(dropPos));
}
- public bool ClientPlaneSpawned { get; private set; }
- public AirdropLootResultModel ClientAirdropLootResultModel { get; private set; }
- public AirdropConfigModel ClientAirdropConfigModel { get; private set; }
- public bool ClientLootBuilt { get; private set; }
-
- private void BuildLootContainer(AirdropConfigModel config)
+ private async Task BuildLootContainer(AirdropConfigModel config)
{
- if (SITMatchmaking.IsClient)
+ if (!SITMatchmaking.IsServer)
return;
- var lootData = factory.GetLoot();
+ // Get the lootData for this Raid
+ AirdropLootResultModel lootData = await factory.GetLoot() ?? throw new Exception("Airdrops. Tried to BuildLootContainer without any Loot.");
- // Get the lootData. Sent to Clients.
- if (SITMatchmaking.IsServer)
+ // Send the lootData to Clients.
+ AirdropLootPacket airdropLootPacket = new()
{
- var packet = new Dictionary();
- packet.Add("serverId", SITGameComponent.GetServerId());
- packet.Add("m", "AirdropLootPacket");
- packet.Add("config", config);
- packet.Add("result", lootData);
- GameClient.SendData(packet.SITToJson());
- }
+ AirdropLootResultModelJson = lootData.SITToJson(),
+ AirdropConfigModelJson = config.SITToJson()
+ };
+ GameClient.SendData(airdropLootPacket.Serialize());
- if (lootData == null)
- throw new System.Exception("Airdrops. Tried to BuildLootContainer without any Loot.");
-
factory.BuildContainer(AirdropBox.Container, config, lootData.DropType);
factory.AddLoot(AirdropBox.Container, lootData);
ClientLootBuilt = true;
- if (AirdropBox.Container != null)
+
+ if (AirdropBox.Container != null && SITGameComponent.TryGetCoopGameComponent(out SITGameComponent coopGameComponent))
{
- if (SITGameComponent.TryGetCoopGameComponent(out var coopGameComponent))
- {
- List oldInteractiveObjectList = new List(coopGameComponent.ListOfInteractiveObjects)
- {
- AirdropBox.Container
- };
- coopGameComponent.ListOfInteractiveObjects = [.. oldInteractiveObjectList];
- }
+ Logger.LogDebug($"Adding Airdrop box with id {AirdropBox.Container.Id}");
+ coopGameComponent.WorldnteractiveObjects.TryAdd(AirdropBox.Container.Id, AirdropBox.Container);
}
}
public void ReceiveBuildLootContainer(AirdropLootResultModel lootData, AirdropConfigModel config)
{
+ Logger.LogDebug(nameof(ReceiveBuildLootContainer));
ClientAirdropConfigModel = config;
ClientAirdropLootResultModel = lootData;
}
@@ -281,5 +278,11 @@ private void SetDistanceToDrop()
new Vector3(AirdropParameters.RandomAirdropPoint.x, AirdropParameters.DropHeight, AirdropParameters.RandomAirdropPoint.z),
airdropPlane.transform.position);
}
+
+ protected void OnDestroy()
+ {
+ if (Singleton.Instantiated)
+ Singleton.Release(Singleton.Instance);
+ }
}
}
\ No newline at end of file
diff --git a/Source/Coop/Components/ActionPacketHandlerComponent.cs b/Source/Coop/Components/ActionPacketHandlerComponent.cs
index 300058b35..c300b9be4 100644
--- a/Source/Coop/Components/ActionPacketHandlerComponent.cs
+++ b/Source/Coop/Components/ActionPacketHandlerComponent.cs
@@ -1,45 +1,20 @@
-using Aki.Custom.Airdrops;
-using Aki.Custom.Airdrops.Models;
-using BepInEx.Logging;
+using BepInEx.Logging;
using Comfort.Common;
using EFT;
-using EFT.MovingPlatforms;
-using EFT.UI.BattleTimer;
-using StayInTarkov.AkiSupport.Airdrops.Models;
using StayInTarkov.Coop.Components.CoopGameComponents;
-using StayInTarkov.Coop.Matchmaker;
using StayInTarkov.Coop.NetworkPacket;
-using StayInTarkov.Coop.NetworkPacket.Player;
using StayInTarkov.Coop.Players;
-using StayInTarkov.Coop.SITGameModes;
-using StayInTarkov.Coop.World;
-//using StayInTarkov.Core.Player;
-using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Net.Sockets;
-using System.Reflection;
-using System.Threading.Tasks;
using UnityEngine;
-using UnityEngine.Profiling;
namespace StayInTarkov.Coop.Components
{
public class ActionPacketHandlerComponent : MonoBehaviour
{
public readonly BlockingCollection ActionSITPackets = new(9999);
-
- public readonly BlockingCollection> ActionPackets = new(9999);
- public BlockingCollection> ActionPacketsMovement { get; private set; } = new(9999);
public ConcurrentDictionary Players => CoopGameComponent.Players;
public ManualLogSource Logger { get; private set; }
- private List RemovedFromAIPlayers = new();
-
- private CoopSITGame CoopGame { get; } = (CoopSITGame)Singleton.Instance;
-
private SITGameComponent CoopGameComponent { get; set; }
void Awake()
@@ -48,59 +23,25 @@ void Awake()
// Create a BepInEx Logger for ActionPacketHandlerComponent
Logger = BepInEx.Logging.Logger.CreateLogSource("ActionPacketHandlerComponent");
Logger.LogDebug("Awake");
-
- CoopGameComponent = CoopPatches.CoopGameComponentParent.GetComponent();
- ActionPacketsMovement = new();
}
void Start()
{
- CoopGameComponent = CoopPatches.CoopGameComponentParent.GetComponent();
- ActionPacketsMovement = new();
+ CoopGameComponent = this.gameObject.GetComponent();
}
- //void Update()
- //{
- // ProcessActionPackets();
- //}
-
- void LateUpdate()
+ void Update()
{
ProcessActionPackets();
}
-
-
-
- public static ActionPacketHandlerComponent GetThisComponent()
- {
- if (CoopPatches.CoopGameComponentParent == null)
- return null;
-
- if (CoopPatches.CoopGameComponentParent.TryGetComponent(out var component))
- return component;
-
- return null;
- }
-
private void ProcessActionPackets()
{
- if (CoopGameComponent == null)
- {
- if (CoopPatches.CoopGameComponentParent != null)
- {
- CoopGameComponent = CoopPatches.CoopGameComponentParent.GetComponent();
- if (CoopGameComponent == null)
- return;
- }
- }
+ CoopGameComponent = gameObject.GetComponent();
if (Singleton.Instance == null)
return;
- if (ActionPackets == null)
- return;
-
if (Players == null)
return;
@@ -127,466 +68,7 @@ private void ProcessActionPackets()
#endif
}
- if (ActionPackets.Count > 0)
- {
-#if DEBUGPACKETS
- Stopwatch stopwatchActionPackets = Stopwatch.StartNew();
-#endif
- while (ActionPackets.TryTake(out var result))
- {
-#if DEBUGPACKETS
- Stopwatch stopwatchActionPacket = Stopwatch.StartNew();
-#endif
- if (!ProcessLastActionDataPacket(result))
- {
- //ActionPackets.Add(result);
- continue;
- }
-
-#if DEBUGPACKETS
- if (stopwatchActionPacket.ElapsedMilliseconds > 1)
- Logger.LogDebug($"ActionPacket {result["m"]} took {stopwatchActionPacket.ElapsedMilliseconds}ms to process!");
-#endif
- }
-#if DEBUGPACKETS
- if (stopwatchActionPackets.ElapsedMilliseconds > 1)
- Logger.LogDebug($"ActionPackets took {stopwatchActionPackets.ElapsedMilliseconds}ms to process!");
-#endif
- }
-
- if (ActionPacketsMovement != null && ActionPacketsMovement.Count > 0)
- {
-#if DEBUGPACKETS
- Stopwatch stopwatchActionPacketsMovement = Stopwatch.StartNew();
-#endif
- while (ActionPacketsMovement.TryTake(out var result))
- {
- if (!ProcessLastActionDataPacket(result))
- {
- //ActionPacketsMovement.Add(result);
- continue;
- }
- }
-#if DEBUGPACKETS
- if (stopwatchActionPacketsMovement.ElapsedMilliseconds > 1)
- {
- Logger.LogDebug($"ActionPacketsMovement took {stopwatchActionPacketsMovement.ElapsedMilliseconds}ms to process!");
- }
-#endif
- }
-
-
- //if (ActionPacketsDamage != null && ActionPacketsDamage.Count > 0)
- //{
- // Stopwatch stopwatchActionPacketsDamage = Stopwatch.StartNew();
- // while (ActionPacketsDamage.TryTake(out var packet))
- // {
- // if (!packet.ContainsKey("profileId"))
- // continue;
-
- // var profileId = packet["profileId"].ToString();
-
- // // The person is missing. Lets add this back until they exist
- // if (!CoopGameComponent.Players.ContainsKey(profileId))
- // {
- // //ActionPacketsDamage.Add(packet);
- // continue;
- // }
-
- // var playerKVP = CoopGameComponent.Players[profileId];
- // if (playerKVP == null)
- // continue;
-
- // var coopPlayer = (CoopPlayer)playerKVP;
- // coopPlayer.ReceiveDamageFromServer(packet);
- // }
- // if (stopwatchActionPacketsDamage.ElapsedMilliseconds > 1)
- // {
- // Logger.LogDebug($"ActionPacketsDamage took {stopwatchActionPacketsDamage.ElapsedMilliseconds}ms to process!");
- // }
- //}
-
-
return;
}
-
- bool ProcessSITActionPackets(ISITPacket packet)
- {
- //Logger.LogInfo($"{nameof(ProcessSITActionPackets)} received {packet.GetType()}");
-
- packet.Process();
-
- //var playerPacket = packet as BasePlayerPacket;
- //if (playerPacket != null)
- //{
- // if (!Players.ContainsKey(playerPacket.ProfileId))
- // return false;
-
- // Players[playerPacket.ProfileId].ProcessSITPacket(playerPacket);
- //}
- //else
- //{
- // // Process Player States Packet
- // if(packet is PlayerStatesPacket playerStatesPacket)
- // {
- // foreach(var psp in playerStatesPacket.PlayerStates)
- // {
- // ProcessSITActionPackets(psp);
- // }
- // }
- //}
-
- return false;
- }
-
- bool ProcessLastActionDataPacket(Dictionary packet)
- {
- if (Singleton.Instance == null)
- return false;
-
- if (packet == null || packet.Count == 0)
- {
- Logger.LogInfo("No Data Returned from Last Actions!");
- return false;
- }
-
-
- bool result = ProcessPlayerPacket(packet);
- if (!result)
- result = ProcessWorldPacket(ref packet);
-
- return result;
- }
-
- private bool ProcessPlayerStatesPacket(PlayerStatesPacket playerStatesPacket)
- {
- return false;
- }
-
- bool ProcessWorldPacket(ref Dictionary packet)
- {
- // this isn't a world packet. return true
- if (packet.ContainsKey("profileId"))
- return true;
-
- // this isn't a world packet. return true
- if (!packet.ContainsKey("m"))
- return true;
-
- var result = false;
- string method = packet["m"].ToString();
-
- foreach (var coopPatch in CoopPatches.NoMRPPatches)
- {
- if (coopPatch is IModuleReplicationWorldPatch imrwp)
- {
- if (imrwp.MethodName == method)
- {
- imrwp.Replicated(ref packet);
- result = true;
- }
- }
- }
-
- switch (method)
- {
- case "AirdropPacket":
- ReplicateAirdrop(packet);
- result = true;
- break;
- case "AirdropLootPacket":
- ReplicateAirdropLoot(packet);
- result = true;
- break;
- //case "RaidTimer":
- // ReplicateRaidTimer(packet);
- // result = true;
- // break;
- case "TimeAndWeather":
- ReplicateTimeAndWeather(packet);
- result = true;
- break;
- case "ArmoredTrainTime":
- ReplicateArmoredTrainTime(packet);
- result = true;
- break;
- case "LootableContainer_Interact":
- LootableContainer_Interact_Patch.Replicated(packet);
- result = true;
- break;
- }
-
- return result;
- }
-
- bool ProcessPlayerPacket(Dictionary packet)
- {
-
- if (packet == null)
- return true;
-
- var profileId = "";
-
- if (packet.ContainsKey("profileId"))
- profileId = packet["profileId"].ToString();
-
- if (packet.ContainsKey("ProfileId"))
- profileId = packet["ProfileId"].ToString();
-
- if (string.IsNullOrEmpty(profileId))
- return false;
-
- if (Players == null)
- {
- Logger.LogDebug("Players is Null");
- return false;
- }
-
- if (Players.Count == 0)
- {
- Logger.LogDebug("Players is Empty");
- return false;
- }
-
- if (!Players.ContainsKey(profileId))
- return false;
-
- var plyr = Players[profileId];
- if(plyr == null)
- return false;
-
- plyr.ProcessModuleReplicationPatch(packet);
-
- return true;
- }
-
- async Task WaitForPlayerAndProcessPacket(string profileId, Dictionary packet)
- {
- // Start the timer.
- var startTime = DateTime.Now;
- var maxWaitTime = TimeSpan.FromMinutes(2);
-
- while (true)
- {
- // Check if maximum wait time has been reached.
- if (DateTime.Now - startTime > maxWaitTime)
- {
- Logger.LogError($"{DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss.fff")}: WaitForPlayerAndProcessPacket waited for {maxWaitTime.TotalMinutes} minutes, but player {profileId} still did not exist after timeout period.");
- return;
- }
-
- if (Players == null)
- continue;
-
- var registeredPlayers = Singleton.Instance.RegisteredPlayers;
-
- // If the player now exists, process the packet and end the thread.
- if (Players.Any(x => x.Key == profileId) || registeredPlayers.Any(x => x.Profile.ProfileId == profileId))
- {
- // Logger.LogDebug($"{DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss.fff")}: WaitForPlayerAndProcessPacket waited for {(DateTime.Now - startTime).TotalSeconds}s");
- ProcessPlayerPacket(packet);
- return;
- }
-
- // Wait for a short period before checking again.
- await Task.Delay(1000);
- }
- }
-
- void ReplicateAirdrop(Dictionary packet)
- {
- if (!Singleton.Instantiated)
- return;
-
- Logger.LogInfo("--- RAW AIRDROP PACKET ---");
- Logger.LogInfo(packet.SITToJson());
-
- Singleton.Instance.AirdropParameters = packet["model"].ToString().SITParseJson();
- }
-
- void ReplicateAirdropLoot(Dictionary packet)
- {
- if (!Singleton.Instantiated)
- return;
-
- Logger.LogInfo("--- RAW AIRDROP-LOOT PACKET ---");
- Logger.LogInfo(packet.SITToJson());
-
- Singleton.Instance.ReceiveBuildLootContainer(
- packet["result"].ToString().SITParseJson(),
- packet["config"].ToString().SITParseJson());
- }
-
- //void ReplicateRaidTimer(Dictionary packet)
- //{
- // SITGameComponent coopGameComponent = SITGameComponent.GetCoopGameComponent();
- // if (coopGameComponent == null)
- // return;
-
- // if (SITMatchmaking.IsClient)
- // {
- // var sessionTime = new TimeSpan(long.Parse(packet["sessionTime"].ToString()));
- // Logger.LogDebug($"RaidTimer: Remaining session time {sessionTime.TraderFormat()}");
-
- // if (coopGameComponent.LocalGameInstance is CoopSITGame coopGame)
- // {
- // var gameTimer = coopGame.GameTimer;
- // if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue)
- // {
- // if (gameTimer.PastTime.TotalSeconds < 3)
- // return;
-
- // var timeRemain = gameTimer.PastTime + sessionTime;
-
- // if (Math.Abs(gameTimer.SessionTime.Value.TotalSeconds - timeRemain.TotalSeconds) < 5)
- // return;
-
- // Logger.LogInfo($"RaidTimer: New SessionTime {timeRemain.TraderFormat()}");
- // gameTimer.ChangeSessionTime(timeRemain);
-
- // MainTimerPanel mainTimerPanel = ReflectionHelpers.GetFieldOrPropertyFromInstance(coopGame.GameUi.TimerPanel, "_mainTimerPanel", false);
- // if (mainTimerPanel != null)
- // {
- // FieldInfo extractionDateTimeField = ReflectionHelpers.GetFieldFromType(typeof(TimerPanel), "dateTime_0");
- // extractionDateTimeField.SetValue(mainTimerPanel, gameTimer.StartDateTime.Value.AddSeconds(timeRemain.TotalSeconds));
-
- // MethodInfo UpdateTimerMI = ReflectionHelpers.GetMethodForType(typeof(MainTimerPanel), "UpdateTimer");
- // UpdateTimerMI.Invoke(mainTimerPanel, new object[] { });
- // }
- // }
- // }
- // }
- //}
-
- void ReplicateTimeAndWeather(Dictionary packet)
- {
- SITGameComponent coopGameComponent = SITGameComponent.GetCoopGameComponent();
- if (coopGameComponent == null)
- return;
-
- if (SITMatchmaking.IsClient)
- {
- Logger.LogDebug(packet.ToJson());
-
- var gameDateTime = new DateTime(long.Parse(packet["GameDateTime"].ToString()));
- if (coopGameComponent.LocalGameInstance is CoopSITGame coopGame && coopGame.GameDateTime != null)
- coopGame.GameDateTime.Reset(gameDateTime);
-
- var weatherController = EFT.Weather.WeatherController.Instance;
- if (weatherController != null)
- {
- var weatherDebug = weatherController.WeatherDebug;
- if (weatherDebug != null)
- {
- weatherDebug.Enabled = true;
-
- weatherDebug.CloudDensity = float.Parse(packet["CloudDensity"].ToString());
- weatherDebug.Fog = float.Parse(packet["Fog"].ToString());
- weatherDebug.LightningThunderProbability = float.Parse(packet["LightningThunderProbability"].ToString());
- weatherDebug.Rain = float.Parse(packet["Rain"].ToString());
- weatherDebug.Temperature = float.Parse(packet["Temperature"].ToString());
- weatherDebug.TopWindDirection = new(float.Parse(packet["TopWindDirection.x"].ToString()), float.Parse(packet["TopWindDirection.y"].ToString()));
-
- Vector2 windDirection = new(float.Parse(packet["WindDirection.x"].ToString()), float.Parse(packet["WindDirection.y"].ToString()));
-
- // working dog sh*t, if you are the programmer, DON'T EVER DO THIS! - dounai2333
- static bool BothPositive(float f1, float f2) => f1 > 0 && f2 > 0;
- static bool BothNegative(float f1, float f2) => f1 < 0 && f2 < 0;
- static bool VectorIsSameQuadrant(Vector2 v1, Vector2 v2, out int flag)
- {
- flag = 0;
- if (v1.x != 0 && v1.y != 0 && v2.x != 0 && v2.y != 0)
- {
- if ((BothPositive(v1.x, v2.x) && BothPositive(v1.y, v2.y))
- || (BothNegative(v1.x, v2.x) && BothNegative(v1.y, v2.y))
- || (BothPositive(v1.x, v2.x) && BothNegative(v1.y, v2.y))
- || (BothNegative(v1.x, v2.x) && BothPositive(v1.y, v2.y)))
- {
- flag = 1;
- return true;
- }
- }
- else
- {
- if (v1.x != 0 && v2.x != 0)
- {
- if (BothPositive(v1.x, v2.x) || BothNegative(v1.x, v2.x))
- {
- flag = 1;
- return true;
- }
- }
- else if (v1.y != 0 && v2.y != 0)
- {
- if (BothPositive(v1.y, v2.y) || BothNegative(v1.y, v2.y))
- {
- flag = 2;
- return true;
- }
- }
- }
- return false;
- }
-
- for (int i = 1; i < WeatherClass.WindDirections.Count(); i++)
- {
- Vector2 direction = WeatherClass.WindDirections[i];
- if (VectorIsSameQuadrant(windDirection, direction, out int flag))
- {
- weatherDebug.WindDirection = (EFT.Weather.WeatherDebug.Direction)i;
- weatherDebug.WindMagnitude = flag switch
- {
- 1 => windDirection.x / direction.x,
- 2 => windDirection.y / direction.y,
- _ => weatherDebug.WindMagnitude
- };
- break;
- }
- }
- }
- else
- {
- Logger.LogError("TimeAndWeather: WeatherDebug is null!");
- }
- }
- else
- {
- Logger.LogError("TimeAndWeather: WeatherController is null!");
- }
- }
- }
-
- void ReplicateArmoredTrainTime(Dictionary packet)
- {
- SITGameComponent coopGameComponent = SITGameComponent.GetCoopGameComponent();
- if (coopGameComponent == null)
- return;
-
- if (SITMatchmaking.IsClient)
- {
- DateTime utcTime = new(long.Parse(packet["utcTime"].ToString()));
-
- if (coopGameComponent.LocalGameInstance is CoopSITGame coopGame)
- {
- Timer1 gameTimer = coopGame.GameTimer;
-
- // Process only after raid began.
- if (gameTimer.StartDateTime.HasValue && gameTimer.SessionTime.HasValue)
- {
- // Looking for Armored Train, if there is nothing, then we are not on the Reserve or Lighthouse.
- Locomotive locomotive = FindObjectOfType();
- if (locomotive != null)
- {
- // The time won't change, if we already have replicated the time, don't override it again.
- FieldInfo departField = ReflectionHelpers.GetFieldFromType(typeof(MovingPlatform), "_depart");
- if (utcTime == (DateTime)departField.GetValue(locomotive))
- return;
-
- locomotive.Init(utcTime);
- }
- }
- }
- }
- }
}
}
diff --git a/Source/Coop/Components/CoopGameComponents/SITGameComponent.cs b/Source/Coop/Components/CoopGameComponents/SITGameComponent.cs
index 2ba41010f..866b3fd07 100644
--- a/Source/Coop/Components/CoopGameComponents/SITGameComponent.cs
+++ b/Source/Coop/Components/CoopGameComponents/SITGameComponent.cs
@@ -1,42 +1,37 @@
using Comfort.Common;
using EFT;
using EFT.Communications;
+using EFT.Counters;
using EFT.Interactive;
using EFT.InventoryLogic;
using EFT.UI;
+using Google.FlatBuffers;
using Newtonsoft.Json.Linq;
+using StayInTarkov.AkiSupport.Singleplayer.Utils.InRaid;
using StayInTarkov.Configuration;
using StayInTarkov.Coop.Components;
using StayInTarkov.Coop.Controllers.Health;
using StayInTarkov.Coop.Matchmaker;
using StayInTarkov.Coop.NetworkPacket.Player;
using StayInTarkov.Coop.NetworkPacket.Player.Health;
+using StayInTarkov.Coop.NetworkPacket.World;
using StayInTarkov.Coop.Player;
using StayInTarkov.Coop.Players;
using StayInTarkov.Coop.SITGameModes;
using StayInTarkov.Coop.Web;
-//using StayInTarkov.Core.Player;
-using StayInTarkov.EssentialPatches;
-using StayInTarkov.Memory;
+using StayInTarkov.FlatBuffers;
using StayInTarkov.Networking;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
+using Systems.Effects;
using UnityEngine;
-
+using FBPacket = StayInTarkov.FlatBuffers.Packet;
using Rect = UnityEngine.Rect;
-using StayInTarkov.Coop.NetworkPacket.Raid;
-using Diz.Jobs;
-using System.Net.NetworkInformation;
-using EFT.Counters;
-using StayInTarkov.AkiSupport.Singleplayer.Utils.InRaid;
-using StayInTarkov.Coop.NetworkPacket.World;
namespace StayInTarkov.Coop.Components.CoopGameComponents
{
@@ -46,7 +41,7 @@ namespace StayInTarkov.Coop.Components.CoopGameComponents
public class SITGameComponent : MonoBehaviour
{
#region Fields/Properties
- public WorldInteractiveObject[] ListOfInteractiveObjects { get; set; }
+ public ConcurrentDictionary WorldnteractiveObjects { get; set; } = [];
private AkiBackendCommunication RequestingObj { get; set; }
public SITConfig SITConfig { get; private set; } = new SITConfig();
public string ServerId { get; set; } = null;
@@ -63,7 +58,6 @@ public class SITGameComponent : MonoBehaviour
///
public HashSet PlayerClients { get; } = new();
- //public EFT.Player[] PlayerUsers
public IEnumerable PlayerUsers
{
get
@@ -102,9 +96,9 @@ public EFT.Player[] PlayerBots
}
///
- /// This is all the spawned players via the spawning process. Not anyone else.
+ /// This is all the spawned characters created via the spawning process. Not anyone else.
///
- public Dictionary SpawnedPlayers { get; private set; } = new();
+ public Dictionary SpawnedCharacters { get; private set; } = new();
public BepInEx.Logging.ManualLogSource Logger { get; private set; }
public ConcurrentDictionary PlayersToSpawn { get; private set; } = new();
@@ -118,10 +112,6 @@ public EFT.Player[] PlayerBots
public List SpawnedPlayersToFinalize { get; private set; } = new();
- public BlockingCollection> ActionPackets => ActionPacketHandler.ActionPackets;
-
- private Dictionary[] m_CharactersJson { get; set; }
-
public bool RunAsyncTasks { get; set; } = true;
float screenScale = 1.0f;
@@ -130,23 +120,21 @@ public EFT.Player[] PlayerBots
public ActionPacketHandlerComponent ActionPacketHandler { get; set; }
+ public static SITGameComponent Instance { get; set; }
+
#endregion
#region Public Voids
public static SITGameComponent GetCoopGameComponent()
{
- if (CoopPatches.CoopGameComponentParent == null)
- {
- StayInTarkovHelperConstants.Logger.LogError($"Attempted to use {nameof(GetCoopGameComponent)} before {nameof(SITGameComponent)} has been created.");
- return null;
- }
-
- var coopGameComponent = CoopPatches.CoopGameComponentParent.GetComponent();
- if (coopGameComponent != null)
- return coopGameComponent;
-
+ if (Instance != null)
+ return Instance;
+#if DEBUG
StayInTarkovHelperConstants.Logger.LogError($"Attempted to use {nameof(GetCoopGameComponent)} before {nameof(SITGameComponent)} has been created.");
+ StayInTarkovHelperConstants.Logger.LogError(new System.Diagnostics.StackTrace());
+#endif
+
return null;
}
@@ -158,14 +146,7 @@ public static bool TryGetCoopGameComponent(out SITGameComponent coopGameComponen
public static string GetServerId()
{
- var coopGC = GetCoopGameComponent();
- if (coopGC == null)
- {
- StayInTarkovHelperConstants.Logger.LogError($"Attempted to use {nameof(GetServerId)} before {nameof(SITGameComponent)} has been created.");
- return null;
- }
-
- return coopGC.ServerId;
+ return SITMatchmaking.GetGroupId();
}
#endregion
@@ -181,7 +162,7 @@ void Awake()
Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(SITGameComponent));
Logger.LogDebug($"{nameof(SITGameComponent)}:{nameof(Awake)}");
- ActionPacketHandler = CoopPatches.CoopGameComponentParent.GetOrAddComponent();
+ ActionPacketHandler = gameObject.GetOrAddComponent();
gameObject.AddComponent();
SITCheck();
@@ -207,13 +188,7 @@ void SITCheck()
///
async void Start()
{
- Logger.LogDebug("CoopGameComponent:Start");
-
- // Get Reference to own Player
- //OwnPlayer = (LocalPlayer)Singleton.Instance.MainPlayer;
-
- //// Add own Player to Players list
- //Players.TryAdd(OwnPlayer.ProfileId, (CoopPlayer)OwnPlayer);
+ Instance = this;
// Instantiate the Requesting Object for Aki Communication
RequestingObj = AkiBackendCommunication.GetRequestInstance(false, Logger);
@@ -241,7 +216,7 @@ await RequestingObj.PostJsonAsync("/SIT/Config", "{}").ContinueWith(x
// Enable the Coop Patches
CoopPatches.EnableDisablePatches();
- Singleton.Instance.AfterGameStarted += GameWorld_AfterGameStarted;;
+ Singleton.Instance.AfterGameStarted += GameWorld_AfterGameStarted; ;
// In game ping system.
if (Singleton.Instantiated)
@@ -257,42 +232,94 @@ await RequestingObj.PostJsonAsync("/SIT/Config", "{}").ContinueWith(x
private IEnumerator SendPlayerStatePacket()
{
- using PlayerStatesPacket playerStatesPacket = new PlayerStatesPacket();
-
- List packets = new List();
- //foreach (var player in Players.Values)
- foreach (var player in Singleton.Instance.AllAlivePlayersList)
+ var players = Singleton.Instance.AllAlivePlayersList;
+ foreach (var player in players)
{
- //if (!GameWorldGameStarted)
- // continue;
-
if (player == null)
continue;
if (player is CoopPlayerClient)
continue;
- //if (!player.TryGetComponent(out PlayerReplicatedComponent prc))
- // continue;
-
- //if (prc.IsClientDrone)
- // continue;
-
if (!player.enabled)
continue;
if (!player.isActiveAndEnabled)
continue;
- CreatePlayerStatePacketFromPRC(ref packets, player);
- }
+ var builder = new FlatBufferBuilder(1024);
+
+ var profileId = builder.CreateString(player.ProfileId);
- //playerStates.Add("dataList", playerStateArray);
- //Logger.LogDebug(playerStates.SITToJson());
- playerStatesPacket.PlayerStates = packets.ToArray();
- var serializedPlayerStates = playerStatesPacket.Serialize();
+ PlayerState.StartPlayerState(builder);
- //Logger.LogDebug($"{nameof(playerStatesPacket)} is {serialized.Length} in Length");
+ // Iterate over the BodyParts
+ {
+ // no Span, Sadge
+ var currents = new float[Enum.GetValues(typeof(EBodyPart)).Length];
+ var maximums = new float[Enum.GetValues(typeof(EBodyPart)).Length];
+ foreach (EBodyPart bodyPart in Enum.GetValues(typeof(EBodyPart)))
+ {
+ var health = player.HealthController.GetBodyPartHealth(bodyPart);
+ currents[(byte)bodyPart] = health.Current;
+ maximums[(byte)bodyPart] = health.Maximum;
+ }
+ PlayerState.AddBodyPartsHealth(builder, BodyPartsHealth.CreateBodyPartsHealth(builder, currents, maximums));
+ }
+
+ // TODO(belette) add this later? seems unused today since these packets only carry profileId and no relevant info atm
+ //if (player.HealthController is SITHealthController sitHealthController)
+ //{
+ // var tmpHealthEffectPacketList = new List();
+ // while (sitHealthController.PlayerHealthEffectPackets.TryDequeue(out var p))
+ // {
+ // tmpHealthEffectPacketList.Add(p);
+ // }
+ // playerHealth.HealthEffectPackets = tmpHealthEffectPacketList.ToArray();
+ //}
+
+ PlayerState.AddProfileId(builder, profileId);
+ PlayerState.AddIsAlive(builder, player.HealthController.IsAlive);
+ PlayerState.AddEnergy(builder, player.HealthController.Energy.Current);
+ PlayerState.AddHydration(builder, player.HealthController.Hydration.Current);
+ PlayerState.AddPosition(builder, Vec3.CreateVec3(builder, player.Position.x, player.Position.y, player.Position.z));
+ PlayerState.AddRotation(builder, Vec2.CreateVec2(builder, player.Rotation.x, player.Rotation.y));
+ PlayerState.AddHeadRotation(builder, Vec3.CreateVec3(builder, player.HeadRotation.x, player.HeadRotation.y, player.HeadRotation.z));
+ PlayerState.AddMovementDirection(builder, Vec2.CreateVec2(builder, player.MovementContext.MovementDirection.x, player.MovementContext.MovementDirection.y));
+ PlayerState.AddState(builder, (FlatBuffers.EPlayerState)player.MovementContext.CurrentState.Name);
+ PlayerState.AddTilt(builder, player.MovementContext.Tilt);
+ PlayerState.AddStep(builder, (sbyte)player.MovementContext.Step);
+ PlayerState.AddAnimatorStateIndex(builder, (byte)player.MovementContext.CurrentAnimatorStateIndex);
+ PlayerState.AddCharacterMovementSpeed(builder, player.MovementContext.CharacterMovementSpeed);
+ PlayerState.AddIsProne(builder, player.MovementContext.IsInPronePose);
+ PlayerState.AddPoseLevel(builder, player.MovementContext.PoseLevel);
+ PlayerState.AddIsSprinting(builder, player.MovementContext.IsSprintEnabled);
+ PlayerState.AddInputDirection(builder, Vec2.CreateVec2(builder, player.InputDirection.x, player.InputDirection.y));
+ PlayerState.AddLeftStance(builder, player.MovementContext.LeftStanceController.LastAnimValue);
+ PlayerState.AddHandsExhausted(builder, player.Physical.SerializationStruct.HandsExhausted);
+ PlayerState.AddStaminaExhausted(builder, player.Physical.SerializationStruct.StaminaExhausted);
+ PlayerState.AddOxygenExhausted(builder, player.Physical.SerializationStruct.OxygenExhausted);
+ PlayerState.AddBlindfire(builder, (sbyte)player.MovementContext.BlindFire);
+ PlayerState.AddLinearSpeed(builder, player.MovementContext.ActualLinearVelocity);
+ var pstateOffset = PlayerState.EndPlayerState(builder);
+
+ FBPacket.StartPacket(builder);
+ FBPacket.AddPacketType(builder, AnyPacket.player_state);
+ FBPacket.AddPacket(builder, pstateOffset.Value);
+ var packetOffset = FBPacket.EndPacket(builder);
+
+ builder.Finish(packetOffset.Value);
+
+ if (Singleton.Instance.GameClient is GameClientUDP udp)
+ {
+ var seg = builder.DataBuffer.ToArraySegment(builder.DataBuffer.Position, builder.DataBuffer.Length - builder.DataBuffer.Position);
+ udp.SendData(seg.Array, seg.Offset, seg.Count, SITGameServerClientDataProcessing.FLATBUFFER_CHANNEL_NUM, LiteNetLib.DeliveryMethod.Sequenced);
+ }
+ else if (Singleton.Instance.GameClient is GameClientTCPRelay)
+ {
+ GameClient.SendData(builder.SizedByteArray());
+ }
+ }
// ----------------------------------------------------------------------------------------------------
// Paulov: Keeping this here as a note. DO NOT DELETE.
@@ -313,10 +340,6 @@ private IEnumerator SendPlayerStatePacket()
// }
// ----------------------------------------------------------------------------------------------------
- GameClient.SendData(serializedPlayerStates);
-
- LastPlayerStateSent = DateTime.Now;
-
yield return new WaitForSeconds(PluginConfigSettings.Instance.CoopSettings.SETTING_PlayerStateTickRateInMS / 1000f);
StartCoroutine(SendPlayerStatePacket());
}
@@ -325,14 +348,15 @@ private void GameWorld_AfterGameStarted()
{
GameWorldGameStarted = true;
Logger.LogDebug(nameof(GameWorld_AfterGameStarted));
- //if (Singleton.Instance.RegisteredPlayers.Any())
- //{
- // // Send My Player to Aki, so that other clients know about me
- // CoopSITGame.SendPlayerDataToServer((LocalPlayer)Singleton.Instance.RegisteredPlayers.First(x => x.IsYourPlayer));
- //}
- GarbageCollect();
- StartCoroutine(GarbageCollectSIT());
+ this.GetOrAddComponent();
+ this.GetOrAddComponent();
+
+ // Add Server Authority components
+ if (SITMatchmaking.IsServer)
+ {
+ this.GetOrAddComponent();
+ }
StartCoroutine(SendPlayerStatePacket());
@@ -355,62 +379,14 @@ private void GameWorld_AfterGameStarted()
}
// Get a List of Interactive Objects (this is a slow method), so run once here to maintain a reference
- ListOfInteractiveObjects = FindObjectsOfType();
- }
-
- private void GarbageCollect()
- {
- // Start the SIT Garbage Collector
- Logger.LogDebug($"{nameof(GarbageCollect)}");
- BSGMemoryGC.RunHeapPreAllocation();
- BSGMemoryGC.Collect(force: true);
- BSGMemoryGC.EmptyWorkingSet();
- BSGMemoryGC.GCEnabled = true;
- //Resources.UnloadUnusedAssets();
- }
-
- ///
- /// Runs the Garbage Collection every 5 minutes
- ///
- ///
- private IEnumerator GarbageCollectSIT()
- {
- while(true)
+ foreach (WorldInteractiveObject worldInteractiveObject in FindObjectsOfType())
{
- if (PluginConfigSettings.Instance.AdvancedSettings.SETTING_EnableSITGC)
- {
- var nearestEnemyDist = float.MaxValue;
- foreach(var p in Players)
- {
- if (p.Key == Singleton.Instance.MainPlayer.ProfileId)
- continue;
-
- var dist = Vector3.Distance(p.Value.Transform.position, Singleton.Instance.MainPlayer.Transform.position);
- if(dist < nearestEnemyDist)
- nearestEnemyDist = dist;
- }
-
- if (nearestEnemyDist > 10)
- {
- var mem = MemoryInfo.GetCurrentStatus();
- if (mem == null)
- {
- yield return new WaitForSeconds(1);
- continue;
- }
-
- var memPercentInUse = mem.dwMemoryLoad;
- Logger.LogDebug($"Total memory used: {mem.dwMemoryLoad}%");
- if (memPercentInUse > PluginConfigSettings.Instance.AdvancedSettings.SETTING_SITGCMemoryThreshold)
- GarbageCollect();
-
- }
- }
-
- yield return new WaitForSeconds(60);
+ this.WorldnteractiveObjects.TryAdd(worldInteractiveObject.Id, worldInteractiveObject);
}
}
+
+
///
/// This is a simple coroutine to allow methods to run every second.
///
@@ -419,168 +395,85 @@ private IEnumerator EverySecondCoroutine()
{
var waitSeconds = new WaitForSeconds(1.0f);
- while (RunAsyncTasks)
+ if (!Singleton.Instantiated)
{
yield return waitSeconds;
+ StartCoroutine(EverySecondCoroutine());
+ yield break;
+ }
- try
- {
- if (!Singleton.Instantiated)
- continue;
-
- if (!Singleton.Instantiated)
- continue;
-
- if (!GameWorldGameStarted)
- continue;
-
- //Logger.LogDebug($"DEBUG: {nameof(EverySecondCoroutine)}");
-
- var coopGame = Singleton.Instance;
-
- var playersToExtract = new HashSet();
- foreach (var exfilPlayer in coopGame.ExtractingPlayers)
- {
- var exfilTime = new TimeSpan(0, 0, (int)exfilPlayer.Value.Item1);
- var timeInExfil = new TimeSpan(DateTime.Now.Ticks - exfilPlayer.Value.Item2);
- if (timeInExfil >= exfilTime)
- {
- if (!playersToExtract.Contains(exfilPlayer.Key))
- {
- Logger.LogDebug(exfilPlayer.Key + " should extract");
- playersToExtract.Add(exfilPlayer.Key);
- }
- }
- else
- {
- Logger.LogDebug(exfilPlayer.Key + " extracting " + timeInExfil);
-
- }
- }
-
- // Trigger all countdown exfils (e.g. car), clients are responsible for their own extract
- // since exfilpoint.Entered is local because of collision logic being local
- // we start from the end because we remove as we go in `CoopSITGame.ExfiltrationPoint_OnStatusChanged`
- for (int i = coopGame.EnabledCountdownExfils.Count - 1; i >= 0; i--)
- {
- var ep = coopGame.EnabledCountdownExfils[i];
- if (coopGame.PastTime - ep.ExfiltrationStartTime >= ep.Settings.ExfiltrationTime)
- {
- var game = Singleton.Instance;
- foreach (var player in ep.Entered)
- {
- var hasUnmetRequirements = ep.UnmetRequirements(player).Any();
- if (player != null && player.HealthController.IsAlive && !hasUnmetRequirements)
- {
- game.ExtractingPlayers.Remove(player.ProfileId);
- game.ExtractedPlayers.Add(player.ProfileId);
- }
- }
- ep.SetStatusLogged(ep.Reusable ? EExfiltrationStatus.UncompleteRequirements : EExfiltrationStatus.NotPresent, nameof(EverySecondCoroutine));
- }
- }
-
- foreach (var player in playersToExtract)
- {
- coopGame.ExtractingPlayers.Remove(player);
- coopGame.ExtractedPlayers.Add(player);
- }
-
- var world = Singleton.Instance;
-
- // Hide extracted Players
- foreach (var profileId in coopGame.ExtractedPlayers)
- {
- var player = world.RegisteredPlayers.Find(x => x.ProfileId == profileId) as EFT.Player;
- if (player == null)
- continue;
-
- if (!ExtractedProfilesSent.Contains(profileId))
- {
- ExtractedProfilesSent.Add(profileId);
- if (player.Profile.Side == EPlayerSide.Savage)
- {
- player.Profile.EftStats.SessionCounters.AddDouble(0.01,
- [
- CounterTag.FenceStanding,
- EFenceStandingSource.ExitStanding
- ]);
- }
- AkiBackendCommunicationCoop.PostLocalPlayerData(player
- , new Dictionary() { { "m", "Extraction" }, { "Extracted", true } }
- );
- }
-
- if (player.ActiveHealthController != null)
- {
- if (!player.ActiveHealthController.MetabolismDisabled)
- {
- player.ActiveHealthController.AddDamageMultiplier(0);
- player.ActiveHealthController.SetDamageCoeff(0);
- player.ActiveHealthController.DisableMetabolism();
- player.ActiveHealthController.PauseAllEffects();
-
- //player.SwitchRenderer(false);
-
- // TODO: Currently. Destroying your own Player just breaks the game and it appears to be "frozen". Need to learn a new way to do a FreeCam!
- //if (Singleton.Instance.MainPlayer.ProfileId != profileId)
- // Destroy(player);
- }
- }
- //force close all screens to disallow looting open crates after extract
- if (profileId == world.MainPlayer.ProfileId)
- {
- ScreenManager instance = ScreenManager.Instance;
- instance.CloseAllScreensForced();
- }
-
- PlayerUtils.MakeVisible(player, false);
- }
+ if (!Singleton.Instantiated)
+ {
+ yield return waitSeconds;
+ StartCoroutine(EverySecondCoroutine());
+ yield break;
+ }
- // Add players who have joined to the AI Enemy Lists
- var botController = (BotsController)ReflectionHelpers.GetFieldFromTypeByFieldType(typeof(BaseLocalGame), typeof(BotsController)).GetValue(Singleton.Instance);
- if (botController != null)
- {
- while (PlayersForAIToTarget.TryDequeue(out var otherPlayer))
- {
- Logger.LogDebug($"Adding {otherPlayer.Profile.Nickname} to Enemy list");
- botController.AddActivePLayer(otherPlayer);
- botController.AddEnemyToAllGroups(otherPlayer, otherPlayer, otherPlayer);
- }
- }
+ if (!GameWorldGameStarted)
+ {
+ yield return waitSeconds;
+ StartCoroutine(EverySecondCoroutine());
+ yield break;
+ }
- if (Singleton.Instance.GameClient is GameClientUDP udp)
- {
- coopGame.GameClient.ResetStats();
- }
+ var coopGame = Singleton.Instance;
- ProcessOtherModsSpawnedPlayers();
+ var world = Singleton.Instance;
- }
- catch (Exception ex)
+ // Add players who have joined to the AI Enemy Lists
+ var botController = (BotsController)ReflectionHelpers.GetFieldFromTypeByFieldType(typeof(BaseLocalGame), typeof(BotsController)).GetValue(Singleton.Instance);
+ if (botController != null)
+ {
+ while (PlayersForAIToTarget.TryDequeue(out var otherPlayer))
{
- Logger.LogError($"{nameof(EverySecondCoroutine)}: caught exception:\n{ex}");
+ Logger.LogDebug($"Adding {otherPlayer.Profile.Nickname} to Enemy list");
+ botController.AddActivePLayer(otherPlayer);
+ botController.AddEnemyToAllGroups(otherPlayer, otherPlayer, otherPlayer);
}
}
- }
- private void ProcessOtherModsSpawnedPlayers()
- {
- // If another mod has spawned people, attempt to handle it.
- foreach (var p in Singleton.Instance.AllAlivePlayersList)
+ if (Singleton.Instance.GameClient is GameClientUDP udp)
+ {
+ coopGame.GameClient.ResetStats();
+ }
+
+ // TODO: Make this work.
+ //ProcessOtherModsSpawnedPlayers();
+
+ if (DespawnList.Count > 0)
{
- if (!Players.ContainsKey(p.ProfileId))
+ foreach (var despawnId in DespawnList)
{
- // As these created players are unlikely to be CoopPlayer, throw an error!
- if((p as CoopPlayer) == null)
- {
- Logger.LogError($"Player of Id:{p.ProfileId} is not found in the SIT {nameof(Players)} list?!");
- }
+ Logger.LogInfo($"Attempting to despawn queued character: {despawnId}");
+
+ if (DespawnCharacter(despawnId))
+ DespawnList.Remove(despawnId);
}
- }
+ };
+
+ yield return waitSeconds;
+
+ //Logger.LogInfo($"{nameof(EverySecondCoroutine)}");
+ StartCoroutine(EverySecondCoroutine());
+
}
- private HashSet ExtractedProfilesSent = new();
+ // TODO: Make this work.
+ //private void ProcessOtherModsSpawnedPlayers()
+ //{
+ // // If another mod has spawned people, attempt to handle it.
+ // foreach (var p in Singleton.Instance.AllAlivePlayersList)
+ // {
+ // if (!Players.ContainsKey(p.ProfileId))
+ // {
+ // // As these created players are unlikely to be CoopPlayer, throw an error!
+ // if ((p as CoopPlayer) == null)
+ // {
+ // Logger.LogError($"Player of Id:{p.ProfileId} is not found in the SIT {nameof(Players)} list?!");
+ // }
+ // }
+ // }
+ //}
void OnDestroy()
{
@@ -590,16 +483,16 @@ void OnDestroy()
PlayersToSpawnProfiles.Clear();
PlayersToSpawnPositions.Clear();
PlayersToSpawnPacket.Clear();
+ DespawnList.Clear();
RunAsyncTasks = false;
- //StopCoroutine(ProcessServerCharacters());
StopCoroutine(EverySecondCoroutine());
CoopPatches.EnableDisablePatches();
+ GameObject.Destroy(this.GetComponent());
+ GameObject.Destroy(this.GetComponent());
+ Instance = null;
}
- TimeSpan LateUpdateSpan = TimeSpan.Zero;
- Stopwatch swActionPackets { get; } = new Stopwatch();
- bool PerformanceCheck_ActionPackets { get; set; } = false;
public bool RequestQuitGame { get; set; }
int ForceQuitGamePressed = 0;
@@ -695,7 +588,7 @@ void ProcessQuitting()
var quitState = GetQuitState();
if (
- Input.GetKeyDown(KeyCode.F8)
+ PluginConfigSettings.Instance.CoopSettings.SETTING_PressToExtractKey.Value.IsDown()
&&
quitState != EQuitState.NONE
&& !RequestQuitGame
@@ -725,7 +618,7 @@ void ProcessQuitting()
return;
}
else if (
- Input.GetKeyDown(KeyCode.F7)
+ PluginConfigSettings.Instance.CoopSettings.SETTING_PressToForceExtractKey.Value.IsDown()
&&
quitState != EQuitState.NONE
&& !RequestQuitGame
@@ -801,8 +694,8 @@ private void FindALocationAndProcessQuit()
if (Singleton.Instance.MyExitLocation == null)
{
var gameWorld = Singleton.Instance;
- List scavExfilFiltered = new List();
- List pmcExfilPiltered = new List();
+ List scavExfilFiltered = new();
+ List pmcExfilPiltered = new();
foreach (var exfil in gameWorld.ExfiltrationController.ExfiltrationPoints)
{
if (exfil is ScavExfiltrationPoint scavExfil)
@@ -875,8 +768,8 @@ void ProcessServerHasStopped()
if (Singleton.Instance.MyExitLocation == null)
{
var gameWorld = Singleton.Instance;
- List scavExfilFiltered = new List();
- List pmcExfilPiltered = new List();
+ List scavExfilFiltered = new();
+ List pmcExfilPiltered = new();
foreach (var exfil in gameWorld.ExfiltrationController.ExfiltrationPoints)
{
if (exfil is ScavExfiltrationPoint scavExfil)
@@ -951,9 +844,6 @@ void Update()
ProcessServerHasStopped();
ProcessServerCharacters();
- if (ActionPackets == null)
- return;
-
if (Players == null)
return;
@@ -963,10 +853,6 @@ void Update()
if (RequestingObj == null)
return;
-
-
-
-
if (SpawnedPlayersToFinalize == null)
return;
@@ -1006,142 +892,6 @@ void Update()
#endregion
- //private async Task ReadFromServerCharactersLoop()
- //{
- // if (GetServerId() == null)
- // return;
-
-
- // while (RunAsyncTasks)
- // {
- // await Task.Delay(10000);
-
- // if (Players == null)
- // continue;
-
- // await ReadFromServerCharacters();
-
- // }
- //}
-
- //private async Task ReadFromServerCharacters()
- //{
- // // -----------------------------------------------------------------------------------------------------------
- // // We must filter out characters that already exist on this match!
- // //
- // var playerList = new List();
- // if (!PluginConfigSettings.Instance.CoopSettings.SETTING_DEBUGSpawnDronesOnServer)
- // {
- // if (PlayersToSpawn.Count > 0)
- // playerList.AddRange(PlayersToSpawn.Keys.ToArray());
- // if (Players.Keys.Any())
- // playerList.AddRange(Players.Keys.ToArray());
- // if (Singleton.Instance.RegisteredPlayers.Any())
- // playerList.AddRange(Singleton.Instance.RegisteredPlayers.Select(x => x.ProfileId));
- // if (Singleton.Instance.AllAlivePlayersList.Count > 0)
- // playerList.AddRange(Singleton.Instance.AllAlivePlayersList.Select(x => x.ProfileId));
- // }
- // //
- // // -----------------------------------------------------------------------------------------------------------
- // await Task.Run(() =>
- // {
- // // Ensure this is a distinct list of Ids
- // var distinctExistingProfileIds = playerList.Distinct().ToArray();
- // RequestSpawnPlayersPacket requestSpawnPlayersPacket = new RequestSpawnPlayersPacket(distinctExistingProfileIds);
- // GameClient.SendData(requestSpawnPlayersPacket.Serialize());
- // });
- // //try
- // //{
- // // m_CharactersJson = await RequestingObj.PostJsonAsync[]>("/coop/server/read/players", jsonDataToSend, 30000);
- // // if (m_CharactersJson == null)
- // // return;
-
- // // if (!m_CharactersJson.Any())
- // // return;
-
- // // if (m_CharactersJson[0].ContainsKey("notFound"))
- // // {
- // // // Game is broken and doesn't exist!
- // // if (LocalGameInstance != null)
- // // {
- // // ServerHasStopped = true;
- // // }
- // // return;
- // // }
-
- // // //Logger.LogDebug($"CoopGameComponent.ReadFromServerCharacters:{actionsToValues.Length}");
-
- // // var packets = m_CharactersJson
- // // .Where(x => x != null);
- // // if (packets == null)
- // // return;
-
- // // foreach (var queuedPacket in packets)
- // // {
- // // if (queuedPacket != null && queuedPacket.Count > 0)
- // // {
- // // if (queuedPacket != null)
- // // {
- // // if (queuedPacket.ContainsKey("m"))
- // // {
- // // var method = queuedPacket["m"].ToString();
- // // if (method != "PlayerSpawn")
- // // continue;
-
- // // string profileId = queuedPacket["profileId"].ToString();
- // // if (!PluginConfigSettings.Instance.CoopSettings.SETTING_DEBUGSpawnDronesOnServer)
- // // {
- // // if (Players == null
- // // || Players.ContainsKey(profileId)
- // // || Singleton.Instance.RegisteredPlayers.Any(x => x.ProfileId == profileId)
- // // )
- // // {
- // // Logger.LogDebug($"Ignoring call to Spawn player {profileId}. The player already exists in the game.");
- // // continue;
- // // }
- // // }
-
- // // if (PlayersToSpawn.ContainsKey(profileId))
- // // continue;
-
- // // if (!PlayersToSpawnPacket.ContainsKey(profileId))
- // // PlayersToSpawnPacket.TryAdd(profileId, queuedPacket);
-
- // // if (!PlayersToSpawn.ContainsKey(profileId))
- // // PlayersToSpawn.TryAdd(profileId, ESpawnState.None);
-
- // // if (queuedPacket.ContainsKey("isAI"))
- // // Logger.LogDebug($"{nameof(ReadFromServerCharacters)}:isAI:{queuedPacket["isAI"]}");
-
- // // if (queuedPacket.ContainsKey("isAI") && queuedPacket["isAI"].ToString() == "True" && !ProfileIdsAI.Contains(profileId))
- // // {
- // // ProfileIdsAI.Add(profileId);
- // // Logger.LogDebug($"Added AI Character {profileId} to {nameof(ProfileIdsAI)}");
- // // }
-
- // // if (queuedPacket.ContainsKey("isAI") && queuedPacket["isAI"].ToString() == "False" && !ProfileIdsUser.Contains(profileId))
- // // {
- // // ProfileIdsUser.Add(profileId);
- // // Logger.LogDebug($"Added User Character {profileId} to {nameof(ProfileIdsUser)}");
- // // }
-
- // // }
- // // }
- // // }
- // // }
- // //}
- // //catch (Exception ex)
- // //{
-
- // // Logger.LogError(ex.ToString());
-
- // //}
- // //finally
- // //{
-
- // //}
- //}
-
//private IEnumerator ProcessServerCharacters()
private void ProcessServerCharacters()
{
@@ -1419,8 +1169,8 @@ private LocalPlayer SpawnCharacter(Profile profile, Vector3 position, int player
// Cant use ObservedPlayerMode, it causes the player to fall through the floor and die
, BackendConfigManager.Config.CharacterController.ObservedPlayerMode
//, BackendConfigManager.Config.CharacterController.ClientPlayerMode
- , () => Singleton.Instance.Control.Settings.MouseSensitivity
- , () => Singleton.Instance.Control.Settings.MouseAimingSensitivity
+ , () => Singleton.Instance.Control.Settings.MouseSensitivity
+ , () => Singleton.Instance.Control.Settings.MouseAimingSensitivity
, FilterCustomizationClass.Default
, null
, isYourPlayer: false
@@ -1448,8 +1198,8 @@ private LocalPlayer SpawnCharacter(Profile profile, Vector3 position, int player
Singleton.Instance.RegisterPlayer(otherPlayer);
- if (!SpawnedPlayers.ContainsKey(profile.ProfileId))
- SpawnedPlayers.Add(profile.ProfileId, otherPlayer);
+ if (!SpawnedCharacters.ContainsKey(profile.ProfileId))
+ SpawnedCharacters.Add(profile.ProfileId, otherPlayer);
// Create/Add PlayerReplicatedComponent to the LocalPlayer
// This shouldn't be needed. Handled in CoopPlayer.Create code
@@ -1567,74 +1317,38 @@ public void SetWeaponInHandsOfNewPlayer(EFT.Player person, Action successCallbac
});
}
- private Dictionary LastPlayerHealthPackets = new();
+ private HashSet DespawnList = new();
+
+ public void AddToDespawnList(string ProfileId) => DespawnList.Add(ProfileId);
- private void CreatePlayerStatePacketFromPRC(ref List playerStates, EFT.Player player)
+ private bool DespawnCharacter(string ProfileId)
{
- // What this does is create a ISITPacket for the Character's health that can be SIT Serialized.
- PlayerHealthPacket playerHealth = new PlayerHealthPacket(player.ProfileId);
- playerHealth.IsAlive = player.HealthController.IsAlive;
- playerHealth.Energy = player.HealthController.Energy.Current;
- playerHealth.Hydration = player.HealthController.Hydration.Current;
- var bpIndex = 0;
- // Iterate over the BodyParts
- foreach (EBodyPart bodyPart in Enum.GetValues(typeof(EBodyPart)))
+ //Player is already existent and should have already spawned, we can proceed immediately to removal.
+ if (SpawnedCharacters.ContainsKey(ProfileId))
{
- var health = player.HealthController.GetBodyPartHealth(bodyPart);
- playerHealth.BodyParts[bpIndex] = new PlayerBodyPartHealthPacket();
- playerHealth.BodyParts[bpIndex].BodyPart = bodyPart;
- playerHealth.BodyParts[bpIndex].Current = health.Current;
- playerHealth.BodyParts[bpIndex].Maximum = health.Maximum;
- bpIndex++;
- }
- if (player.HealthController is SITHealthController sitHealthController)
- {
- // Paulov: TODO: Continue from here in another branch
- var tmpHealthEffectPacketList = new List();
- while (sitHealthController.PlayerHealthEffectPackets.TryDequeue(out var p))
- {
- tmpHealthEffectPacketList.Add(p);
- }
- playerHealth.HealthEffectPackets = tmpHealthEffectPacketList.ToArray();
- }
+ var Bot = SpawnedCharacters[ProfileId];
- if (playerHealth != null)
- {
- if (!LastPlayerHealthPackets.ContainsKey(player.ProfileId))
- LastPlayerHealthPackets.Add(player.ProfileId, playerHealth);
+ //Bleeding causes an exception on despawn, stop the bleeding effect.
+ Singleton.Instance.EffectsCommutator.StopBleedingForPlayer(Bot);
- LastPlayerHealthPackets[player.ProfileId] = playerHealth;
+ Bot.Dispose();
+ if (Bot.gameObject != null)
+ DestroyImmediate(Bot.gameObject);
+
+ Destroy(Bot);
+ ProfileIdsAI.Remove(ProfileId);
+ SpawnedCharacters.Remove(ProfileId);
+ Players.TryRemove(ProfileId, out _);
+ return true;
}
- // Create the ISITPacket for the Character's Current State
- PlayerStatePacket playerStatePacket = new PlayerStatePacket(
- player.ProfileId
- , player.Position
- , player.Rotation
- , player.HeadRotation
- , player.MovementContext.MovementDirection
- , player.MovementContext.CurrentState.Name
- , player.MovementContext.Tilt
- , player.MovementContext.Step
- , player.MovementContext.CurrentAnimatorStateIndex
- , player.MovementContext.CharacterMovementSpeed
- , player.MovementContext.IsInPronePose
- , player.MovementContext.PoseLevel
- , player.MovementContext.IsSprintEnabled
- , player.InputDirection
- , player.MovementContext.LeftStanceController.LastAnimValue
- , playerHealth
- , player.Physical.SerializationStruct
- , player.MovementContext.BlindFire
- , player.MovementContext.ActualLinearVelocity
- );
- ;
-
- // Add the serialized packets to the PlayerStates JArray
- playerStates.Add(playerStatePacket);
+ Logger.LogWarning($"Character ({ProfileId}) has not spawned yet! Cannot remove");
+ return false;
}
+ private Dictionary LastPlayerHealthPackets = new();
+
private DateTime LastPlayerStateSent { get; set; } = DateTime.Now;
public ulong LocalIndex { get; set; }
diff --git a/Source/Coop/Components/CoopGameComponents/SITGameExtractionComponent.cs b/Source/Coop/Components/CoopGameComponents/SITGameExtractionComponent.cs
new file mode 100644
index 000000000..b67ffd8dc
--- /dev/null
+++ b/Source/Coop/Components/CoopGameComponents/SITGameExtractionComponent.cs
@@ -0,0 +1,154 @@
+using BepInEx.Logging;
+using Comfort.Common;
+using EFT.Counters;
+using EFT;
+using EFT.Interactive;
+using HarmonyLib.Tools;
+using StayInTarkov.Coop.Players;
+using StayInTarkov.Coop.SITGameModes;
+using StayInTarkov.Coop.Web;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using StayInTarkov.Networking;
+using StayInTarkov.Coop.NetworkPacket.Raid;
+
+namespace StayInTarkov.Coop.Components.CoopGameComponents
+{
+ public sealed class SITGameExtractionComponent : MonoBehaviour
+ {
+ ManualLogSource Logger { get; set; }
+ HashSet ExtractedProfilesSent {get;set;} = new HashSet();
+
+
+ void Awake()
+ {
+ Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(SITGameExtractionComponent));
+ }
+
+ void Update()
+ {
+ if (!Singleton.Instantiated)
+ return;
+
+ if (!Singleton.Instantiated)
+ return;
+
+ ProcessExtractingPlayers();
+ ProcessExtractionRequirements();
+ HideExtractedPlayers();
+
+ }
+
+ private void HideExtractedPlayers()
+ {
+ var world = Singleton.Instance;
+ var gameInstance = Singleton.Instance;
+
+ // Hide extracted Players
+ foreach (var profileId in gameInstance.ExtractedPlayers)
+ {
+ var player = world.RegisteredPlayers.Find(x => x.ProfileId == profileId) as EFT.Player;
+ if (player == null)
+ continue;
+
+ if (!ExtractedProfilesSent.Contains(profileId))
+ {
+ ExtractedProfilesSent.Add(profileId);
+ if (player.Profile.Side == EPlayerSide.Savage)
+ {
+ player.Profile.EftStats.SessionCounters.AddDouble(0.01,
+ [
+ CounterTag.FenceStanding,
+ EFenceStandingSource.ExitStanding
+ ]);
+ }
+ // Send the Extracted Packet to other Clients
+ GameClient.SendData(new ExtractedPlayerPacket(profileId).Serialize());
+ }
+
+ if (player.ActiveHealthController != null)
+ {
+ if (!player.ActiveHealthController.MetabolismDisabled)
+ {
+ player.ActiveHealthController.AddDamageMultiplier(0);
+ player.ActiveHealthController.SetDamageCoeff(0);
+ player.ActiveHealthController.DisableMetabolism();
+ player.ActiveHealthController.PauseAllEffects();
+ }
+ }
+
+ //force close all screens to disallow looting open crates after extract
+ if (profileId == world.MainPlayer.ProfileId)
+ {
+ ScreenManager instance = ScreenManager.Instance;
+ instance.CloseAllScreensForced();
+ }
+
+ PlayerUtils.MakeVisible(player, false);
+ }
+ }
+
+ private void ProcessExtractionRequirements()
+ {
+ var gameInstance = Singleton.Instance;
+ // Trigger all countdown exfils (e.g. car), clients are responsible for their own extract
+ // since exfilpoint.Entered is local because of collision logic being local
+ // we start from the end because we remove as we go in `CoopSITGame.ExfiltrationPoint_OnStatusChanged`
+ for (int i = gameInstance.EnabledCountdownExfils.Count - 1; i >= 0; i--)
+ {
+ var ep = gameInstance.EnabledCountdownExfils[i];
+ if (gameInstance.PastTime - ep.ExfiltrationStartTime >= ep.Settings.ExfiltrationTime)
+ {
+ var game = Singleton.Instance;
+ foreach (var player in ep.Entered)
+ {
+ var hasUnmetRequirements = ep.UnmetRequirements(player).Any();
+ if (player != null && player.HealthController.IsAlive && !hasUnmetRequirements)
+ {
+ game.ExtractingPlayers.Remove(player.ProfileId);
+ game.ExtractedPlayers.Add(player.ProfileId);
+ }
+ }
+ ep.SetStatusLogged(ep.Reusable ? EExfiltrationStatus.UncompleteRequirements : EExfiltrationStatus.NotPresent, nameof(ProcessExtractionRequirements));
+ }
+ }
+ }
+
+ private void ProcessExtractingPlayers()
+ {
+ var gameInstance = Singleton.Instance;
+ var playersToExtract = new HashSet();
+ foreach (var exfilPlayer in gameInstance.ExtractingPlayers)
+ {
+ var exfilTime = new TimeSpan(0, 0, (int)exfilPlayer.Value.Item1);
+ var timeInExfil = new TimeSpan(DateTime.Now.Ticks - exfilPlayer.Value.Item2);
+ if (timeInExfil >= exfilTime)
+ {
+ if (!playersToExtract.Contains(exfilPlayer.Key))
+ {
+#if DEBUG
+ Logger.LogDebug(exfilPlayer.Key + " should extract");
+#endif
+ playersToExtract.Add(exfilPlayer.Key);
+ }
+ }
+#if DEBUG
+ else
+ {
+ Logger.LogDebug(exfilPlayer.Key + " extracting " + timeInExfil);
+ }
+#endif
+ }
+
+ foreach (var player in playersToExtract)
+ {
+ gameInstance.ExtractingPlayers.Remove(player);
+ gameInstance.ExtractedPlayers.Add(player);
+ }
+ }
+ }
+}
diff --git a/Source/Coop/Components/CoopGameComponents/SITGameGCComponent.cs b/Source/Coop/Components/CoopGameComponents/SITGameGCComponent.cs
new file mode 100644
index 000000000..518800f79
--- /dev/null
+++ b/Source/Coop/Components/CoopGameComponents/SITGameGCComponent.cs
@@ -0,0 +1,102 @@
+using Comfort.Common;
+using EFT;
+using StayInTarkov.Configuration;
+using StayInTarkov.Memory;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace StayInTarkov.Coop.Components.CoopGameComponents
+{
+ public sealed class SITGameGCComponent : MonoBehaviour
+ {
+ private DateTime LastTimeRun { get; set; } = DateTime.MinValue;
+ private BepInEx.Logging.ManualLogSource Logger { get; set; }
+
+ private int LastNumberOfPlayers { get; set; }
+
+ private int NumberOfAlivePlayers => Singleton.Instance.AllAlivePlayersList.Count;
+
+ #region Unity methods
+
+ void Awake()
+ {
+ Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(SITGameGCComponent));
+ Logger.LogDebug($"{nameof(SITGameGCComponent)}:{nameof(Awake)}");
+ }
+
+ void Start()
+ {
+ Logger.LogDebug($"{nameof(SITGameGCComponent)}:{nameof(Start)}");
+ GarbageCollect();
+ }
+
+ void Update()
+ {
+ if (!PluginConfigSettings.Instance.AdvancedSettings.SETTING_EnableSITGC)
+ return;
+
+ if ((DateTime.Now - LastTimeRun).TotalSeconds > PluginConfigSettings.Instance.AdvancedSettings.SETTING_SITGCMemoryCheckTime)
+ {
+ LastTimeRun = DateTime.Now;
+ GarbageCollectSIT();
+ }
+
+ if (NumberOfAlivePlayers != LastNumberOfPlayers)
+ {
+ LastNumberOfPlayers = NumberOfAlivePlayers;
+ BSGMemoryGC.Collect(force: false);
+ }
+ }
+
+ #endregion
+
+ private void GarbageCollect()
+ {
+ Logger.LogDebug($"{nameof(GarbageCollect)}");
+ BSGMemoryGC.RunHeapPreAllocation();
+ BSGMemoryGC.Collect(force: true);
+ BSGMemoryGC.EmptyWorkingSet();
+ BSGMemoryGC.GCEnabled = true;
+ Resources.UnloadUnusedAssets();
+ }
+
+ ///
+ /// Runs the Garbage Collection
+ ///
+ ///
+ private void GarbageCollectSIT()
+ {
+ var nearestEnemyDist = float.MaxValue;
+ foreach (var p in Singleton.Instance.AllAlivePlayersList)
+ {
+ if (p.ProfileId == Singleton.Instance.MainPlayer.ProfileId)
+ continue;
+
+ var dist = Vector3.Distance(p.Transform.position, Singleton.Instance.MainPlayer.Transform.position);
+ if (dist < nearestEnemyDist)
+ nearestEnemyDist = dist;
+ }
+
+ if (nearestEnemyDist > 10)
+ {
+ var mem = MemoryInfo.GetCurrentStatus();
+ if (mem == null)
+ {
+ return;
+ }
+
+ var memPercentInUse = mem.dwMemoryLoad;
+ Logger.LogDebug($"Total memory used: {mem.dwMemoryLoad}%");
+ if (memPercentInUse > PluginConfigSettings.Instance.AdvancedSettings.SETTING_SITGCMemoryThreshold)
+ GarbageCollect();
+
+ }
+ }
+
+ }
+}
diff --git a/Source/Coop/Components/CoopGameComponents/SITGameGUIComponent.cs b/Source/Coop/Components/CoopGameComponents/SITGameGUIComponent.cs
index 470c8bb99..58f28a3ea 100644
--- a/Source/Coop/Components/CoopGameComponents/SITGameGUIComponent.cs
+++ b/Source/Coop/Components/CoopGameComponents/SITGameGUIComponent.cs
@@ -23,7 +23,6 @@ public class SITGameGUIComponent : MonoBehaviour
GUIStyle middleLargeLabelStyle;
GUIStyle normalLabelStyle;
- private ISITGame LocalGameInstance { get; } = Singleton.Instance;
private SITGameComponent CoopGameComponent { get { return SITGameComponent.GetCoopGameComponent(); } }
private ConcurrentDictionary Players => CoopGameComponent?.Players;
@@ -47,8 +46,6 @@ public class SITGameGUIComponent : MonoBehaviour
void Awake()
{
- // ----------------------------------------------------
- // Create a BepInEx Logger for CoopGameComponent
Logger = BepInEx.Logging.Logger.CreateLogSource($"{nameof(SITGameGUIComponent)}");
Logger.LogDebug($"{nameof(SITGameGUIComponent)}:Awake");
@@ -57,8 +54,6 @@ void Awake()
void OnGUI()
{
-
-
if (normalLabelStyle == null)
{
normalLabelStyle = new GUIStyle(GUI.skin.label);
@@ -95,15 +90,6 @@ void OnGUI()
var numberOfPlayersDead = Users.Count(x => !x.HealthController.IsAlive);
- if (LocalGameInstance == null)
- return;
-
- var coopGame = LocalGameInstance as CoopSITGame;
- if (coopGame == null)
- return;
-
- //rect = DrawSITStats(rect, numberOfPlayersDead, coopGame);
-
var quitState = CoopGameComponent.GetQuitState();
switch (quitState)
{
@@ -130,7 +116,6 @@ void OnGUI()
break;
}
- //OnGUI_DrawPlayerList(rect);
OnGUI_DrawPlayerFriendlyTags(rect);
OnGUI_DrawPlayerEnemyTags(rect);
@@ -160,7 +145,6 @@ private Rect DrawPing(Rect rect)
_ => "",
};
var text = new GUIContent($"{protocol}{(SITMatchmaking.IsClient ? "client" : "host")} ping:{gameclient.Ping} up:{gameclient.UploadSpeedKbps:0.00} down:{gameclient.DownloadSpeedKbps:0.00} loss:{gameclient.PacketLoss:0.00}% {(AkiBackendCommunication.Instance.HighPingMode ? "hpm" : "")}");
- // var newX = GUI.skin.label.CalcSize(text);
GUI.Label(rect, text);
GUI.contentColor = Color.white;
@@ -207,7 +191,7 @@ private void OnGUI_DrawPlayerFriendlyTags(Rect rect)
return;
}
- if (FPSCamera.Instance == null)
+ if (CameraClass.Instance == null)
return;
if (Players == null)
@@ -222,8 +206,8 @@ private void OnGUI_DrawPlayerFriendlyTags(Rect rect)
if (!Singleton.Instantiated)
return;
- if (FPSCamera.Instance.SSAA != null && FPSCamera.Instance.SSAA.isActiveAndEnabled)
- screenScale = FPSCamera.Instance.SSAA.GetOutputWidth() / (float)FPSCamera.Instance.SSAA.GetInputWidth();
+ if (CameraClass.Instance.SSAA != null && CameraClass.Instance.SSAA.isActiveAndEnabled)
+ screenScale = CameraClass.Instance.SSAA.GetOutputWidth() / (float)CameraClass.Instance.SSAA.GetInputWidth();
var ownPlayer = Singleton.Instance.MainPlayer;
if (ownPlayer == null)
@@ -306,7 +290,7 @@ private void OnGUI_DrawPlayerEnemyTags(Rect rect)
return;
}
- if (FPSCamera.Instance == null)
+ if (CameraClass.Instance == null)
return;
if (Players == null)
@@ -322,8 +306,8 @@ private void OnGUI_DrawPlayerEnemyTags(Rect rect)
return;
- if (FPSCamera.Instance.SSAA != null && FPSCamera.Instance.SSAA.isActiveAndEnabled)
- screenScale = FPSCamera.Instance.SSAA.GetOutputWidth() / (float)FPSCamera.Instance.SSAA.GetInputWidth();
+ if (CameraClass.Instance.SSAA != null && CameraClass.Instance.SSAA.isActiveAndEnabled)
+ screenScale = CameraClass.Instance.SSAA.GetOutputWidth() / (float)CameraClass.Instance.SSAA.GetInputWidth();
var ownPlayer = Singleton.Instance.MainPlayer;
if (ownPlayer == null)
@@ -360,36 +344,5 @@ private void OnGUI_DrawPlayerEnemyTags(Rect rect)
}
}
- private void OnGUI_DrawPlayerList(Rect rect)
- {
- if (!PluginConfigSettings.Instance.CoopSettings.SETTING_DEBUGShowPlayerList)
- return;
-
- rect.y += 15;
-
- if (Singleton.Instance != null)
- {
- var players = Singleton.Instance.RegisteredPlayers.ToList();
- players.AddRange(Players.Values);
- players = players.Distinct(x => x.ProfileId).ToList();
-
- rect.y += 15;
- GUI.Label(rect, $"{StayInTarkovPlugin.LanguageDictionary["PLAYERS_COUNT"]} [{players.Count}]:");
- //
- rect.y += 15;
- foreach (var p in players)
- {
- string aiPlayerText = (string)(p.IsAI ? StayInTarkovPlugin.LanguageDictionary["AI-SESSION"] : StayInTarkovPlugin.LanguageDictionary["PLAYER-SESSION"]);
- string aliveDeadText = (string)(p.HealthController.IsAlive ? StayInTarkovPlugin.LanguageDictionary["ALIVE"] : StayInTarkovPlugin.LanguageDictionary["DEAD"]);
-
- GUI.Label(rect, $"{p.Profile.Nickname}:{aiPlayerText}:{aliveDeadText}");
- //GUI.Label(rect, $"{p.Profile.Nickname}:{(p.IsAI ? "AI" : "Player")}:{(p.HealthController.IsAlive ? "Alive" : "Dead")}");
- rect.y += 15;
- }
-
- players.Clear();
- players = null;
- }
- }
}
}
\ No newline at end of file
diff --git a/Source/Coop/Components/CoopGameComponents/SITGameTimeAndWeatherSyncComponent.cs b/Source/Coop/Components/CoopGameComponents/SITGameTimeAndWeatherSyncComponent.cs
new file mode 100644
index 000000000..25ff730c7
--- /dev/null
+++ b/Source/Coop/Components/CoopGameComponents/SITGameTimeAndWeatherSyncComponent.cs
@@ -0,0 +1,71 @@
+using Comfort.Common;
+using EFT;
+using EFT.Weather;
+using StayInTarkov.Coop.NetworkPacket.Raid;
+using StayInTarkov.Coop.SITGameModes;
+using System;
+using UnityEngine.Networking;
+
+namespace StayInTarkov.Coop.Components.CoopGameComponents
+{
+ public sealed class SITGameTimeAndWeatherSyncComponent : NetworkBehaviour
+ {
+ private DateTime LastTimeSent = DateTime.MinValue;
+
+ private BepInEx.Logging.ManualLogSource Logger { get; set; }
+
+ void Awake()
+ {
+ Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(SITGameTimeAndWeatherSyncComponent));
+ Logger.LogDebug($"{nameof(SITGameTimeAndWeatherSyncComponent)}:{nameof(Awake)}");
+ }
+
+ void Start()
+ {
+ Logger.LogDebug($"{nameof(SITGameTimeAndWeatherSyncComponent)}:{nameof(Start)}");
+ }
+
+
+ void Update()
+ {
+ if ((DateTime.Now - LastTimeSent).Seconds > 15)
+ {
+ LastTimeSent = DateTime.Now;
+
+ TimeAndWeatherPacket packet = new();
+
+ var sitGame = Singleton.Instance;
+
+ if (sitGame.GameDateTime != null)
+ packet.GameDateTime = sitGame.GameDateTime.Calculate().Ticks;
+
+ var weatherController = WeatherController.Instance;
+ if (weatherController != null)
+ {
+ if (weatherController.CloudsController != null)
+ packet.CloudDensity = weatherController.CloudsController.Density;
+
+ var weatherCurve = weatherController.WeatherCurve;
+ if (weatherCurve != null)
+ {
+ packet.Fog = weatherCurve.Fog;
+ packet.LightningThunderProbability = weatherCurve.LightningThunderProbability;
+ packet.Rain = weatherCurve.Rain;
+ packet.Temperature = weatherCurve.Temperature;
+ packet.Wind = weatherCurve.Wind;
+ packet.TopWind = weatherCurve.TopWind;
+ }
+
+ Networking.GameClient.SendData(packet.Serialize());
+ }
+
+ if (sitGame.GameTimer.StartDateTime.HasValue && sitGame.GameTimer.SessionTime.HasValue)
+ {
+ RaidTimerPacket raidTimerPacket = new();
+ raidTimerPacket.SessionTime = (sitGame.GameTimer.SessionTime - sitGame.GameTimer.PastTime).Value.Ticks;
+ Networking.GameClient.SendData(raidTimerPacket.Serialize());
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Coop/Components/IPlayerPacketHandler.cs b/Source/Coop/Components/IPlayerPacketHandler.cs
deleted file mode 100644
index 1f7b4d6e8..000000000
--- a/Source/Coop/Components/IPlayerPacketHandler.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-
-namespace StayInTarkov.Coop.Components
-{
- internal interface IPlayerPacketHandler
- {
- public void ProcessPacket(Dictionary packet);
- public void ProcessPacket(byte[] packet);
- }
-}
diff --git a/Source/Coop/Components/SITMatchmakerGUIComponent.cs b/Source/Coop/Components/SITMatchmakerGUIComponent.cs
index c38220dfb..e52c4a09f 100644
--- a/Source/Coop/Components/SITMatchmakerGUIComponent.cs
+++ b/Source/Coop/Components/SITMatchmakerGUIComponent.cs
@@ -3,9 +3,11 @@
using EFT.Bots;
using EFT.UI;
using EFT.UI.Matchmaker;
+using HarmonyLib.Tools;
using Newtonsoft.Json.Linq;
using StayInTarkov.Configuration;
using StayInTarkov.Coop.Matchmaker;
+using StayInTarkov.Coop.SITGameModes;
using StayInTarkov.Networking;
using StayInTarkov.UI;
using System;
@@ -13,14 +15,20 @@
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
+using Systems.Effects;
using UnityEngine;
+using UnityEngine.Yoga;
using Color = UnityEngine.Color;
using FontStyle = UnityEngine.FontStyle;
namespace StayInTarkov.Coop.Components
{
+ ///
+ /// This is the "Server Browser" and replacement of the "last screen"
+ ///
internal class SITMatchmakerGUIComponent : MonoBehaviour
{
private UnityEngine.Rect windowRect = new(20, 20, 120, 50);
@@ -40,7 +48,7 @@ internal class SITMatchmakerGUIComponent : MonoBehaviour
private Task GetMatchesTask { get; set; }
- private Dictionary[] m_Matches { get; set; }
+ private JArray m_Matches { get; set; }
private CancellationTokenSource m_cancellationTokenSource;
@@ -53,7 +61,9 @@ internal class SITMatchmakerGUIComponent : MonoBehaviour
private int botAmountInput = 0;
private int botDifficultyInput = 0;
- private int protocolInput = 0;
+ private int protocolInput = 1;
+ private bool p2pModeSelected => protocolInput == 1;
+
private string pendingServerId = "";
private int p2pModeOptionInput;
@@ -91,6 +101,10 @@ internal class SITMatchmakerGUIComponent : MonoBehaviour
public Profile Profile { get; internal set; }
public Rect hostGameWindowInnerRect { get; private set; }
+ public bool IsMatchmakingAvailable { get; private set; } = false;
+
+ public bool IsHeadlessServerAvailable { get; private set; } = false;
+
#region TextMeshPro Game Objects
private GameObject GOIPv4_Text { get; set; }
@@ -118,20 +132,7 @@ void Start()
Logger.LogInfo("Start");
TMPManager = new PaulovTMPManager();
- //DrawIPAddresses();
- //DrawSITButtons();
- //// Get Canvas
- //Canvas = GameObject.FindObjectOfType