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(); RegisterInternal(); 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() { EcsManager.RegisterAll(); RemoteEventMessageHandler.RegisterMod(); AutoRegistry.Register(); PlayerData.Register(); } public static void Register() { GamePhaseManager.Registry.RegisterAll(); LogicTeamManager.Registry.RegisterAll(); RegisterInternal(); } } } 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() != 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() { 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 : INetSerializable where TValue : AutoSerialized { private static readonly List Serializables; static AutoSerialized() { Serializables = new List(); Type typeFromHandle = typeof(TValue); bool flag = typeFromHandle.GetCustomAttribute() != null; FieldInfo[] fields = typeFromHandle.GetFields(); foreach (FieldInfo fieldInfo in fields) { if (flag || fieldInfo.GetCustomAttribute() != 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(this Dictionary dict, TKey key, Func factory) where TKey : notnull { if (dict.TryGetValue(key, out TValue value)) { return value; } return dict[key] = factory(); } public static TValue GetValueOrCreate(this Dictionary 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(this Dictionary dict, Action> onEach) where TKey : notnull { IEnumerableExtensions.ForEach>((IEnumerable>)dict, onEach); dict.Clear(); } public static void Clear(this Dictionary dict, Action onEach) where TKey : notnull { Action onEach2 = onEach; dict.Clear(delegate(KeyValuePair 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.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(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.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(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 TimeRemainingEvent = new RemoteEvent(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 OnHit; public float Time; public MarkerType Type; public TimeMarker(MarkerType type, float time, Action 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? 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 { ulong CreateID(Type type); ulong CreateID() where T : notnull, TKey; ulong CreateID(TKey instance); ulong GetID() where T : notnull, TKey { return this.CreateID(); } ulong GetID(Type type) { return CreateID(type); } ulong GetID(TKey instance) { return CreateID(instance); } } } namespace MashGamemodeLibrary.Registry.Typed { public class FactoryTypedRegistry : TypedRegistry, TValue> where TValue : class { private const int NullID = 0; protected override Func Create() { return () => (TValue)(object)new T(); } protected override bool TryToValue(Func? 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 : IKeyable where TValue : notnull { new ulong CreateID(Type type); void Register() where T : TValue, new(); void RegisterAll(); Type? GetType(ulong id); bool TryGetType(ulong id, [MaybeNullWhen(false)] out Type type); TValue? Get(ulong id); TValue? Get(Type type); T? Get() where T : TValue; bool TryGet(ulong id, [MaybeNullWhen(false)] out TValue entry); bool TryGet(Type type, [MaybeNullWhen(false)] out TValue entry); bool TryGet([MaybeNullWhen(false)] out T entry) where T : TValue; IEnumerable GetAllTypes(); } public class SingletonTypedRegistry : TypedRegistry where TValue : notnull { public void Register(T value) where T : TValue { ulong orCreateId = base.GetOrCreateId(); base.Register(orCreateId, value); } protected override TValue Create() { 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 : KeyedRegistry, ITypedRegistry, IKeyable where TInternal : notnull where TValue : notnull { private readonly Dictionary _stableHashCache = new Dictionary(); private readonly Dictionary _typeCache = new Dictionary(); public ulong CreateID(Type type) { return type.GetStableHash(); } public ulong CreateID() where T : TValue { return CreateID(typeof(T)); } public ulong CreateID(TValue instance) { return CreateID(instance.GetType()); } public void Register() where T : TValue, new() { ulong orCreateId = this.GetOrCreateId(); TInternal value = Create(); Register(orCreateId, value); } public void RegisterAll() { 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((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)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() where T : TValue { ulong iD = this.GetID(); 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([MaybeNullWhen(false)] out T entry) where T : TValue { ulong iD = this.GetID(); if (!TryGet(iD, out var value)) { entry = default(T); return false; } entry = (T)(object)value; return true; } public IEnumerable GetAllTypes() { return _typeCache.Values; } public IEnumerable 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() 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() where T : TValue { return GetOrCreateId(typeof(T)); } protected abstract TInternal Create() where T : TValue, new(); protected abstract bool TryToValue(TInternal? from, [MaybeNullWhen(false)] out TValue value); } public abstract class TypedRegistry : TypedRegistry where TValue : notnull { } } namespace MashGamemodeLibrary.Registry.Keyed { public interface IKeyedRegistry : IEnumerable>, IEnumerable, IGuaranteeStaticConstructor where TKey : notnull where TValue : notnull { public delegate void OnRegisterHandler(TKey key, TValue value); event OnRegisterHandler? OnRegister; void Register(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 : IKeyedRegistry, IEnumerable>, IEnumerable, IGuaranteeStaticConstructor where TKey : notnull where TValue : notnull { private readonly Dictionary _dictionary = new Dictionary(); public int Count => _dictionary.Count; public event IKeyedRegistry.OnRegisterHandler? OnRegister; public void Register(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> GetEnumerator() { return _dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } namespace MashGamemodeLibrary.Registry.Generic { public class RuntimeTypedRegistry : KeyedRegistry, IKeyable { private readonly Type RootType; public RuntimeTypedRegistry(Type rootType) { RootType = rootType; } public ulong CreateID(Type type) { return type.Name.GetStableHash(); } public ulong CreateID() where T : notnull { return CreateID(typeof(T)); } public ulong CreateID(object instance) { return instance.GetType().Name.GetStableHash(); } public void Register(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(); Register(id, value); } public object? Get() where T : notnull { ulong id = CreateID(); 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(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 FetchLoadoutEvent = new RemoteEvent("FetchLoadoutEvent", OnFetchLoadout, CommonNetworkRoutes.HostToAll); private static readonly RemoteEvent AssignLoadoutEvent = new RemoteEvent("AssignLoadoutEvent", OnAssignLoadout, CommonNetworkRoutes.HostToAll); private static readonly Dictionary> Items = new Dictionary>(); private static void ClearWeapons() { Items.GetValueOrDefault(WeaponType.Primary)?.Clear(); Items.GetValueOrDefault(WeaponType.Secondary)?.Clear(); Items.GetValueOrDefault(WeaponType.Tertiary)?.Clear(); } private static List GetCrateList(WeaponType type) { if (Items.TryGetValue(type, out List value)) { return value; } List list = new List(); Items[type] = list; return list; } private static WeaponType? GetCrateType(Crate crate) { Enumerator 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 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 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 barcodes) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown List 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 value)) { return null; } if (value.Count == 0) { return null; } return ((Scannable)IEnumerableExtensions.GetRandom((IEnumerable)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 barcodes) { FetchLoadoutEvent.Call(new FetchLoadoutPacket { LoadCommandType = LoadCommandType.Weapon, Barcodes = barcodes.ToArray() }); } public static void LoadUtility(IEnumerable 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 SpawnedGuns = new HashSet(); 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)rig.inventory.bodySlots).FirstOrDefault((Func)((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(); if (extender != null) { WeaponSlot component = ((EntityComponentExtender)(object)extender).Component; if (!((Object)(object)component == (Object)null) && !((Object)(object)component.interactableHost == (Object)null) && !((EntityComponentExtender)(object)extender).Component.interactableHost.IsAttached) { ((InventoryHandReceiver)slot).OnHandDrop(((Il2CppObjectBase)component.interactableHost).TryCast()); } } }, 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 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(); if (Object.op_Implicit((Object)(object)componentInParent)) { return ((Scannable)componentInParent.SpawnableCrate)._barcode; } return null; } private static Barcode? GetHeldSpawnableBarcode() { return ((IEnumerable)(object)new Hand[2] { Player.LeftHand, Player.RightHand }).Select(GetHeldSpawnableBarcode).OfType().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 EnabledTeams; public static readonly FactoryTypedRegistry Registry; private static readonly SyncedDictionary 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(); Registry = new FactoryTypedRegistry(); AssignedTeams = new SyncedDictionary("sync.AssignedTeams", new ByteEncoder(), new DynamicInstanceEncoder(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() where T : LogicTeam { return GetTeamID(typeof(T)); } public static void Enable() where T : LogicTeam { ulong teamID = GetTeamID(); 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(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() where T : LogicTeam { return PlayerIDManager.LocalID.IsTeam(); } 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(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(this PlayerID playerID) where T : LogicTeam { PlayerID playerID2 = playerID; Executor.RunIfHost(delegate { if (!Registry.TryGet(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 list = EnabledTeams.Select((ulong id) => Registry.Get(id)).OfType().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() where T : LogicTeam { Executor.RunIfHost(delegate { if (!Registry.TryGet(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 dictionary = new Dictionary(); foreach (ulong enabledTeam in EnabledTeams) { if (Registry.TryGetType(enabledTeam, out Type type)) { dictionary[enabledTeam] = AssignedTeams.Count>((KeyValuePair kv) => kv.Value.GetType() == type); } } KeyValuePair keyValuePair = dictionary.DefaultIfEmpty().MinBy((KeyValuePair kv) => kv.Value); LogicTeam value = Registry.Get(keyValuePair.Key); AssignedTeams[PlayerID.op_Implicit(playerID2)] = value; } }); } public static void AssignRandom(IRandomProvider? provider = null) where T : LogicTeam { if (provider == null) { provider = new BasicRandomProvider(() => (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(); } } 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 PlayerTeamIndices = new SyncedDictionary("PersistentTeams_PlayerTeamIndices", new ByteEncoder(), new IntEncoder()); private static readonly SyncedDictionary TeamScores = new SyncedDictionary("PersistentTeams_TeamScores", new IntEncoder(), new IntEncoder()); private static readonly HashSet LateJoinerQueue = new HashSet(); private static readonly List TeamIds = new List(); 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() where T : LogicTeam { AddTeamID(LogicTeamManager.Registry.CreateID()); } 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 playerIds) { int num = 0; foreach (PlayerID item in playerIds.Shuffle()) { Assign(item, num); num = (num + 1) % TeamIds.Count; } } public static void OverwritePlayers(IEnumerable playerIds) { PlayerTeamIndices.Clear(); AddPlayers(playerIds); } public static void OverwritePlayers(IEnumerable> teamPlayerIds) { PlayerTeamIndices.Clear(); int num = 0; foreach (IEnumerable 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 dictionary = (from p in PlayerTeamIndices select p.Value into i group i by i).ToDictionary((IGrouping g) => g.Key, (IGrouping 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 t) => t.Value).Key; Assign(item, key); Dictionary dictionary2 = dictionary; value = key; dictionary2[value]++; } } _shift++; foreach (KeyValuePair 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 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(OnAssigned); } internal void Remove() { if (_owner != null) { Owner.Icon.Texture = null; Owner.Icon.Visible = false; Executor.RunChecked(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 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.Default.GetHashCode(Position) * -1521134295 + EqualityComparer.Default.GetHashCode(Radius)) * -1521134295 + EqualityComparer.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.Default.Equals(Position, other.Position) && EqualityComparer.Default.Equals(Radius, other.Radius)) { return EqualityComparer.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 _validSpawnPoints = new List(); 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 val2 = new List(); NavMeshBuilder.CollectSources(val, 8193, (NavMeshCollectGeometry)1, 0, new List(), 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 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 sortedList = new SortedList(); 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)(object)val2.corners) { _ = item; } if ((int)val2.status == 1) { target = ((IEnumerable)val2.corners).Last(); } if (CanReachAny(((IEnumerable)val2.corners).LastOrDefault(target), targets) && CanWalkPath((IReadOnlyList)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 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)val4.corners).LastOrDefault(val3), targets) && CanWalkPath((IReadOnlyList)val4.corners)) { return val3; } } if (_validSpawnPoints.Count <= 0) { return null; } return IEnumerableExtensions.GetRandom((IEnumerable)_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 AllSlotTypes = Enum.GetValues(typeof(SlotType)).Cast().ToList(); private static readonly SlotData DefaultSlotData = new SlotData(null); private readonly Dictionary _slotAssigners = new Dictionary(); 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() : 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)(object)ColorAdjustments.colorFilter).value = color; } public readonly void SetBrightness(float value) { ((VolumeParameter)(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(); obj.isGlobal = true; obj.priority = 10f; obj.weight = 1f; VolumeProfile val4 = (obj.sharedProfile = ScriptableObject.CreateInstance()); ColorAdjustments val5 = val4.Add(true); ((VolumeParameter)(object)val5.contrast).value = 20f; ((VolumeParameter)(object)val5.postExposure).value = _nightVisionBrightness; ((VolumeParameter)(object)val5.colorFilter).value = Color.white; Light obj2 = val2.AddComponent(); 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(this NetworkPlayer player, [MaybeNullWhen(false)] out T component) where T : class, IComponent { if (player.NetworkEntity == null) { component = null; return false; } component = player.NetworkEntity.GetComponent(); return component != null; } public static bool HasComponent(this NetworkPlayer player) where T : class, IComponent { NetworkEntity networkEntity = player.NetworkEntity; return ((networkEntity != null) ? networkEntity.GetComponent() : null) != null; } public static bool HasComponent(this NetworkPlayer player, Func predicate) where T : class, IComponent { if (player.NetworkEntity == null) { return false; } T component = player.NetworkEntity.GetComponent(); 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(this NetworkPlayer player, Func factory) where T : class, IComponent { if (player.NetworkEntity.GetComponent() != null) { return false; } player.NetworkEntity.AddComponent(factory()); return true; } public static void RemoveComponent(this NetworkPlayer player) where T : class, IComponent { player.NetworkEntity?.RemoveComponent(); } public static void ToggleComponent(this NetworkPlayer player, bool state, Func factory) where T : class, IComponent { if (player.NetworkEntity != null) { if (state) { player.TryAddComponent(factory); } else { player.RemoveComponent(); } } } } 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 GetOrCreateSpectatingModifier(PlayerID playerId) { return (PlayerDataManager.GetPlayerData(PlayerID.op_Implicit(playerId)) ?? throw new Exception($"Player data not found for player ID: {playerId}")).GetRuleInstance(); } 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().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 PlayerRuleChangedCache = BehaviourManager.CreateCache(); private static readonly FactoryTypedRegistry ExtenderRegistry = new FactoryTypedRegistry(); private static readonly FactoryTypedRegistry RuleRegistry = new FactoryTypedRegistry(); private static readonly FactoryTypedRegistry EventCallerRegistry = new FactoryTypedRegistry(); private readonly Dictionary _extenderCache = new Dictionary(); private readonly Dictionary _ruleInstanceCache = new Dictionary(); private readonly Dictionary _ruleHashCache = new Dictionary(); private readonly Dictionary _eventCallerCache = new Dictionary(); private readonly Dictionary> _ruleChangeCallbacks = new Dictionary>(); private readonly Dictionary> _eventCallbacks = new Dictionary>(); 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 Extenders => _extenderCache.Values; public IEnumerable RuleInstances => _ruleInstanceCache.Values; public IEnumerable EventCallers => _eventCallerCache.Values; public static void Register() { ExtenderRegistry.RegisterAll(); RuleRegistry.RegisterAll(); EventCallerRegistry.RegisterAll(); } public PlayerData(PlayerID playerID) { PlayerID = playerID; IEnumerableExtensions.ForEach(ExtenderRegistry.GetAll(), (Action)AddExtender); IEnumerableExtensions.ForEach(RuleRegistry.GetAllTypes(), (Action)AddRule); IEnumerableExtensions.ForEach(EventCallerRegistry.GetAll(), (Action)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() where TRule : class, IPlayerRule, new() { PlayerRuleInstance playerRuleInstance = new PlayerRuleInstance(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(Extenders, (Action)delegate(IPlayerExtender e) { e.OnPlayerChanged(player2, rigManager2); }); IEnumerableExtensions.ForEach(EventCallers, (Action)delegate(IEventCaller e) { e.OnEnable(this, player2); }); } public bool CheckRule(Func predicate) where TRule : class, IPlayerRule, new() { if (!_ruleInstanceCache.TryGetValue(typeof(TRule), out IPlayerRuleInstance value)) { return false; } if (!(value is PlayerRuleInstance playerRuleInstance)) { return false; } return predicate(playerRuleInstance.GetRule()); } public TRule GetRule() 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) ?? throw new InvalidCastException($"Rule instance of type {value.GetType()} cannot be cast to PlayerRuleInstance<{typeof(TRule)}> for player {PlayerID}")).GetRule(); } public void ModifyRule(PlayerRuleInstance.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) ?? 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 GetRuleInstance() 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; } public T GetExtender() 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(Extenders, (Action)delegate(IPlayerExtender e) { e.OnEvent(playerEvent2); }); } } public static class PlayerDataManager { private static readonly Dictionary 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(); 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 action) { foreach (PlayerData value in PlayerData.Values) { action(value); } } public static void ModifyAll(PlayerRuleInstance.ModifyRuleDelegate modifier) where TRule : class, IPlayerRule, new() { PlayerRuleInstance.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 : 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(); } } public void Deserialize(NetReader reader, bool notifyChange = true) { ((INetSerializable)_networkedRule).Serialize((INetSerializer)(object)reader); if (notifyChange) { NotifyChange(); } } } } namespace MashGamemodeLibrary.Player.Data.Rules.Rules { public class ForceHideRule : IPlayerRule, INetSerializable { private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isEnabled); } public int GetHash() { return _isEnabled.GetHashCode(); } } public class HideEnemyNametagsRule : IPlayerRule, INetSerializable { private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isEnabled); } public int GetHash() { return _isEnabled.GetHashCode(); } } public class PlayerAmmunitionLimitRule : IPlayerRule, INetSerializable { private int? _ammunitionLimit; public int? AmmunitionLimit { get { return _ammunitionLimit; } set { _ammunitionLimit = value; } } public bool IsEnabled => _ammunitionLimit.HasValue; public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _ammunitionLimit); } public int GetHash() { return _ammunitionLimit ?? (-1); } } public class PlayerCrippledRule : IPlayerRule, INetSerializable { private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isEnabled); } public bool IsEqual(IPlayerRule playerRule) { if (!(playerRule is PlayerCrippledRule playerCrippledRule)) { return false; } return _isEnabled == playerCrippledRule._isEnabled; } public int GetHash() { return _isEnabled.GetHashCode(); } } public class PlayerSpectatingRule : IPlayerRule, INetSerializable { private bool _isSpectating; public bool IsSpectating { get { return _isSpectating; } set { _isSpectating = value; } } public bool IsEnabled => _isSpectating; public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isSpectating); } public int GetHash() { return _isSpectating.GetHashCode(); } } public class SpectatorNightvisionRule : IPlayerRule, INetSerializable { private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isEnabled); } public int GetHash() { return _isEnabled.GetHashCode(); } } public class WindBuffetSFXEnabled : IPlayerRule, INetSerializable { private bool _isEnabled = true; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _isEnabled); } public int GetHash() { return _isEnabled.GetHashCode(); } } } namespace MashGamemodeLibrary.Player.Data.Rules.Networking { internal record NetworkRuleBulkChangePacket(PlayerID PlayerID, IEnumerable Rules); internal class NetworkRuleBulkChangeEvent : GenericRemoteEvent, ICatchup { public NetworkRuleBulkChangeEvent(string name) : base(name, CommonNetworkRoutes.HostToRemote) { } public void Send(PlayerData playerData) { NetworkRuleBulkChangePacket data = new NetworkRuleBulkChangePacket(playerData.PlayerID, playerData.RuleInstances); Relay(data); } public void SendTo(PlayerID playerId, PlayerData playerData) { NetworkRuleBulkChangePacket data = new NetworkRuleBulkChangePacket(playerData.PlayerID, playerData.RuleInstances); Relay(data, PlayerID.op_Implicit(playerId)); } protected override int? GetSize(NetworkRuleBulkChangePacket data) { return null; } protected override void Write(NetWriter writer, NetworkRuleBulkChangePacket data) { IPlayerRuleInstance[] array = data.Rules.ToArray(); writer.Write(PlayerID.op_Implicit(data.PlayerID)); writer.Write(array.Length); IPlayerRuleInstance[] array2 = array; foreach (IPlayerRuleInstance playerRuleInstance in array2) { writer.Write(playerRuleInstance.Hash); ((INetSerializable)playerRuleInstance.GetBaseRule()).Serialize((INetSerializer)(object)writer); } } protected override void Read(byte smallId, NetReader reader) { PlayerData playerData = PlayerDataManager.GetPlayerData(reader.ReadByte()); if (playerData != null) { int num = reader.ReadInt32(); for (int i = 0; i < num; i++) { ulong hash = reader.ReadUInt64(); playerData.GetRuleByHash(hash)?.Deserialize(reader, notify: false); } playerData.NotifyAllRules(); } } public void OnCatchup(PlayerID playerId) { PlayerDataManager.SendCatchup(playerId); } } internal record NetworkRuleChangePacket(PlayerID PlayerID, ulong RuleHash, IPlayerRule Rule); internal class NetworkRuleChangeEvent : GenericRemoteEvent { public NetworkRuleChangeEvent(string name) : base(name, CommonNetworkRoutes.HostToRemote) { } public void Send(PlayerID playerId, IPlayerRuleInstance ruleInstance) { NetworkRuleChangePacket data = new NetworkRuleChangePacket(playerId, ruleInstance.Hash, ruleInstance.GetBaseRule()); Relay(data); } protected override int? GetSize(NetworkRuleChangePacket data) { return null; } protected override void Write(NetWriter writer, NetworkRuleChangePacket data) { writer.Write(PlayerID.op_Implicit(data.PlayerID)); writer.Write(data.RuleHash); ((INetSerializable)data.Rule).Serialize((INetSerializer)(object)writer); } protected override void Read(byte smallId, NetReader reader) { PlayerData playerData = PlayerDataManager.GetPlayerData(reader.ReadByte()); if (playerData != null) { ulong hash = reader.ReadUInt64(); playerData.GetRuleByHash(hash)?.Deserialize(reader); } } } } namespace MashGamemodeLibrary.Player.Data.Components.Visibility.Parts { public class PlayerAvatarVisibility : IPlayerVisibility { private RigManager? _rigManager; private bool _isVisible = true; private void UpdateAvatarVisibility() { if (!((Object)(object)_rigManager == (Object)null)) { Avatar avatar = _rigManager._avatar; if (!((Object)(object)avatar == (Object)null)) { ((Component)avatar).gameObject.SetActive(_isVisible); } } } public void SetVisible(PlayerVisibility visibility) { _isVisible = visibility.VisibleForLocalPlayer; UpdateAvatarVisibility(); } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _rigManager = rigManager; UpdateAvatarVisibility(); } public void OnAvatarChanged(Avatar avatar) { UpdateAvatarVisibility(); } } } namespace MashGamemodeLibrary.Player.Data.Extenders { public interface IPlayerExtender { IEnumerable RuleTypes => Array.Empty(); IEnumerable EventTypes => Array.Empty(); void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager); void OnRuleChanged(PlayerData data); void OnEvent(IPlayerEvent playerEvent); } } namespace MashGamemodeLibrary.Player.Data.Extenders.WindBuffetSFX { public class WindBuffetSFXExtender : IPlayerExtender { private GameObject? _windBuffetSfxObject; private NetworkPlayer? _player; private bool _enabled; private bool _isSpectating; public IEnumerable RuleTypes => new Type[2] { typeof(WindBuffetSFXEnabled), typeof(PlayerSpectatingRule) }; public IEnumerable EventTypes => new Type[1] { typeof(PlayerRuleChangedEvent) }; private void Update() { NetworkPlayer? player = _player; PlayerID val = ((player != null) ? player.PlayerID : null); if (val == null || !val.IsMe) { bool flag = _enabled && (!_isSpectating || SpectatorExtender.IsLocalPlayerSpectating()); if ((Object)(object)_windBuffetSfxObject != (Object)null && _windBuffetSfxObject.activeSelf != flag) { _windBuffetSfxObject.SetActive(flag); } } } private void ConfigureSfx() { if ((Object)(object)_windBuffetSfxObject == (Object)null || _player == null || _player.PlayerID.IsMe) { return; } WindBuffetSFX component = _windBuffetSfxObject.GetComponent(); if (!((Object)(object)component == (Object)null)) { AudioSource buffetSrc = component._buffetSrc; if (!((Object)(object)buffetSrc == (Object)null)) { buffetSrc.outputAudioMixerGroup = Audio3dManager.ambience; buffetSrc.spatialBlend = 1f; buffetSrc.spatialize = true; } } } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _player = networkPlayer; Transform obj = ((Component)rigManager).transform.Find("VRControllerRig/TrackingSpace/Headset/WindBuffetSFX"); _windBuffetSfxObject = ((obj != null) ? ((Component)obj).gameObject : null); ConfigureSfx(); Update(); } public void OnRuleChanged(PlayerData data) { _enabled = data.CheckRule((WindBuffetSFXEnabled p) => p.IsEnabled); _isSpectating = data.CheckRule((PlayerSpectatingRule p) => p.IsSpectating); Update(); } public void OnEvent(IPlayerEvent playerEvent) { if (playerEvent is PlayerRuleChangedEvent playerRuleChangedEvent && playerRuleChangedEvent.Rule is PlayerSpectatingRule && playerRuleChangedEvent.Player.Equals(PlayerIDManager.LocalID)) { Update(); } } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Visibility { public interface IPlayerVisibility { void SetVisible(PlayerVisibility visibility); void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager); void OnAvatarChanged(Avatar avatar); } public class PlayerVisibility : IPlayerExtender { private bool _hasNightVision; private bool _nightVisionEnabled; private bool _hideNametagForEnemies; private bool _isVisible = true; private bool _visibleForLocalPlayerCache; private bool _nametagVisibleCache; private readonly IPlayerVisibility[] _playerVisibilities = new IPlayerVisibility[5] { new PlayerAvatarVisibility(), new PlayerVoiceVisibility(), new PlayerNametagVisibility(), new PlayerHolsterVisibility(), new PlayerBodylogVisibility() }; public NetworkPlayer? Player { get; set; } public bool VisibleForLocalPlayer => _visibleForLocalPlayerCache; public bool NametagVisible => _nametagVisibleCache; public IEnumerable RuleTypes => new Type[4] { typeof(PlayerSpectatingRule), typeof(SpectatorNightvisionRule), typeof(HideEnemyNametagsRule), typeof(ForceHideRule) }; public IEnumerable EventTypes => new Type[3] { typeof(AvatarChangedEvent), typeof(PlayerRuleChangedEvent), typeof(TeamChangedEvent) }; private void SetVisibility(bool isVisible) { _isVisible = isVisible; if (Player == null || Player.PlayerID.IsMe) { return; } _visibleForLocalPlayerCache = isVisible || SpectatorExtender.IsLocalPlayerSpectating(); if (!CommonPreferences.NameTags) { _nametagVisibleCache = false; } else { if (_hideNametagForEnemies) { NetworkPlayer player = Player; if (player != null && player.HasRig) { _nametagVisibleCache = _visibleForLocalPlayerCache && Player.PlayerID.IsTeamMember(); goto IL_008d; } } _nametagVisibleCache = _visibleForLocalPlayerCache; } goto IL_008d; IL_008d: IPlayerVisibility[] playerVisibilities = _playerVisibilities; for (int i = 0; i < playerVisibilities.Length; i++) { playerVisibilities[i].SetVisible(this); } } private void ToggleNightVision(bool enabled) { _hasNightVision = enabled; NetworkPlayer? player = Player; PlayerID val = ((player != null) ? player.PlayerID : null); if (val != null && val.IsMe && _hasNightVision != _nightVisionEnabled) { bool flag = _hasNightVision && !_isVisible; if (flag != _nightVisionEnabled) { _nightVisionEnabled = flag; NightVisionHelper.Enabled = flag; } } } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { NetworkPlayer networkPlayer2 = networkPlayer; RigManager rigManager2 = rigManager; Player = networkPlayer2; if (!Player.PlayerID.IsMe) { IEnumerableExtensions.ForEach((IEnumerable)_playerVisibilities, (Action)delegate(IPlayerVisibility p) { p.OnPlayerChanged(networkPlayer2, rigManager2); p.SetVisible(this); }); } } public void OnRuleChanged(PlayerData data) { bool flag = !data.CheckRule((PlayerSpectatingRule p) => p.IsSpectating) && !data.CheckRule((ForceHideRule p) => p.IsEnabled); bool flag2 = data.CheckRule((SpectatorNightvisionRule p) => p.IsEnabled); _hideNametagForEnemies = data.CheckRule((HideEnemyNametagsRule p) => p.IsEnabled); SetVisibility(flag); ToggleNightVision(flag2 && flag); } public void OnEvent(IPlayerEvent playerEvent) { if (!(playerEvent is AvatarChangedEvent)) { if (playerEvent is TeamChangedEvent teamChangedEvent) { PlayerID playerID = teamChangedEvent.PlayerID; if (!playerID.Equals(PlayerIDManager.LocalID)) { return; } } else { if (!(playerEvent is PlayerRuleChangedEvent playerRuleChangedEvent) || !(playerRuleChangedEvent.Rule is PlayerSpectatingRule)) { return; } PlayerID player = playerRuleChangedEvent.Player; if (!player.Equals(PlayerIDManager.LocalID)) { return; } } } SetVisibility(_isVisible); } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Visibility.Patches { [HarmonyPatch(typeof(FootstepSFX))] public static class FootstepSfxPatch { private static readonly Dictionary> PlayerFootstepSfx; private static readonly Dictionary FootstepSfxPlayer; static FootstepSfxPatch() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown PlayerFootstepSfx = new Dictionary>(); FootstepSfxPlayer = new Dictionary(); MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)delegate(PlayerID id) { if (!PlayerFootstepSfx.Remove(id, out List value)) { return; } foreach (FootstepSFX item in value) { FootstepSfxPlayer.Remove(item); } }; NetworkPlayer.OnNetworkRigCreated += delegate(NetworkPlayer player, RigManager rig) { Il2CppArrayBase componentsInChildren = ((Component)rig).GetComponentsInChildren(); if (componentsInChildren == null) { return; } foreach (FootstepSFX item2 in componentsInChildren) { PlayerID playerID = player.PlayerID; PlayerFootstepSfx.GetValueOrCreate(playerID).Add(item2); FootstepSfxPlayer[item2] = playerID; } }; MultiplayerHooking.OnLoadingBegin += (UpdateEvent)delegate { PlayerFootstepSfx.Clear(); FootstepSfxPlayer.Clear(); }; } private static PlayerID? GetPlayerId(FootstepSFX sfx) { return FootstepSfxPlayer.GetValueOrDefault(sfx); } [HarmonyPatch("PlayStep")] [HarmonyPrefix] public static bool PlayStepPrefix(FootstepSFX __instance) { if ((Object)(object)__instance == (Object)null) { return true; } if (!NetworkInfo.HasServer) { return true; } PlayerID playerId = GetPlayerId(__instance); if (playerId == null) { return true; } if (!playerId.IsSpectating()) { return true; } return false; } } [HarmonyPatch(typeof(HeadSFX))] public static class HeadSfxPatches { private static bool CanPlay(HeadSFX headSfx) { if ((Object)(object)headSfx == (Object)null) { return true; } PhysicsRig physRig = headSfx._physRig; RigManager val = ((physRig != null) ? ((Rig)physRig).manager : null); if ((Object)(object)val == (Object)null) { return true; } NetworkPlayer val2 = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(val, ref val2)) { return true; } if (val2.PlayerID.IsSpectating()) { return false; } return true; } [HarmonyPatch("Speak")] [HarmonyPrefix] public static bool SpeakPrefix(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("JumpEffort")] [HarmonyPrefix] public static bool JumpEffort(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("DoubleJump")] [HarmonyPrefix] public static bool DoubleJump(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("DyingVocal")] [HarmonyPrefix] public static bool DyingVocal(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("DeathVocal")] [HarmonyPrefix] public static bool DeathVocal(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("RecoveryVocal")] [HarmonyPrefix] public static bool RecoveryVocal(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("BigDamageVocal")] [HarmonyPrefix] public static bool BigDamageVocal(HeadSFX __instance) { return CanPlay(__instance); } [HarmonyPatch("SmallDamageVocal")] [HarmonyPrefix] public static bool SmallDamageVocal(HeadSFX __instance) { return CanPlay(__instance); } } [HarmonyPatch(typeof(PhysGrounder))] public class PhysGrounderPatches { [HarmonyPatch("HighFallClipSpawn")] [HarmonyPrefix] public static bool HighFallClipSpawnPrefix(PhysGrounder __instance) { if ((Object)(object)__instance == (Object)null) { return true; } PhysicsRig physRig = __instance.physRig; RigManager val = ((physRig != null) ? ((Rig)physRig).manager : null); if ((Object)(object)val == (Object)null) { return true; } NetworkPlayer val2 = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(val, ref val2)) { return true; } if (val2.PlayerID.IsSpectating()) { return false; } return true; } } [HarmonyPatch(typeof(RigHeadUI))] public static class PlayerHeadUIPatch { private static readonly Dictionary HeadUIPlayerCache = new Dictionary(); [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void Visible_Setter_Prefix(RigHeadUI __instance, ref bool value) { if (HeadUIPlayerCache.TryGetValue(__instance, out NetworkPlayer value2)) { PlayerData playerData; if (!value2.PlayerID.IsValid) { HeadUIPlayerCache.Remove(__instance); } else if (PlayerDataManager.TryGetPlayerData(value2.PlayerID, out playerData) && !playerData.GetExtender().NametagVisible) { value = false; } } } [HarmonyPatch("UpdateTransform")] [HarmonyPostfix] public static void UpdateTransform_Postfix(RigHeadUI __instance, RigManager rigManager) { NetworkPlayer value = default(NetworkPlayer); if (NetworkPlayerManager.TryGetPlayer(rigManager, ref value)) { HeadUIPlayerCache[__instance] = value; } } [HarmonyPatch("Despawn")] [HarmonyPostfix] public static void Despawn_Postfix(RigHeadUI __instance) { HeadUIPlayerCache.Remove(__instance); } } [HarmonyPatch(typeof(RigArt))] public class RigArtPatches { [HarmonyPatch("TogglePhysicsRenderers")] [HarmonyPrefix] private static bool TogglePhysicsRenderers_Prefix(RigArt __instance) { if (__instance == null) { return true; } if (SpectatorExtender.IsLocalPlayerSpectating()) { return true; } RigManager value = Traverse.Create((object)__instance).Field("_rigManager").Value; if ((Object)(object)value == (Object)null) { return true; } NetworkPlayer player = default(NetworkPlayer); if (!NetworkPlayer.RigCache.TryGet(value, ref player)) { return true; } if (player.IsSpectating()) { return false; } return true; } [HarmonyPatch("ToggleAvatar")] [HarmonyPrefix] private static bool ToggleAvatar_Prefix(RigArt __instance) { if (__instance == null) { return true; } if (SpectatorExtender.IsLocalPlayerSpectating()) { return true; } RigManager value = Traverse.Create((object)__instance).Field("_rigManager").Value; if ((Object)(object)value == (Object)null) { return true; } NetworkPlayer player = default(NetworkPlayer); if (!NetworkPlayer.RigCache.TryGet(value, ref player)) { return true; } if (player.IsSpectating()) { return false; } return true; } [HarmonyPatch("ToggleAmmoPouch")] [HarmonyPrefix] private static bool ToggleAmmoPouch_Prefix(RigArt __instance) { if (__instance == null) { return true; } if (SpectatorExtender.IsLocalPlayerSpectating()) { return true; } RigManager value = Traverse.Create((object)__instance).Field("_rigManager").Value; if ((Object)(object)value == (Object)null) { return true; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayer.RigCache.TryGet(value, ref val)) { return true; } return !val.PlayerID.IsSpectating(); } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Visibility.Parts { public class PlayerBodylogVisibility : IPlayerVisibility { private static readonly string[] BodylogParts = new string[5] { "BodyLog/BodyLog", "BodyLog/VFX", "BodyLog/spheregrip/Sphere/Art/GrabGizmo", "BodyLog/PreviewPoint", "BodyLog/Dial" }; private bool _isVisible = true; private List _bodylogObjects = new List(); private void UpdateVisiblity() { foreach (GameObject bodylogObject in _bodylogObjects) { if ((Object)(object)bodylogObject != (Object)null) { bodylogObject.SetActive(_isVisible); } } } public void SetVisible(PlayerVisibility visibility) { _isVisible = visibility.VisibleForLocalPlayer; UpdateVisiblity(); } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { foreach (GameObject bodylogObject in _bodylogObjects) { if ((Object)(object)bodylogObject != (Object)null) { bodylogObject.SetActive(true); } } _bodylogObjects.Clear(); SlotContainer val = ((Il2CppArrayBase)(object)rigManager.inventory.specialItems)[0]; if ((Object)(object)val == (Object)null) { return; } string[] bodylogParts = BodylogParts; foreach (string text in bodylogParts) { Transform obj = ((Component)val).transform.Find(text); GameObject val2 = ((obj != null) ? ((Component)obj).gameObject : null); if (!((Object)(object)val2 == (Object)null)) { _bodylogObjects.Add(val2); } } UpdateVisiblity(); } public void OnAvatarChanged(Avatar avatar) { } } internal class SlotContainerHider { private GameObject? _art; private static readonly string[] HolsterSlotNames = new string[3] { "prop_handGunHolster", "prop_pouch", "InventoryAmmoReceiver" }; private static GameObject? GetArt(SlotContainer slotContainer) { if ((Object)(object)slotContainer == (Object)null) { return null; } Transform transform = ((Component)slotContainer).transform; string[] holsterSlotNames = HolsterSlotNames; foreach (string text in holsterSlotNames) { Transform obj = transform.Find(text); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val != (Object)null) { return val; } } return null; } public void SetSlotContainer(SlotContainer slotContainer) { GameObject art = GetArt(slotContainer); if (!((Object)(object)art == (Object)(object)_art)) { if ((Object)(object)_art != (Object)null) { _art.SetActive(true); } _art = art; } } public void SetVisible(bool isVisible) { if (!((Object)(object)_art == (Object)null)) { _art.SetActive(isVisible); } } } public class PlayerHolsterVisibility : IPlayerVisibility { private bool _isVisible = true; private readonly Dictionary _holsterHiders = new Dictionary(); public void SetVisible(PlayerVisibility visibility) { _isVisible = visibility.VisibleForLocalPlayer; IEnumerableExtensions.ForEach((IEnumerable)_holsterHiders.Values, (Action)delegate(SlotContainerHider hider) { hider.SetVisible(_isVisible); }); } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _holsterHiders.Clear(); Il2CppArrayBase componentsInChildren = ((Component)rigManager.physicsRig).GetComponentsInChildren(); if (componentsInChildren == null) { return; } foreach (SlotContainer item in componentsInChildren) { SlotContainerHider valueOrCreate = _holsterHiders.GetValueOrCreate(((Object)item).name, () => new SlotContainerHider()); valueOrCreate.SetSlotContainer(item); valueOrCreate.SetVisible(_isVisible); } } public void OnAvatarChanged(Avatar avatar) { } } public class PlayerNametagVisibility : IPlayerVisibility { private NetworkPlayer? _player; private bool _isVisible = true; public void SetVisible(PlayerVisibility visibility) { _isVisible = visibility.NametagVisible; if (_player != null) { _player.HeadUI.Visible = _isVisible; } } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _player = networkPlayer; _player.HeadUI.Visible = _isVisible; } public void OnAvatarChanged(Avatar avatar) { } } public class PlayerVoiceVisibility : IPlayerVisibility { private NetworkPlayer? _player; private bool TryGetAudioSource([MaybeNullWhen(false)] out AudioSource audioSource) { if (_player == null) { audioSource = null; return false; } RigVoiceSource voiceSource = _player.VoiceSource; audioSource = ((voiceSource != null) ? voiceSource.VoiceSource.AudioSource : null); return (Object)(object)audioSource != (Object)null; } public void SetVisible(PlayerVisibility visibility) { if (TryGetAudioSource(out AudioSource audioSource)) { audioSource.mute = !visibility.VisibleForLocalPlayer; } } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _player = networkPlayer; if (TryGetAudioSource(out AudioSource audioSource)) { audioSource.mute = !audioSource.mute; } } public void OnAvatarChanged(Avatar avatar) { } } } namespace MashGamemodeLibrary.Player.Data.Extenders.MagazineLimiter { public class AmmunitionLimiterExtender : IPlayerExtender { private int? _ammunitionLimit; public int AmmunitionUsed { get; private set; } public IEnumerable RuleTypes => new Type[1] { typeof(PlayerAmmunitionLimitRule) }; public void UseMagazine(int amount) { AmmunitionUsed = Math.Max(0, AmmunitionUsed + amount); } public bool CanUseMagazine() { if (!_ammunitionLimit.HasValue) { return true; } return AmmunitionUsed < _ammunitionLimit; } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { } public void OnRuleChanged(PlayerData data) { PlayerAmmunitionLimitRule rule = data.GetRule(); _ammunitionLimit = rule.AmmunitionLimit; AmmunitionUsed = 0; } public void OnEvent(IPlayerEvent playerEvent) { } } } namespace MashGamemodeLibrary.Player.Data.Extenders.MagazineLimiter.Patches { [HarmonyPatch(typeof(InventoryAmmoReceiverPatches))] public class InventoryAmmoReceiverPatches { private const int MinimumAmmoConsumption = 4; [HarmonyPatch("OnHandGrabPrefix")] [HarmonyPrefix] public static bool OnHandGrab([HarmonyArgument(0)] InventoryAmmoReceiver instance) { if (!NetworkSceneManager.IsLevelNetworked || !FusionPlayer.IsLocalPlayer(instance._parentRigManager)) { return true; } MagazineData selectedMagazineData = instance._selectedMagazineData; if ((Object)(object)selectedMagazineData == (Object)null) { return true; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(instance._parentRigManager, ref val) || !val.PlayerID.IsMe) { return true; } AmmoInventory instance2 = AmmoInventory.Instance; if ((Object)(object)instance2 == (Object)null || instance2.GetCartridgeCount(instance._selectedCartridgeData) <= 0) { return true; } AmmunitionLimiterExtender ammunitionLimiterExtender = PlayerDataManager.GetLocalPlayerData()?.GetExtender(); if (ammunitionLimiterExtender == null) { return true; } if (!ammunitionLimiterExtender.CanUseMagazine()) { return false; } ammunitionLimiterExtender.UseMagazine(Math.Max(selectedMagazineData.rounds, 4)); return true; } [HarmonyPatch("OnHandDropPrefix")] [HarmonyPostfix] public static void OnHandDrop([HarmonyArgument(0)] InventoryAmmoReceiver instance, IGrippable host, bool __result) { if (!NetworkSceneManager.IsLevelNetworked || !FusionPlayer.IsLocalPlayer(instance._parentRigManager)) { return; } InteractableHost val = ((Il2CppObjectBase)host).TryCast(); NetworkEntity val2 = default(NetworkEntity); if ((Object)(object)val == (Object)null || !InteractableHostExtender.Cache.TryGet(val, ref val2) || !val2.IsRegistered) { return; } MagazineExtender extender = val2.GetExtender(); NetworkPlayer val3 = default(NetworkPlayer); if (extender != null && NetworkPlayerManager.TryGetPlayer(instance._parentRigManager, ref val3) && val3.PlayerID.IsMe && !((AlignPlug)((EntityComponentExtender)(object)extender).Component.magazinePlug)._isLocked) { AmmunitionLimiterExtender ammunitionLimiterExtender = PlayerDataManager.GetLocalPlayerData()?.GetExtender(); int rounds = ((EntityComponentExtender)(object)extender).Component.magazineState.magazineData.rounds; int ammoCount = ((EntityComponentExtender)(object)extender).Component.magazineState.AmmoCount; if (rounds < 4) { int num = rounds - ammoCount; ammunitionLimiterExtender?.UseMagazine(-(4 - num)); } else { ammunitionLimiterExtender?.UseMagazine(-ammoCount); } } } } } namespace MashGamemodeLibrary.Player.Data.Extenders.LocalInteractions { public class LocalInteractionsExtender : IPlayerExtender { private NetworkPlayer? _player; private bool _areInteractionsEnabled = true; private GameObject? _overlayObject; public IEnumerable RuleTypes => new Type[1] { typeof(PlayerSpectatingRule) }; private GameObject GetOverlayObject() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if ((Object)(object)_overlayObject != (Object)null) { return _overlayObject; } _overlayObject = new GameObject("SpectatorEffect"); Volume obj = _overlayObject.AddComponent(); obj.isGlobal = true; obj.priority = 50000f; obj.weight = 1f; VolumeProfile val2 = (obj.sharedProfile = ScriptableObject.CreateInstance()); ((VolumeParameter)(object)val2.Add(true).saturation).value = -100f; return _overlayObject; } private void SetInteractions(bool areInteractionsEnabled) { _areInteractionsEnabled = areInteractionsEnabled; NetworkPlayer? player = _player; PlayerID val = ((player != null) ? player.PlayerID : null); if (val != null && val.IsMe) { if (!FusionSceneManager.IsLoading()) { GetOverlayObject().SetActive(!_areInteractionsEnabled); } RigManager rigManager = _player.RigRefs.RigManager; if (!_areInteractionsEnabled) { MashGamemodeLibrary.Player.Loadout.Loadout.ClearPlayerLoadout(rigManager); } LocalControls.DisableInteraction = !_areInteractionsEnabled; LocalControls.DisableInventory = !_areInteractionsEnabled; LocalControls.DisableAmmoPouch = !_areInteractionsEnabled; DevToolsPatches.CanSpawn = _areInteractionsEnabled; } } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { if (networkPlayer.PlayerID.IsMe) { if ((Object)(object)_overlayObject != (Object)null) { Object.Destroy((Object)(object)_overlayObject); _overlayObject = null; } _player = networkPlayer; SetInteractions(_areInteractionsEnabled); } } public void OnRuleChanged(PlayerData data) { NetworkPlayer? player = _player; PlayerID val = ((player != null) ? player.PlayerID : null); if (val != null && val.IsMe) { bool flag = data.CheckRule((PlayerSpectatingRule p) => p.IsSpectating); SetInteractions(!flag); } } public void OnEvent(IPlayerEvent playerEvent) { } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Colliders { public class PlayerCollisionsExtender : IPlayerExtender { private bool _isColliding = true; private NetworkPlayer? _networkPlayer; private CachedPhysicsRig? _cachedPhysicsRig; public IEnumerable RuleTypes => new Type[1] { typeof(PlayerSpectatingRule) }; private CachedPhysicsRig? GetPhysicsRig() { if (_networkPlayer == null) { return null; } CachedPhysicsRig cachedPhysicsRig = _cachedPhysicsRig; if (cachedPhysicsRig != null && cachedPhysicsRig.IsValid) { return _cachedPhysicsRig; } if (!_networkPlayer.HasRig) { return null; } _cachedPhysicsRig = new CachedPhysicsRig(_networkPlayer.RigRefs.RigManager.physicsRig); return _cachedPhysicsRig; } private void SetColliding(bool isColliding) { _isColliding = isColliding; GetPhysicsRig()?.SetColliding(_isColliding); } public void OnPlayerChanged(NetworkPlayer networkPlayer, RigManager rigManager) { _networkPlayer = networkPlayer; SetColliding(_isColliding); } public void OnRuleChanged(PlayerData data) { bool flag = data.CheckRule((PlayerSpectatingRule p) => p.IsSpectating); _isColliding = !flag; SetColliding(_isColliding); } public void OnEvent(IPlayerEvent playerEvent) { } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Colliders.Patches { [HarmonyPatch(typeof(ImpactProperties))] public class ImpactPropertiesPatches { [HarmonyPatch("Awake")] [HarmonyPostfix] public static void AwakePostfix(ImpactProperties __instance) { try { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).transform == (Object)null)) { MarrowEntityEventHandler.FixColliderLayers(((Component)__instance).transform, 2); } } catch (Exception value) { InternalLogger.Error($"An error occurred in ImpactPropertiesPatches.AwakePostfix: {value}"); InternalLogger.Error("Stack Trace: " + new StackTrace()); } } } [HarmonyPatch(typeof(MarrowEntity))] public class MarrowEntityPatches { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void Awake_Postfix(MarrowEntity __instance) { try { if (!((Object)(object)__instance == (Object)null)) { MarrowEntityEventHandler.OnMarrowEntityCreated(__instance); } } catch (Exception value) { InternalLogger.Error($"An error occurred in MarrowEntityPatches.Awake_Postfix: {value}"); } } } [HarmonyPatch(typeof(TriggerEvents3D))] public class TriggerEvent3dPatches { private static bool IsValid(Il2CppObjectBase collider) { if (collider.WasCollected) { return false; } IntPtr nativeClassPtr = Il2CppClassPointerStore.NativeClassPtr; if (nativeClassPtr == IntPtr.Zero) { return false; } IntPtr intPtr = IL2CPP.il2cpp_object_get_class(collider.Pointer); return IL2CPP.il2cpp_class_is_assignable_from(nativeClassPtr, intPtr); } [HarmonyPatch("OnTriggerEnter")] [HarmonyPrefix] public static bool OnTriggerEnterPrefix(TriggerEvents3D __instance, Collider collider) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)collider == (Object)null) { return true; } if (!IsValid((Il2CppObjectBase)(object)collider)) { return true; } if ((Object)(object)((Component)collider).gameObject == (Object)null) { return true; } if (((Component)collider).gameObject.layer == CachedPhysicsRig.SpectatorLayer) { return false; } return true; } [HarmonyPatch("OnTriggerExit")] [HarmonyPrefix] public static bool OnTriggerExitPrefix(TriggerEvents3D __instance, Collider collider) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)collider == (Object)null) { return true; } if (!IsValid((Il2CppObjectBase)(object)collider)) { return true; } if ((Object)(object)((Component)collider).gameObject == (Object)null) { return true; } if (((Component)collider).gameObject.layer == CachedPhysicsRig.SpectatorLayer) { return false; } return true; } [HarmonyPatch("OnTriggerStay")] [HarmonyPrefix] public static bool OnTriggerStayPrefix(TriggerEvents3D __instance, Collider collider) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)collider == (Object)null) { return true; } if (!IsValid((Il2CppObjectBase)(object)collider)) { return true; } if ((Object)(object)((Component)collider).gameObject == (Object)null) { return true; } if (((Component)collider).gameObject.layer == CachedPhysicsRig.SpectatorLayer) { return false; } return true; } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Colliders.Data { public class CachedCollider { private readonly Collider _collider; private readonly int _sourceLayer; public CachedCollider(Collider collider, int sourceLayer) { _collider = collider; _sourceLayer = sourceLayer; } public void SetLayer(int? layer) { if (!((Object)(object)_collider == (Object)null)) { ((Component)_collider).gameObject.layer = layer ?? _sourceLayer; } } } public class CachedPhysicsRig { public static readonly int SpectatorLayer; private static readonly Dictionary PhysicsRigLayout; public static readonly IReadOnlyList SpectatorIgnoredLayers; public static readonly int SpectatorIgnoredLayerMask; public ImmutableArray Colliders; public PhysicsRig PhysicsRig { get; init; } public bool IsColliding { get; private set; } public bool IsValid => (Object)(object)PhysicsRig != (Object)null; static CachedPhysicsRig() { SpectatorLayer = 2; PhysicsRigLayout = new Dictionary { { "Feet", 24 }, { "Foot (right)", 9 }, { "Foot (left)", 9 }, { "Head", 8 }, { "Neck", 8 }, { "Chest", 8 }, { "ShoulderLf", 8 }, { "ElbowLf", 8 }, { "Hand (left)", 8 }, { "l_fingers_col", 8 }, { "ShoulderRt", 8 }, { "ElbowRt", 8 }, { "Hand (right)", 8 }, { "r_fingers_col", 8 }, { "Spine", 8 }, { "Pelvis", 8 }, { "HipLf", 9 }, { "KneeLf", 9 }, { "HipRt", 9 }, { "KneeRt", 9 }, { "BreastLf", 17 }, { "BreastRt", 17 }, { "UpperarmLf", 17 }, { "ForearmLf", 17 }, { "SoftHandLf", 17 }, { "UpperarmRt", 17 }, { "ForearmRt", 17 }, { "SoftHandRt", 17 }, { "ButtLf", 17 }, { "ButtRt", 17 }, { "ThighLf", 17 }, { "ThighRt", 17 }, { "ItemReciever", 18 }, { "InventoryAmmoReceiver", 18 }, { "Knee", 24 }, { "KneetoPelvis", 24 }, { "DeciHead", 17 }, { "DeciChest", 17 }, { "DeciShoulderLf", 17 }, { "DeciElbowLf", 17 }, { "DeciHandLf", 17 }, { "DeciShoulderRt", 17 }, { "DeciElbowRt", 17 }, { "DeciHandRt", 17 }, { "DeciSpine", 17 }, { "DeciPelvis", 17 }, { "DeciHipLf", 17 }, { "DeciHipRt", 17 } }; SpectatorIgnoredLayers = new int[11] { 7, 8, 9, 10, 12, 15, 17, 18, 21, 30, 31 }; SpectatorIgnoredLayerMask = SpectatorIgnoredLayers.Aggregate(0, (int mask, int layer) => mask | (1 << layer)); Physics.IgnoreLayerCollision(SpectatorLayer, 0, false); Physics.IgnoreLayerCollision(SpectatorLayer, 23, true); Physics.IgnoreLayerCollision(SpectatorLayer, SpectatorLayer, true); foreach (int spectatorIgnoredLayer in SpectatorIgnoredLayers) { Physics.IgnoreLayerCollision(SpectatorLayer, spectatorIgnoredLayer, true); } } public CachedPhysicsRig(PhysicsRig physicsRig) { PhysicsRig = physicsRig; Colliders = ((IEnumerable)((Component)physicsRig).GetComponentsInChildren()).Select((Collider c) => (!PhysicsRigLayout.TryGetValue(((Object)c).name, out var value)) ? null : new CachedCollider(c, value)).OfType().ToImmutableArray(); } public void SetColliding(bool isColliding) { IsColliding = isColliding; if (IsValid) { ImmutableArray.Enumerator enumerator = Colliders.GetEnumerator(); while (enumerator.MoveNext()) { enumerator.Current.SetLayer(isColliding ? null : new int?(SpectatorLayer)); } } } } } namespace MashGamemodeLibrary.Player.Data.Extenders.Colliders.Caches { public static class MarrowEntityEventHandler { public static void FixColliderLayer(Collider collider) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 if ((Object)(object)collider == (Object)null) { return; } GameObject gameObject = ((Component)collider).gameObject; if ((Object)(object)gameObject == (Object)null || gameObject.isStatic) { return; } int layer = ((Component)collider).gameObject.layer; if (layer == 0 && (CachedPhysicsRig.SpectatorIgnoredLayerMask & (1 << layer)) == 0) { Rigidbody attachedRigidbody = collider.attachedRigidbody; if (!((Object)(object)attachedRigidbody == (Object)null) && (attachedRigidbody == null || !attachedRigidbody.freezeRotation || (int)attachedRigidbody.constraints != 14) && (int)attachedRigidbody.constraints != 126) { ((Component)collider).gameObject.layer = 10; } } } public static void FixColliderLayers(Transform transform, int maxRecursion = 0) { Collider collider = default(Collider); if ((Object)(object)transform == (Object)null || !((Component)transform).TryGetComponent(ref collider)) { return; } FixColliderLayer(collider); if (maxRecursion <= 0) { return; } for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); if (!((Object)(object)child == (Object)null)) { FixColliderLayers(child, maxRecursion - 1); } } } private static void FixColliderLayers(MarrowEntity entity) { if ((Object)(object)entity == (Object)null || entity._bodies == null) { return; } foreach (MarrowBody item in (Il2CppArrayBase)(object)entity._bodies) { if ((Object)(object)item == (Object)null || item._colliders == null) { continue; } foreach (Collider item2 in (Il2CppArrayBase)(object)item.Colliders) { FixColliderLayer(item2); } } } public static void OnMarrowEntityCreated(MarrowEntity entity) { if (!((Object)(object)entity == (Object)null) && !((Object)(object)((Component)entity).gameObject == (Object)null) && !entity.IsDespawned && !(((Object)entity).name == "PhysicsRig")) { FixColliderLayers(entity); } } } } namespace MashGamemodeLibrary.Player.Data.Events.Data { public record AvatarChangedEvent(Avatar Avatar) : IPlayerEvent; public interface IPlayerEvent { } public record PlayerRuleChangedEvent(PlayerID Player, IPlayerRule Rule) : IPlayerEvent; public record TeamChangedEvent(PlayerID PlayerID, LogicTeam? Team) : IPlayerEvent; } namespace MashGamemodeLibrary.Player.Data.Events.Callers { public class AvatarChangeEventCaller : EventCaller { private void OnAvatarChanged() { if (base.NetworkPlayer != null) { Invoke(new AvatarChangedEvent(base.NetworkPlayer.RigRefs.RigManager.avatar)); } } protected override void OnPlayerChanged(NetworkPlayer networkPlayer, NetworkPlayer? oldPlayer) { if (oldPlayer != null) { oldPlayer.AvatarSetter.OnAvatarChanged -= OnAvatarChanged; } networkPlayer.AvatarSetter.OnAvatarChanged += OnAvatarChanged; } protected override void OnPlayerRemoved(NetworkPlayer networkPlayer) { networkPlayer.AvatarSetter.OnAvatarChanged -= OnAvatarChanged; } } public abstract class EventCaller : IEventCaller { private IEventReceiver? _eventReceiver; private NetworkPlayer? _networkPlayer; protected NetworkPlayer? NetworkPlayer => _networkPlayer; protected abstract void OnPlayerChanged(NetworkPlayer networkPlayer, NetworkPlayer? oldPlayer); protected abstract void OnPlayerRemoved(NetworkPlayer networkPlayer); protected void Invoke(IPlayerEvent playerEvent) { _eventReceiver?.ReceiveEvent(playerEvent); } public void OnEnable(IEventReceiver eventReceiver, NetworkPlayer networkPlayer) { _eventReceiver = eventReceiver; if (_networkPlayer == null || !((object)_networkPlayer).Equals((object?)networkPlayer)) { NetworkPlayer networkPlayer2 = _networkPlayer; _networkPlayer = networkPlayer; OnPlayerChanged(networkPlayer, networkPlayer2); } } public void OnDisable() { if (_networkPlayer != null) { OnPlayerRemoved(_networkPlayer); _networkPlayer = null; } } } public interface IEventCaller { void OnEnable(IEventReceiver eventReceiver, NetworkPlayer networkPlayer); void OnDisable(); } public interface IEventReceiver { void ReceiveEvent(IPlayerEvent playerEvent); } } namespace MashGamemodeLibrary.Player.Actions { public class PlayerStatistics : INetSerializable { public byte PlayerID; private Dictionary Statistics { get; } = new Dictionary(); public PlayerStatistics() { PlayerID = 0; } public PlayerStatistics(byte playerID) { PlayerID = playerID; } public void SetStatistic(ulong hash, int value) { Statistics[hash] = value; } public int GetValue(Enum key) { if (GlobalStatisticsCollector.StatisticKeyIds.TryGet(key, out var value)) { return Statistics.GetValueOrDefault(value, 0); } return 0; } public void Serialize(INetSerializer serializer) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown serializer.SerializeValue(ref PlayerID); if (serializer.IsReader) { NetReader val = (NetReader)serializer; int num = val.ReadInt32(); Statistics.Clear(); for (int i = 0; i < num; i++) { ulong key = val.ReadUInt64(); int value = val.ReadInt32(); Statistics[key] = value; } return; } NetWriter val2 = (NetWriter)serializer; val2.Write(Statistics.Count); foreach (KeyValuePair statistic in Statistics) { val2.Write(statistic.Key); val2.Write(statistic.Value); } } } internal class StatisticChangePacket : INetSerializable, IKnownSenderPacket { public ulong KeyHash; public int Value; public byte SenderSmallId { get; set; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref KeyHash); serializer.SerializeValue(ref Value); } } [RequireStaticConstructor] public static class GlobalStatisticsCollector { internal static readonly KeyedRegistry StatisticKeyIds = new KeyedRegistry(); private static readonly KeyedRegistry StatisticKeys = new KeyedRegistry(); private static readonly SyncedDictionary PlayerStatistics = new SyncedDictionary("syncedStatistics", new ByteEncoder(), new InstanceEncoder(), CommonNetworkRoutes.HostToAll); private static readonly RemoteEvent StatisticChangeEvent = new RemoteEvent("statisticChange", OnStatisticChangeEvent, CommonNetworkRoutes.AllToHost); public static IEnumerable Statistics => PlayerIDManager.PlayerIDs.Select((PlayerID playerID) => PlayerStatistics.GetValueOrDefault(playerID.SmallID) ?? new PlayerStatistics(PlayerID.op_Implicit(playerID))); internal static void RegisterStatisticKey(T key) where T : Enum { ulong stableHash = key.GetStableHash(); StatisticKeyIds.Register(key, stableHash); StatisticKeys.Register(stableHash, key); } internal static void SyncChangeToHost(T key, int value) where T : Enum { StatisticChangeEvent.CallFor(PlayerIDManager.GetHostID(), new StatisticChangePacket { KeyHash = StatisticKeyIds.Get(key), Value = value }); } internal static void Clear() { Executor.RunIfHost(delegate { PlayerStatistics.Clear(); }); } private static void OnStatisticChangeEvent(StatisticChangePacket packet) { StatisticChangePacket packet2 = packet; PlayerStatistics.GetValueOrCreate(packet2.SenderSmallId, () => new PlayerStatistics(packet2.SenderSmallId)).SetStatistic(packet2.KeyHash, packet2.Value); PlayerStatistics.Sync(packet2.SenderSmallId); } } public static class GlobalStatisticsManager { private static readonly string StatisticsFolder = Mod.ModDataDirectory + "/statistics/"; private static readonly Dictionary> ScopedStatistics = new Dictionary>(); private static Dictionary LoadStatistics(Gamemode gamemode) { if (ScopedStatistics.TryGetValue(gamemode, out Dictionary value)) { return value; } string path = StatisticsFolder + gamemode.Title + ".json"; Dictionary dictionary = null; try { Directory.CreateDirectory(StatisticsFolder); if (File.Exists(path)) { using FileStream fileStream = File.OpenRead(path); fileStream.SetLength(0L); dictionary = JsonSerializer.Deserialize>(fileStream); } if (dictionary == null) { dictionary = new Dictionary(); } } catch (Exception ex) { MelonLogger.Error("Failed to load global statistics", ex); dictionary = new Dictionary(); } ScopedStatistics.Add(gamemode, dictionary); return dictionary; } public static void SaveStatistics(Gamemode gamemode) { Dictionary statisticsSnapshot = PlayerStatisticsTracker.GetStatisticsSnapshot(); Dictionary dictionary = LoadStatistics(gamemode); foreach (KeyValuePair item in statisticsSnapshot) { item.Deconstruct(out var key, out var value); string text = key; int num = value; if (!dictionary.TryAdd(text, num)) { Dictionary dictionary2 = dictionary; key = text; dictionary2[key] += num; } } } } public enum PlayerInputType { Ability, Grip } public static class PlayerActionManager { private static readonly Dictionary LastGripStateMap; private static readonly IAssociatedBehaviourCache PlayerActionTags; private static readonly IAssociatedBehaviourCache PlayerInputTags; static PlayerActionManager() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown LastGripStateMap = new Dictionary(); PlayerActionTags = BehaviourManager.CreateCache(); PlayerInputTags = BehaviourManager.CreateCache(); MultiplayerHooking.OnPlayerAction += new PlayerAction(OnPlayerAction); } private static void OnPlayerAction(PlayerID playerId, PlayerActionType type, PlayerID otherPlayer) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) foreach (IPlayerActionCallback item in PlayerActionTags.GetAll(PlayerID.op_Implicit(playerId))) { item.OnAction(type, otherPlayer); } } private static void CheckHand(Hand hand) { //IL_0008: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0044: Unknown result type (might be due to invalid IL or missing references) BaseController controller = hand._controller; Handedness handedness = controller.handedness; if (controller._menuTap) { InvokeInputEvent(PlayerInputType.Ability, state: true, handedness); } bool isBelowGripThreshold = controller.isBelowGripThreshold; bool valueOrDefault = LastGripStateMap.GetValueOrDefault(handedness, defaultValue: false); if (isBelowGripThreshold != valueOrDefault) { InvokeInputEvent(PlayerInputType.Grip, isBelowGripThreshold, handedness); LastGripStateMap[handedness] = controller.isBelowGripThreshold; } } private static void CheckHands() { if (NetworkInfo.HasServer && Player.HandsExist) { CheckHand(Player.LeftHand); CheckHand(Player.RightHand); } } public static void Update() { CheckHands(); } private static void InvokeInputEvent(PlayerInputType type, bool state, Handedness handedness = 3) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (NetworkInfo.HasServer) { PlayerInputTags.ForEach(delegate(IPlayerInputCallback tag) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) tag.OnInput(type, state, handedness); }); } } } public enum PlayerDamageStatistics { Kills, Assists, Deaths } [RequireStaticConstructor] public static class PlayerDamageTracker { private static readonly IAssociatedBehaviourCache DamageCallbacks; private static readonly Dictionary> DamageMarks; static PlayerDamageTracker() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown DamageCallbacks = BehaviourManager.CreateCache(); DamageMarks = new Dictionary>(); MultiplayerHooking.OnPlayerAction += new PlayerAction(OnAction); PlayerStatisticsTracker.Register(PlayerDamageStatistics.Kills, (int v) => v * 10); PlayerStatisticsTracker.Register(PlayerDamageStatistics.Assists, (int v) => v * 5); PlayerStatisticsTracker.Register(PlayerDamageStatistics.Deaths, (int v) => v * -5); LocalHealth.OnAttackedByPlayer += OnDamagedSelf; } private static void OnDamagedSelf(Attack attack, BodyPart bodyPart, PlayerID damager) { PlayerID damager2 = damager; DamageCallbacks.ForEach(PlayerIDManager.LocalSmallID, delegate(IPlayerTakeDamageCallback p) { p.OnDamageTaken(damager2); }); } public static void Reset() { DamageMarks.Clear(); } private static HashSet GetInner(byte attackedID) { bool exists; ref HashSet valueRefOrAddDefault = ref CollectionsMarshal.GetValueRefOrAddDefault(DamageMarks, attackedID, out exists); if (!exists) { valueRefOrAddDefault = new HashSet(); } return valueRefOrAddDefault; } private static void OnDamage(PlayerID damager, PlayerID damaged) { if (!damaged.IsMe && !damaged.Equals(damager)) { GetInner(PlayerID.op_Implicit(damaged)).Add(PlayerID.op_Implicit(damager)); } } private static void OnDeath(PlayerID damager, PlayerID damaged) { if (!damaged.IsMe && !damaged.Equals(damager)) { HashSet inner = GetInner(PlayerID.op_Implicit(damaged)); if (damager.IsMe) { PlayerStatisticsTracker.Increment(PlayerDamageStatistics.Kills); } else if (inner.Contains(PlayerIDManager.LocalSmallID)) { PlayerStatisticsTracker.Increment(PlayerDamageStatistics.Assists); } inner.Clear(); } } private static void OnAction(PlayerID playerId, PlayerActionType type, PlayerID otherPlayer) { //IL_0000: 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) //IL_0014: Expected I4, but got Unknown switch (type - 3) { case 1: OnDamage(playerId, otherPlayer); break; case 2: OnDeath(otherPlayer, playerId); break; case 0: if (playerId.IsMe) { PlayerStatisticsTracker.Increment(PlayerDamageStatistics.Deaths); } break; } } } public static class PlayerStatisticsTracker { private static readonly Dictionary> Awarders = new Dictionary>(); private static readonly Dictionary Statistics = new Dictionary(); public static void Increment(Enum key, int value = 1) { Statistics.TryAdd(key, 0); Statistics[key] += value; GlobalStatisticsCollector.SyncChangeToHost(key, Statistics[key]); } public static void Clear() { Statistics.Clear(); GlobalStatisticsCollector.Clear(); } public static void Register(Enum key, Func mapper) { Awarders[key] = mapper; GlobalStatisticsCollector.RegisterStatisticKey(key); } private static int GetTotalBits(int extraBits = 0) { Func value; return Math.Max(Statistics.Sum>((KeyValuePair kvp) => Awarders.TryGetValue(kvp.Key, out value) ? value(kvp.Value) : 0) + extraBits, 0); } public static Dictionary GetStatisticsSnapshot() { return Statistics.ToDictionary, string, int>((KeyValuePair kvp) => kvp.Key.ToString(), (KeyValuePair kvp) => kvp.Value); } public static void AwardBits() { PointItemManager.RewardBits(GetTotalBits(), true); } } } namespace MashGamemodeLibrary.Phase { public abstract class GamePhase { private MarkableTimer? _timer; private float? InternalDuration => _timer?.Duration; public abstract string Name { get; } public bool IsActive { get; private set; } public abstract float Duration { get; } protected virtual TimeMarker[] Markers { get; } = Array.Empty(); public float ElapsedTime => _timer?.GetElapsedTime() ?? 0f; public bool HasReachedDuration() { return _timer?.HasReachedTimeout() ?? false; } public abstract PhaseIdentifier GetNextPhase(); protected virtual void OnPhaseEnter() { } protected virtual void OnPhaseExit() { } protected virtual void OnUpdate() { } public virtual void OnPlayerAction(PlayerID playerId, PlayerGameActions action, Handedness handedness) { } public virtual void OnPlayerJoined(PlayerID player) { } public virtual void OnPlayerLeft(PlayerID player) { } public virtual bool CanTimerTick() { return true; } public void Update(float delta) { if (_timer != null && !object.Equals(InternalDuration, Duration)) { _timer?.SetTimeout(Duration); } if (CanTimerTick()) { _timer?.Update(delta); } Executor.RunUnchecked(OnUpdate); } public void Enter() { IsActive = true; if (_timer == null) { _timer = new MarkableTimer(Duration, Markers); } _timer.Reset(); Executor.RunUnchecked(OnPhaseEnter); } public void Exit() { IsActive = false; Executor.RunUnchecked(OnPhaseExit); } } public static class GamePhaseManager { public static readonly SingletonTypedRegistry Registry; private static readonly SyncedVariable WantedPhase; private static readonly IBehaviourCache PhaseChangedBehaviours; private static readonly Dictionary LastGripStateMap; public static GamePhase? ActivePhase { get; private set; } static GamePhaseManager() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown Registry = new SingletonTypedRegistry(); WantedPhase = new SyncedVariable("GamePhaseManager_WantedPhase", new NullableValueEncoder(new ULongEncoder()), null); PhaseChangedBehaviours = BehaviourManager.CreateCache(); LastGripStateMap = new Dictionary(); WantedPhase.OnValidate += PhaseHashOnValidate; WantedPhase.OnValueChanged += PhaseChanged; MultiplayerHooking.OnPlayerAction += new PlayerAction(OnPlayerAction); MultiplayerHooking.OnPlayerJoined += new PlayerUpdate(OnPlayerJoined); MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft); } private static bool PhaseHashOnValidate(ulong? id) { if (id.HasValue) { return Registry.Contains(id.Value); } return true; } public static void Enable() where T : GamePhase { Executor.RunIfHost(delegate { WantedPhase.Value = Registry.CreateID(); }); } public static void Disable() { Executor.RunIfHost(delegate { WantedPhase.Value = null; }); } private static void PhaseChanged(ulong? id) { ActivePhase?.Exit(); ActivePhase = (id.HasValue ? Registry.Get(id.Value) : null); if (ActivePhase != null) { ActivePhase.Enter(); PhaseChangedBehaviours.ForEach(delegate(IPhaseChanged behaviour) { behaviour.OnPhaseChange(ActivePhase); }); LogicTeamManager.OnPhaseChanged(ActivePhase); } } public static void Update(float delta) { if (ActivePhase == null) { return; } CheckHands(); ActivePhase.Update(delta); Executor.RunIfHost(delegate { if (ActivePhase.Try((GamePhase a) => a.GetNextPhase(), PhaseIdentifier.Empty).TryGetValue(out var value)) { WantedPhase.Value = value; } }); } public static bool IsPhase() where T : GamePhase { return ActivePhase is T; } private static void CheckHand(Hand hand) { //IL_0008: 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_002e: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) BaseController controller = hand._controller; Handedness handedness = controller.handedness; if (controller._menuTap) { OnAction(PlayerIDManager.LocalID, PlayerGameActions.Ability, handedness); } bool isBelowGripThreshold = controller.isBelowGripThreshold; bool valueOrDefault = LastGripStateMap.GetValueOrDefault(handedness, defaultValue: false); if (isBelowGripThreshold != valueOrDefault) { PlayerGameActions action = (isBelowGripThreshold ? PlayerGameActions.HandClose : PlayerGameActions.HandOpen); OnAction(PlayerIDManager.LocalID, action, handedness); LastGripStateMap[handedness] = controller.isBelowGripThreshold; } } private static void CheckHands() { CheckHand(Player.LeftHand); CheckHand(Player.RightHand); } private static void OnAction(PlayerID player, PlayerGameActions action, Handedness handedness = 3) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) ActivePhase?.OnPlayerAction(player, action, handedness); } private static PlayerGameActions? GetPhaseAction(PlayerActionType playerAction) { //IL_0000: 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) //IL_0014: Expected I4, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 switch (playerAction - 1) { default: if ((int)playerAction == 8) { return PlayerGameActions.Respawned; } return null; case 1: return PlayerGameActions.Death; case 0: return PlayerGameActions.Jump; case 2: return PlayerGameActions.Dying; } } private static void OnPlayerAction(PlayerID playerId, PlayerActionType type, PlayerID otherPlayer) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (ActivePhase != null) { PlayerGameActions? phaseAction = GetPhaseAction(type); if (phaseAction.HasValue) { OnAction(playerId, phaseAction.Value, (Handedness)3); } } } private static void OnPlayerJoined(PlayerID playerId) { ActivePhase?.OnPlayerJoined(playerId); } private static void OnPlayerLeft(PlayerID playerId) { ActivePhase?.OnPlayerLeft(playerId); } } public struct PhaseIdentifier { private readonly ulong? _id; public bool IsEmpty() { return !_id.HasValue; } public bool TryGetValue([MaybeNullWhen(false)] out ulong value) { value = _id.GetValueOrDefault(); return _id.HasValue; } public PhaseIdentifier() { _id = null; } private PhaseIdentifier(ulong id) { _id = id; } public static PhaseIdentifier Of() where T : GamePhase { return new PhaseIdentifier(GamePhaseManager.Registry.CreateID()); } public static PhaseIdentifier Empty() { return new PhaseIdentifier(); } } public enum PlayerGameActions { Death, Dying, Respawned, Jump, HandClose, HandOpen, Ability } internal class WinPacket : INetSerializable { public ulong TeamID; public int? GetSize() { return 8; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref TeamID); } } [RequireStaticConstructor] public static class WinManager { private static readonly RemoteEvent WinEvent; static WinManager() { WinEvent = new RemoteEvent(OnWinEvent, CommonNetworkRoutes.HostToAll); PlayerStatisticsTracker.Register(TeamStatisticKeys.RoundsWon, (int v) => v * 50); } public static void Win() where T : LogicTeam { Executor.RunIfHost(delegate { if (InternalGamemodeManager.InRound) { ulong num = LogicTeamManager.Registry.CreateID(); WinEvent.Call(new WinPacket { TeamID = num }); InternalGamemodeManager.EndRound(num); } }, "Sending win state"); } private static void OnWinEvent(WinPacket packet) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_002d: 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_0038: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown if (LogicTeamManager.GetLocalTeamID() == packet.TeamID) { PlayerStatisticsTracker.Increment(TeamStatisticKeys.RoundsWon); Notifier.Send(new Notification { Title = NotificationText.op_Implicit("You won!"), Message = NotificationText.op_Implicit("Game over"), ShowPopup = true, SaveToMenu = false, Type = (NotificationType)3, PopupLength = 5f }); } else { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("You lost!"), Message = NotificationText.op_Implicit("Game over"), ShowPopup = true, SaveToMenu = false, Type = (NotificationType)2, PopupLength = 5f }); } } } } namespace MashGamemodeLibrary.Patches { [HarmonyPatch(typeof(Avatar))] public static class AvatarPatches { private static Avatar? _currentLocalAvatar; private static AvatarStats? _currentStats; private static bool IsLocalAvatar(Avatar instance) { if ((Object)(object)instance == (Object)null) { return false; } RigManager componentInParent = ((Component)instance).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null) { return false; } if (!FusionPlayer.IsLocalPlayer(componentInParent)) { return false; } return true; } private static void RefreshLocalAvatar(Avatar avatar) { if (IsLocalAvatar(avatar)) { if (((Object)avatar).name == "[RealHeptaRig (Marrow1)]") { _currentLocalAvatar = null; _currentStats = null; } else { _currentLocalAvatar = avatar; _currentStats = AvatarStatManager.GetLocalStats(avatar); } } } private static bool TryGetStatistics(Avatar instance, out AvatarStats avatarStats) { avatarStats = default(AvatarStats); if (!_currentStats.HasValue || !((Object)instance).Equals((Object)(object)_currentLocalAvatar)) { return false; } avatarStats = _currentStats.Value; return true; } [HarmonyPatch("ComputeBaseStats")] [HarmonyPostfix] public static void ComputeBaseStatsPostfix(Avatar __instance) { RefreshLocalAvatar(__instance); if (TryGetStatistics(__instance, out var avatarStats)) { __instance._speed = avatarStats.Speed; __instance._agility = avatarStats.Agility; __instance._strengthUpper = avatarStats.UpperStrength; __instance._strengthGrip = avatarStats.UpperStrength; __instance._strengthLower = avatarStats.LowerStrength; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void SpeedGetterPostfix(Avatar __instance, ref float __result) { if (TryGetStatistics(__instance, out var avatarStats)) { __result = avatarStats.Speed; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void AgilityGetterPostfix(Avatar __instance, ref float __result) { if (TryGetStatistics(__instance, out var avatarStats)) { __result = avatarStats.Agility; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void StrengthUpperGetterPostfix(Avatar __instance, ref float __result) { if (TryGetStatistics(__instance, out var avatarStats)) { __result = avatarStats.UpperStrength; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void StrengthGripGetterPostfix(Avatar __instance, ref float __result) { if (TryGetStatistics(__instance, out var avatarStats)) { __result = avatarStats.UpperStrength; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void StrengthLowerGetterPostfix(Avatar __instance, ref float __result) { if (TryGetStatistics(__instance, out var avatarStats)) { __result = avatarStats.LowerStrength; } } } [HarmonyPatch(typeof(NetworkAssetSpawner))] public static class DevToolsPatches { public static bool CanSpawn = true; [HarmonyPatch("Spawn")] [HarmonyPrefix] public static bool Prefix() { if (!NetworkInfo.IsHost) { return CanSpawn; } return true; } } [HarmonyPatch(typeof(GamemodeRegistration))] public class GamemodeRegistryPatch { [HarmonyPatch("RegisterGamemode", new Type[] { typeof(Type) })] [HarmonyPostfix] private static void RegisterGamemode_Postfix(Type type) { Type type2 = type; GamemodeCompatibilityChecker.RegisterGamemodeInfo(GamemodeRegistration.Gamemodes.First((Gamemode v) => ((object)v).GetType() == type2)); } } [HarmonyPatch] public class GripPatches { [HarmonyPatch(typeof(InventoryHand), "OnOverlapEnter")] [HarmonyPrefix] public static bool OnOverlapEnter(InventoryHand __instance, GameObject other) { if ((Object)(object)__instance == (Object)null || (Object)(object)other == (Object)null) { return true; } Grip grip = default(Grip); if (!Grip.Cache.TryGet(other, ref grip)) { return true; } if (!new GrabRequest(__instance, grip).CanGrab()) { return false; } return true; } [HarmonyPatch(typeof(Hand), "AttachObject")] [HarmonyPatch(typeof(Hand), "AttachJoint")] [HarmonyPatch(typeof(Hand), "AttachIgnoreBodyJoints")] [HarmonyPrefix] public static bool AttachObject_Prefix(Hand __instance, GameObject objectToAttach) { if ((Object)(object)__instance == (Object)null || (Object)(object)objectToAttach == (Object)null) { return true; } Grip grip = default(Grip); if (!Grip.Cache.TryGet(objectToAttach, ref grip)) { return true; } if (!new GrabRequest(__instance, grip).CanGrab()) { return false; } return true; } [HarmonyPatch(typeof(InventorySlotReceiver), "OnHandHoverBegin")] [HarmonyPatch(typeof(InventoryHandReceiver), "OnHandHoverBegin")] [HarmonyPrefix] [HarmonyPriority(10000)] public static bool HandHoverBegin_Prefix(InventorySlotReceiver __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return true; } IGrippable weaponHost = __instance._weaponHost; InteractableHost val = ((weaponHost != null) ? ((Il2CppObjectBase)weaponHost).TryCast() : null); if ((Object)(object)val == (Object)null) { return true; } if (val._grips.Count == 0) { return true; } return new GrabRequest(hand, val._grips[0]).CanGrab(); } [HarmonyPrefix] [HarmonyPatch(typeof(InventorySlotReceiver), "OnHandGrab")] [HarmonyPriority(10000)] public static bool InventoryGrabAttempt(InventorySlotReceiver __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return true; } IGrippable weaponHost = __instance._weaponHost; InteractableHost val = ((weaponHost != null) ? ((Il2CppObjectBase)weaponHost).TryCast() : null); if ((Object)(object)val == (Object)null) { return true; } if (val._grips.Count == 0) { return true; } return new GrabRequest(hand, val._grips[0]).CanGrab(); } [HarmonyPatch(typeof(InventoryAmmoReceiver), "OnHandGrab")] [HarmonyPrefix] [HarmonyPriority(10000)] public static bool InventoryHandGrabAttempt(InventoryHandReceiver __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return true; } return InteractionExtender.HasLocalInteractions(); } [HarmonyPrefix] [HarmonyPatch(typeof(InteractableIcon), "MyFarHandHoverBegin")] [HarmonyPatch(typeof(InteractableIcon), "MyHandHoverBegin")] [HarmonyPriority(10000)] public static bool IconAttempt(InteractableIcon __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return true; } Grip grip = __instance.m_Grip; if ((Object)(object)grip == (Object)null) { return true; } return new GrabRequest(hand, grip).CanGrab(); } [HarmonyPrefix] [HarmonyPatch(typeof(ForcePullGrip), "CoPull")] [HarmonyPatch(typeof(ForcePullGrip), "OnStartAttach")] [HarmonyPatch(typeof(ForcePullGrip), "OnFarHandHoverBegin")] [HarmonyPatch(typeof(ForcePullGrip), "OnFarHandHoverUpdate")] [HarmonyPatch(typeof(ForcePullGrip), "OnFarHandHoverEnd")] [HarmonyPatch(typeof(ForcePullGrip), "OnForcePullComplete")] [HarmonyPriority(10000)] public static bool ForceGrabAttempt(ForcePullGrip __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return true; } Grip grip = __instance._grip; if ((Object)(object)grip == (Object)null) { return true; } return new GrabRequest(hand, grip).CanGrab(); } [HarmonyPatch(typeof(Grip), "OnAttachedToHand")] [HarmonyPostfix] public static void OnAttachedToHand_Postfix(Grip __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return; } try { PlayerGrabManager.OnGrab(new GrabRequest(hand, __instance)); } catch (Exception ex) { MelonLogger.Error("Failed to postfix grab", ex); } } [HarmonyPatch(typeof(Grip), "OnDetachedFromHand")] [HarmonyPostfix] public static void OnDetachFromHand(Grip __instance, Hand hand) { if ((Object)(object)__instance == (Object)null || (Object)(object)hand == (Object)null) { return; } try { PlayerGrabManager.OnDrop(new GrabRequest(hand, __instance)); } catch (Exception ex) { MelonLogger.Error("Failed to prefix drop", ex); } } } [HarmonyPatch(typeof(Gun))] public static class GunPatches { [HarmonyPatch("Fire")] [HarmonyPostfix] private static void Fire_Postfix(Gun __instance) { if (!((Object)(object)__instance == (Object)null)) { PlayerGunManager.InvokeGunFired(__instance); } } [HarmonyPatch("OnTriggerGripAttached")] [HarmonyPostfix] private static void OnGripAttached_Postfix(Gun __instance) { if (!((Object)(object)__instance == (Object)null)) { PlayerGunManager.OnGunGrabbed(__instance); } } } } namespace MashGamemodeLibrary.Networking.Variable.Encoder.Util { public class NullableReferenceEncoder : IEncoder where TValue : class { private readonly IEncoder _childEncoder; public NullableReferenceEncoder(IEncoder childEncoder) { _childEncoder = childEncoder; } public int GetSize(TValue? value) { if (value == null) { return 1; } return _childEncoder.GetSize(value); } public TValue? Read(NetReader reader) { if (!reader.ReadBoolean()) { return null; } return _childEncoder.Read(reader); } public void Write(NetWriter writer, TValue? value) { if (value == null) { writer.Write(false); return; } writer.Write(true); _childEncoder.Write(writer, value); } } public class NullableValueEncoder : IEncoder where TValue : struct { private readonly IEncoder _childEncoder; public NullableValueEncoder(IEncoder childEncoder) { _childEncoder = childEncoder; } public int GetSize(TValue? value) { if (!value.HasValue) { return 1; } return _childEncoder.GetSize(value.Value); } public TValue? Read(NetReader reader) { if (!reader.ReadBoolean()) { return null; } return _childEncoder.Read(reader); } public void Write(NetWriter writer, TValue? value) { if (!value.HasValue) { writer.Write(false); return; } writer.Write(true); _childEncoder.Write(writer, value.Value); } } } namespace MashGamemodeLibrary.Networking.Remote { public abstract class GenericRemoteEvent : IGuaranteeStaticConstructor { private readonly ulong _assignedId; private readonly string _name; protected readonly INetworkRoute Route; protected GenericRemoteEvent(string name, INetworkRoute route) { _name = name; Route = route; string name2 = GetModName(GetType()) + "." + name; _assignedId = RemoteEventMessageHandler.RegisterEvent(name2, this); } private static string GetModName(Type type) { NetworkIdentifiable customAttribute = type.Assembly.GetCustomAttribute(); if (customAttribute != null) { return customAttribute.Identifier; } throw new Exception("Ensure the mod is Network Identifiable before registering it."); } ~GenericRemoteEvent() { RemoteEventMessageHandler.UnregisterEvent(_assignedId); } protected abstract int? GetSize(TData data); protected abstract void Write(NetWriter writer, TData data); protected abstract void Read(byte smallId, NetReader reader); private void Relay(TData data, MessageRoute route) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) NetWriter val = NetWriter.Create(GetSize(data)); try { Write(val, data); RemoteEventMessageHandler.Relay(_assignedId, val.Buffer, route); } finally { ((IDisposable)val)?.Dispose(); } } protected void Relay(TData data) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (!(Route is IBroadcastNetworkRoute broadcastNetworkRoute)) { MelonLogger.Error($"Attempted to broadcast event: {_name} on route: {Route.GetName()}. It is not broadcasting type."); return; } byte localSmallID = PlayerIDManager.LocalSmallID; if (!broadcastNetworkRoute.IsValid(localSmallID, out string error)) { MelonLogger.Error($"Remote Event Validation error for event: {_name} on route {Route.GetName()}: {error}"); } else { Relay(data, broadcastNetworkRoute.GetMessageRoute()); } } protected void Relay(TData data, byte targetId) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) if (!(Route is ITargetedNetworkRoute targetedNetworkRoute)) { MelonLogger.Error($"Attempted to broadcast event: {_name} on route: {Route.GetName()}. It is not of a targeted type."); return; } byte localSmallID = PlayerIDManager.LocalSmallID; if (!targetedNetworkRoute.IsValid(localSmallID, targetId, out string error)) { MelonLogger.Error($"Remote Event Validation error for event: {_name} on route {Route.GetName()}: {error}"); } else { Relay(data, targetedNetworkRoute.GetTargetedMessageRoute(targetId)); } } internal void OnPacket(byte smallId, byte[] bytes) { if (!Route.ValidFromSender(smallId)) { MelonLogger.Error($"Received event from: {smallId} {((smallId == 0) ? "(Host)" : "")}. Which is invalid on route: {Route.GetType().Name}"); return; } NetReader val = NetReader.Create(bytes); try { Read(smallId, val); } finally { ((IDisposable)val)?.Dispose(); } } } public class RemoteEvent : GenericRemoteEvent where T : class, INetSerializable, new() { public delegate void PacketHandler(T packet); private readonly PacketHandler _onEvent; public RemoteEvent(PacketHandler onEvent, INetworkRoute? route = null) : base(typeof(T).FullName ?? throw new Exception("Type has no full name, cannot create RemoteEvent for it."), route ?? CommonNetworkRoutes.HostToRemote) { _onEvent = onEvent; } public RemoteEvent(string name, PacketHandler onEvent, INetworkRoute? route = null) : base(name, route ?? CommonNetworkRoutes.HostToRemote) { _onEvent = onEvent; } private void OnEvent(byte sender, T data) { if (data is IKnownSenderPacket knownSenderPacket) { knownSenderPacket.SenderSmallId = sender; } Executor.RunChecked(_onEvent, data); } public void Call(T data) { if (LocalPlayer.GetNetworkPlayer() == null) { MelonLogger.Warning("No local player found, cannot call remote event. Is there a server running?"); return; } Relay(data); if (Route.CallOnSender()) { OnEvent(PlayerIDManager.LocalSmallID, data); } } public void CallFor(PlayerID playerId, T data) { if (playerId.IsMe && Route.CallOnSender()) { OnEvent(playerId.SmallID, data); } else if (LocalPlayer.GetNetworkPlayer() == null) { MelonLogger.Warning("No local player found, cannot call remote event. Is there a server running?"); } else { Relay(data, playerId.SmallID); } } protected override int? GetSize(T data) { return ((INetSerializable)data).GetSize(); } protected override void Write(NetWriter writer, T data) { ((INetSerializable)data).Serialize((INetSerializer)(object)writer); } protected override void Read(byte smallId, NetReader reader) { T val = new T(); ((INetSerializable)val).Serialize((INetSerializer)(object)reader); OnEvent(smallId, val); } } public class PlayerOnlyPacker : INetSerializable, IKnownSenderPacket { public byte SenderSmallId { get; set; } public void Serialize(INetSerializer serializer) { } } public class RemoteEvent : RemoteEvent { public delegate void EventHandler(byte senderId); public RemoteEvent(string name, EventHandler onEvent, INetworkRoute? route = null) { EventHandler onEvent2 = onEvent; base..ctor(name, (RemoteEvent.PacketHandler)delegate(PlayerOnlyPacker e) { onEvent2(e.SenderSmallId); }, route); } public void Call() { Call(new PlayerOnlyPacker()); } public void CallFor(PlayerID playerId) { CallFor(playerId, new PlayerOnlyPacker()); } } internal class RemoteSceneLoadedPacket : DummySerializable, IKnownSenderPacket { public byte SenderSmallId { get; set; } } internal class EventMessage : INetSerializable { public ulong EventId; public byte[] Payload; public EventMessage() { EventId = 0uL; Payload = Array.Empty(); } public EventMessage(ulong eventId, byte[] payload) { EventId = eventId; Payload = payload; } public int? GetSize() { return 12 + Payload.Length; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref EventId); serializer.SerializeValue(ref Payload); } } public class RemoteEventMessageHandler : ModuleMessageHandler { private static readonly Dictionary> EventCallbacks; private static readonly List Catchups; private static readonly List Resettables; private static readonly Dictionary EventNames; private static readonly RemoteEvent LevelLoadedEvent; static RemoteEventMessageHandler() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown EventCallbacks = new Dictionary>(); Catchups = new List(); Resettables = new List(); EventNames = new Dictionary(); LevelLoadedEvent = new RemoteEvent("RML_LevelLoadedEvent", OnRemoteLevelLoader, CommonNetworkRoutes.RemoteToHost); MultiplayerHooking.OnJoinedServer += new ServerEvent(OnServerChanged); MultiplayerHooking.OnStartedServer += new ServerEvent(OnServerChanged); MultiplayerHooking.OnDisconnected += new ServerEvent(OnServerChanged); LocalPlayer.OnLocalRigCreated = (Action)Delegate.Combine(LocalPlayer.OnLocalRigCreated, (Action)delegate { if (NetworkInfo.HasServer && !NetworkInfo.IsHost) { LevelLoadedEvent.CallFor(PlayerIDManager.GetHostID(), new RemoteSceneLoadedPacket()); } }); } public static ulong RegisterEvent(string name, GenericRemoteEvent callback) { ulong num = StableHash.Fnv1A64(name); EventCallbacks[num] = callback.OnPacket; if (callback is ICatchup item) { Catchups.Add(item); } if (callback is IResettable item2) { Resettables.Add(item2); } return num; } public static void RegisterAssembly(Assembly assembly) { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (type.IsGenericType) { continue; } bool flag = false; FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int j = 0; j < fields.Length; j++) { Type baseType = fields[j].FieldType.BaseType; while (baseType != null) { if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(GenericRemoteEvent<>)) { flag = true; break; } baseType = baseType.BaseType; } if (flag) { break; } } if (flag) { RuntimeHelpers.RunClassConstructor(type.TypeHandle); } } } public static void RegisterMod() { RegisterAssembly(typeof(T).Assembly); } public static void UnregisterEvent(ulong id) { EventCallbacks.Remove(id); } protected override void OnHandleMessage(ReceivedMessage received) { //IL_0002: 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_000a: Unknown result type (might be due to invalid IL or missing references) MessageRoute route = ((ReceivedMessage)(ref received)).Route; if ((int)((MessageRoute)(ref route)).Type == 0) { MelonLogger.Error("Received EventMessage with no relay type, cannot process."); return; } EventMessage eventMessage = ((ReceivedMessage)(ref received)).ReadData(); if (!EventCallbacks.TryGetValue(eventMessage.EventId, out Action value)) { MelonLogger.Msg("Received EventMessage with unregistered event ID: " + eventMessage.EventId); return; } byte value2 = ((ReceivedMessage)(ref received)).Sender.Value; value(value2, eventMessage.Payload); } public static void Relay(ulong eventId, ArraySegment buffer, MessageRoute route) { //IL_0002: 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) if ((int)((MessageRoute)(ref route)).Type == 0) { MelonLogger.Error("Cannot relay EventMessage with no relay type."); } else { MessageRelay.RelayModule(new EventMessage(eventId, buffer.ToArray()), route); } } private static void OnRemoteLevelLoader(RemoteSceneLoadedPacket packet) { RemoteSceneLoadedPacket packet2 = packet; Executor.RunIfHost(delegate { PlayerID playerID = PlayerIDManager.GetPlayerID(packet2.SenderSmallId); if (playerID != null) { foreach (ICatchup catchup in Catchups) { catchup.OnCatchup(playerID); } InternalGamemodeManager.OnLateJoin(playerID); } }); } private static void OnServerChanged() { Resettables.ForEach(delegate(IResettable r) { r.Reset(); }); } } } namespace MashGamemodeLibrary.networking { internal class FusionModule : Module { public override string Name => "Mash Gamemode Library"; protected override void OnModuleRegistered() { ModuleMessageManager.RegisterHandler(); } } } namespace MashGamemodeLibrary.networking.Variable { public enum DictionaryEditType { Set, Remove, Clear } public record DictionaryEdit(DictionaryEditType Type, TKey Key, TValue Value) where TKey : notnull where TValue : notnull { public static DictionaryEdit Set(TKey key, TValue value) { return new DictionaryEdit(DictionaryEditType.Set, key, value); } public static DictionaryEdit Remove(TKey key) { return new DictionaryEdit(DictionaryEditType.Remove, key, default(TValue)); } public static DictionaryEdit Clear() { return new DictionaryEdit(DictionaryEditType.Clear, default(TKey), default(TValue)); } } public class SyncedDictionary : GenericRemoteEvent>, IEnumerable>, IEnumerable, ICatchup, IResettable where TKey : notnull where TValue : notnull { public delegate void ValueChangedHandler(TKey key, TValue value); public delegate void ValueClearHandler(); public delegate void ValueRemovedHandler(TKey key, TValue oldValue); private readonly Dictionary _dictionary = new Dictionary(); private readonly IEncoder _keyEncoder; private readonly IRefEncoder? _refEncoder; private readonly IEncoder _valueEncoder; public TValue this[TKey key] { get { return _dictionary[key]; } set { SetValue(key, value, sendUpdate: true); } } public Dictionary.KeyCollection Keys => _dictionary.Keys; public Dictionary.ValueCollection Values => _dictionary.Values; public int Count => _dictionary.Count; public event ValueChangedHandler? OnValueAdded; public event ValueChangedHandler? OnValueChanged; public event ValueRemovedHandler? OnValueRemoved; public event ValueClearHandler? OnValuesCleared; public SyncedDictionary(string name, IEncoder keyEncoder, IEncoder valueEncoder, INetworkRoute route) : base(name, route) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown _keyEncoder = keyEncoder; _valueEncoder = valueEncoder; _refEncoder = valueEncoder as IRefEncoder; MultiplayerHooking.OnJoinedServer += new ServerEvent(ClearLocal); } public SyncedDictionary(string name, IEncoder keyEncoder, IEncoder valueEncoder) : base(name, CommonNetworkRoutes.HostToAll) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown _keyEncoder = keyEncoder; _valueEncoder = valueEncoder; _refEncoder = valueEncoder as IRefEncoder; MultiplayerHooking.OnJoinedServer += new ServerEvent(ClearLocal); } public void OnCatchup(PlayerID playerId) { Executor.RunIfHost(delegate { foreach (var (key, value) in _dictionary) { Relay(DictionaryEdit.Set(key, value)); } }); } public IEnumerator> GetEnumerator() { return _dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Reset() { _dictionary.Clear(); } ~SyncedDictionary() { MultiplayerHooking.OnJoinedServer -= new ServerEvent(ClearLocal); } private void SetValue(TKey key, TValue value, bool sendUpdate) { if (_dictionary.TryGetValue(key, out var value2)) { this.OnValueRemoved?.Invoke(key, value2); } _dictionary[key] = value; this.OnValueAdded?.Invoke(key, value); if (sendUpdate) { Relay(DictionaryEdit.Set(key, value)); } } private bool RemoveValue(TKey key, bool sendUpdate) { if (!_dictionary.Remove(key, out var value)) { return false; } this.OnValueRemoved?.Invoke(key, value); if (sendUpdate) { Relay(DictionaryEdit.Remove(key)); } return true; } public void Clear(bool sendUpdate = true) { ImmutableDictionary immutableDictionary = _dictionary.ToImmutableDictionary(); _dictionary.Clear(); IEnumerableExtensions.ForEach>((IEnumerable>)immutableDictionary, (Action>)delegate(KeyValuePair pair) { this.OnValueRemoved?.Invoke(pair.Key, pair.Value); }); this.OnValuesCleared?.Invoke(); if (sendUpdate) { Relay(DictionaryEdit.Clear()); } } private void ClearLocal() { Clear(sendUpdate: false); } public bool Remove(TKey key) { return RemoveValue(key, sendUpdate: true); } public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { return _dictionary.TryGetValue(key, out value); } public TValue? GetValueOrDefault(TKey key, TValue d = default(TValue)) { return _dictionary.GetValueOrDefault(key, d); } public TValue GetValueOrCreate(TKey key, Func createFunc) { if (_dictionary.TryGetValue(key, out var value)) { return value; } TValue val = createFunc(); SetValue(key, val, sendUpdate: true); return val; } public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } public void Sync(TKey key) { if (TryGetValue(key, out var value)) { this.OnValueChanged?.Invoke(key, value); Relay(DictionaryEdit.Set(key, value)); } } protected override int? GetSize(DictionaryEdit data) { int num = 4; DictionaryEditType type = data.Type; if (type == DictionaryEditType.Set || type == DictionaryEditType.Remove) { num += _keyEncoder.GetSize(data.Key); } if (data.Type == DictionaryEditType.Set) { num += _valueEncoder.GetSize(data.Value); } return num; } protected override void Write(NetWriter writer, DictionaryEdit data) { writer.Write(data.Type); DictionaryEditType type = data.Type; if (type == DictionaryEditType.Set || type == DictionaryEditType.Remove) { _keyEncoder.Write(writer, data.Key); } if (data.Type == DictionaryEditType.Set) { if (_refEncoder == null) { _valueEncoder.Write(writer, data.Value); } else { _refEncoder.Write(writer, data.Value); } } } private void SetOrUpdate(NetReader reader) { TKey key = _keyEncoder.Read(reader); if (_refEncoder != null && TryGetValue(key, out var value)) { _refEncoder.Serialize((INetSerializer)(object)reader, value); this.OnValueChanged?.Invoke(key, value); } else { TValue value2 = _valueEncoder.Read(reader); SetValue(key, value2, sendUpdate: false); } } protected override void Read(byte smallId, NetReader reader) { switch (reader.ReadEnum()) { case DictionaryEditType.Set: SetOrUpdate(reader); break; case DictionaryEditType.Remove: { TKey key = _keyEncoder.Read(reader); RemoveValue(key, sendUpdate: false); break; } case DictionaryEditType.Clear: Clear(sendUpdate: false); break; default: throw new ArgumentOutOfRangeException(); } } } public enum ChangeType { Add, Remove, Clear } public record ChangePacket(ChangeType Type, T Value); public class SyncedSet : GenericRemoteEvent>, IEnumerable, IEnumerable, ICatchup, IResettable where TValue : notnull { public delegate void ValueChangedHandler(TValue value); public delegate void ValueRemovedHandler(TValue oldValue); private readonly IEncoder _encoder; private readonly HashSet _set = new HashSet(); public int Count => _set.Count; public event ValueChangedHandler? OnValueAdded; public event ValueRemovedHandler? OnValueRemoved; public SyncedSet(string name, IEncoder encoder) : this(name, encoder, CommonNetworkRoutes.HostToAll) { } public SyncedSet(string name, IEncoder encoder, INetworkRoute route) : base(name, route) { _encoder = encoder; } public void OnCatchup(PlayerID playerId) { foreach (TValue item in _set) { RelayAdd(item); } } public IEnumerator GetEnumerator() { return _set.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Reset() { Clear(sendUpdate: false); } private void RelayAdd(TValue value) { Relay(new ChangePacket(ChangeType.Add, value)); } private void RelayRemove(TValue value) { Relay(new ChangePacket(ChangeType.Remove, value)); } private void OnServerChanged() { _set.Clear(); } private void AddValue(TValue value, bool sendUpdate) { if (_set.Add(value)) { this.OnValueAdded?.Invoke(value); if (sendUpdate) { RelayAdd(value); } } } private bool RemoveValue(TValue value, bool sendUpdate) { if (!_set.Remove(value)) { return false; } this.OnValueRemoved?.Invoke(value); if (sendUpdate) { RelayRemove(value); } return true; } public void Clear(bool sendUpdate = true) { List list = _set.ToList(); _set.Clear(); list.ForEach(delegate(TValue value) { this.OnValueRemoved?.Invoke(value); }); if (sendUpdate) { Relay(new ChangePacket(ChangeType.Clear, default(TValue))); } } public void Add(TValue value) { AddValue(value, sendUpdate: true); } public void Remove(TValue value) { RemoveValue(value, sendUpdate: true); } protected override int? GetSize(ChangePacket data) { if (data.Type == ChangeType.Clear) { return 4; } return 4 + _encoder.GetSize(data.Value); } protected override void Write(NetWriter writer, ChangePacket data) { writer.Write(data.Type); if (data.Type != ChangeType.Clear) { _encoder.Write(writer, data.Value); } } protected override void Read(byte smallId, NetReader reader) { switch (reader.ReadEnum()) { case ChangeType.Add: AddValue(_encoder.Read(reader), sendUpdate: false); break; case ChangeType.Remove: RemoveValue(_encoder.Read(reader), sendUpdate: false); break; case ChangeType.Clear: Clear(sendUpdate: false); break; default: throw new ArgumentOutOfRangeException(); } } } public class SyncedVariable : GenericRemoteEvent, ICatchup, IResettable { public delegate void OnChangedHandler(TValue newValue); public delegate bool ValidatorHandler(TValue newValue); private readonly TValue _default; private readonly IEncoder _encoder; private readonly string _name; private TValue _value; public TValue Value { get { return _value; } set { SetValue(value); } } public event OnChangedHandler? OnValueChanged; public event ValidatorHandler? OnValidate; public SyncedVariable(string name, IEncoder encoder, TValue defaultValue) : this(name, encoder, defaultValue, CommonNetworkRoutes.HostToAll) { } public SyncedVariable(string name, IEncoder encoder, TValue defaultValue, INetworkRoute route) : base("sync." + name, route) { _name = name; _encoder = encoder; _default = defaultValue; _value = defaultValue; } public void OnCatchup(PlayerID playerId) { Relay(_value, playerId.SmallID); } public void Reset() { _value = _default; } public static implicit operator TValue(SyncedVariable variable) { return variable.Value; } protected override int? GetSize(TValue data) { return _encoder.GetSize(data); } protected override void Write(NetWriter writer, TValue data) { _encoder.Write(writer, data); } protected override void Read(byte smallId, NetReader reader) { _value = _encoder.Read(reader); this.OnValueChanged?.Invoke(_value); } public void SetAndSync(TValue value) { ValidatorHandler? onValidate = this.OnValidate; if (onValidate == null || onValidate(value)) { _value = value; this.OnValueChanged?.Invoke(_value); Sync(); } } public void Sync() { Relay(_value); } public void SyncTo(byte playerId) { Relay(_value, playerId); } private void SetValue(TValue newValue) { if (!object.Equals(_value, newValue)) { SetAndSync(newValue); } } } } namespace MashGamemodeLibrary.networking.Variable.Encoder { public interface IEncoder { int GetSize(TValue value); TValue Read(NetReader reader); void Write(NetWriter riter, TValue value); } public interface IRefEncoder : IEncoder { void Serialize(INetSerializer serializer, TValue value); } } namespace MashGamemodeLibrary.networking.Variable.Encoder.Impl { public class BoolEncoder : IEncoder { public int GetSize(bool value) { return 1; } public bool Read(NetReader reader) { return reader.ReadBoolean(); } public void Write(NetWriter writer, bool value) { writer.Write(value); } } public class ByteEncoder : IEncoder { public int GetSize(byte value) { return 1; } public byte Read(NetReader reader) { return reader.ReadByte(); } public void Write(NetWriter writer, byte value) { writer.Write(value); } } public class DynamicInstanceEncoder : IRefEncoder, IEncoder where TValue : class { private readonly ITypedRegistry _typedRegistry; public DynamicInstanceEncoder(ITypedRegistry typedRegistry) { _typedRegistry = typedRegistry; } public int GetSize(TValue value) { int num = 8; INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { num += val.GetSize() ?? 4096; } return num; } public TValue Read(NetReader reader) { ulong num = reader.ReadUInt64(); if (!_typedRegistry.TryGet(num, out var entry)) { throw new Exception($"Failed to fetch: {num} from registry of type: {typeof(TValue).Name}"); } if (entry != null) { INetSerializable val = (INetSerializable)(object)((entry is INetSerializable) ? entry : null); if (val != null) { val.Serialize((INetSerializer)(object)reader); } return entry; } MelonLogger.Error($"No value registered by id {num} in {typeof(TValue).Name}'s registry."); return null; } public void Write(NetWriter writer, TValue value) { writer.Write(_typedRegistry.CreateID(value)); INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { val.Serialize((INetSerializer)(object)writer); } } public void Serialize(INetSerializer serializer, TValue value) { ulong num = (serializer.IsReader ? 0 : _typedRegistry.CreateID(value)); serializer.SerializeValue(ref num); INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { val.Serialize(serializer); } } } public class EnumEncoder : IEncoder where T : struct, Enum { public int GetSize(T value) { return 4; } public T Read(NetReader reader) { return reader.ReadEnum(); } public void Write(NetWriter writer, T value) { writer.Write(value); } } public class FloatEncoder : IEncoder { public int GetSize(float value) { return 4; } public float Read(NetReader reader) { return reader.ReadSingle(); } public void Write(NetWriter writer, float value) { writer.Write(value); } } public class InstanceEncoder : IRefEncoder, IEncoder where TValue : class, INetSerializable, new() { public int GetSize(TValue value) { int num = 8; if (value != null) { num += ((INetSerializable)value).GetSize() ?? 4096; } return num; } public TValue Read(NetReader reader) { TValue val = new TValue(); ((INetSerializable)val).Serialize((INetSerializer)(object)reader); return val; } public void Write(NetWriter writer, TValue value) { ((INetSerializable)value).Serialize((INetSerializer)(object)writer); } public void Serialize(INetSerializer serializer, TValue value) { ((INetSerializable)value).Serialize(serializer); } } public class IntEncoder : IEncoder { public int GetSize(int value) { return 4; } public int Read(NetReader reader) { return reader.ReadInt32(); } public void Write(NetWriter writer, int value) { writer.Write(value); } } public class MappedDynamicInstanceEncoder : IRefEncoder, IEncoder where TInternal : class where TValue : class { public delegate TValue MapToValueDelegate(TInternal @internal); public delegate ulong MapToKeyDelegate(ITypedRegistry registry, TValue value); private readonly ITypedRegistry _typedRegistry; private readonly MapToValueDelegate _mapToValue; private readonly MapToKeyDelegate _mapToKey; public MappedDynamicInstanceEncoder(ITypedRegistry typedRegistry, MapToValueDelegate mapToValue, MapToKeyDelegate mapToKey) { _typedRegistry = typedRegistry; _mapToValue = mapToValue; _mapToKey = mapToKey; } public int GetSize(TValue value) { int num = 8; INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { num += val.GetSize() ?? 4096; } return num; } public TValue Read(NetReader reader) { ulong num = reader.ReadUInt64(); if (!_typedRegistry.TryGet(num, out var entry)) { throw new Exception($"Failed to fetch: {num} from registry of type: {typeof(TValue).Name}"); } if (entry != null) { INetSerializable val = (INetSerializable)(object)((entry is INetSerializable) ? entry : null); if (val != null) { val.Serialize((INetSerializer)(object)reader); } return _mapToValue(entry); } MelonLogger.Error($"No value registered by id {num} in {typeof(TValue).Name}'s registry."); return null; } public void Write(NetWriter writer, TValue value) { writer.Write(_mapToKey(_typedRegistry, value)); INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { val.Serialize((INetSerializer)(object)writer); } } public void Serialize(INetSerializer serializer, TValue value) { ulong num = (serializer.IsReader ? 0 : _mapToKey(_typedRegistry, value)); serializer.SerializeValue(ref num); INetSerializable val = (INetSerializable)(object)((value is INetSerializable) ? value : null); if (val != null) { val.Serialize(serializer); } } } public class ULongEncoder : IEncoder { public int GetSize(ulong value) { return 8; } public ulong Read(NetReader reader) { return reader.ReadUInt64(); } public void Write(NetWriter writer, ulong value) { writer.Write(value); } } public class Vector3Encoder : IEncoder { public int GetSize(Vector3 value) { return 12; } public Vector3 Read(NetReader reader) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) float num = reader.ReadSingle(); float num2 = reader.ReadSingle(); float num3 = reader.ReadSingle(); return new Vector3(num, num2, num3); } public void Write(NetWriter writer, Vector3 value) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) writer.Write(value.x); writer.Write(value.y); writer.Write(value.z); } } } namespace MashGamemodeLibrary.networking.Validation { public static class CommonNetworkRoutes { public static readonly INetworkRoute BiDirectional = new BiDirectionalNetworkRoute(); public static readonly INetworkRoute HostToRemote = new HostToRemoteNetworkRoute(); public static readonly INetworkRoute RemoteToHost = new RemoteToHostNetworkRoute(); public static readonly INetworkRoute AllToHost = new AllToHostNetworkRoute(); public static readonly INetworkRoute HostToAll = new HostToAllNetworkRoute(); public static readonly INetworkRoute AllToAll = new AllToAllNetworkRoute(); } public interface IBroadcastNetworkRoute : INetworkRoute { bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error); MessageRoute GetMessageRoute(); } public interface INetworkRoute { string GetName(); bool ValidFromSender(byte id); bool CallOnSender() { return false; } } public interface ITargetedNetworkRoute : INetworkRoute { bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error); MessageRoute GetTargetedMessageRoute(byte targetID); } public static class NetworkValidatorHelper { public static bool IsClient(byte smallId) { return smallId != 0; } public static bool IsHost(byte smallId) { return smallId == 0; } } } namespace MashGamemodeLibrary.networking.Validation.Routes { public class AllToAllNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Client To Host Route"; } public bool CallOnSender() { return true; } public bool ValidFromSender(byte id) { return true; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToOtherClients; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToOtherClients; } } public class AllToHostNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Client To Host Route"; } public bool CallOnSender() { return true; } public bool ValidFromSender(byte id) { return NetworkInfo.IsHost; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToServer; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { if (NetworkValidatorHelper.IsClient(smallIDTo)) { error = $"{smallIDTo} is receiving as a client"; return false; } error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToServer; } } public class BiDirectionalNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Bi-directional Route"; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToOtherClients; } public bool ValidFromSender(byte id) { if (id != 0 || NetworkInfo.IsHost) { if (id != 0) { return NetworkInfo.IsHost; } return false; } return true; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { if (NetworkValidatorHelper.IsClient(smallIdFrom) == NetworkValidatorHelper.IsClient(smallIDTo)) { error = $"{smallIdFrom} and {smallIDTo} are both clients."; return false; } error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return new MessageRoute(targetID, (NetworkChannel)0); } } public class HostToAllNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Host To Client Route"; } public bool CallOnSender() { return true; } public bool ValidFromSender(byte id) { return id == 0; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToOtherClients; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { if (!NetworkValidatorHelper.IsHost(smallIdFrom)) { error = $"{smallIdFrom} is not a host."; return false; } error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { if (NetworkValidatorHelper.IsClient(smallIdFrom)) { error = $"{smallIdFrom} is a sending as a client"; return false; } if (NetworkValidatorHelper.IsHost(smallIDTo)) { error = $"{smallIDTo} is receiving as a host"; return false; } error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return new MessageRoute(targetID, (NetworkChannel)0); } } public class HostToRemoteNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Host To Client Route"; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToOtherClients; } public bool ValidFromSender(byte id) { return id == 0; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { if (!NetworkValidatorHelper.IsHost(smallIdFrom)) { error = $"{smallIdFrom} is not a host."; return false; } error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { if (NetworkValidatorHelper.IsClient(smallIdFrom)) { error = $"{smallIdFrom} is a sending as a client"; return false; } if (NetworkValidatorHelper.IsHost(smallIDTo)) { error = $"{smallIDTo} is receiving as a host"; return false; } error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return new MessageRoute(targetID, (NetworkChannel)0); } } public class RemoteToHostNetworkRoute : IBroadcastNetworkRoute, INetworkRoute, ITargetedNetworkRoute { public string GetName() { return "Client To Host Route"; } public MessageRoute GetMessageRoute() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToServer; } public bool ValidFromSender(byte id) { return id != 0; } public bool IsValid(byte smallIdFrom, [MaybeNullWhen(true)] out string error) { if (!NetworkValidatorHelper.IsClient(smallIdFrom)) { error = $"{smallIdFrom} is not a client"; return false; } error = null; return true; } public bool IsValid(byte smallIdFrom, byte smallIDTo, [MaybeNullWhen(true)] out string error) { if (NetworkValidatorHelper.IsHost(smallIdFrom)) { error = $"{smallIdFrom} is a sending as a host"; return false; } if (NetworkValidatorHelper.IsClient(smallIDTo)) { error = $"{smallIDTo} is receiving as a client"; return false; } error = null; return true; } public MessageRoute GetTargetedMessageRoute(byte targetID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CommonMessageRoutes.ReliableToServer; } } } namespace MashGamemodeLibrary.networking.Mods { internal class ModDownloadRequest : INetSerializable { private int? _fileId; private int _modId; public int ModId => _modId; public ModIOFile ModFile { get { //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new ModIOFile(_modId, _fileId); } set { _modId = ((ModIOFile)(ref value)).ModID; _fileId = ((ModIOFile)(ref value)).FileID; } } public ModDownloadRequest() { _modId = 0; _fileId = null; } public ModDownloadRequest(ModIOModTarget target) { _modId = (int)target.ModId; _fileId = (int)target.ModfileId; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _modId); serializer.SerializeValue(ref _fileId); } } internal class ModDownloadResponse : INetSerializable, IKnownSenderPacket { public int ModId; public bool Success; public byte SenderSmallId { get; set; } public ModDownloadResponse() { ModId = 0; Success = false; } public ModDownloadResponse(int modId, bool success = true) { ModId = modId; Success = success; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref ModId); serializer.SerializeValue(ref Success); } } internal class PalletState { public ModDownloadRequest Request { get; } public DateTime LastRequestAt { get; set; } public HashSet AwaitingResponseFrom { get; } = new HashSet(); public HashSet ApprovedBy { get; } = new HashSet(); public PalletState(ModDownloadRequest request) { Request = request; } public void FilterOfflinePlayers() { IEnumerable onlinePlayers = NetworkPlayer.Players.Select((NetworkPlayer p) => p.PlayerID); AwaitingResponseFrom.RemoveWhere((PlayerID p) => !onlinePlayers.Contains(p)); ApprovedBy.RemoveWhere((PlayerID p) => !onlinePlayers.Contains(p)); } public IEnumerable GetUnapprovedUsers() { return (from p in NetworkPlayer.Players select p.PlayerID into p where !p.IsHost select p).Except(ApprovedBy); } public void Approve(PlayerID playerId) { if (!playerId.IsHost) { AwaitingResponseFrom.Remove(playerId); ApprovedBy.Add(playerId); } } public bool IsApproved() { FilterOfflinePlayers(); if (AwaitingResponseFrom.Count > 0) { return false; } int count = NetworkPlayer.Players.Count; return ApprovedBy.Count + 1 >= count; } } public static class ModRequirementSyncer { private static readonly RemoteEvent ModDownloadRequestEvent = new RemoteEvent(OnModDownloadRequest, CommonNetworkRoutes.HostToRemote); private static readonly RemoteEvent ModDownloadResponseEvent = new RemoteEvent(OnModDownloadResponse, CommonNetworkRoutes.RemoteToHost); private static readonly Dictionary _palletStates = new Dictionary(); private static bool TryGetModInfo(Pallet pallet, [MaybeNullWhen(false)] out ModIOModTarget modTarget) { modTarget = null; if (pallet.IsInMarrowGame()) { return false; } PalletManifest manifest = CrateFilterer.GetManifest(pallet); if (manifest == null) { return false; } ModListing modListing = manifest.ModListing; modTarget = ModIOManager.GetTargetFromListing(modListing); return modTarget != null; } private static void AddRequiredPallet(Pallet pallet) { if (TryGetModInfo(pallet, out ModIOModTarget modTarget)) { ModDownloadRequest modDownloadRequest = new ModDownloadRequest(modTarget); _palletStates.TryAdd(modDownloadRequest.ModId, new PalletState(modDownloadRequest)); } } public static void SetPallets(IEnumerable pallets) { _palletStates.Clear(); IEnumerableExtensions.ForEach(pallets, (Action)AddRequiredPallet); } public static bool CheckApprovals(TimeSpan timeout) { DateTime now = DateTime.UtcNow; return _palletStates.Values.Where((PalletState palletState) => now - palletState.LastRequestAt <= timeout).All((PalletState palletState) => palletState.IsApproved()); } private static void SendApprovalRequest(PalletState state) { state.LastRequestAt = DateTime.UtcNow; foreach (PlayerID unapprovedUser in state.GetUnapprovedUsers()) { state.AwaitingResponseFrom.Add(unapprovedUser); ModDownloadRequestEvent.CallFor(unapprovedUser, state.Request); } } public static void SendApprovalRequests() { foreach (PalletState value in _palletStates.Values) { SendApprovalRequest(value); } } private static IEnumerator WaitAndInstallMod(ModDownloadRequest downloadRequest) { ModDownloadRequest downloadRequest2 = downloadRequest; float elapsed = 0f; bool receivedCallback = false; bool temporary = !ClientSettings.Downloading.KeepDownloadedMods.Value; ModIODownloader.EnqueueDownload(new ModTransaction { ModFile = downloadRequest2.ModFile, Temporary = temporary, Callback = new DownloadCallback(ModDownloadedCallback) }); while (!receivedCallback && elapsed < 5f) { elapsed += Time.deltaTime; yield return null; } void ModDownloadedCallback(DownloadCallbackInfo info) { receivedCallback = true; OnModInstalled(downloadRequest2.ModId); } } private static void OnModInstalled(int modId) { ModDownloadResponseEvent.Call(new ModDownloadResponse(modId)); } private static bool HasModInstalled(int modId) { return AssetWarehouse.Instance.modioPalletManifestsLookup.ContainsKey(modId); } [RunIf(ExecutionContext.Remote)] private static void OnModDownloadRequest(ModDownloadRequest packet) { if (HasModInstalled(packet.ModId)) { OnModInstalled(packet.ModId); } else { MelonCoroutines.Start(WaitAndInstallMod(packet)); } } private static void OnModDownloadResponse(ModDownloadResponse packet) { if (_palletStates.TryGetValue(packet.ModId, out PalletState value)) { PlayerID playerID = PlayerIDManager.GetPlayerID(packet.SenderSmallId); if (playerID != null) { value.Approve(playerID); } } } } } namespace MashGamemodeLibrary.networking.Control { public interface ICatchup { void OnCatchup(PlayerID playerId); } public interface IKnownSenderPacket { byte SenderSmallId { get; set; } } public interface IResettable { void Reset(); } [AttributeUsage(AttributeTargets.Assembly)] public class NetworkIdentifiable : Attribute { public string Identifier { get; } public NetworkIdentifiable(string identifier) { Identifier = identifier; } } } namespace MashGamemodeLibrary.networking.Compatiblity { internal readonly record struct GamemodeCompatibilityInfo(string GamemodeId, string Version) { public ulong Hash { get; } = GamemodeId.GetStableHash() + Version.GetStableHash(); } public class GamemodeHashPacket : INetSerializable, IKnownSenderPacket { public List Hashes = new List(); public byte SenderSmallId { get; set; } public void Serialize(INetSerializer serializer) { ulong item = 0uL; int count = Hashes.Count; serializer.SerializeValue(ref count); if (serializer.IsReader) { Hashes.Clear(); for (int i = 0; i < count; i++) { serializer.SerializeValue(ref item); Hashes.Add(item); } } else { for (int j = 0; j < count; j++) { item = Hashes[j]; serializer.SerializeValue(ref item); } } } } public static class GamemodeCompatibilityChecker { private static readonly RemoteEvent GamemodeHashRemoteEvent; private static Gamemode? _activeGamemode; private static readonly HashSet ValidatedPlayers; private static readonly Dictionary LocalGamemodeInfo; private static readonly Dictionary> RemoteGamemodeHashes; static GamemodeCompatibilityChecker() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown GamemodeHashRemoteEvent = new RemoteEvent("GlobalGamemodeHashEvent", OnGamemodeHashesReceived, new RemoteToHostNetworkRoute()); ValidatedPlayers = new HashSet(); LocalGamemodeInfo = new Dictionary(); RemoteGamemodeHashes = new Dictionary>(); MultiplayerHooking.OnJoinedServer += new ServerEvent(SendGamemodeHashes); } public static void SetActiveGamemode(Gamemode? gamemode) { _activeGamemode = gamemode; ValidatedPlayers.Clear(); } private static void KickPlayer(byte smallId) { if (_activeGamemode != null) { GamemodeCompatibilityInfo gamemodeCompatibilityInfo = LocalGamemodeInfo[((object)_activeGamemode).GetType()]; ConnectionSender.SendDisconnect((ulong)smallId, "The server is running a gamemode that you don't have: " + gamemodeCompatibilityInfo.GamemodeId + " - " + gamemodeCompatibilityInfo.Version); RemoteGamemodeHashes.Remove(smallId); } } public static void ValidatePlayer(byte smallId) { if (_activeGamemode == null || ValidatedPlayers.Contains(smallId)) { return; } if (!RemoteGamemodeHashes.TryGetValue(smallId, out List value)) { KickPlayer(smallId); return; } GamemodeCompatibilityInfo gamemodeCompatibilityInfo = LocalGamemodeInfo[((object)_activeGamemode).GetType()]; if (!value.Contains(gamemodeCompatibilityInfo.Hash)) { KickPlayer(smallId); } else { ValidatedPlayers.Add(smallId); } } public static void RegisterGamemodeInfo(Gamemode gamemode) { MelonInfoAttribute? customAttribute = ((object)gamemode).GetType().Assembly.GetCustomAttribute(); string version = ((customAttribute != null) ? customAttribute.Version : null) ?? "1.0.0"; GamemodeCompatibilityInfo value = new GamemodeCompatibilityInfo(gamemode.Title, version); LocalGamemodeInfo.Add(((object)gamemode).GetType(), value); } public static void SendGamemodeHashes() { Executor.RunIfRemote(delegate { GamemodeHashRemoteEvent.Call(new GamemodeHashPacket { Hashes = LocalGamemodeInfo.Values.Select((GamemodeCompatibilityInfo v) => v.Hash).ToList() }); }); } public static void ClearRemoteHashes() { RemoteGamemodeHashes.Clear(); } private static void OnGamemodeHashesReceived(GamemodeHashPacket packet) { RemoteGamemodeHashes[packet.SenderSmallId] = packet.Hashes; } } } namespace MashGamemodeLibrary.Integrations { public record struct MedalEvent { public string EventId { get; init; } public string EventName { get; init; } } public class MedalIntegration { private const string MedalApiUrl = "http://localhost:12665"; private const string ApiKey = "pub_ZKPrQhyrdUozjY077hrsPbiLxOr0EkKV"; public static void SendEvent(MedalEvent medalEvent) { SendEventAsync(medalEvent); } private static async Task SendEventAsync(MedalEvent medalEvent) { try { using HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("publicKey", "pub_ZKPrQhyrdUozjY077hrsPbiLxOr0EkKV"); StringContent content = new StringContent(JsonSerializer.Serialize(new { eventId = medalEvent.EventId, eventName = medalEvent.EventName }), Encoding.UTF8, "application/json"); await client.PostAsync("http://localhost:12665/api/v1/event/invoke", content); } catch (Exception ex) { InternalLogger.Error("Failed to send event to Medal API: " + ex.Message); } } } public static class MIDTIntegration { private static class MIDTIntegrationPatches { [HarmonyPrefix] public static bool CanSpawnDevTools() { if (!NetworkInfo.HasServer) { return true; } if (FusionDevTools.DevToolsDisabled) { return false; } return true; } } private static void TryPatch() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown try { Harmony val = new Harmony("com.mash.gamemodes.mitdintegration"); MethodInfo method = typeof(AddDevMenuPatch).GetMethod("OnSpawnDelegateFusion", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { MelonLogger.Warning("MIDT integration: Could not find OnSpawnDelegateFusion method"); return; } MethodInfo method2 = typeof(MIDTIntegrationPatches).GetMethod("CanSpawnDevTools"); if (val.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null) != null) { MelonLogger.Msg("MIDT integration: Successfully applied OnSpawnDelegateFusion patch"); } else { MelonLogger.Warning("MIDT integration: Failed to apply patch, harmony.Patch returned null"); } } catch (Exception value) { MelonLogger.Error($"Failed to patch MIDT integration: {value}"); } } public static void TryInitialize() { if (MelonBase.FindMelon("More Items in Dev Tools", "doge15567") != null) { TryPatch(); } } } public static class ModIntegrations { public static void TryInitialize() { SpidermanModIntegrations.TryInitialize(); MIDTIntegration.TryInitialize(); } } public static class SpidermanModIntegrations { private static class SpidermanModPatches { [HarmonyPrefix] public static bool WebShooter_CheckGestureInput_Prefix(WebShooter __instance) { if (__instance == null) { return true; } if (CrippleHelper.IsCrippled) { return false; } return true; } } public static void TryPatch() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown try { Harmony val = new Harmony("com.mash.gamemodes.spidermanmod"); MethodInfo method = typeof(WebShooter).GetMethod("CheckGestureInput", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { MelonLogger.Warning("Spiderman integration: Could not find CheckGestureInput method"); return; } MethodInfo method2 = typeof(SpidermanModPatches).GetMethod("WebShooter_CheckGestureInput_Prefix"); if (val.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null) != null) { MelonLogger.Msg("Spiderman integration: Successfully applied CheckGestureInput patch"); } else { MelonLogger.Warning("Spiderman integration: Failed to apply patch, harmony.Patch returned null"); } } catch (Exception value) { MelonLogger.Error($"Failed to patch Spiderman integration: {value}"); } } public static void TryInitialize() { if (MelonBase.FindMelon("Spiderman", "notnotnotswipez") != null) { TryPatch(); } } } } namespace MashGamemodeLibrary.Execution { public static class Executor { public delegate void Runnable(); private static void Run(Runnable runnable) { try { runnable(); } catch (Exception value) { MelonLogger.Error($"[Mash Gamemode Library] An error occurred during execution: {value}"); MelonLogger.Error("Stack Trace: " + new StackTrace()); } } public static void RunIfHost(Runnable runnable, string? error = null) { if (!NetworkInfo.IsHost) { if (error != null) { MelonLogger.Error("This can only be ran from a host: " + error); MelonLogger.Error("Stack Trace: " + new StackTrace()); } } else { Run(runnable); } } public static void RunIfRemote(Runnable runnable, string? error = null) { if (NetworkInfo.IsHost) { if (error != null) { MelonLogger.Error("This can only be ran from a remote user: " + error); } } else { Run(runnable); } } public static void RunIfNotMe(PlayerID id, Runnable runnable) { if (!id.IsMe) { Run(runnable); } } public static void RunIfMe(PlayerID id, Runnable runnable) { if (id.IsMe) { Run(runnable); } } public static void EnsureHost() { } private static bool IsInContext(ExecutionContext executionContext) { return executionContext switch { ExecutionContext.Host => NetworkInfo.IsHost, ExecutionContext.Remote => !NetworkInfo.IsHost, _ => throw new ArgumentOutOfRangeException("executionContext", executionContext, null), }; } private static bool MayExecute() where T : Delegate { RunIf customAttribute = typeof(T).GetCustomAttribute(); if (customAttribute != null) { return IsInContext(customAttribute.ExecutionContext); } return true; } public static void RunUnchecked(Action action) { try { action(); } catch (Exception ex) { MelonLogger.Error("Failed to execute executor call", ex); } } public static void RunChecked(T action) where T : Delegate { T action2 = action; if (MayExecute()) { RunUnchecked(delegate { action2.DynamicInvoke(); }); } } public static void RunChecked(T action, object argument1) where T : Delegate { T action2 = action; object argument2 = argument1; if (MayExecute()) { RunUnchecked(delegate { action2.DynamicInvoke(argument2); }); } } private static IEnumerator DelayThenInvoke(T action, float timeout) where T : Delegate { yield return (object)new WaitForSeconds(timeout); try { action.DynamicInvoke(); } catch (Exception ex) { MelonLogger.Error("Failed to execute delayed executor call", ex); } } public static void RunCheckedInFuture(T action, TimeSpan timeout) where T : Delegate { if (MayExecute()) { MelonCoroutines.Start(DelayThenInvoke(action, timeout.Seconds)); } } public static TReturn Try(Func action, TReturn defaultValue) { try { return action(); } catch (Exception ex) { MelonLogger.Error("Failed to execute executor call", ex); return defaultValue; } } public static void Try(this T value, Action action) { try { action(value); } catch (Exception ex) { MelonLogger.Error("Failed to execute " + typeof(T).FullName, ex); } } public static TReturn Try(this TInstance value, Func action, Func defaultValue) { try { return action(value); } catch (Exception ex) { MelonLogger.Error("Failed to execute " + typeof(TInstance).FullName, ex); } return defaultValue(); } public static TReturn Try(this TInstance value, Func action, TReturn defaultValue) { TReturn defaultValue2 = defaultValue; return value.Try(action, () => defaultValue2); } } public enum ExecutionContext { Host, Remote } [AttributeUsage(AttributeTargets.Method)] public class RunIf : Attribute { public ExecutionContext ExecutionContext { get; } public RunIf(ExecutionContext executionContext) { ExecutionContext = executionContext; } } } namespace MashGamemodeLibrary.Environment { public abstract class EnvironmentEffector { public abstract Enum Track { get; } public virtual bool CanApply(TContext context) { return true; } public virtual void Apply(TContext context) { } public virtual void Update(TContext context, float delta) { } public virtual void Remove(TContext context) { } } public class Track { private EnvironmentEffector? _effector; private Enum _id; public Track(Enum id) { _id = id; } public void SetEffector(EnvironmentEffector? effector, TInternalContext context) { if (_effector != effector) { _effector?.Remove(context); _effector = effector; _effector?.Apply(context); } } public void Update(TInternalContext context, float delta) { _effector?.Update(context, delta); } } internal class EnvironmentChangePacket : INetSerializable { public ulong StateHash; public int? GetSize() { return 8; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref StateHash); } } public class EnvironmentManager : IUpdating, IContextfull, IStoppable where TContext : GameModeContext, new() { private readonly Func _contextBuilder; private readonly Dictionary> _tracks = new Dictionary>(); private TInternalContext _context; private ImmutableSortedSet _knownLayers; private EnvironmentProfile? _profile; public EnvironmentManager(Func contextBuilder) { _contextBuilder = contextBuilder; } public void SetContext(TContext context) { _context = _contextBuilder(context); } public void Stop() { if (_profile != null) { IEnumerableExtensions.ForEach>((IEnumerable>)_tracks.Values, (Action>)delegate(Track track) { track.SetEffector(null, _context); }); _tracks.Clear(); _profile.Cleanup(); _profile = null; } } public void Update(float delta) { if (_profile == null) { return; } IEnumerableExtensions.ForEach>((IEnumerable>)_tracks.Values, (Action>)delegate(Track track) { track.Update(_context, delta); }); HashSet hashSet = new HashSet(); foreach (int knownLayer in _knownLayers) { EnvironmentState wantedState = _profile.GetWantedState(knownLayer, _context); if (wantedState == null) { continue; } EnvironmentEffector[] effectors = wantedState.Effectors; foreach (EnvironmentEffector environmentEffector in effectors) { Enum track2 = environmentEffector.Track; if (environmentEffector.CanApply(_context) && !hashSet.Contains(track2)) { GetOrCreateTrack(track2).SetEffector(environmentEffector, _context); hashSet.Add(track2); } } } } private Track GetOrCreateTrack(Enum id) { if (_tracks.TryGetValue(id, out Track value)) { return value; } Track track = new Track(id); _tracks[id] = track; return track; } public void StartPlaying(EnvironmentProfile profile) { _profile = profile; _knownLayers = (from e in profile.GetAllStates() select e.Layer).ToImmutableSortedSet(Comparer.Create((int a, int b) => b.CompareTo(a))); } } internal class EnvironmentStateComparer : IComparer> { public int Compare(EnvironmentState? x, EnvironmentState? y) { int value = x?.Priority ?? 0; return (y?.Priority ?? 0).CompareTo(value); } } public class EnvironmentProfile { private readonly Action _cleanupAction; private readonly ImmutableDictionary> _stateLookup; private readonly ImmutableSortedSet> _states; public string Name { get; } public EnvironmentProfile(string name, EnvironmentState[] states, Action cleanupAction) { Name = name; _stateLookup = states.ToImmutableDictionary((EnvironmentState state) => (state.GetType().FullName ?? throw new InvalidOperationException()).GetStableHash()); _states = states.ToImmutableSortedSet>(new EnvironmentStateComparer()); _cleanupAction = cleanupAction; } public EnvironmentState? GetStateByHash(ulong hash) { return _stateLookup.GetValueOrDefault(hash); } public ImmutableSortedSet> GetAllStates() { return _states; } public EnvironmentState? GetWantedState(int layer, TInternalContent context) { TInternalContent context2 = context; return _states.Where((EnvironmentState e) => e.Layer == layer).FirstOrDefault((EnvironmentState musicState) => musicState?.CanPlay(context2) ?? false, null); } public void Cleanup() { _cleanupAction(); } } } namespace MashGamemodeLibrary.Environment.State { public abstract class EnvironmentState { public ulong StateHash { get; init; } public abstract int Priority { get; } public virtual int Layer => 0; public EnvironmentEffector[] Effectors { get; } public EnvironmentState(EnvironmentEffector[] effectors) { StateHash = (GetType().FullName ?? throw new InvalidOperationException()).GetStableHash(); Effectors = effectors; } public abstract bool CanPlay(TContext context); } } namespace MashGamemodeLibrary.Environment.Effector { public abstract class AudioEffector : EnvironmentEffector { private readonly IContinuousPlayer _audioContainer; public AudioEffector(IContinuousPlayer audioContainer) { _audioContainer = audioContainer; } public override void Apply(TContext context) { _audioContainer.Start(); } public override void Update(TContext context, float delta) { _audioContainer.Update(delta); } public override void Remove(TContext context) { _audioContainer.Stop(); } } public abstract class CustomAudioEffector : EnvironmentEffector where TPlayer : IAudioPlayer { private readonly TPlayer _audioPlayer; public CustomAudioEffector(TPlayer audioPlayer) { _audioPlayer = audioPlayer; } protected abstract void Play(TPlayer audioPlayer, TContext context); public override void Apply(TContext context) { Play(_audioPlayer, context); } public override void Update(TContext context, float delta) { _audioPlayer.Update(delta); } public override void Remove(TContext context) { _audioPlayer.Stop(); } } public abstract class MappedSelector : EnvironmentEffector where TKey : notnull { private readonly ImmutableDictionary> _effectors; private EnvironmentEffector? _currentEffector; protected MappedSelector() { Dictionary> map = new Dictionary>(); BuildMap(ref map); _effectors = map.ToImmutableDictionary(); } protected abstract void BuildMap(ref Dictionary> map); protected abstract TKey Selector(TContext context); public override void Apply(TContext context) { _currentEffector?.Apply(context); } public override bool CanApply(TContext context) { TKey val = Selector(context); if (!_effectors.TryGetValue(val, out EnvironmentEffector value)) { MelonLogger.Error($"MappedSelector: No effector found for key {val}"); return false; } _currentEffector = value; return _currentEffector.CanApply(context); } public override void Update(TContext context, float delta) { _currentEffector?.Update(context, delta); } public override void Remove(TContext context) { _currentEffector?.Remove(context); } } } namespace MashGamemodeLibrary.Environment.Effector.Weather { public static class LocalWeatherManager { private static readonly List WeatherEntities = new List(); public static void ClearLocalWeather() { foreach (Poolee item in WeatherEntities.OfType()) { AssetSpawner.Despawn(item); } WeatherEntities.Clear(); } public static void SpawnLocalWeather(string barcode) { //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) Spawnable obj = LocalAssetSpawner.CreateSpawnable(barcode); LocalAssetSpawner.Register(obj); LocalAssetSpawner.Spawn(obj, Vector3.zero, Quaternion.identity, (Action)delegate(Poolee poolee) { MarrowEntity component = ((Component)poolee).GetComponent(); if ((Object)(object)component != (Object)null) { component.PreventDisableOnCull(true); } WeatherEntities.Add(poolee); }); } } public abstract class WeatherEffector : EnvironmentEffector { private readonly string[] _barcodes; private readonly Func? _canApply; public WeatherEffector(string[] barcodes, Func? canApply = null) { _barcodes = barcodes; _canApply = canApply; } public override bool CanApply(TContext context) { if (_canApply == null) { return true; } return _canApply(context); } public override void Apply(TContext context) { IEnumerableExtensions.ForEach((IEnumerable)_barcodes, (Action)LocalWeatherManager.SpawnLocalWeather); } public override void Remove(TContext context) { LocalWeatherManager.ClearLocalWeather(); } } } namespace MashGamemodeLibrary.Entities { public static class GameAssetSpawner { private static Spawnable GetSpawnable(string barcode) { Spawnable obj = LocalAssetSpawner.CreateSpawnable(barcode); LocalAssetSpawner.Register(obj); return obj; } public static void SpawnNetworkAsset(string barcode, Vector3 position, params IComponent[] components) { //IL_0022: 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_0033: Unknown result type (might be due to invalid IL or missing references) //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_005f: Unknown result type (might be due to invalid IL or missing references) IComponent[] components2 = components; string barcode2 = barcode; Spawnable spawnable = GetSpawnable(barcode2); SpawnRequestInfo val = default(SpawnRequestInfo); val.Spawnable = spawnable; val.Position = position; val.Rotation = Quaternion.identity; val.SpawnEffect = false; val.SpawnCallback = delegate(SpawnCallbackInfo result) { //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) if (result.Entity == null) { if (components2.Length != 0) { MelonLogger.Error("Failed to add tags to: " + barcode2 + ". Spawned crate is not synced."); } } else if (components2.Length != 0) { Executor.RunIfHost(delegate { //IL_0001: Unknown result type (might be due to invalid IL or missing references) result.WaitOnMarrowEntity(delegate(NetworkEntity entity, MarrowEntity _) { IComponent[] array = components2; foreach (IComponent component in array) { entity.AddComponent(component); } }); }); } }; NetworkAssetSpawner.Spawn(val); } public static void Despawn(NetworkEntity networkEntity) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (networkEntity.ID <= 255) { MelonLogger.Error($"Attempted to despawn a player entity: {networkEntity.ID}"); } else { DespawnRequestInfo val = default(DespawnRequestInfo); val.EntityID = networkEntity.ID; val.DespawnEffect = true; NetworkAssetSpawner.Despawn(val); } } public static void SpawnLocalAsset(string barcode, Vector3 position, Action callback) { //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) Spawnable obj = LocalAssetSpawner.CreateSpawnable(barcode); LocalAssetSpawner.Register(obj); LocalAssetSpawner.Spawn(obj, position, Quaternion.identity, callback); } } public delegate void OnMarrowEntitySpawned(NetworkEntity networkEntity, MarrowEntity entity); internal record CallbackHolder(ushort EntityId, OnMarrowEntitySpawned Callback) { public NetworkEntity? NetworkEntity { get; init; } public int Tries { get; set; } } public static class SpawnHelper { private static LinkedListNode? _currentNode; private static readonly LinkedList TrackedEntities = new LinkedList(); public static void WaitOnMarrowEntity(this SpawnCallbackInfo callbackInfo, OnMarrowEntitySpawned callback) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) WaitOnMarrowEntity(callbackInfo.Entity, callback); } public static void WaitOnMarrowEntity(NetworkEntity networkEntity, OnMarrowEntitySpawned callback) { WaitOnMarrowEntity(networkEntity.ID, networkEntity, callback); } public static void WaitOnMarrowEntity(this NetworkEntityReference entityReference, OnMarrowEntitySpawned callback) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ushort iD = entityReference.ID; NetworkEntity entity = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(iD); WaitOnMarrowEntity(iD, entity, callback); } public static void WaitOnMarrowEntity(ushort entityId, OnMarrowEntitySpawned callback) { NetworkEntity entity = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(entityId); WaitOnMarrowEntity(entityId, entity, callback); } private static void WaitOnMarrowEntity(ushort entityId, NetworkEntity? networkEntity, OnMarrowEntitySpawned callback) { if (networkEntity != null) { if (entityId == networkEntity.ID) { IMarrowEntityExtender extender = networkEntity.GetExtender(); if (extender != null) { callback(extender.NetworkEntity, extender.MarrowEntity); return; } } else { networkEntity = null; } } TrackedEntities.AddLast(new CallbackHolder(entityId, callback) { NetworkEntity = networkEntity }); } private static void CheckEntity(LinkedListNode node) { NetworkEntity value = node.Value.NetworkEntity; if (value == null && !NetworkEntityManager.IDManager.RegisteredEntities.IDEntityLookup.TryGetValue(node.Value.EntityId, out value)) { return; } if (value.IsDestroyed) { TrackedEntities.Remove(node); return; } IMarrowEntityExtender extender = value.GetExtender(); if (extender != null) { TrackedEntities.Remove(node); node.Value.Callback(extender.NetworkEntity, extender.MarrowEntity); } } internal static void Update() { if (TrackedEntities.Count == 0) { return; } for (int i = 0; i < 2; i++) { if (_currentNode == null) { _currentNode = TrackedEntities.First; } if (_currentNode == null) { break; } LinkedListNode? linkedListNode = _currentNode.Next; _currentNode.Value.Tries++; CheckEntity(_currentNode); LinkedListNode currentNode = _currentNode; if (currentNode != null && currentNode.List != null) { CallbackHolder value = currentNode.Value; if ((object)value != null && value.Tries > 50) { TrackedEntities.Remove(_currentNode); } } if (linkedListNode == null) { linkedListNode = TrackedEntities.First; } _currentNode = linkedListNode; } } } } namespace MashGamemodeLibrary.Entities.Queries { public interface ICachedQuery : IGuaranteeStaticConstructor { CacheKey? TryAdd(object instance); void Remove(CacheKey key); } public class CachedQuery : ICachedQuery, IGuaranteeStaticConstructor, IEnumerable, IEnumerable { private readonly Dictionary _components = new Dictionary(); public CacheKey? TryAdd(object instance) { if (!(instance is T value)) { return null; } Guid guid = Guid.NewGuid(); _components.Add(guid, value); return new CacheKey(this, guid); } public void Remove(CacheKey key) { _components.Remove(key.Guid); } public IEnumerator GetEnumerator() { return _components.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public static class CachedQueryManager { private static readonly Dictionary Queries = new Dictionary(); private static CachedQuery CreateCache() { return new CachedQuery(); } private static KeyedCachedQuery CreateKeyedCache(Func fetcher) where TKey : notnull { return new KeyedCachedQuery(fetcher); } public static CachedQuery Create() { return (CachedQuery)Queries.GetValueOrCreate(typeof(T), CreateCache); } public static KeyedCachedQuery Create(Func fetcher) where TKey : notnull { Func fetcher2 = fetcher; return (KeyedCachedQuery)Queries.GetValueOrCreate(typeof(TValue), () => CreateKeyedCache(fetcher2)); } internal static CacheKey? Add(object queryable) { return Queries.GetValueOrDefault(queryable.GetType())?.TryAdd(queryable); } } public record CacheKey(ICachedQuery Query, Guid Guid) { private bool _isValid = true; public void Remove() { if (_isValid) { _isValid = false; Query.Remove(this); } } [CompilerGenerated] protected virtual bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Query = "); builder.Append(Query); builder.Append(", Guid = "); builder.Append(Guid.ToString()); return true; } } internal record struct KeyComponentPair(TKey Key, TValue Value); public class KeyedCachedQuery : ICachedQuery, IGuaranteeStaticConstructor, IEnumerable, IEnumerable where TKey : notnull { private readonly Dictionary _keys = new Dictionary(); private readonly Dictionary> _components = new Dictionary>(); private readonly Func _fetcher; public KeyedCachedQuery(Func fetcher) { _fetcher = fetcher; } public CacheKey? TryAdd(object instance) { if (!(instance is TValue val)) { return null; } Guid guid = Guid.NewGuid(); TKey key = _fetcher(val); _components.Add(guid, new KeyComponentPair(key, val)); _keys.Add(key, val); return new CacheKey(this, guid); } public void Remove(CacheKey key) { if (_components.Remove(key.Guid, out var value)) { _keys.Remove(value.Key); } } public IEnumerator GetEnumerator() { return _keys.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } namespace MashGamemodeLibrary.Entities.Interaction { internal enum WeaponType { Pistol, Smg, Rifle, Shotgun } internal class DamageRemapper { public delegate float DamageRemapFunction(float damage, float roundsPerSecond, FireMode fireMode, float multIn); private readonly float _highTarget; private readonly float _highValue; private readonly float _lowTarget; private readonly float _lowValue; private readonly float _ttkMultiplier; private readonly DamageRemapFunction[] _remapFunctions; public DamageRemapper(float ttkMultiplier, float lowValue, float highValue, float target, float deviation = 0.5f, params DamageRemapFunction[] remapFunctions) { _ttkMultiplier = ttkMultiplier; _lowValue = lowValue; _highValue = highValue; float num = deviation / 2f; _lowTarget = Math.Max(target - num, 0f); _highTarget = Math.Max(target + num, 0f); _remapFunctions = remapFunctions; } public float GetDamage(float damage, float roundsPerSecond, FireMode fireMode) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 float value = MathUtil.Clamp(damage, _lowValue, _highValue); float num = roundsPerSecond; FireMode val = fireMode; float num2 = (((int)val == 0) ? 0.65f : (((int)val != 1) ? 1f : 0.8f)); float num3 = num * num2 / 12f; float seed = MathUtil.Lerp(_lowTarget, _highTarget, value) / num3 * _ttkMultiplier; return _remapFunctions.Aggregate(seed, (float current, DamageRemapFunction damageRemapFunction) => damageRemapFunction(damage, roundsPerSecond, fireMode, current)); } } public static class PlayerGunManager { public delegate void OnGunFiredHandler(NetworkPlayer shooter, Gun gun); private static readonly ImmutableDictionary WeaponTypeLookup; private static readonly KeyedRegistry DamageRemappers; private static readonly Dictionary DefaultGunDamage; private static readonly Dictionary CachedGunDamage; private static bool _normalizePlayerDamage; public static bool NormalizePlayerDamage { get { return _normalizePlayerDamage; } set { _normalizePlayerDamage = value; if (_normalizePlayerDamage) { return; } foreach (Gun item in CachedGunDamage.Keys.OfType()) { if (DefaultGunDamage.TryGetValue(item, out var value2)) { item.defaultCartridge.projectile.damageMultiplier = value2; } } CachedGunDamage.Clear(); } } public static event OnGunFiredHandler? OnGunFired; static PlayerGunManager() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown WeaponTypeLookup = ImmutableDictionary.CreateRange(new KeyValuePair[4] { KeyValuePair.Create("Pistol", WeaponType.Pistol), KeyValuePair.Create("SMG", WeaponType.Smg), KeyValuePair.Create("Shotgun", WeaponType.Shotgun), KeyValuePair.Create("Rifle", WeaponType.Rifle) }); DamageRemappers = new KeyedRegistry(); DefaultGunDamage = new Dictionary((IEqualityComparer?)new UnityComparer()); CachedGunDamage = new Dictionary((IEqualityComparer?)new UnityComparer()); DamageRemappers.Register(WeaponType.Pistol, new DamageRemapper(1.8f, 0.8f, 1.1f, 1.4f, 0.5f)); DamageRemappers.Register(WeaponType.Smg, new DamageRemapper(1.2f, 0.4f, 0.9f, 0.8f, 0.8f)); DamageRemappers.Register(WeaponType.Shotgun, new DamageRemapper(1f, 0.4f, 1.2f, 1.1f, 0.3f, (float damage, float second, FireMode mode, float @in) => ((int)mode != 1) ? @in : (@in * 0.75f))); DamageRemappers.Register(WeaponType.Rifle, new DamageRemapper(1.1f, 0.6f, 2f, 1.1f, 0.7f, (float damage, float second, FireMode mode, float @in) => ((int)mode != 0) ? @in : (@in * 4f))); } private static WeaponType GetWeaponType(Gun gun) { Poolee poolee = ((SpawnEvents)gun)._poolee; SpawnableCrate val = ((poolee != null) ? poolee.SpawnableCrate : null); if ((Object)(object)val == (Object)null) { return WeaponType.Rifle; } Enumerator enumerator = ((Crate)val)._tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; if (current != null && WeaponTypeLookup.TryGetValue(current, out var value)) { return value; } } return WeaponType.Rifle; } private static float GetGunDamageMultiplier(Gun gun) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) Gun gun2 = gun; if (CachedGunDamage.TryGetValue(gun2, out var value)) { return value; } float valueOrCreate = DefaultGunDamage.GetValueOrCreate(gun2, () => gun2.defaultCartridge.projectile.damageMultiplier); WeaponType weaponType = GetWeaponType(gun2); float damage = DamageRemappers.Get(weaponType).GetDamage(valueOrCreate, gun2.roundsPerSecond, gun2.fireMode); CachedGunDamage[gun2] = damage; return damage; } private static void NormalizeGunDamage(Gun gun) { object obj; if (gun == null) { obj = null; } else { CartridgeData defaultCartridge = gun.defaultCartridge; obj = ((defaultCartridge != null) ? defaultCartridge.projectile : null); } if (!((Object)obj == (Object)null)) { gun.defaultCartridge.projectile.damageMultiplier = GetGunDamageMultiplier(gun); } } public static void OnGunGrabbed(Gun gun) { if (_normalizePlayerDamage) { NormalizeGunDamage(gun); } } public static void InvokeGunFired(Gun instance) { Grip triggerGrip = instance.triggerGrip; if ((Object)(object)triggerGrip == (Object)null) { return; } List attachedHands = triggerGrip.attachedHands; if (attachedHands.Count == 0) { return; } Hand val = attachedHands._items[0]; if (Object.op_Implicit((Object)(object)val)) { RigManager manager = val.manager; NetworkPlayer shooter = default(NetworkPlayer); if (Object.op_Implicit((Object)(object)manager) && NetworkPlayerManager.TryGetPlayer(manager, ref shooter)) { PlayerGunManager.OnGunFired?.Invoke(shooter, instance); } } } public static void Reset() { DefaultGunDamage.Clear(); CachedGunDamage.Clear(); _normalizePlayerDamage = false; } } } namespace MashGamemodeLibrary.Entities.Interaction.Grabbing { public class GrabRequest { public Hand Hand { get; } public bool IsValid { get; } public NetworkPlayer NetworkPlayer { get; } public Grip Grip { get; } public MarrowEntity GrabbedEntity => Grip._marrowEntity; public InteractableHost? GrabbedHost => ((Il2CppObjectBase)((HandReciever)Grip).Host).TryCast(); public NetworkEntity? GrabbedNetworkEntity => GrabbedEntity.GetNetworkEntity(); public GrabRequest(Hand hand, Grip grip) { Hand = hand; Grip = grip; if (NetworkSceneManager.IsLevelNetworked && hand.TryGetNetworkPlayer(out NetworkPlayer networkPlayer)) { IsValid = true; NetworkPlayer = networkPlayer; } } public GrabRequest(InventoryHand hand, Grip grip) : this(hand._hand, grip) { } } public static class MarrowInventoryExtender { public static bool TryGetManager(this Hand hand, [MaybeNullWhen(false)] out RigManager rigManager) { rigManager = hand.manager; return (Object)(object)rigManager != (Object)null; } public static bool TryGetNetworkPlayer(this Hand hand, [MaybeNullWhen(false)] out NetworkPlayer networkPlayer) { networkPlayer = null; if (!hand.TryGetManager(out RigManager rigManager)) { return false; } return NetworkPlayerManager.TryGetPlayer(rigManager, ref networkPlayer); } public static NetworkEntity? GetNetworkEntity(this MarrowEntity entity) { if ((Object)(object)entity == (Object)null) { return null; } return IMarrowEntityExtender.Cache.Get(entity); } public static bool TryGetNetworkedEntity(this MarrowEntity entity, [MaybeNullWhen(false)] out NetworkEntity networkEntity) { networkEntity = entity.GetNetworkEntity(); return networkEntity != null; } public static bool TryGetHeldEntity(this Hand hand, [MaybeNullWhen(false)] out NetworkEntity networkEntity) { networkEntity = null; if (!hand.HasAttachedObject()) { return false; } InteractableHost val = ((Il2CppObjectBase)hand.AttachedReceiver.Host).TryCast(); if ((Object)(object)val == (Object)null) { return false; } return InteractableHostExtender.Cache.TryGet(val, ref networkEntity); } public static bool IsHolding(this Hand hand) where TComponent : class, IComponent { if (!hand.HasAttachedObject()) { return false; } InteractableHost val = ((Il2CppObjectBase)hand.AttachedReceiver.Host).TryCast(); if ((Object)(object)val == (Object)null) { return false; } NetworkEntity entity = default(NetworkEntity); if (!InteractableHostExtender.Cache.TryGet(val, ref entity)) { return false; } return entity.GetComponent() != null; } public static bool IsHolding(this NetworkPlayer player, Handedness handedness) where TComponent : class, IComponent { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (player.HasRig) { return player.RigRefs.GetHand(handedness).IsHolding(); } return false; } public static IEnumerable GetHandsHolding(this NetworkPlayer player) where T : class, IComponent { if (!player.HasRig) { return Array.Empty(); } return player.RigRefs.GetHandsHoldingTag(); } public static IEnumerable GetHandsHoldingTag(this RigRefs rigRefs) where T : class, IComponent { NetworkEntity networkEntity; return ((IEnumerable)(object)new Hand[2] { rigRefs.LeftHand, rigRefs.RightHand }).Where((Hand hand) => hand.TryGetHeldEntity(out networkEntity) && networkEntity.GetComponent() != null); } } [RequireStaticConstructor] public static class PlayerGrabManager { public delegate bool GrabPredicateHanlder(GrabRequest request); public static GrabPredicateHanlder? GrabPredicate; private static readonly IAssociatedBehaviourCache GrabCallbackCache = BehaviourManager.CreateCache(); private static readonly IAssociatedBehaviourCache DropCallbackCache = BehaviourManager.CreateCache(); private static readonly IAssociatedBehaviourCache GrabPredicateCache = BehaviourManager.CreateCache(); public static bool CanGrab(this GrabRequest grabRequest) { GrabRequest grabRequest2 = grabRequest; if (!grabRequest2.IsValid) { return true; } PlayerID playerID = grabRequest2.NetworkPlayer.PlayerID; if (playerID == null || !playerID.IsMe) { return true; } NetworkEntity grabbedNetworkEntity = grabRequest2.GrabbedNetworkEntity; if (grabbedNetworkEntity == null) { return true; } if (SpectatorExtender.IsLocalPlayerSpectating()) { return false; } NetworkEntity? grabbedNetworkEntity2 = grabRequest2.GrabbedNetworkEntity; NetworkPlayer val = ((grabbedNetworkEntity2 != null) ? grabbedNetworkEntity2.GetExtender() : null); if (val != null && val.PlayerID.IsSpectating()) { return false; } if (GrabPredicate != null && !Executor.Try(() => GrabPredicate(grabRequest2), defaultValue: false)) { return false; } return GrabPredicateCache.GetAll(grabbedNetworkEntity.ID).All((IGrabPredicate predicate) => predicate.Try((IGrabPredicate p) => p.CanGrab(grabRequest2), defaultValue: false)); } public static void OnDrop(GrabRequest grab) { GrabRequest grab2 = grab; if (!grab2.IsValid || grab2.GrabbedNetworkEntity == null || (Object)(object)grab2.GrabbedHost == (Object)null || grab2.GrabbedHost.HandCount() > 0) { return; } DropCallbackCache.ForEach(grab2.GrabbedNetworkEntity.ID, delegate(IDropCallback callback) { callback.Try(delegate(IDropCallback c) { c.OnDropped(grab2); }); }); } public static void OnGrab(GrabRequest grab) { GrabRequest grab2 = grab; if (!grab2.IsValid || grab2.GrabbedNetworkEntity == null || (Object)(object)grab2.GrabbedHost == (Object)null || grab2.GrabbedHost.HandCount() > 1) { return; } GrabCallbackCache.ForEach(grab2.GrabbedNetworkEntity.ID, delegate(IGrabCallback callback) { callback.Try(delegate(IGrabCallback c) { c.OnGrabbed(grab2); }); }); } } } namespace MashGamemodeLibrary.Entities.Interaction.Balancing { public class GunBalancer { public readonly IGunModifier[] Modifiers; public GunBalancer(params IGunModifier[] modifiers) { Modifiers = modifiers; } public float GetDamage(GunInfo gunInfo) { float[] array = new float[Modifiers.Length]; for (int i = 0; i < Modifiers.Length; i++) { float modifier2 = Modifiers[i].GetModifier(gunInfo); array[i] = 1f - modifier2; } return array.Aggregate(1f, (float current, float modifier) => current + modifier); } } public enum WeaponType { Pistol, Smg, Rifle, Shotgun } public class GunInfo { private static readonly ImmutableDictionary WeaponTypeLookup = ImmutableDictionary.CreateRange(new KeyValuePair[4] { KeyValuePair.Create("Pistol", WeaponType.Pistol), KeyValuePair.Create("SMG", WeaponType.Smg), KeyValuePair.Create("Shotgun", WeaponType.Shotgun), KeyValuePair.Create("Rifle", WeaponType.Rifle) }); public float RoundsPerSecond { get; init; } public FireMode FireMode { get; init; } public WeaponType WeaponType { get; init; } public float Recoil { get; init; } public float BulletCount { get; init; } public float SpreadAngle { get; init; } public float BulletVelocity { get; init; } public float BulletMass { get; init; } public GunInfo(Gun gun) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) WeaponType = GetWeaponType(gun); FireMode = gun.fireMode; RoundsPerSecond = gun.roundsPerSecond; Recoil = gun.pullAnimationSpeed * gun.pullAnimationPerc - gun.returnAnimationSpeed * gun.returnAnimationPerc; ProjectileData projectile = gun.defaultCartridge.projectile; if (!((Object)(object)projectile == (Object)null)) { BulletCount = projectile.count; SpreadAngle = projectile.angle; BulletVelocity = projectile.startVelocity; BulletMass = projectile.mass; } } private static WeaponType GetWeaponType(Gun gun) { Poolee poolee = ((SpawnEvents)gun)._poolee; SpawnableCrate val = ((poolee != null) ? poolee.SpawnableCrate : null); if ((Object)(object)val == (Object)null) { return WeaponType.Rifle; } Enumerator enumerator = ((Crate)val)._tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; if (current != null && WeaponTypeLookup.TryGetValue(current, out var value)) { return value; } } return WeaponType.Rifle; } } public interface IGunModifier { float GetModifier(GunInfo gunInfo); } } namespace MashGamemodeLibrary.Entities.Interaction.Balancing.Modifiers { public record TtkModifier(float Ttk, float MedianRps, float StepRps) : IGunModifier { public float GetModifier(GunInfo gunInfo) { float num = (gunInfo.RoundsPerSecond - MedianRps) / StepRps; return 1f / Ttk + num; } } } namespace MashGamemodeLibrary.Entities.Extenders { public static class GameObjectExtender { private static readonly HashSet GameObjects = new HashSet(); internal static void DestroyAll() { foreach (GameObject gameObject in GameObjects) { Object.Destroy((Object)(object)gameObject); } GameObjects.Clear(); } public static GameObject CreateSafeObject(this Transform parent, string name) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown GameObject val = new GameObject { name = name }; val.transform.parent = parent; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; GameObject val2 = val; GameObjects.Add(val2); return val2; } public static GameObject CreateSafeObject(this GameObject parent, string name) { return parent.transform.CreateSafeObject(name); } } } namespace MashGamemodeLibrary.Entities.ECS { [RequireStaticConstructor] public static class CommonEcsBehaviours { private static readonly IAssociatedBehaviourCache EntityAttachedCache; private static readonly IAssociatedBehaviourCache PlayerAttachedCache; private static readonly IBehaviourCache UpdateCache; private static readonly IBehaviourCache RemovedCache; static CommonEcsBehaviours() { EntityAttachedCache = BehaviourManager.CreateCache(); PlayerAttachedCache = BehaviourManager.CreateCache(); UpdateCache = BehaviourManager.CreateCache(); RemovedCache = BehaviourManager.CreateCache(); EntityAttachedCache.OnAdded += delegate(NetworkEntityAssociation instance, IEntityAttached component) { NetworkEntity val = default(NetworkEntity); if (((NetworkEntityReference)(ref instance.NetworkID)).TryGetEntity(ref val)) { IMarrowEntityExtender extender = val.GetExtender(); if (extender != null) { component.OnReady(val, extender.MarrowEntity); } } }; PlayerAttachedCache.OnAdded += delegate(NetworkEntityAssociation association, IPlayerAttached component) { ushort iD = association.NetworkID.ID; NetworkPlayer networkPlayer = default(NetworkPlayer); if (iD <= 255 && NetworkPlayerManager.TryGetPlayer((byte)iD, ref networkPlayer)) { component.OnReady(networkPlayer); } }; RemovedCache.OnRemoved += delegate(IRemoved component) { component.OnRemoved(); }; } internal static void Update(float delta) { UpdateCache.ForEach(delegate(IUpdate behaviour) { behaviour.Update(delta); }); } } internal class EcsIndexComparer : IEqualityComparer { public bool Equals(EcsIndex? x, EcsIndex? y) { if (x == y) { return true; } if (x == null || y == null) { return false; } return x.Equals(y); } public int GetHashCode(EcsIndex obj) { return HashCode.Combine(obj.ComponentID.GetHashCode(), obj.Association?.GetHashCode() ?? 0); } } [RequireStaticConstructor] public class EcsManager { private static readonly Dictionary LocalComponents; private static readonly Dictionary>> ComponentLookup; private static readonly Dictionary> EcsIndexLookup; internal static readonly FactoryTypedRegistry ComponentRegistry; private static readonly SyncedDictionary NetworkComponents; static EcsManager() { LocalComponents = new Dictionary(new EcsIndexComparer()); ComponentLookup = new Dictionary>>(); EcsIndexLookup = new Dictionary>(); ComponentRegistry = new FactoryTypedRegistry(); NetworkComponents = new SyncedDictionary("sync.ECS", new InstanceEncoder(), new DynamicInstanceEncoder(ComponentRegistry), CommonNetworkRoutes.HostToRemote); NetworkComponents.OnValueAdded += OnNetworkComponentAdded; NetworkComponents.OnValueRemoved += OnNetworkComponentRemoved; } private static void OnNetworkComponentAdded(EcsIndex key, IComponent component) { if (NetworkInfo.HasServer && !NetworkInfo.IsHost) { Add(new EcsInstance(key, component)); } } private static void OnNetworkComponentRemoved(EcsIndex key, IComponent oldValue) { if (NetworkInfo.HasServer && !NetworkInfo.IsHost) { Remove(key); } } public static void Add(EcsInstance instance) { EcsInstance instance2 = instance; EcsIndex index = instance2.Index; if (!LocalComponents.TryAdd(index, instance2)) { return; } if (index.Association != null) { ComponentLookup.GetValueOrCreate(index.Association.GetType()).GetValueOrCreate(index.Association.GetID()).Add(instance2); } EcsIndexLookup.GetValueOrCreate(instance2.ComponentType).Add(index); if (instance2.IsNetworked) { Executor.RunIfHost(delegate { NetworkComponents[index] = instance2.Component; }); } } public static void Remove(EcsIndex index) { EcsIndex index2 = index; if (LocalComponents.Remove(index2, out EcsInstance value)) { value.Remove(); EcsIndexLookup.GetValueOrDefault(value.ComponentType)?.Remove(index2); if (index2.Association != null) { ComponentLookup.GetValueOrDefault(index2.Association.GetType())?.GetValueOrDefault(index2.Association.GetID())?.Remove(value); } Executor.RunIfHost(delegate { NetworkComponents.Remove(index2); }); } } public static void Add(EcsIndex index, IComponent component) { Add(new EcsInstance(index, component)); } public static TComponent? Get(EcsIndex ecsIndex) where TComponent : class, IComponent { if (!LocalComponents.TryGetValue(ecsIndex, out EcsInstance value)) { return null; } return value.Component as TComponent; } public static EcsInstance? GetInstance(EcsIndex ecsIndex) { return LocalComponents.GetValueOrDefault(ecsIndex); } public static void Clear(IEcsAssociation association) { if (!ComponentLookup.TryGetValue(association.GetType(), out Dictionary> value) || !value.TryGetValue(association.GetID(), out var value2)) { return; } foreach (EcsInstance item in value2.ToList()) { Remove(item.Index); } } public static IEnumerable GetAllAssociated(Type? component = null) where TAssociation : class, IEcsAssociation { Type component2 = component; if (!ComponentLookup.TryGetValue(typeof(TAssociation), out Dictionary> value)) { yield break; } foreach (var (_, source) in value) { if (component2 != null && source.All((EcsInstance i) => i.ComponentType != component2)) { continue; } EcsInstance ecsInstance = source.FirstOrDefault(); if (ecsInstance != null) { EcsIndex index = ecsInstance.Index; if (index != null && index.Association is TAssociation val) { yield return val; } } } } public static IEnumerable GetAllIndices() where TComponent : IComponent { Type typeFromHandle = typeof(TComponent); if (!EcsIndexLookup.TryGetValue(typeFromHandle, out HashSet value)) { yield break; } foreach (EcsIndex item in value) { yield return item; } } public static IEnumerable GetAll() where TComponent : class, IComponent { Type typeFromHandle = typeof(TComponent); if (!EcsIndexLookup.TryGetValue(typeFromHandle, out HashSet value)) { yield break; } foreach (EcsIndex item in value) { if (LocalComponents.TryGetValue(item, out EcsInstance value2) && value2.Component is TComponent val) { yield return val; } } } public static void RegisterAll() { EcsIndex.AssociationRegistry.RegisterAll(); ComponentRegistry.RegisterAll(); } public static void Reset() { foreach (EcsIndex item in LocalComponents.Keys.ToList()) { Remove(item); } LocalComponents.Clear(); ComponentLookup.Clear(); EcsIndexLookup.Clear(); Executor.RunIfHost(delegate { NetworkComponents.Clear(); }); } } public static class NetworkEntityExtender { public static void AddComponent(this NetworkEntity entity, IComponent component) { NetworkEntityAssociation association = new NetworkEntityAssociation(entity); EcsManager.Add(new EcsIndex(component, association), component); } public static void RemoveComponent(this NetworkEntity entity) where TComponent : class, IComponent { NetworkEntityAssociation association = new NetworkEntityAssociation(entity); EcsManager.Remove(new EcsIndex(typeof(TComponent), association)); } public static TComponent? GetComponent(this NetworkEntity entity) where TComponent : class, IComponent { NetworkEntityAssociation association = new NetworkEntityAssociation(entity); return EcsManager.Get(new EcsIndex(typeof(TComponent), association)); } public static void ClearComponents(this NetworkEntity entity) { EcsManager.Clear(new NetworkEntityAssociation(entity)); } } } namespace MashGamemodeLibrary.Entities.ECS.Networking { public class NetEventCarrier { public EcsIndex EcsIndex; public byte EventIndex; public int Size; public Action Writer; } public class ComponentNetworkEventManager : GenericRemoteEvent { private static readonly IBehaviourCache NetworkBehaviourCache = BehaviourManager.CreateCache(); public ComponentNetworkEventManager() : base("sync.ECS.netevents", CommonNetworkRoutes.AllToAll) { } public void Send(INetworkEvents networkEvents, byte eventIndex, int size, Action writer) { if (NetworkBehaviourCache.GetHolder(networkEvents) is EcsInstance ecsInstance) { Relay(new NetEventCarrier { EcsIndex = ecsInstance.Index, EventIndex = eventIndex, Size = size, Writer = writer }); } } protected override int? GetSize(NetEventCarrier data) { return null; } protected override void Write(NetWriter writer, NetEventCarrier data) { data.EcsIndex.Serialize((INetSerializer)(object)writer); writer.Write(data.EventIndex); data.Writer(writer); } protected override void Read(byte smallId, NetReader reader) { EcsIndex ecsIndex = new EcsIndex(); ecsIndex.Serialize((INetSerializer)(object)reader); EcsInstance instance = EcsManager.GetInstance(ecsIndex); if (instance != null && instance.TryCast(out var component)) { component.OnEvent(smallId, reader.ReadByte(), reader); } } } public interface INetworkEvents : IBehaviour { void OnEvent(byte senderId, byte eventIndex, NetReader reader); } public static class NetworkEventsExtender { private static readonly ComponentNetworkEventManager Manager = new ComponentNetworkEventManager(); public static void SendEvent(this INetworkEvents target, byte index, int size, Action writer) { Manager.Send(target, index, size, writer); } public static void Register() { } } } namespace MashGamemodeLibrary.Entities.ECS.Instance { public class EcsInstance : IBehaviourHolder { public readonly Type ComponentType; public readonly bool IsNetworked; private CacheKey? _cacheKey; private List? _behaviourMembers; public EcsIndex Index { get; } public bool IsReady { get; private set; } public IComponent Component { get; } public EcsInstance(EcsIndex index, IComponent component) { Index = index; ComponentType = component.GetType(); Component = component; IsNetworked = ComponentType.GetCustomAttribute() == null; Index.HookCreation(OnReady); } private void OnReady() { IsReady = true; _cacheKey = CachedQueryManager.Add(Component); _behaviourMembers = BehaviourManager.Add(this, Component); Index.HookRemoval(OnRemoval); } private void OnRemoval() { if (!IsReady) { return; } try { _cacheKey?.Remove(); if (_behaviourMembers != null) { BehaviourManager.RemoveAll(_behaviourMembers); } EcsManager.Remove(Index); IsReady = false; } catch (Exception ex) { MelonLogger.Error("Error while unregistering component: " + ex); } } public void Remove() { OnRemoval(); } public TBehaviour? TryCast() where TBehaviour : class, IBehaviour { return Component as TBehaviour; } public bool TryCast([MaybeNullWhen(false)] out TBehaviour component) where TBehaviour : class, IBehaviour { component = TryCast(); return component != null; } } } namespace MashGamemodeLibrary.Entities.ECS.Declerations { public interface IComponent { } } namespace MashGamemodeLibrary.Entities.ECS.BaseComponents { public interface IDropCallback : IBehaviour { void OnDropped(GrabRequest grabRequest); } public interface IEntityAttached : IBehaviour, IComponent { void OnReady(NetworkEntity networkEntity, MarrowEntity marrowEntity); } public interface IGrabCallback : IBehaviour { void OnGrabbed(GrabRequest grabRequest); } public interface IGrabPredicate : IBehaviour { bool CanGrab(GrabRequest grabRequest); } public interface IPhaseChanged : IBehaviour { void OnPhaseChange(GamePhase gamePhase); } public interface IPlayerActionCallback : IBehaviour { void OnAction(PlayerActionType action, PlayerID otherPlayer); } public interface IPlayerAttached : IPlayerBehaviour, IBehaviour { void OnReady(NetworkPlayer networkPlayer); } public interface IPlayerBehaviour : IBehaviour { } public interface IPlayerInputCallback : IBehaviour { void OnInput(PlayerInputType type, bool state, Handedness handedness); } public interface IPlayerRuleChangedCallback : IPlayerBehaviour, IBehaviour { void OnPlayerRuleChanged(NetworkPlayer networkPlayer, IPlayerRule newRule); } public interface IPlayerTakeDamageCallback : IPlayerBehaviour, IBehaviour { void OnDamageTaken(PlayerID? source); } public interface IRemoved : IBehaviour, IComponent { void OnRemoved(); } public interface IUpdate : IBehaviour, IComponent { void Update(float delta); } } namespace MashGamemodeLibrary.Entities.ECS.Attributes { [AttributeUsage(AttributeTargets.Class)] public class LocalOnly : Attribute { } } namespace MashGamemodeLibrary.Entities.CommonComponents { public class ForcePull : IEntityAttached, IBehaviour, IComponent, IRemoved { private InteractableHost? _interactableHost; private ForcePullGrip? _forcePullGrip; public void OnReady(NetworkEntity networkEntity, MarrowEntity marrowEntity) { _interactableHost = ((IEnumerable)marrowEntity._behaviours).OfType().FirstOrDefault(); if (!((Object)(object)_interactableHost == (Object)null)) { Grip grip = _interactableHost.GetGrip(); if (!((Object)(object)grip == (Object)null)) { ForcePullGrip val = ((Component)grip).gameObject.AddComponent(); _interactableHost._fpGrips.Add(val); _forcePullGrip = val; } } } public void OnRemoved() { if (!((Object)(object)_forcePullGrip == (Object)null)) { if ((Object)(object)_interactableHost != (Object)null) { _interactableHost._fpGrips.Remove(_forcePullGrip); } Object.Destroy((Object)(object)_forcePullGrip); } } } public class ImportantEntityMarker : IComponent, IEntityAttached, IBehaviour { public void OnReady(NetworkEntity networkEntity, MarrowEntity marrowEntity) { marrowEntity.PreventDisableOnCull(true); } } internal class RespawnCountPacket : INetSerializable { public int Respawns; public int? GetSize() { return 4; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref Respawns); } } public class LimitedRespawn : IRemoved, IBehaviour, IComponent, IPlayerAttached, IPlayerBehaviour, IPlayerActionCallback { public delegate bool PlayerSpectatePredicate(NetworkPlayer player); public static readonly CachedQuery Query = CachedQueryManager.Create(); private static readonly RemoteEvent RespawnCountChangedEvent = new RemoteEvent(OnRespawnsChanged, CommonNetworkRoutes.HostToAll); private static readonly Dictionary GlobalPredicates = new Dictionary(); private NetworkPlayer _owner; private bool _canEnterSpectator; private bool _isEliminationHandled; public int Respawns { get; private set; } public bool IsEliminated { get; private set; } public LimitedRespawn() { } public LimitedRespawn(int respawns) { Respawns = respawns; } public void OnReady(NetworkPlayer networkPlayer) { _owner = networkPlayer; } public void OnAction(PlayerActionType action, PlayerID otherPlayer) { //IL_0006: 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) Executor.RunIfHost(delegate { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 PlayerActionType val = action; if ((int)val != 2) { if ((int)val == 3) { OnDying(); } } else { OnDeath(); } }); } public void OnRemoved() { Executor.RunIfHost(delegate { NetworkPlayer owner = _owner; if (owner != null) { owner.PlayerID?.SetSpectating(isSpectating: false); } }); } public static void RegisterSpectatePredicate(PlayerSpectatePredicate predicate) where T : Gamemode { GlobalPredicates[typeof(T)] = predicate; } public void SetRespawns(int respawns) { Executor.RunIfHost(delegate { Respawns = respawns; }); } private void OnDying() { if (!IsEliminated) { Respawns--; if (Respawns < 0) { IsEliminated = true; _canEnterSpectator = GamemodeManager.IsGamemodeStarted && GlobalPredicates.TryGetValue(((object)GamemodeManager.ActiveGamemode).GetType(), out PlayerSpectatePredicate value) && value(_owner); } } } private void OnDeath() { if (!_isEliminationHandled) { if (Respawns >= 0) { RespawnCountChangedEvent.CallFor(_owner.PlayerID, new RespawnCountPacket { Respawns = Respawns }); } else if (_canEnterSpectator) { RespawnCountChangedEvent.CallFor(_owner.PlayerID, new RespawnCountPacket { Respawns = Respawns }); _owner.PlayerID.SetSpectating(isSpectating: true); _isEliminationHandled = true; } } } private static void OnRespawnsChanged(RespawnCountPacket packet) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0095: 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_00a8: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_010c: 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_011f: Expected O, but got Unknown int respawns = packet.Respawns; if (respawns >= 0) { if (respawns == 0) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Player Died"), Message = NotificationText.op_Implicit("This is your last try."), PopupLength = 3f, SaveToMenu = false, ShowPopup = true, Type = (NotificationType)2 }); } else { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Player Died"), Message = NotificationText.op_Implicit($"You have {respawns} respawns remaining."), PopupLength = 3f, SaveToMenu = false, ShowPopup = true, Type = (NotificationType)2 }); } } else { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Game over"), Message = NotificationText.op_Implicit("You are now spectating"), PopupLength = 3f, SaveToMenu = false, ShowPopup = true, Type = (NotificationType)2 }); } } } } namespace MashGamemodeLibrary.Entities.Behaviour { public static class BehaviourManager { private static readonly InheritanceCache InheritanceCache = new InheritanceCache(); private static readonly Dictionary Caches = new Dictionary(); private static IEnumerable GetTargetedCaches(IBehaviour behaviour) { return (from baseType in InheritanceCache.GetBaseTypes(behaviour) select Caches.GetValueOrDefault(baseType)).OfType(); } public static IAssociatedBehaviourCache CreateCache() where TAssociated : IEcsAssociation where TBehaviour : IBehaviour { if (Caches.TryGetValue(typeof(TBehaviour), out IBehaviourCache value)) { return (IAssociatedBehaviourCache)value; } AssociatedBehaviourCache associatedBehaviourCache = new AssociatedBehaviourCache(); Caches[typeof(TBehaviour)] = associatedBehaviourCache; return associatedBehaviourCache; } public static IBehaviourCache CreateCache() where TBehaviour : IBehaviour { if (Caches.TryGetValue(typeof(TBehaviour), out IBehaviourCache value)) { return (IBehaviourCache)value; } BehaviourCache behaviourCache = new BehaviourCache(); Caches[typeof(TBehaviour)] = behaviourCache; return behaviourCache; } public static List Add(IBehaviourHolder holder, object behaviour) { IBehaviourHolder holder2 = holder; IBehaviour typedBehaviour = behaviour as IBehaviour; if (typedBehaviour == null) { return new List(); } InheritanceCache.AddComponent(typedBehaviour); return (from cache in GetTargetedCaches(typedBehaviour) select cache.TryAdd(holder2, typedBehaviour)).OfType().ToList(); } internal static void Remove(BehaviourMember behaviour) { behaviour.Remove(); } internal static void RemoveAll(IEnumerable list) { foreach (BehaviourMember item in list) { item.Remove(); } } } } namespace MashGamemodeLibrary.Entities.Behaviour.Helpers { public class BehaviourMember { internal bool InCache = true; internal Guid ID { get; } internal IBehaviour Behaviour { get; } internal IBehaviourCache Cache { get; } internal BehaviourMember(Guid id, IBehaviour behaviour, IBehaviourCache cache) { ID = id; Behaviour = behaviour; Cache = cache; } ~BehaviourMember() { if (InCache) { BehaviourManager.Remove(this); } } public void Remove() { Cache.TryRemove(this); } } internal class InheritanceCache { private Dictionary> _dictionary = new Dictionary>(); public void AddComponent(IBehaviour behaviour) { Type type = behaviour.GetType(); if (_dictionary.ContainsKey(type)) { return; } Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { if (type2.IsAssignableTo(typeof(IBehaviour))) { _dictionary.GetValueOrCreate(type, () => new HashSet()).Add(type2); } } } public IEnumerable GetBaseTypes(IBehaviour behaviour) { if (!_dictionary.TryGetValue(behaviour.GetType(), out HashSet value)) { return Array.Empty(); } return value; } } } namespace MashGamemodeLibrary.Entities.Behaviour.Cache { public class AssociatedBehaviourCache : BehaviourCache, IAssociatedBehaviourCache, IBehaviourCache, IBehaviourCache where TAssociation : IEcsAssociation where TBehaviour : IBehaviour { private readonly Dictionary>> _associationToBehaviourMap = new Dictionary>>(); public new event OnAssociatedAddedDelegate? OnAdded; public new event OnAssociatedRemovedDelegate? OnRemoved; protected override void OnAddedInternal(HeldBehaviour entry) { IEcsAssociation association = entry.Holder.Index.Association; if (association is TAssociation) { TAssociation association2 = (TAssociation)association; int iD = association2.GetID(); _associationToBehaviourMap.GetValueOrCreate(iD).Add(entry.Behaviour.GetType(), entry); this.OnAdded?.Invoke(association2, entry.Behaviour); } } protected override void OnRemovedInternal(HeldBehaviour entry) { IEcsAssociation association = entry.Holder.Index.Association; if (association is TAssociation) { TAssociation association2 = (TAssociation)association; _associationToBehaviourMap.GetValueOrDefault(association2.GetID())?.Remove(entry.Behaviour.GetType()); this.OnRemoved?.Invoke(association2, entry.Behaviour); } } protected override void OnClear() { foreach (KeyValuePair> item in _associationToBehaviourMap.SelectMany>>, KeyValuePair>>((KeyValuePair>> keyValuePair) => keyValuePair.Value)) { if (item.Value.Holder.Index.Association is TAssociation association) { this.OnRemoved?.Invoke(association, item.Value.Behaviour); } } _associationToBehaviourMap.Clear(); } public IEnumerable GetAll(int entityId) { if (!_associationToBehaviourMap.TryGetValue(entityId, out Dictionary> value2)) { return Array.Empty(); } return from value in value2.Values where value.Holder.IsReady select value.Behaviour; } public bool Contains(int id) { return _associationToBehaviourMap.ContainsKey(id); } public void ForEach(int id, Action onEach) { foreach (TBehaviour item in GetAll(id)) { item.Try(onEach.Invoke); } } } public record HeldBehaviour(IBehaviourHolder Holder, TValue Behaviour) where TValue : IBehaviour; public class BehaviourCache : IBehaviourCache, IBehaviourCache where TBehaviour : IBehaviour { private readonly Dictionary _holderLookup = new Dictionary(); private readonly Dictionary> _behaviours = new Dictionary>(); public Type Target { get; } = typeof(TBehaviour); public event OnAddedDelegate? OnAdded; public event OnRemovedDelegate? OnRemoved; protected virtual void OnAddedInternal(HeldBehaviour entry) { HeldBehaviour entry2 = entry; this.OnAdded?.Try(delegate(OnAddedDelegate v) { v(entry2.Behaviour); }); } protected virtual void OnRemovedInternal(HeldBehaviour entry) { HeldBehaviour entry2 = entry; this.OnRemoved?.Try(delegate(OnRemovedDelegate v) { v(entry2.Behaviour); }); } protected virtual void OnClear() { _behaviours.Clear(delegate(KeyValuePair> kvp) { this.OnRemoved?.Try(delegate(OnRemovedDelegate v) { v(kvp.Value.Behaviour); }); }); } public Guid Add(IBehaviourHolder holder, TBehaviour value) { HeldBehaviour heldBehaviour = new HeldBehaviour(holder, value); Guid guid = Guid.NewGuid(); _behaviours[guid] = heldBehaviour; _holderLookup[value] = holder; OnAddedInternal(heldBehaviour); return guid; } public bool Remove(Guid id) { if (!_behaviours.Remove(id, out HeldBehaviour value)) { return false; } _holderLookup.Remove(value.Behaviour); OnRemovedInternal(value); return true; } public void Clear() { if (this.OnRemoved == null) { _behaviours.Clear(); return; } OnClear(); _holderLookup.Clear(); } private IEnumerable> GetReadyEntries() { return _behaviours.Values.Where((HeldBehaviour kvp) => kvp.Holder.IsReady); } public void ForEach(Action onEach) { Action onEach2 = onEach; foreach (HeldBehaviour readyEntry in GetReadyEntries()) { readyEntry.Try(delegate(HeldBehaviour value) { onEach2(value.Behaviour); }); } } public void ForEach(Action onEach) { Action onEach2 = onEach; foreach (HeldBehaviour readyEntry in GetReadyEntries()) { readyEntry.Try(delegate(HeldBehaviour value) { onEach2(value.Holder, value.Behaviour); }); } } public IBehaviourHolder? GetHolder(TBehaviour behaviour) { return _holderLookup.GetValueOrDefault(behaviour); } public BehaviourMember? TryAdd(IBehaviourHolder holder, IBehaviour behaviour) { if (!(behaviour is TBehaviour val)) { return null; } return new BehaviourMember(Add(holder, val), val, this); } public void TryRemove(BehaviourMember member) { if (!(member.Cache.Target != Target)) { member.InCache = false; Remove(member.ID); } } } public interface IBehaviour { } public delegate void OnAddedDelegate(TBehaviour behaviour); public delegate void OnRemovedDelegate(TBehaviour behaviour); public interface IBehaviourCache { Type Target { get; } BehaviourMember? TryAdd(IBehaviourHolder holder, IBehaviour behaviour); void TryRemove(BehaviourMember member); } public interface IBehaviourCache : IBehaviourCache where TBehaviour : IBehaviour { event OnAddedDelegate? OnAdded; event OnRemovedDelegate? OnRemoved; void ForEach(Action onEach); IBehaviourHolder? GetHolder(TBehaviour behaviour); void ForEach(Action onEach); } public delegate void OnAssociatedAddedDelegate(TAssociation association, TBehaviour behaviour); public delegate void OnAssociatedRemovedDelegate(TAssociation association, TBehaviour behaviour); public interface IAssociatedBehaviourCache : IBehaviourCache, IBehaviourCache where TAssociation : IEcsAssociation where TBehaviour : IBehaviour { new event OnAssociatedAddedDelegate? OnAdded; new event OnAssociatedRemovedDelegate? OnRemoved; bool Contains(int id); void ForEach(int id, Action onEach); IEnumerable GetAll(int id); } public interface IBehaviourHolder { EcsIndex Index { get; } bool IsReady { get; } } } namespace MashGamemodeLibrary.Entities.Association { public class EcsIndex : INetSerializable, IEquatable { public static readonly FactoryTypedRegistry AssociationRegistry = new FactoryTypedRegistry(); private ulong _componentID; private IEcsAssociation? _association; public ulong ComponentID => _componentID; public IEcsAssociation? Association => _association; public EcsIndex() { } public EcsIndex(ulong componentID, IEcsAssociation? association = null) { _componentID = componentID; _association = association; } public EcsIndex(Type componentType, IEcsAssociation? association = null) { _componentID = EcsManager.ComponentRegistry.GetID(componentType); _association = association; } public EcsIndex(IComponent component, IEcsAssociation? association = null) { _componentID = EcsManager.ComponentRegistry.GetID(component); _association = association; } public void HookCreation(Action action) { if (_association == null) { action(); } else { _association?.HookReady(action); } } public void HookRemoval(Action action) { if (_association != null) { _association.HookRemoval(action); } } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref _componentID); AssociationRegistry.SerializeValue(serializer, ref _association); } public bool Equals(EcsIndex? other) { if (other == null) { return false; } if (_componentID != other._componentID) { return false; } if (_association == null && other._association == null) { return true; } return _association?.GetID() == other._association?.GetID(); } } public interface IEcsAssociation : INetSerializable { int GetID(); void HookReady(Action action); void HookRemoval(Action action); } } namespace MashGamemodeLibrary.Entities.Association.Impl { public class NetworkEntityAssociation : IEcsAssociation, INetSerializable, IEquatable { public NetworkEntityReference NetworkID; public NetworkEntityAssociation() { } public NetworkEntityAssociation(ushort id) { //IL_0008: 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) NetworkID = new NetworkEntityReference(id); } public NetworkEntityAssociation(NetworkEntityReference reference) { //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) NetworkID = reference; } public NetworkEntityAssociation(NetworkEntity entity) { //IL_0008: 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) NetworkID = new NetworkEntityReference(entity); } public void HookReady(Action action) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Action action2 = action; ((NetworkEntityReference)(ref NetworkID)).HookEntityRegistered((NetworkEntityDelegate)delegate { action2(); }); } public void HookRemoval(Action action) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown Action action2 = action; NetworkEntity entity = default(NetworkEntity); if (!((NetworkEntityReference)(ref NetworkID)).TryGetEntity(ref entity)) { InternalLogger.Error($"Failed to fetch entity {NetworkID.ID}, did you call this HookRemoval too early?"); return; } NetworkEntityDelegate callback = null; callback = (NetworkEntityDelegate)delegate { action2(); entity.OnEntityUnregistered -= callback; }; entity.OnEntityUnregistered += callback; } public void Serialize(INetSerializer serializer) { NetSerializerExtensions.SerializeValue(serializer, ref NetworkID); } public int GetID() { return NetworkID.ID; } public bool Equals(NetworkEntityAssociation? other) { if (other == null) { return false; } if (this == other) { return true; } return NetworkID.ID == other.NetworkID.ID; } public override int GetHashCode() { return GetID(); } } } namespace MashGamemodeLibrary.Debug { public static class DebugRenderer { private static Texture2D CreateColorTexture(Color color) { //IL_0002: 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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } private static void RenderLine(Vector3[] positions, Color color) { } [Conditional("DEBUG")] public static void RenderLine(Vector3 start, Vector3 end, Color? color = null) { } [Conditional("DEBUG")] public static void RenderCube(Vector3 center, Vector3 size, Color? color = null) { } [Conditional("DEBUG")] public static void Clear() { } } } namespace MashGamemodeLibrary.Data.Random { public class BasicRandomProvider : IRandomProvider where TValue : class { public delegate List DataProvider(); private readonly DataProvider _provider; public BasicRandomProvider(DataProvider provider) { _provider = provider; } public TValue? GetRandomValue() { List list = _provider(); if (list.Count != 0) { return IEnumerableExtensions.GetRandom((IEnumerable)list); } return null; } } public class CircularRandomProvider : IRandomProvider { public delegate List DataProvider(); private readonly DataProvider _provider; private int _index; public CircularRandomProvider(DataProvider provider) { _provider = provider; } public TValue? GetRandomValue() { List list = _provider(); if (list.Count == 0) { return default(TValue); } int num = list.Count - 1; int num2 = Random.RandomRange(1, num); _index = (_index + num2) % list.Count; return list[_index]; } } public interface IRandomProvider { TValue? GetRandomValue(); } public static class RandomUtils { public static IEnumerable Shuffle(this IEnumerable from) { System.Random rng = new System.Random(); return from.OrderBy((T _) => rng.Next()); } } public class WeightedRandomProvider : IRandomProvider where TValue : class { public delegate List DataProvider(); private static readonly int BaseWeight = 128; private readonly DataProvider _provider; private readonly Dictionary _weights = new Dictionary(); private TValue? _lastSelectedValue; private int _sameSelectionCount; public WeightedRandomProvider(DataProvider provider) { _provider = provider; } public TValue? GetRandomValue() { TValue val = SelectValue(); if (val == null) { return null; } _weights[val] = GetNewWeight(val); return val; } private int GetWeight(TValue key) { return _weights.GetValueOrDefault(key, BaseWeight); } private TValue? SelectValue() { List list = _provider(); foreach (TValue item in _weights.Keys.Except(list)) { _weights.Remove(item); } int num = _weights.Values.Sum() + (list.Count - _weights.Count) * BaseWeight; int num2 = Random.RandomRange(0, num); foreach (TValue item2 in list) { int weight = GetWeight(item2); if (num2 <= weight) { return item2; } num2 -= weight; } return null; } private int GetNewWeight(TValue value) { if (_lastSelectedValue == value) { _sameSelectionCount++; } else { _sameSelectionCount = 1; } _lastSelectedValue = value; return BaseWeight - (int)Math.Pow(2.0, _sameSelectionCount); } } } namespace MashGamemodeLibrary.Context { public abstract class ExtendedGamemode : Gamemode, IGamemode where TContext : GameModeContext, new() where TConfig : class, IConfig, new() { public delegate void ConfigChangedHandler(TConfig config); private static TContext? _internalContext; private ConfigMenu _configMenu; private Texture? _logo; public static TContext Context => _internalContext ?? throw new InvalidOperationException("Gamemode context is null. Did you forget to call base.OnGamemodeRegistered()?"); public static TConfig Config => ConfigManager.Get(); public virtual string? LogoResource => null; public override Texture Logo => _logo; public virtual bool KnockoutDisabled => true; public virtual bool SlowMotionDisabled => true; public virtual int RoundCount => 1; public static bool IsStarted { get; private set; } public static bool IsInRound { get; private set; } public static event ConfigChangedHandler? OnConfigChanged; protected virtual void OnRegistered() { } protected virtual void OnStart() { } protected virtual void OnEnd() { } protected virtual void OnRoundStart() { } protected virtual void OnRoundEnd(ulong winnerTeamId) { } protected virtual void OnUpdate(float delta) { } protected virtual void OnCleanup() { } public virtual void OnLateJoin(PlayerID playerID) { } public virtual void OnPlayerDisconnect(PlayerID playerID) { } public virtual bool CanAttackPlayer(PlayerID player) { return true; } public void StartRound(int index) { IsInRound = true; Context.OnStart(); LocalHealth.MortalityOverride = (KnockoutDisabled ? new bool?(true) : null); LocalControls.DisableSlowMo = SlowMotionDisabled; Executor.RunChecked(OnRoundStart); } public void EndRound(ulong winnerTeamId) { IsInRound = false; Executor.RunChecked>(OnRoundEnd, winnerTeamId); Reset(); } private void Reset() { Context.OnStop(); OnCleanup(); LocalHealth.MortalityOverride = null; LocalControls.DisableSlowMo = false; SlotData.ClearSpawned(); GamePhaseManager.Disable(); AvatarStatManager.ResetStats(); PlayerGunManager.Reset(); FusionPlayer.ResetSpawnPoints(); LogicTeamManager.Disable(); GameObjectExtender.DestroyAll(); EcsManager.Reset(); NightVisionHelper.Enabled = false; Executor.RunIfHost(delegate { PlayerComponentExtender.ClearPlayerComponents(); PlayerDataManager.ResetRules(); }); } public override void OnGamemodeRegistered() { _internalContext = new TContext(); ConfigManager.Register(); ConfigManager.OnConfigChanged += delegate(IConfig config) { if (config is TConfig config2) { ExtendedGamemode.OnConfigChanged?.Invoke(config2); } }; _configMenu = new ConfigMenu(Config); _logo = (Texture?)(object)((LogoResource != null) ? ImageHelper.LoadEmbeddedImage(LogoResource) : null); OnRegistered(); ((Gamemode)this).OnGamemodeRegistered(); } public override void OnGamemodeReady() { Executor.RunIfHost(ConfigManager.Enable); Context.OnReady(); } public override void OnGamemodeUnready() { Context.OnUnready(); IsStarted = false; IsInRound = false; } public override void OnLevelReady() { ((Gamemode)this).OnLevelReady(); if (!IsStarted) { Reset(); Executor.RunIfHost(PersistentTeams.Clear); InternalGamemodeManager.RoundCount = RoundCount; ((Component)Player.RigManager).GetComponent().deathTimeAmount = 1f; GamemodeCompatibilityChecker.SetActiveGamemode((Gamemode?)(object)this); PlayerStatisticsTracker.Clear(); IsStarted = true; IsInRound = true; OnStart(); InternalGamemodeManager.StartRound(0); } } public override void OnGamemodeStopped() { IsStarted = false; IsInRound = false; GamemodeCompatibilityChecker.SetActiveGamemode(null); GlobalStatisticsManager.SaveStatistics((Gamemode)(object)this); Executor.RunChecked(OnEnd); Reset(); Executor.RunIfHost(PersistentTeams.Clear); } protected override void OnUpdate() { ((Gamemode)this).OnUpdate(); if (IsStarted) { float deltaTime = Time.deltaTime; InternalGamemodeManager.Update(deltaTime); GamePhaseManager.Update(deltaTime); CommonEcsBehaviours.Update(deltaTime); Context.Update(deltaTime); OnUpdate(deltaTime); } } public override bool CanAttack(PlayerID player) { if (!IsStarted) { return true; } return CanAttackPlayer(player); } public override GroupElementData CreateSettingsGroup() { return _configMenu.GetElementData(); } } public abstract class GameModeContext where TContext : GameModeContext { private static readonly HashSet> ContextCache = new HashSet>(); private static readonly HashSet UpdateCache = new HashSet(); private NetworkPlayer? _hostPlayer; private NetworkPlayer? _localPlayer; public NetworkPlayer LocalPlayer => _localPlayer ?? throw new InvalidOperationException("LocalPlayer is not set. Make sure to call OnStart."); public NetworkPlayer HostPlayer => _hostPlayer ?? throw new InvalidOperationException("Failed to get host NetworkPlayer."); public bool IsReady { get; private set; } public bool IsStarted { get; private set; } private void AddToCache(ISet set, FieldInfo field) { if (typeof(T).IsAssignableFrom(field.FieldType)) { object value = field.GetValue(this); if (value is T) { T item = (T)value; set.Add(item); } } } protected GameModeContext() { FieldInfo[] fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { AddToCache(UpdateCache, field); AddToCache(ContextCache, field); } } internal void Update(float delta) { if (!IsStarted) { return; } IEnumerableExtensions.ForEach>((IEnumerable>)ContextCache, (Action>)delegate(IContextfull entry) { entry.Try(delegate(IContextfull e) { e.SetContext((TContext)this); }); }); IEnumerableExtensions.ForEach((IEnumerable)UpdateCache, (Action)delegate(IUpdating entry) { entry.Try(delegate(IUpdating e) { e.Update(delta); }); }); } internal void OnReady() { IsReady = true; _localPlayer = LocalPlayer.GetNetworkPlayer(); if (_localPlayer == null) { throw new InvalidOperationException("Failed to get local NetworkPlayer."); } _hostPlayer = ((IEnumerable)NetworkPlayer.Players).FirstOrDefault((Func)((NetworkPlayer e) => e.PlayerID.IsHost)); if (_hostPlayer == null) { throw new InvalidOperationException("Failed to get host NetworkPlayer."); } } internal void OnStart() { IsStarted = true; } internal void OnStop() { IsStarted = false; foreach (IUpdating item in UpdateCache) { IStoppable stoppable = item as IStoppable; if (stoppable != null) { stoppable.Try(delegate { stoppable.Stop(); }); } } } internal void OnUnready() { IsReady = false; } } public class RoundStartPacket : INetSerializable { public int Index; public int? GetSize() { return 4; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref Index); } } public class RoundEndPacket : INetSerializable { public bool IsLastRound; public float TimeUntilNextRound; public ulong WinningTeamID; public int? GetSize() { return 12; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref WinningTeamID); serializer.SerializeValue(ref IsLastRound); serializer.SerializeValue(ref TimeUntilNextRound); } } public static class InternalGamemodeManager { private static readonly RemoteEvent RoundStartEvent; private static readonly RemoteEvent RoundEndEvent; private static int _roundIndex; private static float _roundCooldown; public static int RoundCount { get; set; } public static float TimeBetweenRounds => GamemodeRoundManager.Settings.TimeBetweenRounds; public static bool InRound { get; private set; } static InternalGamemodeManager() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown RoundStartEvent = new RemoteEvent("OnRoundStart", OnRoundStart, CommonNetworkRoutes.HostToAll); RoundEndEvent = new RemoteEvent("OnRoundEnd", OnRoundEnd, CommonNetworkRoutes.HostToAll); RoundCount = 0; MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft); } private static bool TryGetGamemode([MaybeNullWhen(false)] out IGamemode gamemode) { if (!GamemodeManager.IsGamemodeStarted) { gamemode = null; return false; } gamemode = GamemodeManager.ActiveGamemode as IGamemode; return gamemode != null; } public static void StartRound(int index) { if (!NetworkInfo.IsHost || !GamemodeManager.IsGamemodeStarted) { return; } foreach (NetworkPlayer player in NetworkPlayer.Players) { GamemodeCompatibilityChecker.ValidatePlayer(player.PlayerID.SmallID); } RoundStartEvent.Call(new RoundStartPacket { Index = index }); } public static void EndRound(ulong winningTeamId) { if (NetworkInfo.IsHost && GamemodeManager.IsGamemodeStarted) { InRound = false; bool flag = _roundIndex >= RoundCount - 1; RoundEndEvent.Call(new RoundEndPacket { WinningTeamID = winningTeamId, IsLastRound = flag, TimeUntilNextRound = TimeBetweenRounds }); if (flag) { GamemodeManager.StopGamemode(); } } } public static void OnLateJoin(PlayerID id) { PlayerID id2 = id; if (TryGetGamemode(out IGamemode gamemode)) { GamemodeCompatibilityChecker.ValidatePlayer(PlayerID.op_Implicit(id2)); gamemode.Try(delegate(IGamemode g) { g.OnLateJoin(id2); }); } } private static void OnPlayerLeft(PlayerID playerId) { if (TryGetGamemode(out IGamemode gamemode)) { gamemode.OnPlayerDisconnect(playerId); } } private static void OnRoundStart(RoundStartPacket packet) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown if (TryGetGamemode(out IGamemode gamemode)) { InRound = true; _roundCooldown = 0f; _roundIndex = packet.Index; if (RoundCount > 1) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Round Start!"), Message = NotificationText.op_Implicit($"Round: {_roundIndex + 1} / {RoundCount}"), PopupLength = 4f, SaveToMenu = false, ShowPopup = true, Type = (NotificationType)0 }); } PlayerDamageTracker.Reset(); gamemode.StartRound(packet.Index); } } private static void OnRoundEnd(RoundEndPacket packet) { //IL_0026: 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_0031: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown if (TryGetGamemode(out IGamemode gamemode)) { InRound = false; _roundCooldown = packet.TimeUntilNextRound; if (!packet.IsLastRound) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Cooldown"), Message = NotificationText.op_Implicit($"The next round will start in {MathF.Round(_roundCooldown):N0} seconds"), PopupLength = 4f, SaveToMenu = false, ShowPopup = true, Type = (NotificationType)0 }); } gamemode.EndRound(packet.WinningTeamID); } } public static void Update(float delta) { if (NetworkInfo.IsHost && GamemodeManager.IsGamemodeStarted && !InRound && !(_roundCooldown <= 0f)) { _roundCooldown -= delta; if (!(_roundCooldown > 0f)) { StartRound(_roundIndex + 1); } } } public static void Reset() { Executor.RunIfHost(delegate { if (GamemodeManager.ActiveGamemode is IGamemode) { GamemodeManager.StopGamemode(); } }); } } } namespace MashGamemodeLibrary.Context.Helper { public interface IGamemode { void StartRound(int index); void EndRound(ulong winnerTeamId); void OnLateJoin(PlayerID playerID); void OnPlayerDisconnect(PlayerID playerID); } } namespace MashGamemodeLibrary.Context.Control { public interface IContextfull where T : GameModeContext { void SetContext(T context); } public interface IStoppable { void Stop(); } public interface IUpdating { void Update(float delta); } } namespace MashGamemodeLibrary.Config { public static class ConfigManager { public delegate void ConfigChangedHandler(IConfig config); private static readonly string ConfigDirectoryPath; private static readonly JsonSerializerOptions JsonOptions; private static readonly SingletonTypedRegistry LocalConfigTypedRegistry; private static readonly FactoryTypedRegistry ActiveConfigTypedRegistry; private static readonly SyncedVariable RemoteConfigInstance; private static readonly TimeSpan SyncDelay; private static readonly HashSet DirtyConfigs; private static DateTime _lastChanged; public static event ConfigChangedHandler? OnConfigChanged; static ConfigManager() { ConfigDirectoryPath = Mod.ModDataDirectory + "/configs/"; JsonOptions = new JsonSerializerOptions { WriteIndented = true }; LocalConfigTypedRegistry = new SingletonTypedRegistry(); ActiveConfigTypedRegistry = new FactoryTypedRegistry(); RemoteConfigInstance = new SyncedVariable("ActiveConfig", new NullableReferenceEncoder(new DynamicInstanceEncoder(ActiveConfigTypedRegistry)), null); SyncDelay = TimeSpan.FromSeconds(5.0); DirtyConfigs = new HashSet(); _lastChanged = DateTime.MinValue; RemoteConfigInstance.OnValueChanged += delegate(IConfig? config) { if (config != null) { ConfigManager.OnConfigChanged?.Invoke(config); } }; } private static string GetConfigFilePath(Type configType) { return ConfigDirectoryPath + configType.Name + ".json"; } private static T LoadConfig() where T : class, IConfig, new() { Type typeFromHandle = typeof(T); string configFilePath = GetConfigFilePath(typeFromHandle); T val = null; try { if (File.Exists(configFilePath)) { using FileStream utf8Json = File.OpenRead(configFilePath); T val2 = JsonSerializer.Deserialize(utf8Json, JsonOptions); if (val2 != null) { val = val2; } } } catch (Exception ex) { MelonLogger.Error("Failed to load config for " + typeFromHandle.Name, ex); } if (val == null) { val = new T(); } return val; } private static void WriteConfig(IConfig config) { Type type = config.GetType(); string configFilePath = GetConfigFilePath(type); try { Directory.CreateDirectory(ConfigDirectoryPath); using FileStream fileStream = File.Open(configFilePath, FileMode.OpenOrCreate, FileAccess.Write); fileStream.SetLength(0L); JsonSerializer.Serialize(fileStream, config, type, JsonOptions); } catch (Exception ex) { MelonLogger.Error("Failed to write config for " + type.Name, ex); } } public static void Register() where T : class, IConfig, new() { LocalConfigTypedRegistry.Register(LoadConfig()); ActiveConfigTypedRegistry.Register(); } internal static void Enable() where T : IConfig { T val = LocalConfigTypedRegistry.Get(); object obj = ((val != null) ? val.Clone() : null); if (obj is T) { T val2 = (T)obj; RemoteConfigInstance.SetAndSync(val2); } } public static T Get() where T : class, IConfig { if (RemoteConfigInstance.Value is T result) { return result; } if (!LocalConfigTypedRegistry.TryGet(out var entry)) { throw new Exception("No local config of type " + typeof(T).Name + " has been registered!"); } return entry; } public static void SetLocalConfig(IConfig config, Action func) { Type type = config.GetType(); if (LocalConfigTypedRegistry.TryGet(type, out IConfig entry)) { func(entry); DirtyConfigs.Add(entry); _lastChanged = DateTime.Now; } } public static void Update() { if (DirtyConfigs.Count != 0 && !(DateTime.Now - _lastChanged < SyncDelay)) { Save(); Sync(); } } internal static void Save() { foreach (IConfig dirtyConfig in DirtyConfigs) { WriteConfig(dirtyConfig); } DirtyConfigs.Clear(); } internal static void Sync() { if (NetworkInfo.IsHost && RemoteConfigInstance.Value != null && LocalConfigTypedRegistry.TryGet(RemoteConfigInstance.Value.GetType(), out IConfig entry)) { IConfig andSync = (IConfig)entry.Clone(); RemoteConfigInstance.SetAndSync(andSync); } } } public interface IConfig : INetSerializable, ICloneable { } } namespace MashGamemodeLibrary.Config.Menu { public struct Bounds { public object Lower; public object Upper; } public record ConfigEntryData { public object Value => _overwrite ?? DefaultValue; private static readonly KeyedRegistry ElementProviders; public readonly string? Category; public readonly object DefaultValue; public readonly FieldInfo FieldInfo; public readonly string Name; public readonly Type Type; private ElementData? _cachedElementData; private IConfigElementProvider? _elementProvider; private object? _overwrite; public Bounds? Bounds; public object? Increment; static ConfigEntryData() { ElementProviders = new KeyedRegistry(); ElementProviders.Register(typeof(float), new FloatElementProvider()); ElementProviders.Register(typeof(int), new IntElementProvider()); ElementProviders.Register(typeof(bool), new BoolElementProvider()); ElementProviders.Register(typeof(Enum), new EnumElementProvider()); } public ConfigEntryData(IConfig instance, ConfigMenuEntry menuEntry, FieldInfo fieldInfo) { FieldInfo = fieldInfo; Type = fieldInfo.FieldType; Name = menuEntry.Name; Category = menuEntry.Category; ApplyConfigElementProvider(fieldInfo); ApplyIncrement(fieldInfo); ApplyBounds(fieldInfo); DefaultValue = fieldInfo.GetValue(instance) ?? throw new Exception("Config fields must be initialized (" + fieldInfo.Name + ")"); } private static Type GetBaseType(Type type) { if (type.IsEnum) { return typeof(Enum); } return type; } private static Action WriterFactory(IConfig config) { IConfig config2 = config; return delegate(ConfigEntryData target, object value) { ConfigEntryData target2 = target; object value2 = value; target2._overwrite = value2; ConfigManager.SetLocalConfig(config2, delegate(IConfig localConfig) { target2.FieldInfo.SetValue(localConfig, value2); }); }; } private ElementData CreateElementData(IConfig instance) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (_elementProvider != null) { return _elementProvider.GetElementData(this, WriterFactory(instance)); } if (ElementProviders.TryGet(GetBaseType(Type), out IConfigElementProvider value)) { return value.GetElementData(this, WriterFactory(instance)); } return (ElementData)new LabelElementData { Title = "Field type " + Type.Name + " has no associated element provider." }; } public ElementData GetElementData(IConfig instance) { if (_cachedElementData == null) { _cachedElementData = CreateElementData(instance); } return _cachedElementData; } private void ApplyConfigElementProvider(FieldInfo fieldInfo) { ConfigElementProvider customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { _elementProvider = (IConfigElementProvider)Activator.CreateInstance(customAttribute.ProviderType); } } private void ApplyIncrement(FieldInfo fieldInfo) { ConfigStepSize customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { if (customAttribute.StepSize.GetType() != Type) { MelonLogger.Warning($"Can't step {Name} by {customAttribute.StepSize.GetType().Name}. It is not of the same type as the field. ({Type})"); } else { Increment = customAttribute.StepSize; } } } private void ApplyBounds(FieldInfo fieldInfo) { ConfigRangeConstraint customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { if (customAttribute.Lower.GetType() != Type) { MelonLogger.Warning($"Can't lower bound {Name} by {customAttribute.Lower.GetType().Name}. It is not of the same type as the field. ({Type})"); } else if (customAttribute.Upper.GetType() != Type) { MelonLogger.Warning($"Can't upper bound {Name} by {customAttribute.Upper.GetType().Name}. It is not of the same type as the field. ({Type})"); } else { Bounds = new Bounds { Lower = customAttribute.Lower, Upper = customAttribute.Upper }; } } } [CompilerGenerated] protected virtual bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Category = "); builder.Append((object?)Category); builder.Append(", DefaultValue = "); builder.Append(DefaultValue); builder.Append(", FieldInfo = "); builder.Append(FieldInfo); builder.Append(", Name = "); builder.Append((object?)Name); builder.Append(", Type = "); builder.Append(Type); builder.Append(", Bounds = "); builder.Append(Bounds.ToString()); builder.Append(", Increment = "); builder.Append(Increment); builder.Append(", Value = "); builder.Append(Value); return true; } [CompilerGenerated] protected ConfigEntryData(ConfigEntryData original) { Category = original.Category; DefaultValue = original.DefaultValue; FieldInfo = original.FieldInfo; Name = original.Name; Type = original.Type; _cachedElementData = original._cachedElementData; _elementProvider = original._elementProvider; _overwrite = original._overwrite; Bounds = original.Bounds; Increment = original.Increment; } } public class ConfigMenu { private readonly List _fields = new List(); private readonly IConfig _instance; public ConfigMenu(IConfig instance) { _instance = instance; FieldInfo[] fields = instance.GetType().GetFields(); foreach (FieldInfo fieldInfo in fields) { ConfigMenuEntry customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { _fields.Add(new ConfigEntryData(instance, customAttribute, fieldInfo)); } } } public GroupElementData GetElementData() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown GroupElementData val = new GroupElementData("Root"); Dictionary dictionary = new Dictionary(); foreach (ConfigEntryData field in _fields) { GroupElementData obj = ((field.Category != null) ? dictionary.GetValueOrCreate(field.Category, () => new GroupElementData(field.Category)) : val); ElementData elementData = field.GetElementData(_instance); obj.AddElement(elementData); } foreach (GroupElementData value in dictionary.Values) { val.AddElement(value); } if (_instance is IConfigMenuProvider configMenuProvider) { configMenuProvider.AddExtraFields(val); } return val; } } public interface IConfigMenuProvider { void AddExtraFields(GroupElementData root); } } namespace MashGamemodeLibrary.Config.Menu.Element { public class BoolElementProvider : IConfigElementProvider { public ElementData GetElementData(ConfigEntryData entry, Action setter) { //IL_0014: 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) //IL_002a: 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_0053: Expected O, but got Unknown Action setter2 = setter; ConfigEntryData entry2 = entry; return (ElementData)new BoolElementData { Title = entry2.Name, Value = (bool)entry2.Value, OnValueChanged = delegate(bool value) { setter2(entry2, value); } }; } } public class EnumElementProvider : IConfigElementProvider { public ElementData GetElementData(ConfigEntryData entry, Action setter) { //IL_0014: 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) //IL_002a: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Action setter2 = setter; ConfigEntryData entry2 = entry; return (ElementData)new EnumElementData { Title = entry2.Name, EnumType = entry2.Type, Value = (Enum)entry2.Value, OnValueChanged = delegate(Enum value) { setter2(entry2, value); } }; } } public class FloatElementProvider : IConfigElementProvider { public ElementData GetElementData(ConfigEntryData entry, Action setter) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown Action setter2 = setter; ConfigEntryData entry2 = entry; float minValue = ((float?)entry2.Bounds?.Lower) ?? 1f; float maxValue = ((float?)entry2.Bounds?.Upper) ?? 10f; return (ElementData)new FloatElementData { Title = entry2.Name, Increment = (((float?)entry2.Increment) ?? 1f), MinValue = minValue, MaxValue = maxValue, Value = (float)entry2.Value, OnValueChanged = delegate(float value) { setter2(entry2, value); } }; } } public class IntElementProvider : IConfigElementProvider { public ElementData GetElementData(ConfigEntryData entry, Action setter) { //IL_008b: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: 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_0102: Expected O, but got Unknown Action setter2 = setter; ConfigEntryData entry2 = entry; int minValue = ((int?)entry2.Bounds?.Lower) ?? 1; int maxValue = ((int?)entry2.Bounds?.Upper) ?? 10; return (ElementData)new IntElementData { Title = entry2.Name, Increment = (((int?)entry2.Increment) ?? 1), MinValue = minValue, MaxValue = maxValue, Value = (int)entry2.Value, OnValueChanged = delegate(int value) { setter2(entry2, value); } }; } } } namespace MashGamemodeLibrary.Config.Menu.Attributes { public interface IConfigElementProvider { ElementData GetElementData(ConfigEntryData entry, Action setter); } [AttributeUsage(AttributeTargets.Field)] public class ConfigElementProvider : Attribute { public Type ProviderType { get; } public ConfigElementProvider(Type providerTypeType) { ProviderType = providerTypeType; } } [AttributeUsage(AttributeTargets.Field)] public class ConfigMenuEntry : Attribute { public string Name { get; } public string? Category { get; } public ConfigMenuEntry(string name, string? category = null) { Name = name; Category = category; } } [AttributeUsage(AttributeTargets.Field)] public class ConfigRangeConstraint : Attribute { public object Lower { get; } public object Upper { get; } public ConfigRangeConstraint(object lower, object upper) { Lower = lower; Upper = upper; } } [AttributeUsage(AttributeTargets.Field)] public class ConfigStepSize : Attribute { public object StepSize { get; } public ConfigStepSize(object stepSize) { StepSize = stepSize; } } } namespace MashGamemodeLibrary.Audio.Registry { public class AudioBin { private readonly List _barcodes = new List(); private readonly List _fallbackBarcodes; public string Tag { get; init; } public ulong TagHash { get; init; } public AudioBin(string tag, List fallbackBarcodes) { _ = fallbackBarcodes.Count; Tag = tag; TagHash = tag.GetStableHash(); _fallbackBarcodes = fallbackBarcodes; } public void Add(string barcode) { _barcodes.Add(barcode); } public IReadOnlyList GetAll() { if (_barcodes.Count <= 0) { return _fallbackBarcodes; } return _barcodes; } } internal class RegisterableAudio { public string Tag { get; init; } public ulong TagHash { get; init; } public string Barcode { get; init; } public RegisterableAudio(string tag, string barcode) { Tag = tag; TagHash = tag.GetStableHash(); Barcode = barcode; } } public static class AudioRegistry { private const string TagPrefix = "MashTags."; private static readonly Type MonoDiscType = Il2CppType.Of(); private static bool _isRegistered; private static readonly List RegisterableAudios = new List(); private static readonly Dictionary Bins = new Dictionary(); private static void PopulateBin(AudioBin bin) { AudioBin bin2 = bin; foreach (RegisterableAudio item in RegisterableAudios.Where((RegisterableAudio registerableAudio) => registerableAudio.TagHash == bin2.TagHash)) { bin2.Add(item.Barcode); } } private static void BinAudio(RegisterableAudio audio) { if (Bins.TryGetValue(audio.Tag, out AudioBin value)) { value.Add(audio.Barcode); } } public static AudioBin CreateBin(string tag, params string[] fallbacks) { if (!tag.StartsWith("MashTags.", StringComparison.Ordinal)) { throw new ArgumentException("Tag must start with MashTags.", "tag"); } int length = "MashTags.".Length; string text = tag.Substring(length, tag.Length - length); if (Bins.TryGetValue(text, out AudioBin value)) { return value; } AudioBin audioBin = new AudioBin(text, fallbacks.ToList()); if (_isRegistered) { PopulateBin(audioBin); } Bins.Add(text, audioBin); return audioBin; } public static void RegisterPallet(Pallet pallet) { Enumerator enumerator = pallet.DataCards.GetEnumerator(); while (enumerator.MoveNext()) { DataCard current = enumerator.Current; if (!(((Object)current).GetIl2CppType() != MonoDiscType) && ((Scannable)current)._description.StartsWith("MashTags.", StringComparison.Ordinal)) { string description = ((Scannable)current)._description; int length = "MashTags.".Length; string text = description.Substring(length, description.Length - length); if (text != null) { RegisterableAudio registerableAudio = new RegisterableAudio(text, ((Scannable)current)._barcode._id); BinAudio(registerableAudio); RegisterableAudios.Add(registerableAudio); } } } } public static void RegisterAll() { if (!_isRegistered) { _isRegistered = true; Enumerator enumerator = AssetWarehouse.Instance.GetPallets().GetEnumerator(); while (enumerator.MoveNext()) { RegisterPallet(enumerator.Current); } } } } } namespace MashGamemodeLibrary.Audio.Players.Object { public class AnnouncementAudioPlayer : SyncedAudioPlayer { public AnnouncementAudioPlayer(string name, ISyncedAudioContainer container, AudioModifierFactory? audioModifierFactory = null) : base(name, container, (AudioSourceProvider)new SingleAudioSourceProvider(audioModifierFactory)) { } protected override bool Modifier(DummySerializable data, AudioSource source) { source.spatialBlend = 0f; return true; } public void PlayRandom() { string randomAudioName = GetRandomAudioName(); if (!string.IsNullOrEmpty(randomAudioName)) { Play(randomAudioName, new DummySerializable()); } } } public class ObjectAudioPlayRequest : INetSerializable { public ushort NetworkEntityId; public int? GetSize() { return 2; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref NetworkEntityId); } } public class ObjectAudioPlayer : SyncedAudioPlayer, IRandomAudioPlayer, IRandomAudioPlayer, IAudioPlayer, IUpdating, IStoppable { public ObjectAudioPlayer(string name, ISyncedAudioContainer container, int maxObjectCount, AudioModifierFactory factory) : base(name, container, (AudioSourceProvider)new PooledAudioSourceProvider(maxObjectCount, factory)) { } public void PlayRandom(NetworkEntity entity) { string randomAudioName = GetRandomAudioName(); if (!string.IsNullOrEmpty(randomAudioName)) { Play(randomAudioName, entity); } } protected override bool Modifier(ObjectAudioPlayRequest data, AudioSource source) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) NetworkEntityReference val = new NetworkEntityReference(data.NetworkEntityId); NetworkEntity val2 = default(NetworkEntity); if (!((NetworkEntityReference)(ref val)).TryGetEntity(ref val2)) { return false; } MarrowEntity marrowEntity = val2.GetExtender().MarrowEntity; GameObject gameObject = ((Component)source).gameObject; gameObject.transform.SetParent(((Component)marrowEntity).gameObject.transform); gameObject.transform.localPosition = Vector3.zero; gameObject.transform.localRotation = Quaternion.identity; return true; } public void Play(string name, NetworkEntity entity) { NetworkEntity entity2 = entity; string name2 = name; Executor.RunIfHost(delegate { ObjectAudioPlayRequest data = new ObjectAudioPlayRequest { NetworkEntityId = entity2.ID }; base.Play(name2, data); }); } } public class PositionalAudioPlayRequest : INetSerializable { public Vector3 Position; public int? GetSize() { return 12; } public void Serialize(INetSerializer serializer) { NetSerializerUnityExtensions.SerializeValue(serializer, ref Position); } } public class PositionalAudioPlayer : SyncedAudioPlayer, IParameterDriven, ISyncedAudioPlayer, IRandomAudioPlayer, IRandomAudioPlayer, IAudioPlayer, IUpdating, IStoppable { public PositionalAudioPlayer(string name, ISyncedAudioContainer container, AudioModifierFactory factory) : base(name, container, (AudioSourceProvider)new SingleAudioSourceProvider(factory)) { } public PositionalAudioPlayer(string name, ISyncedAudioContainer container, AudioSourceProvider sourceProvider) : base(name, container, sourceProvider) { } public void PlayRandom(Vector3 position) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) string randomAudioName = GetRandomAudioName(); if (!string.IsNullOrEmpty(randomAudioName)) { Play(randomAudioName, position); } } protected override bool Modifier(PositionalAudioPlayRequest data, AudioSource source) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((Component)source).gameObject.transform.position = data.Position; return true; } public void Play(string name, Vector3 position) { //IL_0006: 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) PositionalAudioPlayRequest data = new PositionalAudioPlayRequest { Position = position }; base.Play(name, data); } } } namespace MashGamemodeLibrary.Audio.Players.Extensions { public interface IAudioPlayer : IUpdating, IStoppable { } public interface IContinuousPlayer : IAudioPlayer, IUpdating, IStoppable { bool IsActive { get; } void Start(); } public interface IParameterDriven : ISyncedAudioPlayer, IRandomAudioPlayer, IRandomAudioPlayer, IAudioPlayer, IUpdating, IStoppable { } public interface IRandomAudioPlayer : IAudioPlayer, IUpdating, IStoppable { string GetRandomAudioName(); } public interface IRandomAudioPlayer : IRandomAudioPlayer, IAudioPlayer, IUpdating, IStoppable { void PlayRandom(T parameter); } public interface ISyncedAudioPlayer { string Name { get; } } } namespace MashGamemodeLibrary.Audio.Players.Callers { public interface IParameterPacket : INetSerializable { TParameter Value { get; init; } } public class ClientCallableAudioPlayer where TPacket : class, IParameterPacket, new() { private readonly RemoteEvent _clientRequestEvent; private readonly IParameterDriven _player; public ClientCallableAudioPlayer(IParameterDriven player) { _player = player; _clientRequestEvent = new RemoteEvent(player.Name + "_clientPlayRequest", OnClientPlayRequest, CommonNetworkRoutes.AllToHost); } public void PlayRandom(TParameter parameter) { TPacket data = new TPacket { Value = parameter }; _clientRequestEvent.Call(data); } public void Update(float delta) { _player.Update(delta); } public void Stop() { _player.Stop(); } public string GetRandomAudioName() { return _player.GetRandomAudioName(); } private void OnClientPlayRequest(TPacket packet) { TParameter value = packet.Value; _player.PlayRandom(value); } } } namespace MashGamemodeLibrary.Audio.Players.Basic { public class AudioPlayer : IRandomAudioPlayer, IAudioPlayer, IUpdating, IStoppable { protected readonly IAudioContainer Container; protected readonly AudioSourceProvider SourceProvider; private IReadOnlyList AudioNames => Container.AudioNames; public bool IsPlaying => SourceProvider.IsPlaying; public AudioPlayer(IAudioContainer container, AudioSourceProvider sourceProvider) { Container = container; SourceProvider = sourceProvider; } public void Update(float delta) { SourceProvider.Update(delta); } public string GetRandomAudioName() { if (AudioNames.Count <= 0) { return ""; } return IEnumerableExtensions.GetRandom((IEnumerable)AudioNames); } public void Stop() { SourceProvider.StopAll(); } public void Play(string name, IAudioModifier? modifier = null) { Container.RequestClip(name, delegate(AudioClip? clip) { if (Object.op_Implicit((Object)(object)clip)) { SourceProvider.GetAudioSource().Play(clip); } }); } } public class PlayRequestPacket : INetSerializable where T : INetSerializable, new() { public ulong AudioHash; public T ExtraData; public bool ShouldPlay => AudioHash != 0; public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref AudioHash); NetSerializerExtensions.SerializeValue(serializer, ref ExtraData); } } public abstract class SyncedAudioPlayer : AudioPlayer, ISyncedAudioPlayer, IStoppable where TPacket : INetSerializable, new() { private readonly ISyncedAudioContainer _container; private readonly RemoteEvent> _playRequestEvent; public string Name { get; } public SyncedAudioPlayer(string name, ISyncedAudioContainer container, AudioSourceProvider provider) : base(container, provider) { Name = name; _container = container; _playRequestEvent = new RemoteEvent>(name + "_PlayRequest", OnPlayRequest, CommonNetworkRoutes.HostToAll); } public new void Stop() { Executor.RunIfHost(delegate { _playRequestEvent.Call(new PlayRequestPacket { AudioHash = 0uL, ExtraData = new TPacket() }); }); } protected abstract bool Modifier(TPacket data, AudioSource source); private void OnPlayRequest(PlayRequestPacket packet) { PlayRequestPacket packet2 = packet; if (!packet2.ShouldPlay) { SourceProvider.StopAll(); return; } _container.RequestClip(packet2.AudioHash, delegate(AudioClip? clip) { if (Object.op_Implicit((Object)(object)clip)) { AudioSourceEntity audioSource = SourceProvider.GetAudioSource(); if (Modifier(packet2.ExtraData, audioSource.SourceRef)) { audioSource.Play(clip); } } }); } public void Play(string name, TPacket data) { ulong? audioHash = _container.GetAudioHash(name); if (!audioHash.HasValue) { MelonLogger.Error("Audio with name " + name + " not found in container of player: " + GetType().FullName); return; } _playRequestEvent.Call(new PlayRequestPacket { AudioHash = audioHash.Value, ExtraData = data }); } } } namespace MashGamemodeLibrary.Audio.Players.Basic.Providers { public class AudioSourceEntity { public readonly HashSet Modifiers = new HashSet(); private AudioSource _source; public AudioSource Source => _source; public ref AudioSource SourceRef => ref _source; public bool IsValid => Object.op_Implicit((Object)(object)Source); public bool IsPlaying => Source.isPlaying; public Transform Transform => ((Component)Source).transform; public AudioSourceEntity(AudioModifierFactory modifierFactory) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown GameObject val = new GameObject("AudioSourceEntity"); _source = val.AddComponent(); _source.playOnAwake = false; Modifiers = modifierFactory.Build(); } public AudioSourceEntity(AudioSource source, AudioModifierFactory modifierFactory) { _source = source; Modifiers = modifierFactory.Build(); } public static implicit operator bool(AudioSourceEntity? entity) { return entity?.IsValid ?? false; } public void Play(AudioClip clip) { IEnumerableExtensions.ForEach((IEnumerable)Modifiers, (Action)delegate(IAudioModifier modifier) { modifier.OnStart(ref _source); }); Source.clip = clip; Source.Play(); } public void Update(float delta) { IEnumerableExtensions.ForEach((IEnumerable)Modifiers, (Action)delegate(IAudioModifier modifier) { modifier.Update(ref _source, delta); }); } public void Stop() { Source.Stop(); } } public abstract class AudioSourceProvider { private readonly AudioModifierFactory _modifierFactory; public abstract bool IsPlaying { get; } public AudioSourceProvider(AudioModifierFactory? modifierFactory) { _modifierFactory = modifierFactory ?? new AudioModifierFactory(); } protected abstract AudioSourceEntity NextAudioSource(AudioModifierFactory modifierFactory); public abstract void StopAll(); public abstract void Update(float delta); public AudioSourceEntity GetAudioSource() { return NextAudioSource(_modifierFactory); } } internal class AudioPoolMember { private AudioSourceEntity? _source; public AudioSourceEntity GetOrCreate(AudioModifierFactory factory) { AudioSourceEntity source = _source; if (source != null && source.IsValid) { return _source; } _source = new AudioSourceEntity(factory); return _source; } public bool TryGet([MaybeNullWhen(false)] out AudioSourceEntity audioSource) { audioSource = _source; return _source; } public void Update(float delta) { AudioSourceEntity source = _source; if (source != null && source.IsValid) { _source.Update(delta); } } } public class PooledAudioSourceProvider : AudioSourceProvider { private readonly int _poolSize; private readonly AudioPoolMember[] _sources; private int _currentIndex; public override bool IsPlaying { get { AudioSourceEntity audioSource; return _sources.Any((AudioPoolMember x) => x.TryGet(out audioSource) && audioSource.IsPlaying); } } public PooledAudioSourceProvider(int size, AudioModifierFactory modifierFactory) : base(modifierFactory) { _poolSize = size; _sources = (from _ in Enumerable.Range(0, _poolSize) select new AudioPoolMember()).ToArray(); } protected override AudioSourceEntity NextAudioSource(AudioModifierFactory modifierFactory) { int num = 0; AudioSourceEntity orCreate; do { _currentIndex = (_currentIndex + 1) % _poolSize; orCreate = _sources[_currentIndex].GetOrCreate(modifierFactory); num++; } while (num < _poolSize && orCreate.IsPlaying); return orCreate; } public override void StopAll() { AudioPoolMember[] sources = _sources; for (int i = 0; i < sources.Length; i++) { if (sources[i].TryGet(out AudioSourceEntity audioSource) && audioSource.IsPlaying) { audioSource.Stop(); } } } public override void Update(float delta) { IEnumerableExtensions.ForEach((IEnumerable)_sources, (Action)delegate(AudioPoolMember source) { source.Update(delta); }); } } public class SingleAudioSourceProvider : AudioSourceProvider { private AudioSourceEntity? _audioSource; public override bool IsPlaying { get { if ((bool)_audioSource) { return _audioSource.IsPlaying; } return false; } } public SingleAudioSourceProvider(AudioModifierFactory? modifierFactory = null) : base(modifierFactory) { } protected override AudioSourceEntity NextAudioSource(AudioModifierFactory modifierFactory) { if ((bool)_audioSource) { return _audioSource; } _audioSource = new AudioSourceEntity(modifierFactory); return _audioSource; } public override void StopAll() { if ((bool)_audioSource) { _audioSource.Stop(); } } public override void Update(float delta) { if ((bool)_audioSource) { _audioSource.Update(delta); } } } } namespace MashGamemodeLibrary.Audio.Players.Background { public class MusicPlayer : IContinuousPlayer, IAudioPlayer, IUpdating, IStoppable { private readonly IAudioContainer _audioContainer; private int _trackIndex; public bool IsActive { get; private set; } public MusicPlayer(IAudioContainer audioContainer) { _audioContainer = audioContainer; } public void Start() { if (!IsActive) { IsActive = true; NextTrack(); } } public void Stop() { if (IsActive) { IsActive = false; StopTrack(); } } public void Update(float delta) { if (IsActive && !IsPlaying()) { NextTrack(); } } private void PlayTrack() { string name = _audioContainer.AudioNames[_trackIndex]; if (string.IsNullOrEmpty(name)) { return; } _audioContainer.RequestClip(name, delegate(AudioClip? clip) { if (!Object.op_Implicit((Object)(object)clip)) { MelonLogger.Error("Failed to load music clip: " + name); } else { Audio2dManager audio2dManager = Audio2dPlugin.Audio2dManager; bool flag = _audioContainer.AudioNames.Count == 1; audio2dManager.CueOverrideMusic(clip, 0.8f, 2f, 2f, flag, false); } }); } private void NextTrack() { IReadOnlyList audioNames = _audioContainer.AudioNames; if (audioNames.Count != 0) { int num = Random.Range(1, audioNames.Count); _trackIndex = (_trackIndex + num) % audioNames.Count; PlayTrack(); } } private static void StopTrack() { Audio2dManager audio2dManager = Audio2dPlugin.Audio2dManager; if (Object.op_Implicit((Object)(object)audio2dManager)) { audio2dManager.StopOverrideMusic(); audio2dManager.StopMusic(0.2f); } } private bool IsPlaying() { if (_audioContainer.IsLoading) { return true; } if (!Audio2dPlugin.Audio2dManager._isOverride) { return false; } AmbAndMusic currentAmbAndMusic = GetCurrentAmbAndMusic(); if (currentAmbAndMusic != null) { return currentAmbAndMusic.ambMus.isPlaying; } return false; } private static AmbAndMusic? GetCurrentAmbAndMusic() { Audio2dManager audio2dManager = Audio2dPlugin.Audio2dManager; int curMus = audio2dManager._curMus; if (curMus <= 0 || curMus >= ((Il2CppArrayBase)(object)audio2dManager.ambAndMusics).Length) { return null; } return ((Il2CppArrayBase)(object)audio2dManager.ambAndMusics)[curMus]; } } } namespace MashGamemodeLibrary.Audio.Players.Background.Timed { public class TimedComponentPlayer : IContinuousPlayer, IAudioPlayer, IUpdating, IStoppable where T : class, IComponent { private readonly Dictionary _entityTimers; private readonly float _maxTimeBetweenPlays; private readonly float _minTimeBetweenPlays; private readonly IRandomAudioPlayer _player; public bool IsActive { get; private set; } public TimedComponentPlayer(IRandomAudioPlayer player, float minTimeBetweenPlays, float? maxTimeBetweenPlays = null) { _player = player; _minTimeBetweenPlays = minTimeBetweenPlays; _maxTimeBetweenPlays = maxTimeBetweenPlays ?? minTimeBetweenPlays; _entityTimers = new Dictionary(); } public void Start() { if (!IsActive) { IsActive = true; _entityTimers.Clear(); } } public void Stop() { if (IsActive) { IsActive = false; _player.Stop(); } } public void Update(float delta) { if (!IsActive) { return; } _player.Update(delta); List list = EcsManager.GetAllAssociated(typeof(T)).ToList(); foreach (var (networkEntityAssociation2, _) in _entityTimers) { if (!list.Contains(networkEntityAssociation2)) { _entityTimers.Remove(networkEntityAssociation2); } } NetworkEntity parameter = default(NetworkEntity); foreach (NetworkEntityAssociation item in list) { if (!_entityTimers.ContainsKey(item)) { _entityTimers.Add(item, GetRandomTimeBetweenPlays()); break; } _entityTimers[item] = Math.Max(0f, _entityTimers[item] - delta); if (!(_entityTimers[item] > 0f)) { _entityTimers[item] = GetRandomTimeBetweenPlays(); if (!((NetworkEntityReference)(ref item.NetworkID)).TryGetEntity(ref parameter)) { _entityTimers.Remove(item); break; } _player.PlayRandom(parameter); } } } private float GetRandomTimeBetweenPlays() { if (Math.Abs(_minTimeBetweenPlays - _maxTimeBetweenPlays) < 1f) { return _minTimeBetweenPlays; } return Random.Range(_minTimeBetweenPlays, _maxTimeBetweenPlays); } } public class TimedPositionalPlayer : IContinuousPlayer, IAudioPlayer, IUpdating, IStoppable { private readonly float _maxTimeBetweenPlays; private readonly float _minTimeBetweenPlays; private readonly IParameterDriven _player; private readonly Func _positionProvider; private float _delayTimer; public bool IsActive { get; private set; } public TimedPositionalPlayer(IParameterDriven player, Func positionProvider, float minTimeBetweenPlays, float? maxTimeBetweenPlays = null) { _player = player; _positionProvider = positionProvider; _minTimeBetweenPlays = minTimeBetweenPlays; _maxTimeBetweenPlays = maxTimeBetweenPlays ?? minTimeBetweenPlays; } public void Start() { if (!IsActive) { IsActive = true; _delayTimer = GetRandomTimeBetweenPlays(); } } public void Stop() { if (IsActive) { IsActive = false; _player.Stop(); } } public void Update(float delta) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (IsActive) { _player.Update(delta); if (_delayTimer > 0f) { _delayTimer -= delta; return; } _delayTimer = GetRandomTimeBetweenPlays(); Vector3 parameter = _positionProvider(); _player.PlayRandom(parameter); } } private float GetRandomTimeBetweenPlays() { if (Math.Abs(_minTimeBetweenPlays - _maxTimeBetweenPlays) < 1f) { return _minTimeBetweenPlays; } return Random.Range(_minTimeBetweenPlays, _maxTimeBetweenPlays); } } } namespace MashGamemodeLibrary.Audio.Music { public interface IMusicPack { List LoadTracks(); } public class MusicPack : IMusicPack { private readonly List _barcodes; public MusicPack(List barcodes) { _barcodes = barcodes; } public MusicPack(IEnumerable barcodes) { _barcodes = barcodes.ToList(); } public MusicPack(IEnumerable barcodes) { _barcodes = barcodes.Select((MonoDiscReference disk) => ((ScannableReference)disk)._barcode._id).ToList(); } public List LoadTracks() { return _barcodes; } } public enum MusicPackTags { Combat, Ambient, Intermission } public static class MusicPackManager { private static readonly Dictionary AudioBins = new Dictionary { { MusicPackTags.Combat, AudioRegistry.CreateBin("MashTags.MusicPack.Combat", FusionMonoDiscReferences.CombatSongReferences.Select((MonoDiscReference s) => ((ScannableReference)s)._barcode._id).Union(new string[6] { "SLZ.BONELAB.Content.MonoDisc.LavaGangLoop", "SLZ.BONELAB.Content.MonoDisc.FistFightFugue", "SLZ.BONELAB.Content.MonoDisc.Bonestep", "SLZ.BONELAB.Content.MonoDisc.BackThroughTheLab", "SLZ.BONELAB.Content.MonoDisc.HallVortex", "SLZ.BONELAB.Content.MonoDisc.RipToBits" }).ToArray()) }, { MusicPackTags.Ambient, AudioRegistry.CreateBin("MashTags.MusicPack.Ambient", ((ScannableReference)FusionMonoDiscReferences.FistfightFusionReference)._barcode._id, "SLZ.BONELAB.Content.MonoDisc.MirrorAlive", "SLZ.BONELAB.Content.MonoDisc.UnsolvedSciences", "SLZ.BONELAB.Content.MonoDisc.EverydayCastle", "SLZ.BONELAB.Content.MonoDisc.BonelightSonata") }, { MusicPackTags.Intermission, AudioRegistry.CreateBin("MashTags.MusicPack.Intermission", "SLZ.BONELAB.Content.MonoDisc.EverydayCastle") } }; public static IReadOnlyList GetTracks(MusicPackTags tag) { return AudioBins.GetValueOrDefault(tag)?.GetAll() ?? Array.Empty(); } } public record MusicPalletPack(Pallet Pallet) : IMusicPack { private static readonly Type MonoDiscType = Il2CppType.Of(); public List LoadTracks() { return (from p in (IEnumerable)Pallet.DataCards.ToArray() where ((Object)p).GetIl2CppType() == MonoDiscType select ((Scannable)p)._barcode._id).ToList(); } [CompilerGenerated] protected MusicPalletPack(MusicPalletPack original) { Pallet = original.Pallet; } } } namespace MashGamemodeLibrary.Audio.Modifiers { public class AudioModifierFactory { public delegate T ModifierDelegate(T source) where T : IAudioModifier; private delegate IAudioModifier ModifierDelegateInternal(); private readonly List _registeredModifiers = new List(); public AudioModifierFactory AddModifier(ModifierDelegate? factory = null) where T : IAudioModifier, new() { ModifierDelegate factory2 = factory; _registeredModifiers.Add(delegate { T val = new T(); return (factory2 == null) ? val : factory2(val); }); return this; } public HashSet Build() { return _registeredModifiers.Select((ModifierDelegateInternal func) => func()).ToHashSet(); } } public class AudioSettingsModifier : IAudioModifier { private AnimationCurve? _customRolloff; private bool _loop; private float _maxDistance = 120f; private float _spatialBlend = 1f; private float _volume = 1f; public void OnStart(ref AudioSource source) { source.volume = _volume; source.maxDistance = _maxDistance; source.spatialBlend = _spatialBlend; source.loop = _loop; if (_customRolloff != null) { source.SetCustomCurve((AudioSourceCurveType)0, _customRolloff); } } public AudioSettingsModifier SetVolume(float volume) { _volume = volume; return this; } public AudioSettingsModifier SetMaxDistance(float maxDistance) { _maxDistance = maxDistance; return this; } public AudioSettingsModifier SetSpatialBlend(float spatialBlend) { _spatialBlend = spatialBlend; return this; } public AudioSettingsModifier SetLoop(bool loop) { _loop = loop; return this; } public AudioSettingsModifier SetCustomRolloff(AnimationCurve curve) { _customRolloff = curve; return this; } } public interface IAudioModifier { void OnStart(ref AudioSource source); void Update(ref AudioSource source, float delta) { } } internal struct RayPassResult { public bool HitWall; public Vector3 WallEndPosition; public float WallThickness; } public class MuffleAudioModifier : IAudioModifier { private const int BaseCutoffFrequency = 5000; private const int MaxRayPasses = 2; private static readonly LayerMask RaycastLayerMask = LayerMask.op_Implicit(-261); private AudioLowPassFilter _filter; public void OnStart(ref AudioSource source) { _filter = ((Component)((Component)source).transform).GetComponent(); if ((Object)(object)_filter == (Object)null) { _filter = ((Component)((Component)source).transform).gameObject.AddComponent(); } _filter.cutoffFrequency = 5000f; _filter.lowpassResonanceQ = 1f; } public void Update(ref AudioSource source, float delta) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) //IL_0044: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; Vector3? val = ((main != null) ? new Vector3?(((Component)main).transform.position) : null); if (!val.HasValue) { return; } Vector3 position = ((Component)source).transform.position; Vector3 val2 = val.Value - position; if (!(((Vector3)(ref val2)).magnitude > source.maxDistance)) { Vector3 normalized = ((Vector3)(ref val2)).normalized; RayPassResult rayPassResult = PerformRayPass(val.Value, position + normalized, 2); if (!rayPassResult.HitWall) { ((Behaviour)_filter).enabled = false; return; } ((Behaviour)_filter).enabled = true; float wallThickness = rayPassResult.WallThickness; float num = 5000f / Mathf.Pow(wallThickness * 0.1f, 2f); num = Mathf.Clamp(num, 1500f, 5000f); _filter.cutoffFrequency = num; } } private RayPassResult PerformRayPass(Vector3 origin, Vector3 target, int passIndex) { //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_0002: 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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0018: 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) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target - origin; float magnitude = ((Vector3)(ref val)).magnitude; Vector3 val2 = val / magnitude; RaycastHit val3 = default(RaycastHit); RayPassResult result; if (!Physics.Raycast(new Ray(origin, val2), ref val3, magnitude, LayerMask.op_Implicit(RaycastLayerMask))) { result = default(RayPassResult); result.HitWall = false; result.WallEndPosition = target; result.WallThickness = 0f; return result; } Ray val4 = default(Ray); ((Ray)(ref val4))..ctor(target, -val2); RaycastHit val5 = default(RaycastHit); if (!((RaycastHit)(ref val3)).collider.Raycast(val4, ref val5, magnitude)) { result = default(RayPassResult); result.HitWall = true; result.WallEndPosition = target; result.WallThickness = magnitude; return result; } Vector3 val6 = ((RaycastHit)(ref val5)).point - ((RaycastHit)(ref val3)).point; float magnitude2 = ((Vector3)(ref val6)).magnitude; if (passIndex <= 0) { result = default(RayPassResult); result.HitWall = true; result.WallEndPosition = ((RaycastHit)(ref val5)).point; result.WallThickness = magnitude2; return result; } RayPassResult rayPassResult = PerformRayPass(((RaycastHit)(ref val5)).point + val2 * 0.01f, target, passIndex - 1); result = default(RayPassResult); result.HitWall = true; result.WallEndPosition = rayPassResult.WallEndPosition; result.WallThickness = magnitude2 + rayPassResult.WallThickness; return result; } } } namespace MashGamemodeLibrary.Audio.Loaders { public class AudioBinLoader : IAudioLoader { private readonly AudioBin _audioBin; public IReadOnlyList AudioNames => _audioBin.GetAll(); public bool IsLoading { get; private set; } public AudioBinLoader(AudioBin audioBin) { _audioBin = audioBin; } public void RefreshNames() { } public void Load(string name, Action onLoaded) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Action onLoaded2 = onLoaded; IsLoading = true; MonoDiscReference val = new MonoDiscReference(name); AudioReference val2 = default(AudioReference); ((AudioReference)(ref val2))..ctor(val); ((AudioReference)(ref val2)).LoadClip((Action)delegate(AudioClip audioClip) { IsLoading = false; onLoaded2(audioClip); }); } } public class AudioFileLoader : IAudioLoader { private static readonly string RootPath = Path.Combine(MelonEnvironment.UserDataDirectory, "Audio"); private readonly string _audioDirectoryPath; private readonly Dictionary _nameToPath = new Dictionary(); public IReadOnlyList AudioNames => _nameToPath.Keys.ToList(); public bool IsLoading { get; private set; } public AudioFileLoader(string subdirectory) { _audioDirectoryPath = Path.Combine(RootPath, subdirectory); if (!Directory.Exists(_audioDirectoryPath)) { Directory.CreateDirectory(_audioDirectoryPath); } RefreshNames(); } public void RefreshNames() { string[] files = Directory.GetFiles(_audioDirectoryPath); foreach (string text in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); _nameToPath.TryAdd(fileNameWithoutExtension, text); } } public void Load(string name, Action onLoaded) { IsLoading = true; if (!_nameToPath.TryGetValue(name, out string value)) { IsLoading = false; onLoaded(null); } else { AudioClip obj = API.LoadAudioClip(value, true); onLoaded(obj); } } } public interface IAudioLoader { IReadOnlyList AudioNames { get; } bool IsLoading { get; } void RefreshNames(); void Load(string name, Action onLoaded); } public class MonoDiscLoader : IAudioLoader { public IReadOnlyList AudioNames { get; } public bool IsLoading { get; private set; } public MonoDiscLoader(AudioBin audioBin) { AudioNames = audioBin.GetAll(); } public MonoDiscLoader(IEnumerable monoDiscs) { AudioNames = monoDiscs.ToList(); } public MonoDiscLoader(IEnumerable monoDiscs) { AudioNames = monoDiscs.Select((MonoDiscReference v) => ((ScannableReference)v)._barcode.ID).ToList(); } public void RefreshNames() { } public void Load(string name, Action onLoaded) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Action onLoaded2 = onLoaded; IsLoading = true; MonoDiscReference val = new MonoDiscReference(name); AudioReference val2 = default(AudioReference); ((AudioReference)(ref val2))..ctor(val); ((AudioReference)(ref val2)).LoadClip((Action)delegate(AudioClip audioClip) { IsLoading = false; onLoaded2(audioClip); }); } } public class MusicPackLoader : IAudioLoader { public IReadOnlyList AudioNames { get; } public bool IsLoading { get; private set; } public MusicPackLoader(MusicPackTags tag) { AudioNames = MusicPackManager.GetTracks(tag); } public void RefreshNames() { } public void Load(string name, Action onLoaded) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Action onLoaded2 = onLoaded; IsLoading = true; MonoDiscReference val = new MonoDiscReference(name); AudioReference val2 = default(AudioReference); ((AudioReference)(ref val2))..ctor(val); ((AudioReference)(ref val2)).LoadClip((Action)delegate(AudioClip audioClip) { IsLoading = false; onLoaded2(audioClip); }); } } } namespace MashGamemodeLibrary.Audio.Containers { public class DesyncedAudioContainer : ISyncedAudioContainer, IAudioContainer { private readonly IAudioContainer _parent; public IReadOnlyList AudioNames => _parent.AudioNames; public bool IsLoading => _parent.IsLoading; public List AudioHashes { get; } = new List { 1uL }; public DesyncedAudioContainer(IAudioContainer parent) { _parent = parent; } public void RequestClip(string name, Action onClipReady) { _parent.RequestClip(name, onClipReady); } public ulong? GetAudioHash(string name) { return 1uL; } public void RequestClip(ulong hash, Action onClipReady) { string random = IEnumerableExtensions.GetRandom((IEnumerable)AudioNames); RequestClip(random, onClipReady); } } public interface IAudioContainer { IReadOnlyList AudioNames { get; } bool IsLoading { get; } void RequestClip(string name, Action onClipReady); } public interface ISyncedAudioContainer : IAudioContainer { List AudioHashes { get; } ulong? GetAudioHash(string name); void RequestClip(ulong hash, Action onClipReady); } public class LoadOnDemandContainer : IAudioContainer { private readonly IAudioLoader _loader; public IReadOnlyList AudioNames => _loader.AudioNames; public bool IsLoading => _loader.IsLoading; public LoadOnDemandContainer(IAudioLoader loader) { _loader = loader; } public void RequestClip(string name, Action onClipReady) { _loader.Load(name, onClipReady); } } public class PreloadAudioContainer : IAudioContainer { private readonly Dictionary _clips = new Dictionary(); private readonly IAudioLoader _loader; public IReadOnlyList AudioNames => _loader.AudioNames; public bool IsLoading { get; private set; } public PreloadAudioContainer(IAudioLoader loader) { _loader = loader; PreloadAll(); } public void RequestClip(string name, Action onClipReady) { if (IsLoading) { InternalLogger.Warn("AudioContainer is not fully loaded, clips may be missing."); } onClipReady(_clips.GetValueOrDefault(name)); } public void PreloadAll() { IsLoading = true; foreach (AudioClip value in _clips.Values) { value.UnloadAudioData(); Object.Destroy((Object)(object)value); } _clips.Clear(); int toLoad = AudioNames.Count; foreach (string name in AudioNames) { _loader.Load(name, delegate(AudioClip? audioClip) { toLoad--; if (!Object.op_Implicit((Object)(object)audioClip)) { MelonLogger.Error("Failed to preload audio clip: " + name); } else { _clips[name] = audioClip; if (toLoad <= 0) { IsLoading = false; } } }); } } } public class SyncedAudioContainer : ISyncedAudioContainer, IAudioContainer { private readonly Dictionary _clipCache = new Dictionary(); private readonly Dictionary _hashToName = new Dictionary(); private readonly IAudioLoader _loader; private readonly Dictionary _nameToHash = new Dictionary(); public IReadOnlyList AudioNames => _loader.AudioNames; public List AudioHashes => _clipCache.Keys.ToList(); public bool IsLoading { get; private set; } public SyncedAudioContainer(IAudioLoader loader) { _loader = loader; PreloadAll(); } public ulong? GetAudioHash(string name) { CheckHashes(); return _nameToHash.GetValueOrDefault(name); } public void RequestClip(string name, Action onClipReady) { if (!GetAudioHash(name).HasValue) { onClipReady(null); } } public void RequestClip(ulong hash, Action onClipReady) { Action onClipReady2 = onClipReady; if (_clipCache.TryGetValue(hash, out AudioClip value)) { onClipReady2(value); } string name = _hashToName[hash]; _loader.Load(name, delegate(AudioClip? clip) { _clipCache[hash] = clip; onClipReady2(clip); }); } private void CheckHashes() { if (_nameToHash.Count != AudioNames.Count) { CacheHashes(); } } private void CacheHashes() { foreach (string audioName in _loader.AudioNames) { ulong stableHash = audioName.GetStableHash(); _nameToHash[audioName] = stableHash; _hashToName[stableHash] = audioName; } } private void PreloadAll() { IsLoading = true; foreach (AudioClip value in _clipCache.Values) { if (!((Object)(object)value == (Object)null)) { value.UnloadAudioData(); Object.Destroy((Object)(object)value); } } _clipCache.Clear(); CacheHashes(); } } }