Skip to content

Commit

Permalink
Airdrops fix (#324)
Browse files Browse the repository at this point in the history
Recreating #316

> Refactor Airdrop packets so they will work without old Json dependant
system
>Add positional sync of Airdrop box whilst its falling

Other things I've done on top:
- Updated a couple of patches from SPT and some other tweaks to
AirdropManager.
- Changed ListOfInteractiveObjects from a list to a ConcurrentDictionary

From the testing I've done everything worked properly, except airdrops
called in by flare it seems to not sync weapons into players inventories
correctly and I'm not entirely sure why.
  • Loading branch information
mihaicm93 authored Jun 3, 2024
2 parents 2699ee8 + 87842d8 commit 85b4733
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 112 deletions.
32 changes: 31 additions & 1 deletion Source/AkiSupport/Airdrops/AirdropBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// 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
/// </summary>
public class AirdropBox : MonoBehaviour
{
Expand All @@ -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;
Expand All @@ -44,12 +48,16 @@ private BetterSource AudioSource
}
}

public Vector3 ClientSyncPosition { get; internal set; }

public static async Task<AirdropBox> Init(float crateFallSpeed)
{
var instance = (await LoadCrate()).AddComponent<AirdropBox>();
instance.soundsDictionary = await LoadSounds();

instance.Container = instance.GetComponentInChildren<LootableContainer>();
instance.Container.Id = $"AirdropBox_{AirdropContainerCount}";
AirdropContainerCount++;

instance.boxSync = instance.GetComponent<AirdropSynchronizableObject>();
instance.boxLogic = new AirdropLogicClass();
Expand Down Expand Up @@ -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;
Expand All @@ -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>();
navMeshObstacle.size = boxSync.CollisionCollider.bounds.size;
navMeshObstacle.carving = true;
}

private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo)
Expand Down
7 changes: 4 additions & 3 deletions Source/AkiSupport/Airdrops/Patches/AirdropFlarePatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GameWorld>.Instance;
var points = LocationScene.GetAll<AirdropPoint>().Any();

if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id))
{
gameWorld.gameObject.AddComponent<SITAirdropsManager>().isFlareDrop = true;
gameWorld.gameObject.AddComponent<SITAirdropsManager>().IsFlareDrop = true;
}
}
}
Expand Down
112 changes: 57 additions & 55 deletions Source/Coop/Airdrops/SITAirdropsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@
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;

Expand All @@ -29,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<SITAirdropsManager>.Create(this);
}
Expand Down Expand Up @@ -68,7 +72,7 @@ public async void Start()

// The server will generate stuff ready for the packet

AirdropParameters = await AirdropUtil.InitAirdropParams(gameWorld, isFlareDrop);
AirdropParameters = await AirdropUtil.InitAirdropParams(gameWorld, IsFlareDrop);

if (!AirdropParameters.AirdropAvailable)
{
Expand Down Expand Up @@ -102,7 +106,6 @@ public async void Start()
await BuildLootContainer(AirdropParameters.Config);

StartCoroutine(SendParamsToClients());

}

public IEnumerator SendParamsToClients()
Expand All @@ -113,11 +116,11 @@ public IEnumerator SendParamsToClients()
yield return new WaitForSeconds(AirdropParameters.TimeToStart);

Logger.LogDebug("Sending Airdrop Params");
var packet = new Dictionary<string, object>();
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;
}
Expand Down Expand Up @@ -148,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<WorldInteractiveObject> oldInteractiveObjectList = new List<WorldInteractiveObject>(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);
}
}

Expand Down Expand Up @@ -198,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;
Expand All @@ -211,8 +223,6 @@ public async void FixedUpdate()
}
}

float distanceTravelled = 0;

private void StartPlane()
{
airdropPlane.gameObject.SetActive(true);
Expand All @@ -228,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 async Task BuildLootContainer(AirdropConfigModel config)
{
if (SITMatchmaking.IsClient)
if (!SITMatchmaking.IsServer)
return;

var lootData = await 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<string, object>();
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<WorldInteractiveObject> oldInteractiveObjectList = new List<WorldInteractiveObject>(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;
}
Expand All @@ -282,5 +278,11 @@ private void SetDistanceToDrop()
new Vector3(AirdropParameters.RandomAirdropPoint.x, AirdropParameters.DropHeight, AirdropParameters.RandomAirdropPoint.z),
airdropPlane.transform.position);
}

protected void OnDestroy()
{
if (Singleton<SITAirdropsManager>.Instantiated)
Singleton<SITAirdropsManager>.Release(Singleton<SITAirdropsManager>.Instance);
}
}
}
7 changes: 5 additions & 2 deletions Source/Coop/Components/CoopGameComponents/SITGameComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace StayInTarkov.Coop.Components.CoopGameComponents
public class SITGameComponent : MonoBehaviour
{
#region Fields/Properties
public WorldInteractiveObject[] ListOfInteractiveObjects { get; set; }
public ConcurrentDictionary<string, WorldInteractiveObject> WorldnteractiveObjects { get; set; } = [];
private AkiBackendCommunication RequestingObj { get; set; }
public SITConfig SITConfig { get; private set; } = new SITConfig();
public string ServerId { get; set; } = null;
Expand Down Expand Up @@ -379,7 +379,10 @@ 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<WorldInteractiveObject>();
foreach (WorldInteractiveObject worldInteractiveObject in FindObjectsOfType<WorldInteractiveObject>())
{
this.WorldnteractiveObjects.TryAdd(worldInteractiveObject.Id, worldInteractiveObject);
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,11 @@ public override bool ToggleLauncher()

public override void CreateFlareShot(BulletClass flareItem, Vector3 shotPosition, Vector3 forward)
{
Logger.LogDebug(nameof(CreateFlareShot));
var createFlareShotPacket = new CreateFlareShotPacket(_player.ProfileId, shotPosition, forward, flareItem.TemplateId);
GameClient.SendData(createFlareShotPacket.Serialize());

base.CreateFlareShot(flareItem, shotPosition, forward);
}

}
}
Loading

0 comments on commit 85b4733

Please sign in to comment.