Decompiled source of MashGamemodeLibrary v1.0.5
Mods/MashGamemodeLibrary.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.Json; using System.Threading.Tasks; using AudioImportLib; using BoneLib; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Interaction; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Audio; using Il2CppSLZ.Marrow.Combat; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Forklift.Model; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSLZ.VRMK; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppUltEvents; using LabFusion.Data; using LabFusion.Downloading; using LabFusion.Downloading.ModIO; using LabFusion.Entities; using LabFusion.Extensions; using LabFusion.Marrow; using LabFusion.Marrow.Extenders; using LabFusion.Marrow.Patching; using LabFusion.Marrow.Pool; using LabFusion.Menu.Data; using LabFusion.Network; using LabFusion.Network.Serialization; using LabFusion.Player; using LabFusion.Preferences; using LabFusion.Preferences.Client; using LabFusion.RPC; using LabFusion.SDK.Gamemodes; using LabFusion.SDK.Modules; using LabFusion.SDK.Points; using LabFusion.Safety; using LabFusion.Scene; using LabFusion.Senders; using LabFusion.UI.Popups; using LabFusion.Utilities; using MashGamemodeLibrary; using MashGamemodeLibrary.Audio.Containers; using MashGamemodeLibrary.Audio.Loaders; using MashGamemodeLibrary.Audio.Modifiers; using MashGamemodeLibrary.Audio.Music; using MashGamemodeLibrary.Audio.Players.Basic; using MashGamemodeLibrary.Audio.Players.Basic.Providers; using MashGamemodeLibrary.Audio.Players.Extensions; using MashGamemodeLibrary.Audio.Registry; using MashGamemodeLibrary.Config; using MashGamemodeLibrary.Config.Menu; using MashGamemodeLibrary.Config.Menu.Attributes; using MashGamemodeLibrary.Config.Menu.Element; using MashGamemodeLibrary.Context; using MashGamemodeLibrary.Context.Control; using MashGamemodeLibrary.Context.Helper; using MashGamemodeLibrary.Data.Random; using MashGamemodeLibrary.Entities; using MashGamemodeLibrary.Entities.Association; using MashGamemodeLibrary.Entities.Association.Impl; using MashGamemodeLibrary.Entities.Behaviour; using MashGamemodeLibrary.Entities.Behaviour.Cache; using MashGamemodeLibrary.Entities.Behaviour.Helpers; using MashGamemodeLibrary.Entities.ECS; using MashGamemodeLibrary.Entities.ECS.Attributes; using MashGamemodeLibrary.Entities.ECS.BaseComponents; using MashGamemodeLibrary.Entities.ECS.Declerations; using MashGamemodeLibrary.Entities.ECS.Instance; using MashGamemodeLibrary.Entities.ECS.Networking; using MashGamemodeLibrary.Entities.Extenders; using MashGamemodeLibrary.Entities.Interaction; using MashGamemodeLibrary.Entities.Interaction.Grabbing; using MashGamemodeLibrary.Entities.Queries; using MashGamemodeLibrary.Environment.State; using MashGamemodeLibrary.Execution; using MashGamemodeLibrary.Integrations; using MashGamemodeLibrary.Loadout; using MashGamemodeLibrary.Networking.Remote; using MashGamemodeLibrary.Networking.Variable.Encoder.Util; using MashGamemodeLibrary.Patches; using MashGamemodeLibrary.Phase; using MashGamemodeLibrary.Player; using MashGamemodeLibrary.Player.Actions; using MashGamemodeLibrary.Player.Data; using MashGamemodeLibrary.Player.Data.Components.Visibility.Parts; using MashGamemodeLibrary.Player.Data.Events.Callers; using MashGamemodeLibrary.Player.Data.Events.Data; using MashGamemodeLibrary.Player.Data.Extenders; using MashGamemodeLibrary.Player.Data.Extenders.Colliders.Caches; using MashGamemodeLibrary.Player.Data.Extenders.Colliders.Data; using MashGamemodeLibrary.Player.Data.Extenders.Visibility; using MashGamemodeLibrary.Player.Data.Extenders.Visibility.Parts; using MashGamemodeLibrary.Player.Data.Rules; using MashGamemodeLibrary.Player.Data.Rules.Networking; using MashGamemodeLibrary.Player.Data.Rules.Rules; using MashGamemodeLibrary.Player.Helpers; using MashGamemodeLibrary.Player.Loadout; using MashGamemodeLibrary.Player.Stats; using MashGamemodeLibrary.Player.Team; using MashGamemodeLibrary.Registry.Keyed; using MashGamemodeLibrary.Registry.Typed; using MashGamemodeLibrary.Util; using MashGamemodeLibrary.Util.Timer; using MashGamemodeLibrary.networking; using MashGamemodeLibrary.networking.Compatiblity; using MashGamemodeLibrary.networking.Control; using MashGamemodeLibrary.networking.Validation; using MashGamemodeLibrary.networking.Validation.Routes; using MashGamemodeLibrary.networking.Variable; using MashGamemodeLibrary.networking.Variable.Encoder; using MashGamemodeLibrary.networking.Variable.Encoder.Impl; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using MoreItemsInDevTools.Patches; using Spiderman; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "Mash's Gamemode Library", "1.0.0", "Mash", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonAdditionalDependencies(new string[] { "LabFusion" })] [assembly: MelonOptionalDependencies(new string[] { "Spiderman" })] [assembly: NetworkIdentifiable("MGL")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("MashGamemodeLibrary")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MashGamemodeLibrary")] [assembly: AssemblyTitle("MashGamemodeLibrary")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace MashGamemodeLibrary { public class Mod : MelonMod { public static readonly string ModDataDirectory = MelonEnvironment.UserDataDirectory + "/mashgamemodelibrary"; public override void OnInitializeMelon() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown if (MelonBase.FindMelon("LabFusion", "Lakatrazz") != null) { ModIntegrations.TryInitialize(); ModuleManager.RegisterModule<FusionModule>(); RegisterInternal<Mod>(); NetworkEventsExtender.Register(); Hooking.OnWarehouseReady += OnWarehouseReady; MultiplayerHooking.OnTargetLevelLoaded += new UpdateEvent(Cleanup); } } public override void OnUpdate() { PlayerActionManager.Update(); ConfigManager.Update(); SpawnHelper.Update(); } private void OnWarehouseReady() { AudioRegistry.RegisterAll(); } private static void Cleanup() { InternalGamemodeManager.Reset(); EcsManager.Reset(); PlayerDataManager.Clear(); PlayerGunManager.Reset(); GamemodeCompatibilityChecker.ClearRemoteHashes(); } private static void RegisterInternal<T>() { EcsManager.RegisterAll<T>(); RemoteEventMessageHandler.RegisterMod<T>(); AutoRegistry.Register<T>(); PlayerData.Register<T>(); } public static void Register<T>() { GamePhaseManager.Registry.RegisterAll<T>(); LogicTeamManager.Registry.RegisterAll<T>(); RegisterInternal<T>(); } } } namespace MashGamemodeLibrary.Util { [AttributeUsage(AttributeTargets.Class)] public class RequireStaticConstructor : Attribute { } public interface IGuaranteeStaticConstructor { } public static class AutoRegistry { private static bool HasField(Type type) { if (type.GetCustomAttribute<RequireStaticConstructor>() != null) { return true; } return (from fieldInfo in type.GetFields() select fieldInfo.FieldType).Any((Type fieldType) => typeof(IGuaranteeStaticConstructor).IsAssignableFrom(fieldType)); } internal static void Register(Assembly assembly) { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (HasField(type)) { RuntimeHelpers.RunClassConstructor(type.TypeHandle); } } } internal static void Register<T>() { Register(typeof(T).Assembly); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field)] public class SerializableField : Attribute { } internal class FieldSerializer { private readonly FieldInfo _field; public FieldSerializer(FieldInfo field) { _field = field; } public void SerializeField(INetSerializer serializer, object instance) { object value = _field.GetValue(instance); NetSerializerExtensions.SerializeValue(serializer, ref value); if (serializer.IsReader) { _field.SetValue(instance, value); } } } public abstract class AutoSerialized<TValue> : INetSerializable where TValue : AutoSerialized<TValue> { private static readonly List<FieldSerializer> Serializables; static AutoSerialized() { Serializables = new List<FieldSerializer>(); Type typeFromHandle = typeof(TValue); bool flag = typeFromHandle.GetCustomAttribute<SerializableField>() != null; FieldInfo[] fields = typeFromHandle.GetFields(); foreach (FieldInfo fieldInfo in fields) { if (flag || fieldInfo.GetCustomAttribute<SerializableField>() != null) { Serializables.Add(new FieldSerializer(fieldInfo)); } } } public void Serialize(INetSerializer serializer) { Serialize(serializer, (TValue)this); } public static void Serialize(INetSerializer serializer, TValue value) { foreach (FieldSerializer serializable in Serializables) { serializable.SerializeField(serializer, value); } } } public static class BonelabLayers { public const int Default = 0; public const int NoRaycast = 2; public const int Fixture = 7; public const int Player = 8; public const int NoCollide = 9; public const int Dynamic = 10; public const int EnemyColliders = 12; public const int Interactable = 15; public const int Deciverse = 17; public const int Socket = 18; public const int PlayerAndNpc = 21; public const int FeetOnly = 23; public const int Feet = 24; public const int EntityTrigger = 30; public const int BeingTrigger = 31; } public static class DictionaryExtender { public static TValue GetValueOrCreate<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, Func<TValue> factory) where TKey : notnull { if (dict.TryGetValue(key, out TValue value)) { return value; } return dict[key] = factory(); } public static TValue GetValueOrCreate<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key) where TKey : notnull where TValue : new() { if (dict.TryGetValue(key, out TValue value)) { return value; } return dict[key] = new TValue(); } public static void Clear<TKey, TValue>(this Dictionary<TKey, TValue> dict, Action<KeyValuePair<TKey, TValue>> onEach) where TKey : notnull { IEnumerableExtensions.ForEach<KeyValuePair<TKey, TValue>>((IEnumerable<KeyValuePair<TKey, TValue>>)dict, onEach); dict.Clear(); } public static void Clear<TKey, TValue>(this Dictionary<TKey, TValue> dict, Action<TKey, TValue> onEach) where TKey : notnull { Action<TKey, TValue> onEach2 = onEach; dict.Clear(delegate(KeyValuePair<TKey, TValue> kvp) { onEach2(kvp.Key, kvp.Value); }); } } public class DummySerializable : INetSerializable { public int? GetSize() { return 0; } public void Serialize(INetSerializer serializer) { } } public static class GameObjectMemoryChecker { private struct MemoryBasicInformation { public IntPtr BaseAddress; public IntPtr AllocationBase; public uint AllocationProtect; public IntPtr RegionSize; public uint State; public uint Protect; public uint Type; } private const uint PageNoaccess = 1u; private const uint PageGuard = 256u; private const uint MemFree = 65536u; [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualQuery(IntPtr lpAddress, out MemoryBasicInformation lpBuffer, IntPtr dwLength); public static bool IsPointerAccessible(IntPtr ptr) { if (ptr == IntPtr.Zero) { return false; } if (VirtualQuery(ptr, out var lpBuffer, new IntPtr(Marshal.SizeOf(typeof(MemoryBasicInformation)))) == IntPtr.Zero) { return false; } if ((lpBuffer.Protect & 1) == 0 && (lpBuffer.Protect & 0x100) == 0) { return lpBuffer.State != 65536; } return false; } } public static class GuidExtender { public static void Serialize(this ref Guid guid, INetSerializer serializer) { byte[] array = ArrayPool<byte>.Shared.Rent(16); if (serializer.IsReader) { serializer.SerializeValue(ref array); guid = new Guid(array); } else { guid.TryWriteBytes(array.AsSpan()); serializer.SerializeValue(ref array); } } } public static class ImageHelper { public static Texture2D? LoadEmbeddedImage<T>(string resourceName) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown using Stream stream = typeof(T).Assembly.GetManifestResourceStream(resourceName); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = resourceName, hideFlags = (HideFlags)32 }; if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array))) { return null; } val.Apply(); return val; } } internal static class InternalLogger { [Conditional("DEBUG")] internal static void Debug(string txt) { MelonLogger.Msg("[Mash's Gamemode Library - DEBUG] " + txt); } public static void Error(string error) { MelonLogger.Error("[Mash's Gamemode Library - ERROR] " + error); } public static void Warn(string warning) { MelonLogger.Warning("[Mash's Gamemode Library - WARNING] " + warning); } } public static class MathUtil { public static float InverseLerp(float from, float to, float value) { if (from > to) { float num = to; float num2 = from; from = num; to = num2; } return (value - from) / (to - from); } public static float Lerp(float from, float to, float value) { if (from > to) { float num = to; float num2 = from; from = num; to = num2; } return from + value * (to - from); } public static float Clamp(float value, float min, float max) { if (value < min) { return min; } if (value > max) { return max; } return value; } public static float Clamp01(this float value) { return Clamp(value, 0f, 1f); } } public static class StableHash { public static ulong Fnv1A64(string input) { ulong num = 14695981039346656037uL; foreach (char c in input) { num ^= c; num *= 1099511628211L; } return num; } public static ulong GetStableHash(this string input) { return Fnv1A64(input); } public static ulong GetStableHash(this Type type) { return Fnv1A64(type.AssemblyQualifiedName ?? type.FullName ?? type.Name); } public static ulong GetStableHash<T>(this T instance) where T : Enum { return Fnv1A64($"{typeof(T).Name}-{instance}"); } } } namespace MashGamemodeLibrary.Util.Timer { internal class TimeRemainingPacket : INetSerializable { public float TimeRemaining; public int? GetSize() { return 4; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref TimeRemaining); } } public static class CommonTimeMarkerEvents { private static readonly RemoteEvent<TimeRemainingPacket> TimeRemainingEvent = new RemoteEvent<TimeRemainingPacket>(OnTimeRemainingEvent, CommonNetworkRoutes.HostToAll); private static readonly string[] Ones = new string[10] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" }; private static readonly string[] Teens = new string[10] { "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" }; private static readonly string[] Tens = new string[7] { "", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty" }; private static string ToText(int number) { if (number < 10) { return Ones[number]; } if (number < 20) { return Teens[number - 10]; } int num = number / 10; int num2 = number % 10; string text = Tens[num]; if (num2 == 0) { return text; } return text + Ones[num2]; } public static TimeMarker TimeRemaining(float time) { return new TimeMarker(MarkerType.BeforeEnd, time, delegate { Executor.RunIfHost(delegate { TimeRemainingEvent.Call(new TimeRemainingPacket { TimeRemaining = time }); }); }); } private static void SendWarning(string text) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown Notifier.Send(new Notification { Title = NotificationText.op_Implicit(text), PopupLength = 3f, ShowPopup = true, SaveToMenu = false, Type = (NotificationType)1 }); } private static void OnTimeRemainingEvent(TimeRemainingPacket packet) { if (packet.TimeRemaining >= 60f) { SendWarning(ToText((int)MathF.Round(packet.TimeRemaining / 60f)) + " Minutes Left"); } else { SendWarning(ToText((int)MathF.Round(packet.TimeRemaining)) + " Seconds Left"); } } } public enum MarkerType { AfterStart, BeforeEnd, Interval } public class TimeMarker { public bool Hit; public Action<float> OnHit; public float Time; public MarkerType Type; public TimeMarker(MarkerType type, float time, Action<float> onHit) { Type = type; Time = time; OnHit = onHit; } } public class MarkableTimer { private bool _hitTimeout; private TimeMarker[] _markers; private float _timeout; private float _timer; public float Duration => _timeout; public event Action? OnReset; public event Action<float>? OnTimeout; public MarkableTimer(float timeout, params TimeMarker[] markers) { _timeout = timeout; _markers = markers; } private float GetActualTime(TimeMarker marker) { float time = marker.Time; return marker.Type switch { MarkerType.AfterStart => time, MarkerType.BeforeEnd => _timeout - time, _ => time, }; } private void HandleIntervalMarker(TimeMarker marker, float delta) { float num = _timer - delta; float time = marker.Time; float num2 = MathF.Floor(_timer / time); float num3 = MathF.Floor(num / time); if (MathF.Abs(num2 - num3) != 0f) { marker.OnHit(MathF.Floor(_timer)); } } private void HandleOffsetMarker(TimeMarker marker) { float actualTime = GetActualTime(marker); if (_timer < actualTime) { marker.Hit = false; } else if (!marker.Hit) { marker.Hit = true; if (!(actualTime >= _timeout) && !(actualTime < 0f)) { marker.OnHit(actualTime); } } } private void CheckMarker(float delta) { TimeMarker[] markers = _markers; foreach (TimeMarker timeMarker in markers) { MarkerType type = timeMarker.Type; if (type == MarkerType.AfterStart || type == MarkerType.BeforeEnd) { HandleOffsetMarker(timeMarker); } else { HandleIntervalMarker(timeMarker, delta); } } } public void Update(float delta) { if (!_hitTimeout) { _timer += delta; if (_timer > _timeout) { this.OnTimeout?.Invoke(_timeout); _hitTimeout = true; } else { CheckMarker(delta); } } } public void SetTimeout(float timeout) { _timeout = timeout; bool hitTimeout = _hitTimeout; _hitTimeout = _timer >= _timeout; if (!hitTimeout && _hitTimeout) { this.OnTimeout?.Invoke(_timeout); } } public bool HasReachedTimeout() { return _hitTimeout; } public float GetElapsedTime() { return _timer; } public void SetMarkers(params TimeMarker[] markers) { _markers = markers; } public void Reset() { if (_timer != 0f) { this.OnReset?.Invoke(); TimeMarker[] markers = _markers; for (int i = 0; i < markers.Length; i++) { markers[i].Hit = false; } } _timer = 0f; _hitTimeout = false; } } } namespace MashGamemodeLibrary.Registry { public interface IKeyable<in TKey> { ulong CreateID(Type type); ulong CreateID<T>() where T : notnull, TKey; ulong CreateID(TKey instance); ulong GetID<T>() where T : notnull, TKey { return this.CreateID<T>(); } ulong GetID(Type type) { return CreateID(type); } ulong GetID(TKey instance) { return CreateID(instance); } } } namespace MashGamemodeLibrary.Registry.Typed { public class FactoryTypedRegistry<TValue> : TypedRegistry<Func<TValue>, TValue> where TValue : class { private const int NullID = 0; protected override Func<TValue> Create<T>() { return () => (TValue)(object)new T(); } protected override bool TryToValue(Func<TValue>? from, [MaybeNullWhen(false)] out TValue value) { value = ((from != null) ? from() : null); return value != null; } private void WriteValue(NetWriter writer, TValue? value) { if (value == null) { writer.Write(0); return; } ulong iD = GetID(value); writer.Write(iD); INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { val.Serialize((INetSerializer)(object)writer); } } private void ReadValue(NetReader reader, ref TValue? value) { ulong num = reader.ReadUInt64(); TValue value2; if (num == 0L) { value = null; } else if (!TryGet(num, out value2)) { InternalLogger.Error($"Failed to read value with id {num} from registry {typeof(TValue).FullName}"); value = null; } else { INetSerializable val = (INetSerializable)(object)((value2 is INetSerializable) ? value2 : null); if (val != null) { val.Serialize((INetSerializer)(object)reader); } value = value2; } } public void SerializeValue(INetSerializer serializer, ref TValue? value) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (serializer.IsReader) { ReadValue((NetReader)serializer, ref value); } else { WriteValue((NetWriter)serializer, value); } } } public interface ITypedRegistry<TValue> : IKeyable<TValue> where TValue : notnull { new ulong CreateID(Type type); void Register<T>() where T : TValue, new(); void RegisterAll<T>(); Type? GetType(ulong id); bool TryGetType(ulong id, [MaybeNullWhen(false)] out Type type); TValue? Get(ulong id); TValue? Get(Type type); T? Get<T>() where T : TValue; bool TryGet(ulong id, [MaybeNullWhen(false)] out TValue entry); bool TryGet(Type type, [MaybeNullWhen(false)] out TValue entry); bool TryGet<T>([MaybeNullWhen(false)] out T entry) where T : TValue; IEnumerable<Type> GetAllTypes(); } public class SingletonTypedRegistry<TValue> : TypedRegistry<TValue, TValue> where TValue : notnull { public void Register<T>(T value) where T : TValue { ulong orCreateId = base.GetOrCreateId<T>(); base.Register(orCreateId, value); } protected override TValue Create<T>() { return (TValue)(object)new T(); } protected override bool TryToValue(TValue? from, [MaybeNullWhen(false)] out TValue value) { if (from == null) { value = default(TValue); return false; } value = from; return true; } } public abstract class TypedRegistry<TInternal, TValue> : KeyedRegistry<ulong, TInternal>, ITypedRegistry<TValue>, IKeyable<TValue> where TInternal : notnull where TValue : notnull { private readonly Dictionary<Type, ulong> _stableHashCache = new Dictionary<Type, ulong>(); private readonly Dictionary<ulong, Type> _typeCache = new Dictionary<ulong, Type>(); public ulong CreateID(Type type) { return type.GetStableHash(); } public ulong CreateID<T>() where T : TValue { return CreateID(typeof(T)); } public ulong CreateID(TValue instance) { return CreateID(instance.GetType()); } public void Register<T>() where T : TValue, new() { ulong orCreateId = this.GetOrCreateId<T>(); TInternal value = Create<T>(); Register(orCreateId, value); } public void RegisterAll<T>() { Assembly assembly = typeof(T).Assembly; MethodInfo registerTypeMethod = GetType().GetMethod("Register", Type.EmptyTypes); if (registerTypeMethod == null) { throw new Exception("Could not find register method."); } IEnumerableExtensions.ForEach<Type>((from t in assembly.GetTypes() where typeof(TValue).IsAssignableFrom(t) && (object)t != null && t.IsClass && !t.IsAbstract && !t.IsInterface select t).Where(delegate(Type t) { if (t.GetConstructor(BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes) != null) { return true; } InternalLogger.Error("Type: " + t.Name + " has no default constructor. Ensure it satisfiers the \"new()\" clause."); return false; }), (Action<Type>)delegate(Type t) { registerTypeMethod.MakeGenericMethod(t).Invoke(this, null); }); } public Type? GetType(ulong id) { return _typeCache.GetValueOrDefault(id); } public bool TryGetType(ulong id, [MaybeNullWhen(false)] out Type type) { return _typeCache.TryGetValue(id, out type); } public new TValue? Get(ulong id) { TryToValue(base.Get(id), out var value); return value; } public TValue? Get(Type type) { ulong iD = GetID(type); TryToValue(base.Get(iD), out var value); return value; } public virtual T Get<T>() where T : TValue { ulong iD = this.GetID<T>(); TValue val = Get(iD); if (val is T) { return (T)(object)val; } return default(T); } public bool TryGet(ulong key, [MaybeNullWhen(false)] out TValue value) { if (!TryGet(key, out TInternal value2) || !TryToValue(value2, out var value3)) { value = default(TValue); return false; } value = value3; return true; } public bool TryGet(Type type, [MaybeNullWhen(false)] out TValue entry) { ulong iD = GetID(type); return TryGet(iD, out entry); } public virtual bool TryGet<T>([MaybeNullWhen(false)] out T entry) where T : TValue { ulong iD = this.GetID<T>(); if (!TryGet(iD, out var value)) { entry = default(T); return false; } entry = (T)(object)value; return true; } public IEnumerable<Type> GetAllTypes() { return _typeCache.Values; } public IEnumerable<TValue> GetAll() { return _typeCache.Keys.Select((ulong k) => Get(k)); } public ulong GetID(Type type) { if (!_stableHashCache.TryGetValue(type, out var value)) { throw new Exception("Failed to get ID for unregistered type: " + type.FullName); } return value; } public ulong GetID<T>() where T : TValue { return GetID(typeof(T)); } public ulong GetID(TValue instance) { return GetID(instance.GetType()); } public ulong GetOrCreateId(Type type) { if (_stableHashCache.TryGetValue(type, out var value)) { return value; } ulong num = CreateID(type); _stableHashCache[type] = num; _typeCache[num] = type; return num; } public ulong GetOrCreateId<T>() where T : TValue { return GetOrCreateId(typeof(T)); } protected abstract TInternal Create<T>() where T : TValue, new(); protected abstract bool TryToValue(TInternal? from, [MaybeNullWhen(false)] out TValue value); } public abstract class TypedRegistry<TValue> : TypedRegistry<TValue, TValue> where TValue : notnull { } } namespace MashGamemodeLibrary.Registry.Keyed { public interface IKeyedRegistry<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, IGuaranteeStaticConstructor where TKey : notnull where TValue : notnull { public delegate void OnRegisterHandler(TKey key, TValue value); event OnRegisterHandler? OnRegister; void Register<T>(TKey key, T value) where T : TValue; TValue? Get(TKey key); bool TryGet(TKey key, [MaybeNullWhen(false)] out TValue value); bool Contains(TKey key); } public class KeyedRegistry<TKey, TValue> : IKeyedRegistry<TKey, TValue>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, IGuaranteeStaticConstructor where TKey : notnull where TValue : notnull { private readonly Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>(); public int Count => _dictionary.Count; public event IKeyedRegistry<TKey, TValue>.OnRegisterHandler? OnRegister; public void Register<T>(TKey id, T value) where T : TValue { _dictionary.Add(id, (TValue)(object)value); this.OnRegister?.Invoke(id, (TValue)(object)value); } public TValue? Get(TKey id) { return _dictionary.GetValueOrDefault(id); } public bool TryGet(TKey key, [MaybeNullWhen(false)] out TValue value) { return _dictionary.TryGetValue(key, out value); } public bool Contains(TKey id) { return _dictionary.ContainsKey(id); } public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } namespace MashGamemodeLibrary.Registry.Generic { public class RuntimeTypedRegistry : KeyedRegistry<ulong, object>, IKeyable<object> { private readonly Type RootType; public RuntimeTypedRegistry(Type rootType) { RootType = rootType; } public ulong CreateID(Type type) { return type.Name.GetStableHash(); } public ulong CreateID<T>() where T : notnull { return CreateID(typeof(T)); } public ulong CreateID(object instance) { return instance.GetType().Name.GetStableHash(); } public void Register<T>(object value) where T : notnull { Type type = RootType.MakeGenericType(typeof(T)); if (!value.GetType().IsAssignableFrom(type)) { MelonLogger.Error("Failed to register: " + value.GetType().Name + ". Expected a value of: " + type.Name); return; } ulong id = CreateID<T>(); Register(id, value); } public object? Get<T>() where T : notnull { ulong id = CreateID<T>(); return Get(id); } } } namespace MashGamemodeLibrary.Loadout { internal enum LoadCommandType { Weapon, Utility } internal enum WeaponType { Primary, Secondary, Tertiary, Utility } internal class FetchLoadoutPacket : INetSerializable { public string[] Barcodes; public LoadCommandType LoadCommandType; public void Serialize(INetSerializer serializer) { serializer.SerializeValue<LoadCommandType>(ref LoadCommandType); serializer.SerializeValue(ref Barcodes); } } public static class PalletLoadoutManager { private static readonly string[] PrimaryTypeTags = new string[3] { "SMG", "Shotgun", "Rifle" }; private static readonly string[] SecondaryTypeTags = new string[1] { "Pistol" }; private static readonly string[] TertiaryTypeTags = new string[2] { "Blade", "Blunt" }; private static readonly RemoteEvent<FetchLoadoutPacket> FetchLoadoutEvent = new RemoteEvent<FetchLoadoutPacket>("FetchLoadoutEvent", OnFetchLoadout, CommonNetworkRoutes.HostToAll); private static readonly RemoteEvent<DummySerializable> AssignLoadoutEvent = new RemoteEvent<DummySerializable>("AssignLoadoutEvent", OnAssignLoadout, CommonNetworkRoutes.HostToAll); private static readonly Dictionary<WeaponType, List<Crate>> Items = new Dictionary<WeaponType, List<Crate>>(); private static void ClearWeapons() { Items.GetValueOrDefault(WeaponType.Primary)?.Clear(); Items.GetValueOrDefault(WeaponType.Secondary)?.Clear(); Items.GetValueOrDefault(WeaponType.Tertiary)?.Clear(); } private static List<Crate> GetCrateList(WeaponType type) { if (Items.TryGetValue(type, out List<Crate> value)) { return value; } List<Crate> list = new List<Crate>(); Items[type] = list; return list; } private static WeaponType? GetCrateType(Crate crate) { Enumerator<string> enumerator = crate._tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; if (current != null) { if (PrimaryTypeTags.Contains(current)) { return WeaponType.Primary; } if (SecondaryTypeTags.Contains(current)) { return WeaponType.Secondary; } if (TertiaryTypeTags.Contains(current)) { return WeaponType.Tertiary; } } } return null; } public static void LoadLocal(IEnumerable<string> barcodes) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) ClearWeapons(); Pallet pallet2 = default(Pallet); foreach (string barcode in barcodes) { Barcode val = new Barcode(barcode); if (ModBlacklist.IsBlacklisted(barcode) || GlobalModBlacklistManager.IsBarcodeBlacklisted(barcode)) { MelonLogger.Error("Failed to load pallet with barcode: " + barcode + ": Mod is blacklisted"); continue; } if (AssetWarehouse.Instance.TryGetPallet(val, ref pallet2)) { AddPallet(pallet2); continue; } MelonLogger.Error("Failed to load pallet with barcode: " + barcode + ": Pallet not found"); if (!ClientSettings.Downloading.DownloadSpawnables.Value) { break; } long value = DataConversions.ConvertMegabytesToBytes((long)ClientSettings.Downloading.MaxFileSize.Value); ModInstallInfo val2 = default(ModInstallInfo); val2.Target = 0; val2.Barcode = barcode; val2.FinishDownloadCallback = new DownloadCallback(OnModDownloaded); val2.MaxBytes = value; NetworkModRequester.RequestAndInstallMod(val2); } static void AddPallet(Pallet pallet) { Enumerator<Crate> enumerator2 = pallet.Crates.GetEnumerator(); while (enumerator2.MoveNext()) { Crate current2 = enumerator2.Current; if (!((Scannable)current2)._redacted) { WeaponType? crateType = GetCrateType(current2); if (crateType.HasValue) { GetCrateList(crateType.Value).Add(current2); } } } } static void OnModDownloaded(DownloadCallbackInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((int)info.Result != 2) { InternalLogger.Warn("Failed downloading spawnable!"); } else { AddPallet(info.Pallet); } } } public static void LoadLocalUtility(IEnumerable<string> barcodes) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown List<Crate> crateList = GetCrateList(WeaponType.Utility); crateList.Clear(); Crate item = default(Crate); foreach (string barcode in barcodes) { if (!AssetWarehouse.Instance.TryGetCrate(new Barcode(barcode), ref item)) { MelonLogger.Error("Failed to load crate with barcode: " + barcode + ": Crate not found"); } else { crateList.Add(item); } } } private static Barcode? Get(WeaponType type) { if (!Items.TryGetValue(type, out List<Crate> value)) { return null; } if (value.Count == 0) { return null; } return ((Scannable)IEnumerableExtensions.GetRandom<Crate>((IEnumerable<Crate>)value)).Barcode; } public static MashGamemodeLibrary.Player.Loadout.Loadout GetLoadout() { Barcode barcode = Get(WeaponType.Primary); Barcode barcode2 = Get(WeaponType.Secondary); Barcode barcode3 = Get(WeaponType.Tertiary); Barcode barcode4 = Get(WeaponType.Utility); return new MashGamemodeLibrary.Player.Loadout.Loadout().SetSlotBarcode(SlotType.RightBack, barcode).SetSlotBarcode(SlotType.RightHolster, barcode2).SetSlotBarcode(SlotType.LeftBack, barcode4) .SetSlotBarcode(SlotType.Belt, barcode3); } public static void Load(string barcode) { Load(new string[1] { barcode }); } public static void Load(IEnumerable<string> barcodes) { FetchLoadoutEvent.Call(new FetchLoadoutPacket { LoadCommandType = LoadCommandType.Weapon, Barcodes = barcodes.ToArray() }); } public static void LoadUtility(IEnumerable<string> barcodes) { FetchLoadoutEvent.Call(new FetchLoadoutPacket { LoadCommandType = LoadCommandType.Utility, Barcodes = barcodes.ToArray() }); } public static void AssignAll() { AssignLoadoutEvent.Call(new DummySerializable()); } public static void ReassignOwnLoadout() { SlotData.ClearSpawned(); GetLoadout().Assign(); } private static void OnFetchLoadout(FetchLoadoutPacket packet) { switch (packet.LoadCommandType) { case LoadCommandType.Weapon: LoadLocal(packet.Barcodes); break; case LoadCommandType.Utility: LoadLocalUtility(packet.Barcodes); break; default: throw new ArgumentOutOfRangeException(); } } private static void OnAssignLoadout(DummySerializable _) { ReassignOwnLoadout(); } } public enum SlotType { LeftHolster, RightHolster, LeftBack, RightBack, Belt } public class SlotData { private static readonly HashSet<Poolee> SpawnedGuns = new HashSet<Poolee>(); public Barcode? Barcode; public SlotData() { } public SlotData(Barcode? barcode) { Barcode = barcode; } public static string GetSlotName(SlotType type) { return type switch { SlotType.LeftHolster => "SideLf", SlotType.RightHolster => "SideRt", SlotType.LeftBack => "BackLf", SlotType.RightBack => "BackRt", SlotType.Belt => "BackCt", _ => throw new ArgumentOutOfRangeException("type", type, null), }; } public void AssignSlot(RigManager rig, SlotType slotType) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) string slotName = GetSlotName(slotType); SlotContainer val = ((IEnumerable<SlotContainer>)rig.inventory.bodySlots).FirstOrDefault((Func<SlotContainer, bool>)((SlotContainer e) => ((Object)e).name.Equals(slotName))); InventorySlotReceiver slot = ((val != null) ? val.inventorySlotReceiver : null); if (slot == null) { return; } WeaponSlot slottedWeapon = slot._slottedWeapon; object obj; if (slottedWeapon == null) { obj = null; } else { Grip grip = slottedWeapon.grip; if (grip == null) { obj = null; } else { MarrowEntity marrowEntity2 = grip._marrowEntity; obj = ((marrowEntity2 != null) ? marrowEntity2._poolee : null); } } Poolee val2 = (Poolee)obj; if ((Object)(object)val2 != (Object)null) { val2.Despawn(); } if (Barcode == (Barcode)null) { return; } Spawnable spawnable = new Spawnable { crateRef = new SpawnableCrateReference(Barcode), policyData = null }; Transform transform = ((Component)slot).transform; SpawnRequestInfo val3 = default(SpawnRequestInfo); val3.Spawnable = spawnable; val3.Position = transform.position; val3.Rotation = transform.rotation; val3.SpawnEffect = false; val3.SpawnCallback = delegate(SpawnCallbackInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) info.WaitOnMarrowEntity(delegate(NetworkEntity networkEntity, MarrowEntity marrowEntity) { NetworkEntity networkEntity2 = networkEntity; SpawnedGuns.Add(marrowEntity._poolee); DelayUtilities.InvokeDelayed((Action)delegate { WeaponSlotExtender extender = networkEntity2.GetExtender<WeaponSlotExtender>(); if (extender != null) { WeaponSlot component = ((EntityComponentExtender<WeaponSlot>)(object)extender).Component; if (!((Object)(object)component == (Object)null) && !((Object)(object)component.interactableHost == (Object)null) && !((EntityComponentExtender<WeaponSlot>)(object)extender).Component.interactableHost.IsAttached) { ((InventoryHandReceiver)slot).OnHandDrop(((Il2CppObjectBase)component.interactableHost).TryCast<IGrippable>()); } } }, 60); }); }; NetworkAssetSpawner.Spawn(val3); } public static void ClearSpawned() { foreach (Poolee spawnedGun in SpawnedGuns) { if (!((Object)(object)spawnedGun == (Object)null) && ((Behaviour)spawnedGun).isActiveAndEnabled) { spawnedGun.Despawn(); } } SpawnedGuns.Clear(); } } public class SpawnableElementData : FunctionElementData { private SpawnableCrateReference? _spawnable; public Action<Barcode> OnSetSpawnable = delegate { }; public SpawnableElementData() { base.OnPressed = OnPressedInternal; } private static Barcode? GetHeldSpawnableBarcode(Hand hand) { if (!hand.HasAttachedObject()) { return null; } Poolee componentInParent = ((Component)hand.AttachedReceiver).gameObject.GetComponentInParent<Poolee>(); if (Object.op_Implicit((Object)(object)componentInParent)) { return ((Scannable)componentInParent.SpawnableCrate)._barcode; } return null; } private static Barcode? GetHeldSpawnableBarcode() { return ((IEnumerable<Hand>)(object)new Hand[2] { Player.LeftHand, Player.RightHand }).Select(GetHeldSpawnableBarcode).OfType<Barcode>().FirstOrDefault(); } private void OnPressedInternal() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Barcode heldSpawnableBarcode = GetHeldSpawnableBarcode(); if (!(heldSpawnableBarcode == (Barcode)null)) { _spawnable = new SpawnableCrateReference(heldSpawnableBarcode); OnSetSpawnable(((ScannableReference)_spawnable)._barcode); } } } } namespace MashGamemodeLibrary.Player { public struct AvatarStats : INetSerializable { public float Vitality; public float Speed; public float UpperStrength; public float Agility; public float LowerStrength; public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref Vitality); serializer.SerializeValue(ref Speed); serializer.SerializeValue(ref UpperStrength); serializer.SerializeValue(ref Agility); serializer.SerializeValue(ref LowerStrength); } public readonly AvatarStats MultiplyHealth(float mult) { AvatarStats result = this; result.Vitality = Vitality * mult; return result; } } } namespace MashGamemodeLibrary.Player.Team { [RequireStaticConstructor] public static class LogicTeamManager { private static readonly HashSet<ulong> EnabledTeams; public static readonly FactoryTypedRegistry<LogicTeam> Registry; private static readonly SyncedDictionary<byte, LogicTeam> AssignedTeams; static LogicTeamManager() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown EnabledTeams = new HashSet<ulong>(); Registry = new FactoryTypedRegistry<LogicTeam>(); AssignedTeams = new SyncedDictionary<byte, LogicTeam>("sync.AssignedTeams", new ByteEncoder(), new DynamicInstanceEncoder<LogicTeam>(Registry)); AssignedTeams.OnValueAdded += OnAssigned; AssignedTeams.OnValueRemoved += OnRemoved; MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeave); } private static ulong GetTeamID(Type type) { return Registry.GetOrCreateId(type); } public static ulong GetTeamID<T>() where T : LogicTeam { return GetTeamID(typeof(T)); } public static void Enable<T>() where T : LogicTeam { ulong teamID = GetTeamID<T>(); EnabledTeams.Add(teamID); } public static void Disable() { EnabledTeams.Clear(); Executor.RunIfHost(delegate { AssignedTeams.Clear(); }); } public static ulong? GetLocalTeamID() { byte localSmallID = PlayerIDManager.LocalSmallID; if (!AssignedTeams.TryGetValue(localSmallID, out LogicTeam value)) { return null; } return Registry.CreateID(value); } public static LogicTeam? GetLocalTeam() { byte localSmallID = PlayerIDManager.LocalSmallID; return AssignedTeams.GetValueOrDefault(localSmallID); } public static bool IsTeam<T>(this PlayerID playerID) where T : LogicTeam { if (AssignedTeams.TryGetValue(PlayerID.op_Implicit(playerID), out LogicTeam value)) { return value.GetType() == typeof(T); } return false; } public static bool IsTeam(this PlayerID playerID, ulong teamID) { if (AssignedTeams.TryGetValue(PlayerID.op_Implicit(playerID), out LogicTeam value)) { return Registry.CreateID(value) == teamID; } return false; } public static bool IsLocalTeam<T>() where T : LogicTeam { return PlayerIDManager.LocalID.IsTeam<T>(); } public static ulong? GetPlayerTeamID(PlayerID player) { if (!AssignedTeams.TryGetValue(PlayerID.op_Implicit(player), out LogicTeam value)) { return null; } return Registry.CreateID(value); } public static LogicTeam? GetPlayerTeam(PlayerID player) { return AssignedTeams.GetValueOrDefault(PlayerID.op_Implicit(player)); } public static bool IsTeamMember(this PlayerID player) { LogicTeam localTeam = GetLocalTeam(); LogicTeam playerTeam = GetPlayerTeam(player); if (localTeam == null || playerTeam == null) { return false; } return localTeam.GetType() == playerTeam.GetType(); } public static void Assign<T>(this NetworkPlayer player, T team) where T : LogicTeam { NetworkPlayer player2 = player; T team2 = team; Executor.RunIfHost(delegate { AssignedTeams[PlayerID.op_Implicit(player2.PlayerID)] = team2; }); } public static void Assign<T>(this PlayerID playerID) where T : LogicTeam { PlayerID playerID2 = playerID; Executor.RunIfHost(delegate { if (!Registry.TryGet<T>(out var entry)) { MelonLogger.Error("Failed to assign team: " + typeof(T).Name + ". Team was not registered"); } else { AssignedTeams[PlayerID.op_Implicit(playerID2)] = entry; } }, "Assigning teams"); } public static void Assign(this PlayerID playerID, ulong teamID) { PlayerID playerID2 = playerID; Executor.RunIfHost(delegate { if (!Registry.TryGet(teamID, out LogicTeam value)) { MelonLogger.Error($"Failed to assign team: {teamID}. Team was not registered"); } else { AssignedTeams[PlayerID.op_Implicit(playerID2)] = value; } }); } public static void AssignAllRandom() { Executor.RunIfHost(delegate { int num = Random.Range(0, 2); List<LogicTeam> list = EnabledTeams.Select((ulong id) => Registry.Get(id)).OfType<LogicTeam>().ToList(); foreach (NetworkPlayer player in NetworkPlayer.Players) { LogicTeam value = list[num]; AssignedTeams[PlayerID.op_Implicit(player.PlayerID)] = value; num = (num + 1) % EnabledTeams.Count; } }); } public static void AssignAll<T>() where T : LogicTeam { Executor.RunIfHost(delegate { if (!Registry.TryGet<T>(out var entry)) { MelonLogger.Error("Failed to assign all teams. " + typeof(T).Name + " is not registered."); return; } foreach (NetworkPlayer player in NetworkPlayer.Players) { AssignedTeams[PlayerID.op_Implicit(player.PlayerID)] = entry; } }); } public static void AssignToSmallest(this PlayerID playerID) { PlayerID playerID2 = playerID; Executor.RunIfHost(delegate { if (EnabledTeams.Count != 0) { Dictionary<ulong, int> dictionary = new Dictionary<ulong, int>(); foreach (ulong enabledTeam in EnabledTeams) { if (Registry.TryGetType(enabledTeam, out Type type)) { dictionary[enabledTeam] = AssignedTeams.Count<KeyValuePair<byte, LogicTeam>>((KeyValuePair<byte, LogicTeam> kv) => kv.Value.GetType() == type); } } KeyValuePair<ulong, int> keyValuePair = dictionary.DefaultIfEmpty().MinBy((KeyValuePair<ulong, int> kv) => kv.Value); LogicTeam value = Registry.Get(keyValuePair.Key); AssignedTeams[PlayerID.op_Implicit(playerID2)] = value; } }); } public static void AssignRandom<T>(IRandomProvider<PlayerID>? provider = null) where T : LogicTeam { if (provider == null) { provider = new BasicRandomProvider<PlayerID>(() => (from p in NetworkPlayer.Players where p.HasRig select p.PlayerID).ToList()); } PlayerID randomValue = provider.GetRandomValue(); if (randomValue == null) { MelonLogger.Error("Failed to select a player to assign to team: " + typeof(T).Name); } else { randomValue.Assign<T>(); } } private static void OnAssigned(byte smallID, LogicTeam team) { NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(smallID, ref val)) { MelonLogger.Error($"Failed to assign player of id {smallID} to {team.Name}"); return; } team.Assign(val); PlayerDataManager.CallEventOnAll(new TeamChangedEvent(val.PlayerID, team)); GamePhase phase = GamePhaseManager.ActivePhase; if (phase != null) { team.Try(delegate(LogicTeam t) { t.OnPhaseChanged(phase); }); } } private static void OnRemoved(byte smallID, LogicTeam team) { team.Remove(); NetworkPlayer val = default(NetworkPlayer); if (NetworkPlayerManager.TryGetPlayer(smallID, ref val)) { PlayerDataManager.CallEventOnAll(new TeamChangedEvent(val.PlayerID, null)); } } public static void OnPhaseChanged(GamePhase activePhase) { GamePhase activePhase2 = activePhase; foreach (LogicTeam value in AssignedTeams.Values) { value.Try(delegate(LogicTeam t) { t.OnPhaseChanged(activePhase2); }); } } private static void OnPlayerLeave(PlayerID playerId) { AssignedTeams.Remove(playerId.SmallID); } } public enum TeamStatisticKeys { RoundsWon } [RequireStaticConstructor] public static class PersistentTeams { private static readonly RemoteEvent WinMessageEvent = new RemoteEvent("PersistentTeams_WinMessage", OnWinMessage, CommonNetworkRoutes.HostToAll); private static readonly SyncedDictionary<byte, int> PlayerTeamIndices = new SyncedDictionary<byte, int>("PersistentTeams_PlayerTeamIndices", new ByteEncoder(), new IntEncoder()); private static readonly SyncedDictionary<int, int> TeamScores = new SyncedDictionary<int, int>("PersistentTeams_TeamScores", new IntEncoder(), new IntEncoder()); private static readonly HashSet<PlayerID> LateJoinerQueue = new HashSet<PlayerID>(); private static readonly List<ulong> TeamIds = new List<ulong>(); private static int _shift = Random.RandomRangeInt(0, 2); private static ulong GetTeamId(int setIndex) { int index = (setIndex + _shift) % TeamIds.Count; return TeamIds[index]; } public static void AddTeamID(ulong id) { TeamIds.Add(id); } public static void AddTeam<T>() where T : LogicTeam { AddTeamID(LogicTeamManager.Registry.CreateID<T>()); } private static void Assign(PlayerID playerID, int index) { if (!PlayerTeamIndices.ContainsKey(PlayerID.op_Implicit(playerID))) { PlayerTeamIndices[PlayerID.op_Implicit(playerID)] = index; } } public static void AddPlayers(IEnumerable<PlayerID> playerIds) { int num = 0; foreach (PlayerID item in playerIds.Shuffle()) { Assign(item, num); num = (num + 1) % TeamIds.Count; } } public static void OverwritePlayers(IEnumerable<PlayerID> playerIds) { PlayerTeamIndices.Clear(); AddPlayers(playerIds); } public static void OverwritePlayers(IEnumerable<IEnumerable<PlayerID>> teamPlayerIds) { PlayerTeamIndices.Clear(); int num = 0; foreach (IEnumerable<PlayerID> teamPlayerId in teamPlayerIds) { foreach (PlayerID item in teamPlayerId) { Assign(item, num); } num = (num + 1) % TeamIds.Count; } } public static void RandomizeShift() { _shift += Random.RandomRangeInt(0, TeamIds.Count); } public static void QueueLateJoiner(PlayerID playerID) { LateJoinerQueue.Add(playerID); } public static void AssignAll() { if (PlayerTeamIndices.Count == 0) { MelonLogger.Error("No valid set found."); return; } Dictionary<int, int> dictionary = (from p in PlayerTeamIndices select p.Value into i group i by i).ToDictionary((IGrouping<int, int> g) => g.Key, (IGrouping<int, int> g) => g.Count()); NetworkPlayer val = default(NetworkPlayer); int value; foreach (PlayerID item in LateJoinerQueue) { if (!item.IsValid) { LateJoinerQueue.Remove(item); } else if (NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(item), ref val) && val.HasRig) { LateJoinerQueue.Remove(item); int key = dictionary.MinBy((KeyValuePair<int, int> t) => t.Value).Key; Assign(item, key); Dictionary<int, int> dictionary2 = dictionary; value = key; dictionary2[value]++; } } _shift++; foreach (KeyValuePair<byte, int> playerTeamIndex in PlayerTeamIndices) { playerTeamIndex.Deconstruct(out var key2, out value); byte num = key2; int setIndex = value; PlayerID playerID = PlayerIDManager.GetPlayerID(num); if (!playerID.IsValid) { break; } playerID.Assign(GetTeamId(setIndex)); } } public static void AddScore(ulong teamId, int score) { int num = (TeamIds.IndexOf(teamId) - _shift) % TeamIds.Count; if (num < 0) { num += TeamIds.Count; } int valueOrDefault = TeamScores.GetValueOrDefault(num, 0); TeamScores[num] = valueOrDefault + score; } public static void Clear() { _shift = 0; TeamIds.Clear(); PlayerTeamIndices.Clear(); TeamScores.Clear(); } public static int GetTeamIndex(PlayerID playerID) { if (!PlayerTeamIndices.TryGetValue(PlayerID.op_Implicit(playerID), out var value)) { return -1; } return value; } public static int GetTeamScore(int teamIndex) { if (!TeamScores.TryGetValue(teamIndex, out var value)) { return 0; } return value; } public static int GetPlayerTeamScore(PlayerID playerID) { return GetTeamScore(GetTeamIndex(playerID)); } public static void SendMessage() { if (PlayerTeamIndices.Count != 0) { WinMessageEvent.Call(); } } private static void OnWinMessage(byte senderId) { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown List<(string, int)> list = new List<(string, int)>(2); List<(int, int)> list2 = (from kvp in TeamScores select (kvp.Key, kvp.Value) into p orderby p.score descending select p).Take(2).ToList(); if (list2.Count == 0) { return; } NetworkPlayer val = default(NetworkPlayer); foreach (var item3 in list2) { int teamID = item3.Item1; int item = item3.Item2; string item2 = (NetworkPlayerManager.TryGetPlayer(PlayerTeamIndices.FirstOrDefault((KeyValuePair<byte, int> p) => p.Value == teamID).Key, ref val) ? val.Username : "Unknown"); list.Add((item2, item)); } int num = PlayerTeamIndices[PlayerIDManager.LocalSmallID]; bool flag = list2.First().Item1 == num; string text = (flag ? "Victory!" : "Defeat!"); string text2 = string.Join("\n", list.Select(((string, int) f) => $"{f.Item1}'s Team: {f.Item2} points")); Notifier.Send(new Notification { Title = NotificationText.op_Implicit(text), Message = NotificationText.op_Implicit(text2), PopupLength = 6f, SaveToMenu = true, ShowPopup = true, Type = (NotificationType)(flag ? 3 : 2) }); PlayerStatisticsTracker.AwardBits(); } } public abstract class LogicTeam { private NetworkPlayer? _owner; public abstract string Name { get; } public virtual Texture? Icon => null; public NetworkPlayer Owner => _owner ?? throw new InvalidOperationException("No player is currently assigned to team: " + Name + "!"); protected virtual void OnAssigned() { } public virtual void OnPhaseChanged(GamePhase phase) { } protected virtual void OnRemoved() { } internal void Assign(NetworkPlayer player) { if (LobbyInfoManager.LobbyInfo.NameTags) { player.Icon.Texture = Icon; player.Icon.Visible = (Object)(object)Icon != (Object)null; } _owner = player; Executor.RunChecked<Action>(OnAssigned); } internal void Remove() { if (_owner != null) { Owner.Icon.Texture = null; Owner.Icon.Visible = false; Executor.RunChecked<Action>(OnRemoved); } } } } namespace MashGamemodeLibrary.Player.Stats { public static class AvatarStatManager { private static AvatarStats? _localStatOverride; private const float TargetHeight = 1.8f; private const float SafeMargin = 0.2f; private const float MaxMargin = 0.8f; private const float Multiplier = 0.5f; private const float TeamUnbalancedSteps = 1f; private const float TeamUnbalancedMultiplier = 0.5f; public static bool BalanceStats { get; set; } private static void SetVitality(float? value) { if (SpectatorExtender.IsLocalPlayerSpectating()) { value = 100f; } if (!LocalHealth.VitalityOverride.Equals(value)) { LocalHealth.VitalityOverride = value; } } public static void RefreshVitality() { SetVitality(GetLocalStats(Player.Avatar)?.Vitality); } public static void SetAvatarAndStats(string barcode, AvatarStats stats) { _localStatOverride = stats; SetVitality(stats.Vitality); LocalAvatar.AvatarOverride = barcode; } public static void SetStats(AvatarStats stats) { _localStatOverride = stats; SetVitality(stats.Vitality); LocalAvatar.RefreshAvatar(); } public static void ResetStats() { _localStatOverride = null; SetVitality(null); LocalAvatar.RefreshAvatar(); } private static float GetBalancedModifier(Avatar avatar) { float num = Mathf.Abs(avatar.height - 1.8f); if (num <= 0.2f) { return 1f; } if (num >= 0.8f) { return 0.5f; } float num2 = (num - 0.2f) / 0.6f; return 1f - num2 * 0.5f; } private static float GetTeamBalanceModifier() { ulong? localTeamId = LogicTeamManager.GetLocalTeamID(); if (!localTeamId.HasValue) { return 1f; } List<NetworkPlayer> list = NetworkPlayer.Players.Where((NetworkPlayer p) => p.HasRig).ToList(); int num = list.Count((NetworkPlayer p) => LogicTeamManager.GetPlayerTeamID(p.PlayerID) == localTeamId.Value); int num2 = list.Count - num; if (num2 <= num) { return 1f; } int num3 = num2 - num; if ((float)num3 >= 1f) { return 1.5f; } float num4 = (float)num3 / 1f; return 1f + num4 * 0.5f; } public static bool TryGetLocalStats(Avatar? avatar, out AvatarStats stats) { if (!_localStatOverride.HasValue) { stats = default(AvatarStats); return false; } if (BalanceStats) { float num = (((Object)(object)avatar != (Object)null) ? GetBalancedModifier(avatar) : 1f); float teamBalanceModifier = GetTeamBalanceModifier(); stats = _localStatOverride.Value.MultiplyHealth(num * teamBalanceModifier); } else { stats = _localStatOverride.Value; } return true; } public static AvatarStats? GetLocalStats(Avatar? avatar) { if (!TryGetLocalStats(avatar, out var stats)) { return null; } return stats; } } } namespace MashGamemodeLibrary.Player.Spawning { public record struct AvoidSpawningNear { internal float Radius { get; } internal float RadiusSquare { get; } public Vector3 Position; public AvoidSpawningNear(Vector3 position, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Position = position; Radius = radius; RadiusSquare = Mathf.Pow(radius, 2f); } [CompilerGenerated] private readonly bool PrintMembers(StringBuilder builder) { builder.Append("Position = "); builder.Append(((object)(Vector3)(ref Position)).ToString()); return true; } [CompilerGenerated] public override readonly int GetHashCode() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return (EqualityComparer<Vector3>.Default.GetHashCode(Position) * -1521134295 + EqualityComparer<float>.Default.GetHashCode(Radius)) * -1521134295 + EqualityComparer<float>.Default.GetHashCode(RadiusSquare); } [CompilerGenerated] public readonly bool Equals(AvoidSpawningNear other) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (EqualityComparer<Vector3>.Default.Equals(Position, other.Position) && EqualityComparer<float>.Default.Equals(Radius, other.Radius)) { return EqualityComparer<float>.Default.Equals(RadiusSquare, other.RadiusSquare); } return false; } } public static class DynamicSpawnCollector { private const float SafeRadius = 4f; private const int StaticLayer = 13; private const int DefaultLayer = 0; private static GameObject? _spawnGameObject; private static Vector3 _center = Vector3.zero; private static float _radius; private static NavMeshData? _navMeshData; private static List<Vector3> _validSpawnPoints = new List<Vector3>(); private static void SetSpawn(Vector3 position) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if ((Object)(object)_spawnGameObject == (Object)null) { _spawnGameObject = new GameObject(); } _spawnGameObject.transform.position = position; FusionPlayer.SetSpawnPoints((Transform[])(object)new Transform[1] { _spawnGameObject.transform }); } public static void CollectAt(Vector3 center, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) _center = center; _radius = radius; Bounds val = default(Bounds); ((Bounds)(ref val))..ctor(center, Vector3.one * (radius * 2f)); List<NavMeshBuildSource> val2 = new List<NavMeshBuildSource>(); NavMeshBuilder.CollectSources(val, 8193, (NavMeshCollectGeometry)1, 0, new List<NavMeshBuildMarkup>(), val2); NavMeshBuildSettings val3 = default(NavMeshBuildSettings); ((NavMeshBuildSettings)(ref val3)).agentHeight = 4f; ((NavMeshBuildSettings)(ref val3)).agentRadius = 4f; ((NavMeshBuildSettings)(ref val3)).agentSlope = 0f; ((NavMeshBuildSettings)(ref val3)).agentClimb = 0f; val3.m_LedgeDropHeight = 50f; val3.m_MaxJumpAcrossDistance = 8f; _navMeshData = NavMeshBuilder.BuildNavMeshData(val3, val2, val, center, Quaternion.identity); _validSpawnPoints.Clear(); } private static Vector3 GetReachablePoint(Vector3 origin, Vector3 direction, float distance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Ray val = default(Ray); ((Ray)(ref val))..ctor(origin, direction); RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(val, ref val2, distance, 13)) { return ((Ray)(ref val)).GetPoint(distance); } return ((RaycastHit)(ref val2)).point + direction * (0f - Math.Min(1f, ((RaycastHit)(ref val2)).distance)); } private static bool CanReachAny(Vector3 source, params Vector3[] targets) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return targets.Any(delegate(Vector3 t) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (Vector3.Distance(source, t) < 1f) { return true; } Vector3 val = t - source; return (!Physics.Raycast(new Ray(source, ((Vector3)(ref val)).normalized), ((Vector3)(ref val)).magnitude, 13)) ? true : false; }); } private static bool CanWalkPath(IReadOnlyList<Vector3> nodes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.up * 1f; for (int i = 0; i < nodes.Count - 1; i++) { int index = i + 1; Vector3 val2 = nodes[i] + val; Vector3 val3 = nodes[index] + val - val2; if (Physics.Raycast(new Ray(val2, ((Vector3)(ref val3)).normalized), ((Vector3)(ref val3)).magnitude, 13)) { return false; } } return true; } public static Vector3? GetRandomPoint(int tries, Vector3 canReach, params AvoidSpawningNear[] avoid) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Invalid comparison between Unknown and I4 //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_navMeshData == (Object)null) { return null; } Vector3[] targets = (Vector3[])(object)new Vector3[3] { canReach, GetReachablePoint(canReach, Vector3.up, 10f), GetReachablePoint(canReach, Vector3.down, 10f) }; float num = _radius / 2f; SortedList<int, Vector3> sortedList = new SortedList<int, Vector3>(); NavMeshHit val = default(NavMeshHit); for (int i = 0; i < tries; i++) { if (!NavMesh.SamplePosition(canReach + Random.insideUnitSphere * num, ref val, _radius, -1)) { continue; } Vector3 target = ((NavMeshHit)(ref val)).position; NavMeshPath val2 = new NavMeshPath(); NavMesh.CalculatePath(target, canReach, -1, val2); foreach (Vector3 item in (Il2CppArrayBase<Vector3>)(object)val2.corners) { _ = item; } if ((int)val2.status == 1) { target = ((IEnumerable<Vector3>)val2.corners).Last(); } if (CanReachAny(((IEnumerable<Vector3>)val2.corners).LastOrDefault(target), targets) && CanWalkPath((IReadOnlyList<Vector3>)val2.corners)) { int num2 = avoid.Count(delegate(AvoidSpawningNear a) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Vector3 val5 = a.Position - target; return ((Vector3)(ref val5)).sqrMagnitude < a.RadiusSquare; }); if (num2 <= 0) { _validSpawnPoints.Add(target); return target; } sortedList.Add(avoid.Length - num2, target); } } foreach (KeyValuePair<int, Vector3> item2 in sortedList) { item2.Deconstruct(out var _, out var value); Vector3 val3 = value; NavMeshPath val4 = new NavMeshPath(); NavMesh.CalculatePath(val3, canReach, -1, val4); if (CanReachAny(((IEnumerable<Vector3>)val4.corners).LastOrDefault(val3), targets) && CanWalkPath((IReadOnlyList<Vector3>)val4.corners)) { return val3; } } if (_validSpawnPoints.Count <= 0) { return null; } return IEnumerableExtensions.GetRandom<Vector3>((IEnumerable<Vector3>)_validSpawnPoints); } public static void SetRandomSpawn(int tries, Vector3 fallback, Vector3 canReach, params AvoidSpawningNear[] avoid) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) Vector3? randomPoint = GetRandomPoint(tries, canReach, avoid); if (!randomPoint.HasValue) { MelonLogger.Error("Failed to find any spawnpoint! this is a bug."); SetSpawn(fallback); } else { SetSpawn(randomPoint.Value); } } } } namespace MashGamemodeLibrary.Player.Loadout { public class Loadout { private static readonly List<SlotType> AllSlotTypes = Enum.GetValues(typeof(SlotType)).Cast<SlotType>().ToList(); private static readonly SlotData DefaultSlotData = new SlotData(null); private readonly Dictionary<SlotType, SlotData> _slotAssigners = new Dictionary<SlotType, SlotData>(); public Loadout() { foreach (SlotType value in Enum.GetValues(typeof(SlotType))) { _slotAssigners[value] = new SlotData(); } } public Loadout SetSlotBarcode(SlotType slotType, Barcode? barcode) { _slotAssigners[slotType] = new SlotData(barcode); return this; } public void Assign() { RigManager rigManager = Player.RigManager; foreach (SlotType allSlotType in AllSlotTypes) { _slotAssigners.GetValueOrDefault(allSlotType, DefaultSlotData).AssignSlot(rigManager, allSlotType); } } public static void ClearPlayerLoadout(RigManager rig) { foreach (SlotType value in Enum.GetValues(typeof(SlotType))) { DefaultSlotData.AssignSlot(rig, value); } ClearHeadSlot(rig); } public static void ClearHeadSlot(RigManager rig) { Transform obj = ((Rig)rig.physicsRig).m_head.FindChild("HeadSlotContainer"); InventorySlotReceiver val = ((obj != null) ? ((Component)obj).GetComponentInChildren<InventorySlotReceiver>() : null); if (!((Object)(object)val == (Object)null)) { val.DespawnContents(); } } } } namespace MashGamemodeLibrary.Player.Helpers { public static class CrippleHelper { public static bool IsCrippled { get { PlayerData localPlayerData = PlayerDataManager.GetLocalPlayerData(); if (localPlayerData == null) { return false; } if (!localPlayerData.CheckRule((PlayerCrippledRule p) => p.IsEnabled)) { return localPlayerData.CheckRule((PlayerSpectatingRule p) => p.IsSpectating); } return true; } } } public static class InteractionExtender { public static bool HasInteractions(this NetworkPlayer player) { if (!NetworkSceneManager.IsLevelNetworked) { return true; } return PlayerDataManager.GetPlayerData(player)?.CheckRule((PlayerSpectatingRule r) => r.IsSpectating) ?? true; } public static bool HasLocalInteractions() { if (!NetworkSceneManager.IsLevelNetworked) { return true; } return PlayerDataManager.GetLocalPlayerData()?.CheckRule((PlayerSpectatingRule r) => r.IsSpectating) ?? true; } } internal struct NightVisionObject { public GameObject GameObject; public ColorAdjustments ColorAdjustments; public readonly void SetActive(bool state) { if (Object.op_Implicit((Object)(object)GameObject)) { GameObject.SetActive(state); } } public readonly void SetColor(Color color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((VolumeParameter<Color>)(object)ColorAdjustments.colorFilter).value = color; } public readonly void SetBrightness(float value) { ((VolumeParameter<float>)(object)ColorAdjustments.postExposure).value = value; } } public static class NightVisionHelper { private static bool _nightVisionEnabled; private static float _nightVisionBrightness = 1f; private static NightVisionObject? _instance; public static bool Enabled { get { return _nightVisionEnabled; } set { ToggleNightVision(value); } } public static float Brightness { get { return _nightVisionBrightness; } set { _nightVisionBrightness = value; _instance?.SetBrightness(value); } } private static void ToggleNightVision(bool isEnabled) { _nightVisionEnabled = isEnabled; GetOrCreate().SetActive(isEnabled); } private static NightVisionObject GetOrCreate() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (_instance.HasValue && Object.op_Implicit((Object)(object)_instance.Value.GameObject)) { return _instance.Value; } GameObject val = new GameObject("VisionManager"); val.transform.rotation = Quaternion.LookRotation(Vector3.down); GameObject val2 = val; Volume obj = val2.AddComponent<Volume>(); obj.isGlobal = true; obj.priority = 10f; obj.weight = 1f; VolumeProfile val4 = (obj.sharedProfile = ScriptableObject.CreateInstance<VolumeProfile>()); ColorAdjustments val5 = val4.Add<ColorAdjustments>(true); ((VolumeParameter<float>)(object)val5.contrast).value = 20f; ((VolumeParameter<float>)(object)val5.postExposure).value = _nightVisionBrightness; ((VolumeParameter<Color>)(object)val5.colorFilter).value = Color.white; Light obj2 = val2.AddComponent<Light>(); obj2.color = Color.white; obj2.range = 120f; obj2.intensity = 1f; obj2.type = (LightType)1; obj2.shadows = (LightShadows)0; NightVisionObject value = default(NightVisionObject); value.GameObject = val2; value.ColorAdjustments = val5; _instance = value; return _instance.Value; } } public static class PlayerComponentExtender { public static void ClearPlayerComponents() { Executor.RunIfHost(delegate { foreach (NetworkPlayer player in NetworkPlayer.Players) { player.NetworkEntity.ClearComponents(); } }); } public static bool TryGetComponent<T>(this NetworkPlayer player, [MaybeNullWhen(false)] out T component) where T : class, IComponent { if (player.NetworkEntity == null) { component = null; return false; } component = player.NetworkEntity.GetComponent<T>(); return component != null; } public static bool HasComponent<T>(this NetworkPlayer player) where T : class, IComponent { NetworkEntity networkEntity = player.NetworkEntity; return ((networkEntity != null) ? networkEntity.GetComponent<T>() : null) != null; } public static bool HasComponent<T>(this NetworkPlayer player, Func<T, bool> predicate) where T : class, IComponent { if (player.NetworkEntity == null) { return false; } T component = player.NetworkEntity.GetComponent<T>(); if (component != null) { return predicate(component); } return false; } public static void AddComponent(this NetworkPlayer player, IComponent component) { player.NetworkEntity?.AddComponent(component); } public static void AddComponents(this NetworkPlayer player, params IComponent[] components) { if (player.NetworkEntity != null) { foreach (IComponent component in components) { player.NetworkEntity.AddComponent(component); } } } public static bool TryAddComponent<T>(this NetworkPlayer player, Func<T> factory) where T : class, IComponent { if (player.NetworkEntity.GetComponent<T>() != null) { return false; } player.NetworkEntity.AddComponent(factory()); return true; } public static void RemoveComponent<T>(this NetworkPlayer player) where T : class, IComponent { player.NetworkEntity?.RemoveComponent<T>(); } public static void ToggleComponent<T>(this NetworkPlayer player, bool state, Func<T> factory) where T : class, IComponent { if (player.NetworkEntity != null) { if (state) { player.TryAddComponent(factory); } else { player.RemoveComponent<T>(); } } } } public static class SpawnPointHelper { private static GameObject? _spawnPoint; private static GameObject GetSpawnPoint() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if ((Object)(object)_spawnPoint == (Object)null) { _spawnPoint = new GameObject(); } return _spawnPoint; } public static void SetSpawnPoint(Vector3 position) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) GameObject spawnPoint = GetSpawnPoint(); spawnPoint.transform.SetPositionAndRotation(position, Quaternion.identity); FusionPlayer.SetSpawnPoints((Transform[])(object)new Transform[1] { spawnPoint.transform }); } } public static class SpectatorExtender { private static PlayerRuleInstance<PlayerSpectatingRule> GetOrCreateSpectatingModifier(PlayerID playerId) { return (PlayerDataManager.GetPlayerData(PlayerID.op_Implicit(playerId)) ?? throw new Exception($"Player data not found for player ID: {playerId}")).GetRuleInstance<PlayerSpectatingRule>(); } public static bool IsSpectating(this NetworkPlayer player) { return PlayerDataManager.GetPlayerData(player)?.CheckRule((PlayerSpectatingRule r) => r.IsSpectating) ?? false; } public static bool IsSpectating(this PlayerID playerId) { return PlayerDataManager.GetPlayerData(PlayerID.op_Implicit(playerId))?.CheckRule((PlayerSpectatingRule r) => r.IsSpectating) ?? false; } public static bool IsLocalPlayerSpectating() { return PlayerDataManager.GetLocalPlayerData()?.CheckRule((PlayerSpectatingRule r) => r.IsSpectating) ?? false; } public static void SetSpectating(this NetworkPlayer player, bool isSpectating) { player.PlayerID.SetSpectating(isSpectating); } public static void SetSpectating(this PlayerID playerID, bool isSpectating) { GetOrCreateSpectatingModifier(playerID).Modify(delegate(PlayerSpectatingRule rule) { rule.IsSpectating = isSpectating; }); } public static void StopSpectatingAll() { PlayerDataManager.ForEachPlayerData(delegate(PlayerData data) { data.GetRuleInstance<PlayerSpectatingRule>().Modify(delegate(PlayerSpectatingRule rule) { rule.IsSpectating = false; }); }); } } } namespace MashGamemodeLibrary.Player.Data { internal record NetworkRulePacket(PlayerID PlayerID, ulong RuleHash); [RequireStaticConstructor] public class PlayerData : IEventReceiver { private static readonly IBehaviourCache<IPlayerRuleChangedCallback> PlayerRuleChangedCache = BehaviourManager.CreateCache<IPlayerRuleChangedCallback>(); private static readonly FactoryTypedRegistry<IPlayerExtender> ExtenderRegistry = new FactoryTypedRegistry<IPlayerExtender>(); private static readonly FactoryTypedRegistry<IPlayerRule> RuleRegistry = new FactoryTypedRegistry<IPlayerRule>(); private static readonly FactoryTypedRegistry<IEventCaller> EventCallerRegistry = new FactoryTypedRegistry<IEventCaller>(); private readonly Dictionary<Type, IPlayerExtender> _extenderCache = new Dictionary<Type, IPlayerExtender>(); private readonly Dictionary<Type, IPlayerRuleInstance> _ruleInstanceCache = new Dictionary<Type, IPlayerRuleInstance>(); private readonly Dictionary<ulong, IPlayerRuleInstance> _ruleHashCache = new Dictionary<ulong, IPlayerRuleInstance>(); private readonly Dictionary<Type, IEventCaller> _eventCallerCache = new Dictionary<Type, IEventCaller>(); private readonly Dictionary<Type, List<IPlayerExtender>> _ruleChangeCallbacks = new Dictionary<Type, List<IPlayerExtender>>(); private readonly Dictionary<Type, List<IPlayerExtender>> _eventCallbacks = new Dictionary<Type, List<IPlayerExtender>>(); private static readonly NetworkRuleChangeEvent NetworkRuleChangeEvent = new NetworkRuleChangeEvent("PlayerData.RuleChange"); private static readonly NetworkRuleBulkChangeEvent NetworkRuleBulkChangeEvent = new NetworkRuleBulkChangeEvent("PlayerData.RuleBulkChange"); public PlayerID PlayerID { get; init; } public NetworkPlayer? NetworkPlayer { get; private set; } public IEnumerable<IPlayerExtender> Extenders => _extenderCache.Values; public IEnumerable<IPlayerRuleInstance> RuleInstances => _ruleInstanceCache.Values; public IEnumerable<IEventCaller> EventCallers => _eventCallerCache.Values; public static void Register<TMod>() { ExtenderRegistry.RegisterAll<TMod>(); RuleRegistry.RegisterAll<TMod>(); EventCallerRegistry.RegisterAll<TMod>(); } public PlayerData(PlayerID playerID) { PlayerID = playerID; IEnumerableExtensions.ForEach<IPlayerExtender>(ExtenderRegistry.GetAll(), (Action<IPlayerExtender>)AddExtender); IEnumerableExtensions.ForEach<Type>(RuleRegistry.GetAllTypes(), (Action<Type>)AddRule); IEnumerableExtensions.ForEach<IEventCaller>(EventCallerRegistry.GetAll(), (Action<IEventCaller>)AddEventCaller); } private void AddExtender(IPlayerExtender playerExtender) { _extenderCache.Add(playerExtender.GetType(), playerExtender); foreach (Type ruleType in playerExtender.RuleTypes) { _ruleChangeCallbacks.GetValueOrCreate(ruleType).Add(playerExtender); } foreach (Type eventType in playerExtender.EventTypes) { _eventCallbacks.GetValueOrCreate(eventType).Add(playerExtender); } } private void AddRule<TRule>() where TRule : class, IPlayerRule, new() { PlayerRuleInstance<TRule> playerRuleInstance = new PlayerRuleInstance<TRule>(this); _ruleInstanceCache.Add(typeof(TRule), playerRuleInstance); _ruleHashCache.Add(playerRuleInstance.Hash, playerRuleInstance); } private void AddRule(Type ruleType) { IPlayerRuleInstance playerRuleInstance = (IPlayerRuleInstance)Activator.CreateInstance(typeof(PlayerRuleInstance<>).MakeGenericType(ruleType), this); _ruleInstanceCache.Add(ruleType, playerRuleInstance); _ruleHashCache.Add(playerRuleInstance.Hash, playerRuleInstance); } private void AddEventCaller(IEventCaller eventCaller) { _eventCallerCache.Add(eventCaller.GetType(), eventCaller); } internal void NotifyRuleChanged(IPlayerRuleInstance ruleInstance) { IPlayerRuleInstance ruleInstance2 = ruleInstance; IPlayerRule rule = ruleInstance2.GetBaseRule(); _ruleChangeCallbacks.GetValueOrDefault(rule.GetType())?.ForEach(delegate(IPlayerExtender e) { e.OnRuleChanged(this); }); PlayerDataManager.CallEventOnAll(new PlayerRuleChangedEvent(PlayerID, rule)); if (NetworkPlayer != null) { PlayerRuleChangedCache.ForEach(delegate(IPlayerRuleChangedCallback e) { e.OnPlayerRuleChanged(NetworkPlayer, rule); }); } Executor.RunIfHost(delegate { NetworkRuleChangeEvent.Send(PlayerID, ruleInstance2); }); } internal void NotifyAllRules() { foreach (IPlayerExtender value in _extenderCache.Values) { value.OnRuleChanged(this); } foreach (IPlayerRuleInstance ruleInstance in RuleInstances) { IPlayerRule rule = ruleInstance.GetBaseRule(); PlayerDataManager.CallEventOnAll(new PlayerRuleChangedEvent(PlayerID, rule)); if (NetworkPlayer != null) { PlayerRuleChangedCache.ForEach(delegate(IPlayerRuleChangedCallback e) { e.OnPlayerRuleChanged(NetworkPlayer, rule); }); } } Executor.RunIfHost(delegate { NetworkRuleBulkChangeEvent.Send(this); }); } public void OnRigCreated(NetworkPlayer player, RigManager rigManager) { NetworkPlayer player2 = player; RigManager rigManager2 = rigManager; NetworkPlayer = player2; IEnumerableExtensions.ForEach<IPlayerExtender>(Extenders, (Action<IPlayerExtender>)delegate(IPlayerExtender e) { e.OnPlayerChanged(player2, rigManager2); }); IEnumerableExtensions.ForEach<IEventCaller>(EventCallers, (Action<IEventCaller>)delegate(IEventCaller e) { e.OnEnable(this, player2); }); } public bool CheckRule<TRule>(Func<TRule, bool> predicate) where TRule : class, IPlayerRule, new() { if (!_ruleInstanceCache.TryGetValue(typeof(TRule), out IPlayerRuleInstance value)) { return false; } if (!(value is PlayerRuleInstance<TRule> playerRuleInstance)) { return false; } return predicate(playerRuleInstance.GetRule()); } public TRule GetRule<TRule>() where TRule : class, IPlayerRule, new() { if (!_ruleInstanceCache.TryGetValue(typeof(TRule), out IPlayerRuleInstance value)) { throw new KeyNotFoundException($"Rule of type {typeof(TRule)} not found for player {PlayerID}"); } return ((value as PlayerRuleInstance<TRule>) ?? throw new InvalidCastException($"Rule instance of type {value.GetType()} cannot be cast to PlayerRuleInstance<{typeof(TRule)}> for player {PlayerID}")).GetRule(); } public void ModifyRule<TRule>(PlayerRuleInstance<TRule>.ModifyRuleDelegate modifier) where TRule : class, IPlayerRule, new() { if (!_ruleInstanceCache.TryGetValue(typeof(TRule), out IPlayerRuleInstance value)) { throw new KeyNotFoundException($"Rule of type {typeof(TRule)} not found for player {PlayerID}"); } ((value as PlayerRuleInstance<TRule>) ?? throw new InvalidCastException($"Rule instance of type {value.GetType()} cannot be cast to PlayerRuleInstance<{typeof(TRule)}> for player {PlayerID}")).Modify(modifier); } public IPlayerRuleInstance? GetRuleByHash(ulong hash) { return _ruleHashCache.GetValueOrDefault(hash); } public PlayerRuleInstance<TRule> GetRuleInstance<TRule>() where TRule : class, IPlayerRule, new() { if (!_ruleInstanceCache.TryGetValue(typeof(TRule), out IPlayerRuleInstance value)) { throw new KeyNotFoundException($"Rule of type {typeof(TRule)} not found for player {PlayerID}"); } return value as PlayerRuleInstance<TRule>; } public T GetExtender<T>() where T : class, IPlayerExtender { if (!_extenderCache.TryGetValue(typeof(T), out IPlayerExtender value)) { throw new KeyNotFoundException($"Extender of type {typeof(T)} not found for player {PlayerID}"); } return value as T; } public void ResetRules() { foreach (IPlayerRuleInstance ruleInstance in RuleInstances) { ruleInstance.Reset(notify: false); } NotifyAllRules(); } public void SendCatchup(PlayerID playerID) { NetworkRuleBulkChangeEvent.SendTo(playerID, this); } public void ReceiveEvent(IPlayerEvent playerEvent) { IPlayerEvent playerEvent2 = playerEvent; IEnumerableExtensions.ForEach<IPlayerExtender>(Extenders, (Action<IPlayerExtender>)delegate(IPlayerExtender e) { e.OnEvent(playerEvent2); }); } } public static class PlayerDataManager { private static readonly Dictionary<byte, PlayerData> PlayerData; static PlayerDataManager() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown PlayerData = new Dictionary<byte, PlayerData>(); MultiplayerHooking.OnPlayerJoined += new PlayerUpdate(OnPlayerJoined); MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft); NetworkPlayer.OnNetworkRigCreated += OnNetworkRigCreated; } private static void OnPlayerJoined(PlayerID playerID) { PlayerID playerID2 = playerID; PlayerData.GetValueOrCreate(PlayerID.op_Implicit(playerID2), () => new PlayerData(playerID2)); } private static void OnNetworkRigCreated(NetworkPlayer networkPlayer, RigManager rigManager) { PlayerID playerID = networkPlayer.PlayerID; PlayerData.GetValueOrCreate(PlayerID.op_Implicit(playerID), () => new PlayerData(playerID)).OnRigCreated(networkPlayer, rigManager); } private static void OnPlayerLeft(PlayerID playerId) { PlayerData.Remove(PlayerID.op_Implicit(playerId)); } public static PlayerData? GetPlayerData(NetworkPlayer networkPlayer) { if (!networkPlayer.HasRig) { return null; } return PlayerData.GetValueOrDefault(PlayerID.op_Implicit(networkPlayer.PlayerID)); } public static PlayerData? GetOrCreatePlayerData(byte playerID) { if (PlayerData.TryGetValue(playerID, out PlayerData value)) { return value; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(playerID, ref val)) { return null; } PlayerData playerData = new PlayerData(val.PlayerID); PlayerData[playerID] = playerData; if (val.HasRig) { playerData.OnRigCreated(val, val.RigRefs.RigManager); } return playerData; } public static PlayerData? GetPlayerData(byte playerID) { return GetOrCreatePlayerData(playerID); } public static bool TryGetPlayerData(PlayerID playerId, [MaybeNullWhen(false)] out PlayerData playerData) { playerData = GetOrCreatePlayerData(PlayerID.op_Implicit(playerId)); return playerData != null; } public static PlayerData? GetLocalPlayerData() { if (PlayerIDManager.LocalID == null) { return null; } return GetOrCreatePlayerData(PlayerIDManager.LocalSmallID); } public static void ForEachPlayerData(Action<PlayerData> action) { foreach (PlayerData value in PlayerData.Values) { action(value); } } public static void ModifyAll<TRule>(PlayerRuleInstance<TRule>.ModifyRuleDelegate modifier) where TRule : class, IPlayerRule, new() { PlayerRuleInstance<TRule>.ModifyRuleDelegate modifier2 = modifier; ForEachPlayerData(delegate(PlayerData playerData) { playerData.ModifyRule(modifier2); }); } public static void ResetRules() { foreach (PlayerData value in PlayerData.Values) { value.ResetRules(); } } public static void Clear() { PlayerData.Clear(); } internal static void SendCatchup(PlayerID playerID) { PlayerID playerID2 = playerID; ForEachPlayerData(delegate(PlayerData playerData) { playerData.SendCatchup(playerID2); }); } internal static void CallEventOnAll(IPlayerEvent playerEvent) { IPlayerEvent playerEvent2 = playerEvent; ForEachPlayerData(delegate(PlayerData playerData) { playerData.ReceiveEvent(playerEvent2); }); } } } namespace MashGamemodeLibrary.Player.Data.Rules { public interface IPlayerRule : INetSerializable { bool IsEnabled { get; } int GetHash(); } public interface IPlayerRuleInstance { ulong Hash { get; } void Deserialize(NetReader reader, bool notify = true); IPlayerRule GetBaseRule(); void Reset(bool notify = true); } public class PlayerRuleInstance<TRule> : IPlayerRuleInstance where TRule : class, IPlayerRule, new() { public delegate void ModifyRuleDelegate(TRule rule); private readonly PlayerData _playerData; private TRule _localRule = new TRule(); private readonly TRule _networkedRule = new TRule(); public ulong Hash { get; } public PlayerRuleInstance(PlayerData playerData) { _playerData = playerData; Hash = typeof(TRule).GetStableHash(); } public void Modify(ModifyRuleDelegate modifier) { if (!NetworkInfo.IsHost) { InternalLogger.Error("Player Rules can't be edited on the client"); return; } int hash = _localRule.GetHash(); modifier.Try(delegate(ModifyRuleDelegate m) { m(_localRule); }); if (hash != _localRule.GetHash()) { NotifyChange(); } } public void NotifyChange() { _playerData.NotifyRuleChanged(this); } public TRule GetRule() { if (NetworkInfo.IsClient) { return _networkedRule; } return _localRule; } public IPlayerRule GetBaseRule() { return GetRule(); } public void Reset(bool notifyChange = true) { _localRule = new TRule(); if (notifyChange) { NotifyChange(