C#:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ConVar;
using Newtonsoft.Json;
using UnityEngine;
using System.Linq;
using Debug = UnityEngine.Debug;
namespace Oxide.Plugins
{
[Info("Heli Network Fix", "ViolationHandler", "0.0.1")]
[Description("Fixes networking issues while speeding in helicopters.")]
public class HeliNetworkFix : RustPlugin
{
private HashSet<NetworkFix> componentList = new HashSet<NetworkFix>();
private Configuration config;
#region Configuration
private class Configuration
{
[JsonProperty("The minimum speed required for this to take effect. (Increasing this will make it so the helicopter needs to be faster to trigger this effect)")]
public double speedMinimum = 20;
[JsonProperty("Distance forward to render.")]
public float renderDistance = 3.75f;
[JsonProperty("The minimum amount of time before attempting to load the area in front of the player.")]
public double minimumTimeSince = 1;
[JsonProperty("The value required to load the area in front of the player. Determined by speed.")]
public double minimumSpeedMultipliedValue = 37.5;
private string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() =>
JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
protected override void LoadDefaultConfig() => config = new Configuration();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
if (config == null)
{
throw new JsonException();
}
if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
{
Puts("Configuration appears to be outdated; updating and saving.");
SaveConfig();
}
}
catch
{
Puts($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
Puts($"Configuration changes saved to {Name}.json");
Config.WriteObject(config, true);
}
#endregion Configuration
private void Init()
{
LoadConfig();
}
private void Unload()
{
foreach (NetworkFix fix in componentList)
{
if (fix == null) continue; // if its somehow null
fix.onUnload = true;
fix.OnDestroy();
}
componentList = null;
}
#region Hooks
private void OnEntityDismounted(BaseMountable entity, BasePlayer player)
{
if (entity == null || player == null) return;
BaseVehicle vehicle = entity.GetParentEntity() as BaseVehicle;
if (vehicle == null || vehicle is not PlayerHelicopter) return;
NextTick(() => {RemoveComponent(vehicle, player, true);});
}
private void OnEntityMounted(BaseMountable entity, BasePlayer player)
{
if (entity == null || player == null) return;
BaseVehicle vehicle = entity.GetParentEntity() as BaseVehicle;
if (vehicle == null || vehicle is not PlayerHelicopter) return;
AddComponent(vehicle, player);
}
#endregion Hooks
#region Helper Methods
private bool AddComponent(BaseVehicle vehicle, BasePlayer player)
{
NetworkFix fix = vehicle.GetComponent<NetworkFix>();
if (fix != null)
{
fix.allPassengers.Add(player);
return false;
}
fix = vehicle.gameObject.AddComponent<NetworkFix>();
fix.vehicle = vehicle;
fix.speedMinimum = config.speedMinimum;
fix.renderDistance = config.renderDistance;
fix.minimumTimeSince = config.minimumTimeSince;
fix.minimumSpeedMultipliedValue = config.minimumSpeedMultipliedValue;
fix._instance = this;
fix.allPassengers.Add(player);
componentList.Add(fix);
return true;
}
private bool RemoveComponent(BaseVehicle vehicle, BasePlayer player, bool hook = false)
{
if (vehicle == null)
{
ResetSecondaryNetworkGroup(player.net);
return false;
}
NetworkFix fix = vehicle.GetComponent<NetworkFix>();
if (fix == null) return false;
if(hook)
{
if (vehicle.AnyMounted())
{
if(!IsPlayerMountedInVehicle(player, vehicle)) ResetSecondaryNetworkGroup(player.net);
return false;
}
componentList.Remove(fix);
UnityEngine.Object.Destroy(fix);
return true;
}
componentList.Remove(fix);
UnityEngine.Object.Destroy(fix);
return true;
}
public bool IsPlayerMountedInVehicle(BasePlayer player, BaseVehicle vehicle)
{
if (vehicle.mountPoints?.Count <= 0) return false;
foreach (BaseVehicle.MountPointInfo mountPoint in vehicle.mountPoints)
{
if (player == mountPoint.mountable?.GetMounted()) return true;
}
return false;
}
public bool ResetSecondaryNetworkGroup(Network.Networkable net)
{
if (net == null) return false;
net.SwitchSecondaryGroup(null);
net.OnSubscriptionChange();
return true;
}
#endregion Helper Methods
#region Custom Class
public class NetworkFix : FacepunchBehaviour
{
public BasePlayer player;
private Network.Visibility.Group group;
public BaseVehicle vehicle;
public HeliNetworkFix _instance;
public HashSet<BasePlayer> allPassengers = new HashSet<BasePlayer>();
private Vector3 projectedPosition = Vector3.zero;
public bool onUnload = false;
private float lastCalled;
public float SinceCalled;
public double minimumTimeSince = 1;
public double minimumSpeedMultipliedValue = 37.5;
public float renderDistance = 4;
public float speed;
public double speedMinimum;
public void Awake()
{
lastCalled = UnityEngine.Time.realtimeSinceStartup;
}
public void FixedUpdate()
{
if (vehicle == null || !vehicle.IsOn()) return;
SinceCalled = UnityEngine.Time.realtimeSinceStartup - lastCalled;
speed = Vector3.Dot(vehicle.GetLocalVelocity(), vehicle.transform.forward);
if (SinceCalled > 5 && speed < speedMinimum)
{
foreach (BaseVehicle.MountPointInfo mountPoint in vehicle.mountPoints)
{
player = mountPoint.mountable.GetMounted();
if ((object)player == null) continue;
ResetSecondaryNetworkGroup(player.net);
}
foreach (BaseEntity child in vehicle.children)
{
player = child as BasePlayer;
if ((object)player == null) continue;
ResetSecondaryNetworkGroup(player.net);
}
SinceCalled = 0;
return;
}
if (speed < speedMinimum || SinceCalled < minimumTimeSince || speed * SinceCalled < minimumSpeedMultipliedValue) return;
Vector3 position = Vector3.zero;
foreach (BaseVehicle.MountPointInfo mountPoint in vehicle.mountPoints)
{
player = mountPoint.mountable.GetMounted();
if ((object)player == null) continue;
if(position == Vector3.zero) position = player.transform.position;
if(projectedPosition == Vector3.zero) projectedPosition = position + player.eyes.HeadForward() * (speed * renderDistance);
projectedPosition.y = position.y-5;
if (group == null) group = player.net.sv.visibility.GetGroup(projectedPosition);
if (group.networkables.Contains(player.net)) continue;
player.net.SwitchSecondaryGroup(group);
player.net.OnSubscriptionChange();
}
foreach (BaseEntity child in vehicle.children)
{
player = child as BasePlayer;
if ((object)player == null) continue;
allPassengers.Add(player);
if (projectedPosition == Vector3.zero || group.networkables.Contains(player.net)) continue;
player.net.SwitchSecondaryGroup(group);
player.net.OnSubscriptionChange();
}
projectedPosition = Vector3.zero;
group = null;
lastCalled = UnityEngine.Time.realtimeSinceStartup;
}
public bool ResetSecondaryNetworkGroup(Network.Networkable net)
{
if (net == null) return false;
net.SwitchSecondaryGroup(null);
net.OnSubscriptionChange();
return true;
}
public void OnDestroy()
{
vehicle = null;
group = null;
if(allPassengers != null)
{
foreach (BasePlayer foundPlayer in allPassengers)
{
ResetSecondaryNetworkGroup(foundPlayer?.net);
}
}
allPassengers = null;
player = null;
if(!onUnload) _instance.componentList.Remove(this);
Destroy(this);
}
}
#endregion Custom Class
}
}