first commit
This commit is contained in:
41
NitroxClient/Helpers/GameObjectReferenceHolderExtensions.cs
Normal file
41
NitroxClient/Helpers/GameObjectReferenceHolderExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using NitroxClient.MonoBehaviours;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.Helpers;
|
||||
|
||||
public static class GameObjectReferenceHolderExtensions
|
||||
{
|
||||
public static ReferenceHolder AddReference<T>(this Component component, T reference)
|
||||
{
|
||||
return AddReference(component.gameObject, reference);
|
||||
}
|
||||
|
||||
public static ReferenceHolder AddReference<T>(this GameObject gameObject, T reference)
|
||||
{
|
||||
ReferenceHolder referenceHolder = gameObject.EnsureComponent<ReferenceHolder>();
|
||||
referenceHolder.AddReference(reference);
|
||||
return referenceHolder;
|
||||
}
|
||||
|
||||
public static bool TryGetReference<T>(this GameObject gameObject, out T reference)
|
||||
{
|
||||
if (gameObject.TryGetComponent(out ReferenceHolder holder))
|
||||
{
|
||||
return holder.TryGetReference(out reference);
|
||||
}
|
||||
|
||||
reference = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetReference<T>(this Component component, out T reference)
|
||||
{
|
||||
if (component.TryGetComponent(out ReferenceHolder holder))
|
||||
{
|
||||
return holder.TryGetReference(out reference);
|
||||
}
|
||||
|
||||
reference = default;
|
||||
return false;
|
||||
}
|
||||
}
|
116
NitroxClient/Helpers/NitroxEntityExtensions.cs
Normal file
116
NitroxClient/Helpers/NitroxEntityExtensions.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NitroxClient.MonoBehaviours;
|
||||
using NitroxClient.Unity.Helper;
|
||||
using NitroxModel.DataStructures;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.Helpers;
|
||||
|
||||
public static class NitroxEntityExtensions
|
||||
{
|
||||
public static bool TryGetNitroxEntity(this Component component, out NitroxEntity nitroxEntity)
|
||||
{
|
||||
nitroxEntity = null;
|
||||
return component && component.TryGetComponent(out nitroxEntity);
|
||||
}
|
||||
|
||||
public static bool TryGetNitroxEntity(this GameObject gameObject, out NitroxEntity nitroxEntity)
|
||||
{
|
||||
nitroxEntity = null;
|
||||
return gameObject && gameObject.TryGetComponent(out nitroxEntity);
|
||||
}
|
||||
|
||||
public static bool TryGetNitroxId(this GameObject gameObject, out NitroxId nitroxId)
|
||||
{
|
||||
if (!gameObject || !gameObject.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
nitroxId = nitroxEntity.Id;
|
||||
return nitroxId != null;
|
||||
}
|
||||
|
||||
public static bool TryGetNitroxId(this Component component, out NitroxId nitroxId)
|
||||
{
|
||||
if (!component || !component.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
nitroxId = nitroxEntity.Id;
|
||||
return nitroxId != null;
|
||||
}
|
||||
|
||||
public static bool TryGetIdOrWarn(
|
||||
this GameObject gameObject,
|
||||
out NitroxId nitroxId,
|
||||
[CallerMemberName] string methodName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
// Since a destroyed but non-null object is normal behavior for Unity, we don't want to warn about it.
|
||||
if (!gameObject)
|
||||
{
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
if (!gameObject.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
Log.Warn($"[{filePath[(filePath.LastIndexOf("\\", StringComparison.Ordinal) + 1)..^2] + methodName}():L{lineNumber}] Couldn't find an id on {gameObject.GetFullHierarchyPath()}");
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
nitroxId = nitroxEntity.Id;
|
||||
return nitroxId != null;
|
||||
}
|
||||
|
||||
public static bool TryGetIdOrWarn(
|
||||
this Component component,
|
||||
out NitroxId nitroxId,
|
||||
[CallerMemberName] string methodName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
// Since a destroyed but non-null object is normal behavior for Unity, we don't want to warn about it.
|
||||
if (!component)
|
||||
{
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
if (!component.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
Log.WarnOnce($"[{filePath[(filePath.LastIndexOf("\\", StringComparison.Ordinal) + 1)..^2] + methodName}():L{lineNumber}] Couldn't find an id on {component.GetFullHierarchyPath()}");
|
||||
nitroxId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
nitroxId = nitroxEntity.Id;
|
||||
return nitroxId != null;
|
||||
}
|
||||
|
||||
public static Optional<NitroxId> GetId(this GameObject gameObject)
|
||||
{
|
||||
if (gameObject && gameObject.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
return Optional.Of(nitroxEntity.Id);
|
||||
}
|
||||
|
||||
return Optional.Empty;
|
||||
}
|
||||
|
||||
public static Optional<NitroxId> GetId(this Component component)
|
||||
{
|
||||
if (component && component.TryGetComponent(out NitroxEntity nitroxEntity))
|
||||
{
|
||||
return Optional.Of(nitroxEntity.Id);
|
||||
}
|
||||
|
||||
return Optional.Empty;
|
||||
}
|
||||
}
|
116
NitroxClient/Helpers/NitroxProtobufSerializer.cs
Normal file
116
NitroxClient/Helpers/NitroxProtobufSerializer.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using ProtoBuf;
|
||||
using ProtoBuf.Meta;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.Helpers
|
||||
{
|
||||
public class NitroxProtobufSerializer
|
||||
{
|
||||
public readonly RuntimeTypeModel model;
|
||||
public readonly Dictionary<Type, int> NitroxTypes = new Dictionary<Type, int>();
|
||||
|
||||
protected RuntimeTypeModel Model => model;
|
||||
|
||||
public NitroxProtobufSerializer(params string[] assemblies)
|
||||
{
|
||||
model = TypeModel.Create();
|
||||
|
||||
foreach (string assembly in assemblies)
|
||||
{
|
||||
RegisterAssemblyClasses(assembly);
|
||||
}
|
||||
|
||||
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
bool hasUweProtobuf = (type.GetCustomAttributes(typeof(ProtoContractAttribute), true).Length > 0);
|
||||
|
||||
if (hasUweProtobuf)
|
||||
{
|
||||
AddType(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddType(Type type)
|
||||
{
|
||||
// As of the latest protobuf update they will automatically register detected attributes.
|
||||
model.Add(type, true);
|
||||
ProtobufSerializerPrecompiled.knownTypes[type] = int.MaxValue; // UWE precompiled is going to pass everything to us
|
||||
NitroxTypes[type] = int.MaxValue;
|
||||
|
||||
if (type.IsSubclassOf(typeof(MonoBehaviour))) // Add Nitrox MonoBehaviours to the Component whitelist
|
||||
{
|
||||
ProtobufSerializer.componentWhitelist.Add(type.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(Stream stream, object o)
|
||||
{
|
||||
model.SerializeWithLengthPrefix(stream, o, o.GetType(), PrefixStyle.Base128, 0);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Stream stream)
|
||||
{
|
||||
T t = (T)Activator.CreateInstance(typeof(T));
|
||||
return (T)Deserialize(stream, t, typeof(T));
|
||||
}
|
||||
|
||||
public object Deserialize(Stream stream, object o, Type t)
|
||||
{
|
||||
return model.DeserializeWithLengthPrefix(stream, o, t, PrefixStyle.Base128, 0);
|
||||
}
|
||||
|
||||
private void RegisterAssemblyClasses(string assemblyName)
|
||||
{
|
||||
foreach (Type type in Assembly.Load(assemblyName).GetTypes())
|
||||
{
|
||||
bool hasUweProtobuf = (type.GetCustomAttributes(typeof(ProtoContractAttribute), true).Length > 0);
|
||||
|
||||
if (hasUweProtobuf)
|
||||
{
|
||||
AddType(type);
|
||||
}
|
||||
else if (HasNitroxProtoContract(type))
|
||||
{
|
||||
AddType(type);
|
||||
|
||||
ManuallyRegisterNitroxProtoMembers(type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasNitroxProtoContract(Type type)
|
||||
{
|
||||
foreach (object o in type.GetCustomAttributes(true))
|
||||
{
|
||||
if (o.GetType().ToString().Contains("ProtoContractAttribute"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ManuallyRegisterNitroxProtoMembers(MemberInfo[] info, Type type)
|
||||
{
|
||||
foreach (MemberInfo property in info)
|
||||
{
|
||||
foreach (object customAttribute in property.GetCustomAttributes(false))
|
||||
{
|
||||
Type attributeType = customAttribute.GetType();
|
||||
|
||||
if (attributeType.ToString().Contains("ProtoMemberAttribute"))
|
||||
{
|
||||
int tag = (int)attributeType.GetProperty("Tag", BindingFlags.Public | BindingFlags.Instance).GetValue(customAttribute, new object[] { });
|
||||
model[type].Add(tag, property.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
NitroxClient/Helpers/ThrottledPacketSender.cs
Normal file
84
NitroxClient/Helpers/ThrottledPacketSender.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NitroxClient.Communication;
|
||||
using NitroxClient.Communication.Abstract;
|
||||
using NitroxModel.Packets;
|
||||
|
||||
namespace NitroxClient.Helpers
|
||||
{
|
||||
public class ThrottledPacketSender
|
||||
{
|
||||
private readonly Dictionary<object, ThrottledPacket> throttledPackets = new();
|
||||
private readonly IPacketSender packetSender;
|
||||
|
||||
public ThrottledPacketSender(IPacketSender packetSender)
|
||||
{
|
||||
this.packetSender = packetSender;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
foreach (ThrottledPacket throttledPacket in throttledPackets.Values)
|
||||
{
|
||||
if (throttledPacket.WasSend || throttledPacket.SendTime > DateTime.UtcNow)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packetSender.Send(throttledPacket.Packet))
|
||||
{
|
||||
throttledPacket.WasSend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendThrottled<T>(T packet, Func<T, object> dedupeMethod, float throttleTime = 0.2f) where T : Packet
|
||||
{
|
||||
if (PacketSuppressor<T>.IsSuppressed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
object dedupeKey = dedupeMethod.Invoke(packet);
|
||||
|
||||
if (throttledPackets.TryGetValue(dedupeKey, out ThrottledPacket throttledPacket))
|
||||
{
|
||||
throttledPacket.ReplacePacket(packet, throttleTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
throttledPackets.Add(dedupeKey, new ThrottledPacket(packet, throttleTime));
|
||||
packetSender.Send(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemovePendingPackets(object dedupeKey)
|
||||
{
|
||||
return throttledPackets.Remove(dedupeKey);
|
||||
}
|
||||
|
||||
private class ThrottledPacket
|
||||
{
|
||||
public DateTime SendTime { get; private set; }
|
||||
public Packet Packet { get; private set; }
|
||||
public bool WasSend { get; set; }
|
||||
|
||||
public ThrottledPacket(Packet packet, float throttleTime)
|
||||
{
|
||||
SendTime = DateTime.UtcNow.AddSeconds(throttleTime);
|
||||
Packet = packet;
|
||||
}
|
||||
|
||||
public void ReplacePacket(Packet packet, float throttleTime)
|
||||
{
|
||||
Packet = packet;
|
||||
|
||||
if (WasSend)
|
||||
{
|
||||
SendTime = DateTime.UtcNow.AddSeconds(throttleTime);
|
||||
WasSend = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
NitroxClient/Helpers/UnityObjectExtensions.cs
Normal file
55
NitroxClient/Helpers/UnityObjectExtensions.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NitroxModel.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.Helpers;
|
||||
|
||||
public static class UnityObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves a type using <see cref="NitroxServiceLocator.LocateService{T}" />. If the result is not null it will cache and return the same type on future calls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Dependency Injection should be limited to UnityEngine object types as in other cases it should be injected as constructor parameter.
|
||||
/// This is the reason for having UnityEngine.Object as first parameter.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">Type to get and cache from <see cref="NitroxServiceLocator" /></typeparam>
|
||||
/// <returns>The requested type or null if not available.</returns>
|
||||
public static T Resolve<T>(this UnityEngine.Object _, bool prelifeTime = false) where T : class
|
||||
{
|
||||
return prelifeTime ? NitroxServiceLocator.Cache<T>.ValuePreLifetime : NitroxServiceLocator.Cache<T>.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a whole component by using reflection. Please note this takes considerable time and every use of this should be thoughtful.
|
||||
/// </summary>
|
||||
public static Component CopyComponent(this Component original, GameObject destination)
|
||||
{
|
||||
Type type = original.GetType();
|
||||
Component copy = destination.AddComponent(type);
|
||||
|
||||
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
foreach (FieldInfo field in fields)
|
||||
{
|
||||
field.SetValue(copy, field.GetValue(original));
|
||||
}
|
||||
|
||||
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
if (property.GetSetMethod(true) != null)
|
||||
{
|
||||
property.SetValue(copy, property.GetValue(original));
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static bool TryFind(string name, out GameObject gameObject)
|
||||
{
|
||||
gameObject = GameObject.Find(name);
|
||||
return gameObject;
|
||||
}
|
||||
}
|
24
NitroxClient/Helpers/VFXConstructingHelper.cs
Normal file
24
NitroxClient/Helpers/VFXConstructingHelper.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace NitroxClient.Helpers;
|
||||
|
||||
public static class VFXConstructingHelper
|
||||
{
|
||||
public static void EndGracefully(this VFXConstructing vfxConstructing)
|
||||
{
|
||||
// tell it we aren't done processing the OnSubComplete side-effects.
|
||||
vfxConstructing.isDone = false;
|
||||
|
||||
// When vehicles are spawned they intentionally have a delay to remove the animation to give it a cinematic effect.
|
||||
// setting to 0 is not enough, value must be negative.
|
||||
vfxConstructing.delay = -1;
|
||||
|
||||
// We only set the constructed amount and don't call any of the end methods. For some reason, if the vfx doesn't
|
||||
// end organically it can bug out the vehicle. Users won't notice a difference.
|
||||
vfxConstructing.constructed = 1;
|
||||
|
||||
// Height is only used to calculate splash effects (vehicle falling into the water). This will disable it.
|
||||
vfxConstructing.heightOffset = -999;
|
||||
|
||||
// Force an update to lock in these changes before anything stateful happens.
|
||||
vfxConstructing.Update();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user