using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ChaosSuite.Core; using GameNetcodeStuff; using HarmonyLib; using LethalLib.Modules; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ChaosSuite.Runtime")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a3464fa3098fa6be253d588a5b7ca9ba01bef4dd")] [assembly: AssemblyProduct("ChaosSuite.Runtime")] [assembly: AssemblyTitle("ChaosSuite.Runtime")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ChaosSuite.Runtime { public static class BundlePathResolver { public static string Resolve(string rootDirectory, string relativePath) { if (string.IsNullOrWhiteSpace(rootDirectory)) { throw new ArgumentException("A bundle root directory is required.", "rootDirectory"); } if (string.IsNullOrWhiteSpace(relativePath)) { throw new ArgumentException("A relative bundle path is required.", "relativePath"); } if (Path.IsPathRooted(relativePath)) { throw new ArgumentException("Bundle paths must be relative to the plugin directory.", "relativePath"); } string fullPath = Path.GetFullPath(rootDirectory); string fullPath2 = Path.GetFullPath(Path.Combine(fullPath, relativePath)); string value = (fullPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) ? fullPath : (fullPath + Path.DirectorySeparatorChar)); if (!fullPath2.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Bundle path escapes the plugin directory.", "relativePath"); } return fullPath2; } } public static class ChaosAssetPaths { public static string BundleName(string moduleName) { return "chaossuite_" + ValidateModuleName(moduleName).ToLowerInvariant(); } public static string VisualPrefab(string moduleName) { string text = ValidateModuleName(moduleName); return "Assets/ChaosSuite/Generated/" + text + "/" + text + "Visual.prefab"; } public static string EnemyType(string moduleName) { string text = ValidateModuleName(moduleName); return "Assets/ChaosSuite/Generated/" + text + "/" + text + "EnemyType.asset"; } public static string ItemDefinition(string moduleName) { string text = ValidateModuleName(moduleName); return "Assets/ChaosSuite/Generated/" + text + "/" + text + "Item.asset"; } public static string ItemDefinition(string moduleName, string itemName) { string text = ValidateModuleName(moduleName); string text2 = ValidateModuleName(itemName); return "Assets/ChaosSuite/Generated/" + text + "/" + text2 + ".asset"; } public static string NetworkPrefab(string moduleName, string prefabName) { string text = ValidateModuleName(moduleName); string text2 = ValidateModuleName(prefabName); return "Assets/ChaosSuite/Generated/" + text + "/" + text2 + ".prefab"; } public static string AudioFolder(string moduleName) { string text = ValidateModuleName(moduleName); return "Assets/ChaosSuite/Generated/Audio/" + text + "/"; } public static string AudioClip(string moduleName, string wavFileName) { if (string.IsNullOrWhiteSpace(wavFileName) || !string.Equals(Path.GetFileName(wavFileName), wavFileName, StringComparison.Ordinal) || !string.Equals(Path.GetExtension(wavFileName), ".wav", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("An audio clip must be a simple .wav file name.", "wavFileName"); } return AudioFolder(moduleName) + wavFileName; } private static string ValidateModuleName(string moduleName) { if (string.IsNullOrWhiteSpace(moduleName)) { throw new ArgumentException("A module name is required.", "moduleName"); } foreach (char c in moduleName) { if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) { throw new ArgumentException("Module names may contain only ASCII letters and digits.", "moduleName"); } } return moduleName; } } public sealed class AssetBundleRegistry { private readonly record struct BundleRecord(string Path, AssetBundle Bundle); private readonly record struct AssetKey(string BundleId, string AssetName, Type Type); private readonly string rootDirectory; private readonly ManualLogSource log; private readonly Dictionary bundles = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary assets = new Dictionary(); public IReadOnlyCollection LoadedBundleIds => bundles.Keys; internal AssetBundleRegistry(string rootDirectory, ManualLogSource log) { this.rootDirectory = Path.GetFullPath(rootDirectory ?? throw new ArgumentNullException("rootDirectory")); this.log = log ?? throw new ArgumentNullException("log"); } public bool TryLoadBundle(string bundleId, string relativePath, out AssetBundle? bundle) { string path = BundlePathResolver.Resolve(rootDirectory, relativePath); return TryLoadBundleAtPath(bundleId, path, out bundle); } public bool TryLoadModuleBundle(string moduleName, Assembly featureAssembly, out AssetBundle? bundle) { if ((object)featureAssembly == null) { throw new ArgumentNullException("featureAssembly"); } if (string.IsNullOrWhiteSpace(featureAssembly.Location)) { throw new ArgumentException("The feature assembly must have an installed file location.", "featureAssembly"); } string text = ChaosAssetPaths.BundleName(moduleName); string path = BundlePathResolver.Resolve(Path.GetDirectoryName(Path.GetFullPath(featureAssembly.Location)), text); return TryLoadBundleAtPath(text, path, out bundle); } private bool TryLoadBundleAtPath(string bundleId, string path, out AssetBundle? bundle) { ValidateBundleId(bundleId); if (bundles.TryGetValue(bundleId, out var value)) { if (!string.Equals(value.Path, path, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Bundle id '" + bundleId + "' is already registered from another path."); } bundle = value.Bundle; return Object.op_Implicit((Object)(object)bundle); } if (!File.Exists(path)) { log.LogError((object)("Asset bundle '" + bundleId + "' was not found at '" + path + "'.")); bundle = null; return false; } try { bundle = AssetBundle.LoadFromFile(path); } catch (Exception ex) { log.LogError((object)("Failed to read asset bundle '" + bundleId + "' at '" + path + "': " + ex.Message)); bundle = null; return false; } if (!Object.op_Implicit((Object)(object)bundle)) { log.LogError((object)("Unity rejected asset bundle '" + bundleId + "' at '" + path + "'. Check the Unity editor version and target platform.")); bundle = null; return false; } bundles.Add(bundleId, new BundleRecord(path, bundle)); log.LogInfo((object)("Loaded asset bundle '" + bundleId + "' from '" + path + "'.")); return true; } public bool TryGetBundle(string bundleId, out AssetBundle? bundle) { if (bundles.TryGetValue(bundleId, out var value) && Object.op_Implicit((Object)(object)value.Bundle)) { bundle = value.Bundle; return true; } bundle = null; return false; } public bool TryLoadAsset(string bundleId, string assetName, out T? asset) where T : Object { return TryLoadAsset(bundleId, assetName, logMissing: true, out asset); } public bool TryLoadAssetIfPresent(string bundleId, string assetName, out T? asset) where T : Object { return TryLoadAsset(bundleId, assetName, logMissing: false, out asset); } private bool TryLoadAsset(string bundleId, string assetName, bool logMissing, out T? asset) where T : Object { if (string.IsNullOrWhiteSpace(assetName)) { throw new ArgumentException("An asset name is required.", "assetName"); } AssetKey key = new AssetKey(bundleId, assetName, typeof(T)); if (assets.TryGetValue(key, out Object value) && Object.op_Implicit(value)) { asset = (T)(object)((value is T) ? value : null); return Object.op_Implicit((Object)(object)asset); } if (!TryGetBundle(bundleId, out AssetBundle bundle)) { asset = default(T); return false; } asset = bundle.LoadAsset(assetName); if (!Object.op_Implicit((Object)(object)asset)) { if (logMissing) { log.LogError((object)("Asset '" + assetName + "' (" + typeof(T).Name + ") was not found in bundle '" + bundleId + "'.")); } asset = default(T); return false; } assets[key] = (Object)(object)asset; return true; } public bool UnloadBundle(string bundleId, bool unloadLoadedObjects) { if (!bundles.Remove(bundleId, out var value)) { return false; } List list = new List(); foreach (AssetKey key in assets.Keys) { if (string.Equals(key.BundleId, bundleId, StringComparison.OrdinalIgnoreCase)) { list.Add(key); } } foreach (AssetKey item in list) { assets.Remove(item); } if (Object.op_Implicit((Object)(object)value.Bundle)) { value.Bundle.Unload(unloadLoadedObjects); } return true; } public void UnloadAll(bool unloadLoadedObjects) { foreach (BundleRecord value in bundles.Values) { if (Object.op_Implicit((Object)(object)value.Bundle)) { value.Bundle.Unload(unloadLoadedObjects); } } assets.Clear(); bundles.Clear(); } private static void ValidateBundleId(string bundleId) { if (string.IsNullOrWhiteSpace(bundleId)) { throw new ArgumentException("A bundle id is required.", "bundleId"); } foreach (char c in bundleId) { bool flag = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); if (!flag) { bool flag2 = ((c == '-' || c == '.' || c == '_') ? true : false); flag = flag2; } if (!flag) { throw new ArgumentException("Bundle ids may contain only ASCII letters, digits, periods, underscores, and hyphens.", "bundleId"); } } } } public sealed class HostRequestValidator { private readonly Func isServer; private readonly Func isConnected; private readonly Func controlsEntity; private readonly MonotonicActionGuard sequences = new MonotonicActionGuard(); public HostRequestValidator(Func isServer, Func isConnected, Func controlsEntity) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown this.isServer = isServer ?? throw new ArgumentNullException("isServer"); this.isConnected = isConnected ?? throw new ArgumentNullException("isConnected"); this.controlsEntity = controlsEntity ?? throw new ArgumentNullException("controlsEntity"); } public ActionReceipt Validate(ulong senderId, EntityId entity, ulong sequence) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0052: 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_0045: 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) //IL_0061: 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_0085: 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_007d: Unknown result type (might be due to invalid IL or missing references) if (!isServer()) { return ActionReceipt.Reject(entity, sequence, "not-host"); } if (!((EntityId)(ref entity)).IsValid) { return ActionReceipt.Reject(entity, sequence, "invalid-entity"); } if (!isConnected(senderId)) { return ActionReceipt.Reject(entity, sequence, "sender-not-connected"); } if (!controlsEntity(senderId, entity)) { return ActionReceipt.Reject(entity, sequence, "sender-does-not-control-entity"); } if (!sequences.TryAccept(senderId, sequence)) { return ActionReceipt.Reject(entity, sequence, "replayed-sequence"); } return ActionReceipt.Accept(entity, sequence); } public void ForgetSender(ulong senderId) { sequences.Forget(senderId); } } public static class NetworkAuthority { public static bool IsHostAuthority { get { if (Object.op_Implicit((Object)(object)NetworkManager.Singleton)) { return NetworkManager.Singleton.IsServer; } return false; } } public static bool IsConnectedClient(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && Object.op_Implicit((Object)(object)singleton) && singleton.IsServer) { return singleton.ConnectedClients.ContainsKey(clientId); } return false; } public static bool ClientOwns(ulong clientId, NetworkObject? target) { if (target != null && Object.op_Implicit((Object)(object)target) && target.IsSpawned) { return target.OwnerClientId == clientId; } return false; } } public sealed class NoiseService { private readonly ManualLogSource log; private readonly RateLimiter limiter = new RateLimiter(); internal NoiseService(ManualLogSource log) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown this.log = log; } public bool TryEmit(EntityId emitter, Vector3 position, float range, float loudness, int noiseId, double now, double minimumInterval, bool insideClosedShip = false) { //IL_0024: 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_008f: Unknown result type (might be due to invalid IL or missing references) if (!NetworkAuthority.IsHostAuthority || !Object.op_Implicit((Object)(object)RoundManager.Instance)) { return false; } if (!((EntityId)(ref emitter)).IsValid || !limiter.TryAcquire(emitter, now, Math.Max(0.0, minimumInterval))) { return false; } RoundManager.Instance.PlayAudibleNoise(position, Mathf.Clamp(range, 0f, 200f), Mathf.Clamp(loudness, 0f, 1f), 1, insideClosedShip, noiseId); log.LogDebug((object)$"Noise {noiseId} emitted by {((EntityId)(ref emitter)).Value} at {position}."); return true; } public void Forget(EntityId emitter) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) limiter.Clear(emitter); } public void Clear() { limiter.ClearAll(); } } public sealed class SystemicThreatBudgetService { private readonly SystemicThreatBudget budget = new SystemicThreatBudget(); private readonly HashSet trackedEntities = new HashSet(); private readonly EffectCleanupRegistry cleanup; private readonly ManualLogSource log; internal SystemicThreatBudgetService(EffectCleanupRegistry cleanup, ManualLogSource log) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown this.cleanup = cleanup; this.log = log; } internal void Configure(int restraintOrDisplacement, int roomScaleEnvironmental, int persistentCurse) { budget.SetCapacity((SystemicThreatKind)0, restraintOrDisplacement); budget.SetCapacity((SystemicThreatKind)1, roomScaleEnvironmental); budget.SetCapacity((SystemicThreatKind)2, persistentCurse); } public bool TryAcquire(NetworkObject target, SystemicThreatKind kind) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_004d: Unknown result type (might be due to invalid IL or missing references) if (!NetworkAuthority.IsHostAuthority || target == null || !Object.op_Implicit((Object)(object)target) || !target.IsSpawned) { return false; } EntityId val = default(EntityId); ((EntityId)(ref val))..ctor(target.NetworkObjectId); if (!budget.TryAcquire(kind, val)) { return false; } Track(target, val); log.LogDebug((object)$"Systemic threat lease acquired: {kind} by {((EntityId)(ref val)).Value}."); return true; } public bool TryTransfer(NetworkObject source, NetworkObject target, SystemicThreatKind kind) { //IL_0055: 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) //IL_0072: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0084: 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_00af: 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) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!NetworkAuthority.IsHostAuthority || (Object)(object)source == (Object)null || !Object.op_Implicit((Object)(object)source) || !source.IsSpawned || (Object)(object)target == (Object)null || !Object.op_Implicit((Object)(object)target) || !target.IsSpawned) { return false; } EntityId val = default(EntityId); ((EntityId)(ref val))..ctor(source.NetworkObjectId); EntityId val2 = default(EntityId); ((EntityId)(ref val2))..ctor(target.NetworkObjectId); if (val == val2) { return budget.TryAcquire(kind, val); } if (!budget.Release(kind, val)) { return false; } if (!budget.TryAcquire(kind, val2)) { budget.TryAcquire(kind, val); return false; } Track(target, val2); log.LogDebug((object)$"Systemic threat lease transferred: {kind} from {((EntityId)(ref val)).Value} to {((EntityId)(ref val2)).Value}."); return true; } public void Release(NetworkObject? target, SystemicThreatKind kind, string reason) { //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_0033: Unknown result type (might be due to invalid IL or missing references) if (target != null && Object.op_Implicit((Object)(object)target)) { EntityId val = default(EntityId); ((EntityId)(ref val))..ctor(target.NetworkObjectId); if (budget.Release(kind, val)) { log.LogDebug((object)$"Systemic threat lease released: {kind} by {((EntityId)(ref val)).Value} ({reason})."); } } } public void Clear() { budget.Clear(); trackedEntities.Clear(); } private void Track(NetworkObject target, EntityId id) { //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_002f: 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) if (trackedEntities.Add(id)) { cleanup.GetOrCreate(id, target.OwnerClientId).Register((Action)delegate { //IL_000c: 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) budget.Release(id); trackedEntities.Remove(id); }); (((Component)target).GetComponent() ?? ((Component)target).gameObject.AddComponent()).Track(cleanup, id); } } } public sealed class EffectCleanupRegistry { private sealed class Entry { internal ulong LifetimeOwner { get; private set; } internal PersistentCleanupCycle Cycle { get; } = new PersistentCleanupCycle(); internal Entry(ulong lifetimeOwner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown LifetimeOwner = lifetimeOwner; } internal void TrackLifetimeOwner(ulong owner) { if (LifetimeOwner == ulong.MaxValue && owner != ulong.MaxValue) { LifetimeOwner = owner; } } } private readonly ManualLogSource log; private readonly Dictionary effects = new Dictionary(); private readonly EffectOwnerLedger owners = new EffectOwnerLedger(); public int Count => effects.Count; internal EffectCleanupRegistry(ManualLogSource log) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown this.log = log; } public IdempotentCleanup GetOrCreate(EntityId id, ulong ownerClientId = ulong.MaxValue) { //IL_001f: 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) //IL_0063: 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) if (!((EntityId)(ref id)).IsValid) { throw new ArgumentException("A spawned network entity id is required.", "id"); } if (effects.TryGetValue(id, out Entry value)) { value.TrackLifetimeOwner(ownerClientId); owners.Track(id, ownerClientId); return value.Cycle.Current; } Entry entry = new Entry(ownerClientId); effects.Add(id, entry); owners.Track(id, ownerClientId); return entry.Cycle.Current; } public void Register(NetworkObject target, Action cleanup) { //IL_0034: 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_006e: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)target)) { throw new ArgumentNullException("target"); } if (!target.IsSpawned) { throw new InvalidOperationException("Cleanup can only be attached to a spawned NetworkObject."); } EntityId val = default(EntityId); ((EntityId)(ref val))..ctor(target.NetworkObjectId); GetOrCreate(val, target.OwnerClientId); effects[val].Cycle.RegisterPersistent(cleanup); (((Component)target).GetComponent() ?? ((Component)target).gameObject.AddComponent()).Track(this, val); } public bool AssociateAffectedOwner(EntityId effectId, ulong ownerClientId) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (effects.ContainsKey(effectId)) { return owners.Associate(effectId, ownerClientId); } return false; } public bool DisassociateAffectedOwner(EntityId effectId, ulong ownerClientId) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (effects.ContainsKey(effectId)) { return owners.Disassociate(effectId, ownerClientId); } return false; } public void Release(EntityId id, string reason) { //IL_0006: 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_001f: Unknown result type (might be due to invalid IL or missing references) if (effects.Remove(id, out Entry value)) { owners.Forget(id); RunCleanup(id, reason, (Action)value.Cycle.Complete); } } public void ReleaseOwner(ulong ownerClientId, string reason) { //IL_0023: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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) if (effects.Count == 0) { return; } foreach (EntityId item in owners.OwnedOrAffectedBy(ownerClientId)) { if (effects.TryGetValue(item, out Entry value)) { if (value.LifetimeOwner == ownerClientId) { Release(item, reason); } else { Reset(item, value, reason); } } } } public void ReleaseAffectedOwner(ulong ownerClientId, string reason) { //IL_0023: 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_002f: 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) if (effects.Count == 0) { return; } foreach (EntityId item in owners.AffectedBy(ownerClientId)) { if (effects.TryGetValue(item, out Entry value)) { Reset(item, value, reason); } } } public void Clear(string reason) { //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_002f: Unknown result type (might be due to invalid IL or missing references) if (effects.Count == 0) { return; } foreach (EntityId item in new List(effects.Keys)) { Release(item, reason); } } private void Reset(EntityId id, Entry entry, string reason) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) owners.Forget(id); owners.Track(id, entry.LifetimeOwner); RunCleanup(id, reason, (Action)entry.Cycle.Reset); } private void RunCleanup(EntityId id, string reason, Action cleanup) { try { cleanup(); } catch (AggregateException arg) { log.LogError((object)$"Cleanup failure for {((EntityId)(ref id)).Value} ({reason}): {arg}"); } } } public static class TeleportCleanupGuard { [ThreadStatic] private static int suppressionDepth; public static bool IsSuppressed => suppressionDepth > 0; public static void RunWithoutCleanup(Action teleport) { if (teleport == null) { throw new ArgumentNullException("teleport"); } suppressionDepth++; try { teleport(); } finally { suppressionDepth--; } } } internal sealed class EffectLifetime : MonoBehaviour { private EffectCleanupRegistry? registry; private EntityId entity; internal void Track(EffectCleanupRegistry owner, EntityId id) { //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_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) if (registry != null && (registry != owner || entity != id)) { throw new InvalidOperationException("An effect lifetime cannot track two network entities."); } registry = owner; entity = id; } private void OnDestroy() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) registry?.Release(entity, "network object despawned"); registry = null; } } public static class ChaosPresentation { private static readonly int Action = Animator.StringToHash("Action"); public static bool TriggerAction(Component source) { if (!Object.op_Implicit((Object)(object)source) || !source.gameObject.activeInHierarchy) { return false; } Animator[] componentsInChildren = source.GetComponentsInChildren(true); foreach (Animator val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val) && ((Component)val).gameObject.activeInHierarchy && !((Object)(object)val.runtimeAnimatorController == (Object)null)) { val.ResetTrigger(Action); val.SetTrigger(Action); return true; } } return false; } } [BepInPlugin("com.chaossuite.core", "Chaos Suite Core", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class ChaosSuiteRuntimePlugin : BaseUnityPlugin { public const string PluginGuid = "com.chaossuite.core"; public const string PluginName = "Chaos Suite Core"; public const string PluginVersion = "0.2.0"; private Harmony? harmony; public static ChaosSuiteRuntimePlugin? Instance { get; private set; } public NoiseService Noise { get; private set; } public EffectCleanupRegistry Cleanup { get; private set; } public SystemicThreatBudgetService ThreatBudget { get; private set; } public AssetBundleRegistry Assets { get; private set; } public SynchronizedConfigService SynchronizedConfig { get; private set; } public ChaosSuiteSettings Settings { get; private set; } public LethalLibContentRegistration Content { get; private set; } private void Awake() { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown Instance = this; Noise = new NoiseService(((BaseUnityPlugin)this).Logger); Cleanup = new EffectCleanupRegistry(((BaseUnityPlugin)this).Logger); Assets = new AssetBundleRegistry(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), ((BaseUnityPlugin)this).Logger); SynchronizedConfig = new SynchronizedConfigService(((BaseUnityPlugin)this).Logger); Settings = new ChaosSuiteSettings(((BaseUnityPlugin)this).Config, SynchronizedConfig); ThreatBudget = new SystemicThreatBudgetService(Cleanup, ((BaseUnityPlugin)this).Logger); ThreatBudget.Configure(Settings.MaximumRestraintOrDisplacement.Value, Settings.MaximumRoomScaleEnvironmental.Value, Settings.MaximumPersistentCurse.Value); Content = new LethalLibContentRegistration(Assets, Settings, ((BaseUnityPlugin)this).Logger); harmony = new Harmony("com.chaossuite.core"); int num = LifecyclePatches.Install(harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Chaos Suite Core loaded with {num} verified lifecycle hooks. Gameplay effects are host-authoritative and round scoped."); } public ContentRegistrationReport RegisterFeatureContent(string moduleName, Assembly featureAssembly) { ContentRegistrationReport result = Content.RegisterModuleContent(moduleName, featureAssembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Chaos Suite '{moduleName}' registration finished: {result.Registered} registered, {result.Skipped} skipped, {result.Failed} failed."); return result; } private void Update() { SynchronizedConfig?.Tick(); if (ThreatBudget != null && Settings != null) { ThreatBudget.Configure(Settings.MaximumRestraintOrDisplacement.Value, Settings.MaximumRoomScaleEnvironmental.Value, Settings.MaximumPersistentCurse.Value); } } private void OnDestroy() { SynchronizedConfig?.Dispose(); Noise?.Clear(); ThreatBudget?.Clear(); Cleanup?.Clear("plugin teardown"); Assets?.UnloadAll(unloadLoadedObjects: false); Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; Instance = null; } } public sealed class ChaosSuiteSettings { private static readonly string[] Modules = new string[8] { "MasklessMimic", "NewtonsApple", "JobApplication", "Professor", "Webhead", "HorrorBowler", "MonkeysPaw", "Relocator" }; private readonly Dictionary> enabled = new Dictionary>(StringComparer.Ordinal); private readonly Dictionary> rarity = new Dictionary>(StringComparer.Ordinal); public ConfigEntry Subtitles { get; } public ConfigEntry EffectsVolume { get; } public ConfigEntry ReducedMotion { get; } public ConfigEntry CameraIntensity { get; } internal SynchronizedConfigEntry MaximumRestraintOrDisplacement { get; } internal SynchronizedConfigEntry MaximumRoomScaleEnvironmental { get; } internal SynchronizedConfigEntry MaximumPersistentCurse { get; } internal ChaosSuiteSettings(ConfigFile config, SynchronizedConfigService synchronized) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Expected O, but got Unknown //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown string[] modules = Modules; foreach (string text in modules) { int num = DefaultRarity(text); enabled.Add(text, synchronized.Register(text + ".Enabled", config.Bind("Host Gameplay", text + " Enabled", true, "Host-authoritative enable state for " + text + "."))); rarity.Add(text, synchronized.Register(text + ".Rarity", config.Bind("Host Gameplay", text + " Rarity", num, new ConfigDescription("Host-authoritative natural spawn or scrap rarity for " + text + ".", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())))); } MaximumRestraintOrDisplacement = synchronized.Register("Suite.MaximumRestraintOrDisplacement", config.Bind("Host Gameplay", "Maximum Active Restraint Or Displacement", 1, new ConfigDescription("Global cap for simultaneous Webhead/Relocator control effects.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 8), Array.Empty()))); MaximumRoomScaleEnvironmental = synchronized.Register("Suite.MaximumRoomScaleEnvironmental", config.Bind("Host Gameplay", "Maximum Active Room Environmental", 1, new ConfigDescription("Global cap for simultaneous room-scale gravity/environment effects.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 8), Array.Empty()))); MaximumPersistentCurse = synchronized.Register("Suite.MaximumPersistentCurse", config.Bind("Host Gameplay", "Maximum Active Persistent Curse", 1, new ConfigDescription("Global cap for simultaneous strong player curse systems.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 8), Array.Empty()))); Subtitles = config.Bind("Local Accessibility", "Subtitles", true, "Show Chaos Suite textual voice cues where supported."); EffectsVolume = config.Bind("Local Accessibility", "Effects Volume", 1f, new ConfigDescription("Local Chaos Suite effects and voice volume multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ReducedMotion = config.Bind("Local Accessibility", "Reduced Motion", false, "Suppress optional camera motion and trails; gameplay state is unchanged."); CameraIntensity = config.Bind("Local Accessibility", "Camera Intensity", 1f, new ConfigDescription("Local optional camera effect intensity.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); } public bool IsEnabled(string module) { if (enabled.TryGetValue(module, out SynchronizedConfigEntry value)) { return value.Value; } return true; } public int Rarity(string module, int fallback) { if (!rarity.TryGetValue(module, out SynchronizedConfigEntry value)) { return Math.Clamp(fallback, 0, 100); } return Math.Clamp(value.Value, 0, 100); } private static int DefaultRarity(string module) { return module switch { "MasklessMimic" => 0, "NewtonsApple" => 18, "JobApplication" => 14, "Professor" => 12, "Webhead" => 15, "HorrorBowler" => 9, "MonkeysPaw" => 8, "Relocator" => 11, _ => 0, }; } } public enum LethalContentKind : byte { NaturalEnemy, ScrapItem, PlainItem, NetworkPrefab } public readonly record struct LethalContentDefinition(string ModuleName, string PluginGuid, LethalContentKind Kind, string AssetPath, int Rarity); public readonly record struct ContentRegistrationReport(int Registered, int Skipped, int Failed); public readonly record struct FallbackComponentDefinition(string ModuleName, string PrefabName, string TypeName); public sealed class LethalLibContentRegistration { private readonly AssetBundleRegistry assets; private readonly ChaosSuiteSettings settings; private readonly ManualLogSource log; private readonly HashSet registeredAssets = new HashSet(StringComparer.Ordinal); private readonly RuntimeContentFactory fallbackFactory; public LethalLibContentRegistration(AssetBundleRegistry assets, ChaosSuiteSettings settings, ManualLogSource log) { this.assets = assets ?? throw new ArgumentNullException("assets"); this.settings = settings ?? throw new ArgumentNullException("settings"); this.log = log ?? throw new ArgumentNullException("log"); fallbackFactory = new RuntimeContentFactory(assets, log); } public ContentRegistrationReport RegisterKnownInstalledContent() { Assembly assembly; return RegisterDefinitions(KnownLethalContent.Definitions, (LethalContentDefinition definition) => (!TryFindFeatureAssembly(definition.PluginGuid, out assembly)) ? null : assembly); } public ContentRegistrationReport RegisterModuleContent(string moduleName, Assembly featureAssembly) { if (string.IsNullOrWhiteSpace(moduleName)) { throw new ArgumentException("A module name is required.", "moduleName"); } if ((object)featureAssembly == null) { throw new ArgumentNullException("featureAssembly"); } return RegisterDefinitions(KnownLethalContent.Definitions.Where((LethalContentDefinition definition) => string.Equals(definition.ModuleName, moduleName, StringComparison.Ordinal)), (LethalContentDefinition _) => featureAssembly); } private ContentRegistrationReport RegisterDefinitions(IEnumerable definitions, Func resolveAssembly) { int num = 0; int num2 = 0; int num3 = 0; HashSet hashSet = new HashSet(StringComparer.Ordinal); HashSet hashSet2 = new HashSet(StringComparer.Ordinal); foreach (LethalContentDefinition definition in definitions) { if (!settings.IsEnabled(definition.ModuleName)) { num2++; continue; } if (registeredAssets.Contains(definition.AssetPath)) { num2++; continue; } Assembly assembly = resolveAssembly(definition); if ((object)assembly == null) { if (hashSet2.Add(definition.ModuleName)) { log.LogDebug((object)("Skipping '" + definition.ModuleName + "' content because plugin '" + definition.PluginGuid + "' is not installed.")); } num2++; continue; } if (hashSet2.Contains(definition.ModuleName)) { num3++; continue; } if (!hashSet.Contains(definition.ModuleName)) { if (!assets.TryLoadModuleBundle(definition.ModuleName, assembly, out AssetBundle _)) { hashSet2.Add(definition.ModuleName); num3++; continue; } hashSet.Add(definition.ModuleName); } try { if (!Register(definition, assembly)) { num3++; continue; } registeredAssets.Add(definition.AssetPath); num++; } catch (Exception ex) { log.LogError((object)$"Failed to register {definition.Kind} asset '{definition.AssetPath}' for '{definition.ModuleName}': {ex}"); num3++; } } return new ContentRegistrationReport(num, num2, num3); } private bool Register(LethalContentDefinition definition, Assembly featureAssembly) { string bundle = ChaosAssetPaths.BundleName(definition.ModuleName); switch (definition.Kind) { case LethalContentKind.NaturalEnemy: { if (!TryResolveAsset(bundle, definition, featureAssembly, out EnemyType asset2) || !Object.op_Implicit((Object)(object)asset2)) { return Missing(definition, "EnemyType"); } if (!ValidateEnemy(definition, asset2)) { return false; } RegisterNetworkPrefab(asset2.enemyPrefab, definition); int num = settings.Rarity(definition.ModuleName, definition.Rarity); Enemies.RegisterEnemy(asset2, num, (LevelTypes)(-1), (TerminalNode)null, (TerminalKeyword)null); log.LogInfo((object)$"Registered enemy '{asset2.enemyName}' from '{definition.ModuleName}' at rarity {num}."); return true; } case LethalContentKind.ScrapItem: { if (!TryResolveAsset(bundle, definition, featureAssembly, out Item asset3) || !Object.op_Implicit((Object)(object)asset3)) { return Missing(definition, "Item"); } if (!ValidateItem(definition, asset3)) { return false; } RegisterNetworkPrefab(asset3.spawnPrefab, definition); int num2 = settings.Rarity(definition.ModuleName, definition.Rarity); Items.RegisterScrap(asset3, num2, (LevelTypes)(-1)); log.LogInfo((object)$"Registered scrap '{asset3.itemName}' from '{definition.ModuleName}' at rarity {num2}."); return true; } case LethalContentKind.PlainItem: { if (!TryResolveAsset(bundle, definition, featureAssembly, out Item asset4) || !Object.op_Implicit((Object)(object)asset4)) { return Missing(definition, "Item"); } if (!ValidateItem(definition, asset4)) { return false; } RegisterNetworkPrefab(asset4.spawnPrefab, definition); Items.RegisterItem(asset4); log.LogInfo((object)("Registered non-random item '" + asset4.itemName + "' from '" + definition.ModuleName + "'.")); return true; } case LethalContentKind.NetworkPrefab: { if (!TryResolveAsset(bundle, definition, featureAssembly, out GameObject asset) || !Object.op_Implicit((Object)(object)asset)) { return Missing(definition, "GameObject"); } RegisterNetworkPrefab(asset, definition); log.LogInfo((object)("Registered supporting network prefab '" + ((Object)asset).name + "' from '" + definition.ModuleName + "'.")); return true; } default: throw new ArgumentOutOfRangeException("definition", definition.Kind, "Unsupported content kind."); } } private bool TryResolveAsset(string bundle, LethalContentDefinition definition, Assembly featureAssembly, out T? asset) where T : Object { if (assets.TryLoadAssetIfPresent(bundle, definition.AssetPath, out asset) && Object.op_Implicit((Object)(object)asset)) { return true; } if (fallbackFactory.TryCreate(definition, featureAssembly, out Object created)) { T val = (T)(object)((created is T) ? created : null); if (val != null && Object.op_Implicit((Object)(object)val)) { asset = val; log.LogInfo((object)("Using original in-memory definition for '" + definition.AssetPath + "'. It was built from the module visual prefab and component '" + ((object)val).GetType().Name + "'; no game asset was copied.")); return true; } } asset = default(T); return false; } private bool ValidateEnemy(LethalContentDefinition definition, EnemyType enemyType) { if (!Object.op_Implicit((Object)(object)enemyType.enemyPrefab)) { return Invalid(definition, "EnemyType.enemyPrefab is missing"); } if (!Object.op_Implicit((Object)(object)enemyType.enemyPrefab.GetComponentInChildren(true))) { return Invalid(definition, "enemy prefab has no EnemyAI component"); } return ValidateNetworkPrefab(definition, enemyType.enemyPrefab); } private bool ValidateItem(LethalContentDefinition definition, Item item) { if (!Object.op_Implicit((Object)(object)item.spawnPrefab)) { return Invalid(definition, "Item.spawnPrefab is missing"); } if (!Object.op_Implicit((Object)(object)item.spawnPrefab.GetComponentInChildren(true))) { return Invalid(definition, "item prefab has no GrabbableObject component"); } return ValidateNetworkPrefab(definition, item.spawnPrefab); } private bool ValidateNetworkPrefab(LethalContentDefinition definition, GameObject prefab) { if (!Object.op_Implicit((Object)(object)prefab.GetComponent())) { return Invalid(definition, "prefab '" + ((Object)prefab).name + "' has no root NetworkObject component"); } return true; } private void RegisterNetworkPrefab(GameObject prefab, LethalContentDefinition definition) { if (!ValidateNetworkPrefab(definition, prefab)) { throw new InvalidOperationException("Invalid network prefab '" + ((Object)prefab).name + "'."); } Utilities.FixMixerGroups(prefab); NetworkPrefabs.RegisterNetworkPrefab(prefab); } private bool Missing(LethalContentDefinition definition, string expectedType) { log.LogError((object)("Required " + expectedType + " asset '" + definition.AssetPath + "' is missing from bundle '" + ChaosAssetPaths.BundleName(definition.ModuleName) + "'; '" + definition.ModuleName + "' content was not registered.")); return false; } private bool Invalid(LethalContentDefinition definition, string reason) { log.LogError((object)("Asset '" + definition.AssetPath + "' for '" + definition.ModuleName + "' is invalid: " + reason + "; content was not registered.")); return false; } private static bool TryFindFeatureAssembly(string pluginGuid, out Assembly? assembly) { if (Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value) && Object.op_Implicit((Object)(object)value.Instance)) { assembly = ((object)value.Instance).GetType().Assembly; return true; } assembly = null; return false; } } internal sealed class RuntimeContentFactory { private readonly record struct EnemyRecipe(GameObject Prefab, EnemyType Type, EnemyAI Behaviour); private readonly record struct ItemRecipe(GameObject Prefab, Item Item, GrabbableObject Behaviour); private readonly AssetBundleRegistry assets; private readonly ManualLogSource log; private readonly Dictionary generated = new Dictionary(StringComparer.Ordinal); private readonly HashSet builtModules = new HashSet(StringComparer.Ordinal); internal RuntimeContentFactory(AssetBundleRegistry assets, ManualLogSource log) { this.assets = assets; this.log = log; } internal bool TryCreate(LethalContentDefinition definition, Assembly featureAssembly, out Object? created) { if (generated.TryGetValue(definition.AssetPath, out created) && Object.op_Implicit(created)) { return true; } if (!builtModules.Contains(definition.ModuleName)) { if (!TryLoadVisual(definition.ModuleName, out GameObject visual)) { created = null; return false; } try { BuildModule(definition.ModuleName, featureAssembly, visual); builtModules.Add(definition.ModuleName); log.LogInfo((object)("Built startup-only content definitions for '" + definition.ModuleName + "'. Templates are hidden and are not spawned by the factory.")); } catch (Exception arg) { log.LogError((object)$"Could not build fallback content for '{definition.ModuleName}': {arg}"); created = null; return false; } } if (generated.TryGetValue(definition.AssetPath, out created)) { return Object.op_Implicit(created); } return false; } private bool TryLoadVisual(string module, out GameObject? visual) { if (assets.TryLoadAsset(ChaosAssetPaths.BundleName(module), ChaosAssetPaths.VisualPrefab(module), out visual)) { return Object.op_Implicit((Object)(object)visual); } return false; } private void BuildModule(string module, Assembly assembly, GameObject visual) { //IL_0184: Unknown result type (might be due to invalid IL or missing references) switch (module) { case "NewtonsApple": { ItemRecipe itemRecipe3 = CreateItem("NewtonsApple", "BlackAppleCore", null, visual, 65, 110, 1.08f); Cache(ChaosAssetPaths.ItemDefinition(module, "BlackAppleCoreItem"), (Object)(object)itemRecipe3.Item); EnemyRecipe enemyRecipe2 = CreateEnemy(module, "NewtonsAppleEnemy", RequireType(assembly, "ChaosSuite.NewtonsApple.NewtonsAppleEnemy"), visual, 1.5f, 2); BoxCollider val = CreateChild(enemyRecipe2.Prefab.transform, "InfluenceVolume").AddComponent(); ((Collider)val).isTrigger = true; val.size = new Vector3(14f, 6f, 14f); SetField(enemyRecipe2.Behaviour, "influenceVolume", val); SetField(enemyRecipe2.Behaviour, "stem", FindChild(enemyRecipe2.Prefab.transform, "Stem") ?? enemyRecipe2.Prefab.transform); SetField(enemyRecipe2.Behaviour, "blackCorePrefab", itemRecipe3.Prefab); Cache(ChaosAssetPaths.EnemyType(module), (Object)(object)enemyRecipe2.Type); break; } case "JobApplication": { Type behaviourType = RequireType(assembly, "ChaosSuite.JobApplication.JobApplicationItem"); Type behaviourType2 = RequireType(assembly, "ChaosSuite.JobApplication.ApplicantEnemyAI"); Type behaviourType3 = RequireType(assembly, "ChaosSuite.JobApplication.PaperEmployeeAI"); ItemRecipe itemRecipe4 = CreateItem(module, "JobApplication", behaviourType, visual, 35, 65, 1.02f); EnemyRecipe enemyRecipe3 = CreateEnemy(module, "ApplicantEnemy", behaviourType2, visual, 1.2f, 1); EnemyRecipe enemyRecipe4 = CreateEnemy(module, "PaperEmployee", behaviourType3, visual, 0.6f, 3); SetField(itemRecipe4.Behaviour, "applicantEnemyPrefab", enemyRecipe3.Prefab); GameObject val2 = CreateChild(itemRecipe4.Prefab.transform, "ResumeSheet"); SetField(itemRecipe4.Behaviour, "resumeSheet", val2.transform); SetField(itemRecipe4.Behaviour, "announcementSource", AddSpatialAudio(itemRecipe4.Prefab)); GameObject val3 = CreatePaperCocoon(enemyRecipe3.Prefab.transform); val3.SetActive(false); SetField(enemyRecipe3.Behaviour, "paperEmployeePrefab", enemyRecipe4.Prefab); SetField(enemyRecipe3.Behaviour, "cocoonVisual", val3); SetField(enemyRecipe4.Behaviour, "applicationPrefab", itemRecipe4.Prefab); Cache(ChaosAssetPaths.ItemDefinition(module), (Object)(object)itemRecipe4.Item); Cache(ChaosAssetPaths.NetworkPrefab(module, "ApplicantEnemy"), (Object)(object)enemyRecipe3.Prefab); Cache(ChaosAssetPaths.NetworkPrefab(module, "PaperEmployee"), (Object)(object)enemyRecipe4.Prefab); break; } case "Professor": CacheEnemy(module, "ProfessorEnemy", "ChaosSuite.Professor.ProfessorEnemyAI", assembly, visual, 2f, 1); break; case "Webhead": CacheEnemy(module, "WebheadEnemy", "ChaosSuite.Webhead.WebheadEnemyAI", assembly, visual, 2f, 2); break; case "HorrorBowler": { EnemyRecipe enemyRecipe = CreateEnemy(module, "HorrorBowlerEnemy", RequireType(assembly, "ChaosSuite.HorrorBowler.HorrorBowlerEnemyAI"), visual, 3f, 1); Transform? obj = FindChild(enemyRecipe.Prefab.transform, "Boulder"); GameObject obj2 = ((obj != null) ? ((Component)obj).gameObject : null) ?? throw new InvalidOperationException("Horror Bowler fallback visual does not contain a Boulder child."); obj2.AddComponent().radius = 0.8f; Type type = RequireType(assembly, "ChaosSuite.HorrorBowler.BoulderController"); Component value = obj2.AddComponent(type); SetField(enemyRecipe.Behaviour, "boulder", value); Cache(ChaosAssetPaths.EnemyType(module), (Object)(object)enemyRecipe.Type); break; } case "MonkeysPaw": { ItemRecipe itemRecipe = CreateItem(module, "CursedFortune", null, visual, 90, 150, 1.05f); ItemRecipe itemRecipe2 = CreateItem(module, "MonkeysPaw", RequireType(assembly, "ChaosSuite.MonkeysPaw.MonkeysPawItem"), visual, 75, 135, 1.04f); SetField(itemRecipe2.Behaviour, "cursedFortunePrefab", itemRecipe.Prefab); SetField(itemRecipe2.Behaviour, "pawAudio", AddSpatialAudio(itemRecipe2.Prefab)); Cache(ChaosAssetPaths.ItemDefinition(module), (Object)(object)itemRecipe2.Item); Cache(ChaosAssetPaths.ItemDefinition(module, "CursedFortuneItem"), (Object)(object)itemRecipe.Item); break; } case "Relocator": CacheEnemy(module, "RelocatorEnemy", "ChaosSuite.Quagmire.RelocatorEnemyAI", assembly, visual, 2f, 1); break; default: throw new InvalidOperationException("No fallback content recipe exists for module '" + module + "'."); } } private void CacheEnemy(string module, string prefabName, string typeName, Assembly assembly, GameObject visual, float power, int maximum) { EnemyRecipe enemyRecipe = CreateEnemy(module, prefabName, RequireType(assembly, typeName), visual, power, maximum); Cache(ChaosAssetPaths.EnemyType(module), (Object)(object)enemyRecipe.Type); } private EnemyRecipe CreateEnemy(string module, string prefabName, Type behaviourType, GameObject visual, float power, int maximum) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Expected O, but got Unknown if (!typeof(EnemyAI).IsAssignableFrom(behaviourType)) { throw new InvalidOperationException("Fallback type '" + behaviourType.FullName + "' is not an EnemyAI."); } GameObject val = NetworkPrefabs.CreateNetworkPrefab("ChaosSuite_" + prefabName + "_Fallback"); NavMeshAgent val2 = val.AddComponent(); val2.radius = 0.45f; val2.height = 1.8f; val2.speed = 3.2f; val2.angularSpeed = 180f; val2.acceleration = 12f; CapsuleCollider obj = val.AddComponent(); obj.radius = 0.42f; obj.height = 1.8f; obj.center = new Vector3(0f, 0.9f, 0f); EnemyAI val3 = (EnemyAI)val.AddComponent(behaviourType); GameObject val4 = AttachVisual(val, visual, prefabName); Type type = Type.GetType("UnityEngine.Animator, UnityEngine.AnimationModule", throwOnError: true); Component value = val4.GetComponentInChildren(type, true) ?? val4.AddComponent(type); Component value2 = AddSpatialAudio(val); Component value3 = AddSpatialAudio(val); GameObject obj2 = CreateChild(val.transform, "EnemyCollisionDetector"); CapsuleCollider obj3 = obj2.AddComponent(); ((Collider)obj3).isTrigger = true; obj3.radius = 0.5f; obj3.height = 1.8f; EnemyAICollisionDetect obj4 = obj2.AddComponent(); obj4.mainScript = val3; obj4.canCollideWithEnemies = true; EnemyType val5 = ScriptableObject.CreateInstance(); ((Object)val5).name = prefabName + "EnemyTypeFallback"; ((Object)val5).hideFlags = (HideFlags)61; val5.enemyName = FriendlyName(module); val5.enemyPrefab = val; val5.PowerLevel = Mathf.Clamp(power, 0.5f, 10f); val5.MaxCount = Mathf.Clamp(maximum, 1, 8); val5.canDie = true; val5.canBeDestroyed = true; val5.destroyOnDeath = true; val5.canBeStunned = true; val5.stunTimeMultiplier = 1f; val5.doorSpeedMultiplier = 1f; val5.probabilityCurve = AnimationCurve.Linear(0f, 1f, 1f, 1f); val5.numberSpawnedFalloff = AnimationCurve.Linear(0f, 1f, 1f, 0.2f); val5.useNumberSpawnedFalloff = true; val5.spawnInGroupsOf = 1; val3.enemyType = val5; val3.agent = val2; SetField(val3, "creatureAnimator", value); SetField(val3, "creatureVoice", value2); SetField(val3, "creatureSFX", value3); val3.meshRenderers = val4.GetComponentsInChildren(true); val3.skinnedMeshRenderers = val4.GetComponentsInChildren(true); val3.enemyBehaviourStates = (EnemyBehaviourState[])(object)new EnemyBehaviourState[1] { new EnemyBehaviourState { name = "Active" } }; val3.AIIntervalTime = 0.2f; return new EnemyRecipe(val, val5, val3); } private ItemRecipe CreateItem(string module, string prefabName, Type? behaviourType, GameObject visual, int minimumValue, int maximumValue, float weight) { //IL_0076: 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_009b: Expected O, but got Unknown if ((object)behaviourType != null && !typeof(GrabbableObject).IsAssignableFrom(behaviourType)) { throw new InvalidOperationException("Fallback type '" + behaviourType.FullName + "' is not a GrabbableObject."); } GameObject val = NetworkPrefabs.CreateNetworkPrefab("ChaosSuite_" + prefabName + "_Fallback"); Rigidbody val2 = val.AddComponent(); val2.mass = 1f; val2.collisionDetectionMode = (CollisionDetectionMode)1; BoxCollider val3 = val.AddComponent(); val3.size = new Vector3(0.45f, 0.3f, 0.45f); GrabbableObject val4 = (GrabbableObject)val.AddComponent(behaviourType ?? typeof(RuntimeFallbackItem)); if (val4 == null || !Object.op_Implicit((Object)(object)val4)) { throw new InvalidOperationException("Unity could not add fallback item component '" + (behaviourType ?? typeof(RuntimeFallbackItem)).FullName + "'."); } GameObject val5 = AttachVisual(val, visual, prefabName); Item val6 = ScriptableObject.CreateInstance(); ((Object)val6).name = prefabName + "ItemFallback"; ((Object)val6).hideFlags = (HideFlags)61; val6.itemName = FriendlyName(prefabName); val6.spawnPrefab = val; val6.isScrap = true; val6.itemSpawnsOnGround = true; val6.canBeGrabbedBeforeGameStart = true; val6.weight = Mathf.Clamp(weight, 1f, 3.75f); val6.minValue = Math.Max(1, minimumValue); val6.maxValue = Math.Max(val6.minValue, maximumValue); val6.highestSalePercentage = 100; val6.grabAnimationTime = 0.4f; val6.verticalOffset = 0.05f; val6.spawnPositionTypes = new List(); val6.toolTips = new string[2] { "Use item : [LMB]", "Alternate use : [RMB]" }; val6.meshVariants = Array.Empty(); val6.materialVariants = Array.Empty(); val4.itemProperties = val6; val4.propBody = val2; val4.propColliders = (Collider[])(object)new Collider[1] { (Collider)val3 }; int visited = 0; val4.mainObjectRenderer = FindFirstActiveMeshRenderer(val5.transform, 0, ref visited) ?? throw new InvalidOperationException("Fallback item '" + prefabName + "' selected a presentation form with no active MeshRenderer."); val4.grabbable = true; val4.grabbableToEnemies = true; return new ItemRecipe(val, val6, val4); } private static GameObject AttachVisual(GameObject prefab, GameObject visual, string prefabName) { GameObject obj = Object.Instantiate(visual, prefab.transform, false); ((Object)obj).name = ((Object)visual).name + "_FallbackVisual"; SelectPresentationForm(obj.transform, prefabName); return obj; } private static void SelectPresentationForm(Transform visual, string prefabName) { Transform val = FindChild(visual, "EnemyForm"); Transform val2 = FindChild(visual, "ItemForm"); Transform val3 = FindChild(visual, "PawForm"); Transform val4 = FindChild(visual, "FortuneForm"); bool flag = prefabName == "BlackAppleCore" || prefabName == "JobApplication"; if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(!flag); } if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(flag); } if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(prefabName == "MonkeysPaw"); } if ((Object)(object)val4 != (Object)null) { ((Component)val4).gameObject.SetActive(prefabName == "CursedFortune"); } } private static Component AddSpatialAudio(GameObject prefab) { Type type = Type.GetType("UnityEngine.AudioSource, UnityEngine.AudioModule", throwOnError: true); Component obj = prefab.AddComponent(type); SetProperty(obj, "playOnAwake", false); SetProperty(obj, "spatialBlend", 1f); SetProperty(obj, "minDistance", 2f); SetProperty(obj, "maxDistance", 28f); return obj; } private static GameObject CreateChild(Transform parent, string name) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown GameObject val = new GameObject(name) { hideFlags = (HideFlags)61 }; val.transform.SetParent(parent, false); return val; } private static GameObject CreatePaperCocoon(Transform parent) { //IL_0012: 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_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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0084: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateChild(parent, "PaperCocoon"); val.transform.localPosition = Vector3.up * 0.85f; Material sharedMaterial = new Material(Shader.Find("Standard")) { color = new Color(0.48f, 0.42f, 0.31f, 1f) }; Material sharedMaterial2 = new Material(Shader.Find("Standard")) { color = new Color(0.055f, 0.045f, 0.035f, 1f) }; GameObject obj = GameObject.CreatePrimitive((PrimitiveType)1); ((Object)obj).name = "CrumpledPaperShell"; obj.transform.SetParent(val.transform, false); obj.transform.localScale = new Vector3(0.75f, 1.05f, 0.62f); obj.GetComponent().sharedMaterial = sharedMaterial; Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } for (int i = 0; i < 3; i++) { GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)obj2).name = "PaperBinding" + i; obj2.transform.SetParent(val.transform, false); obj2.transform.localPosition = Vector3.up * (-0.48f + (float)i * 0.48f); obj2.transform.localScale = new Vector3(0.62f, 0.035f, 0.52f); obj2.GetComponent().sharedMaterial = sharedMaterial2; Collider component2 = obj2.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } } return val; } private static Transform? FindChild(Transform root, string name) { if (string.Equals(((Object)root).name, name, StringComparison.Ordinal)) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindChild(root.GetChild(i), name); if (Object.op_Implicit((Object)(object)val)) { return val; } } return null; } private static MeshRenderer? FindFirstActiveMeshRenderer(Transform root, int depth, ref int visited) { if (!Object.op_Implicit((Object)(object)root) || depth > 24 || visited++ >= 512 || !((Component)root).gameObject.activeSelf) { return null; } MeshRenderer component = ((Component)root).GetComponent(); if ((Object)(object)component != (Object)null && Object.op_Implicit((Object)(object)component) && ((Renderer)component).enabled) { return component; } int num = Math.Min(root.childCount, 512 - visited); for (int i = 0; i < num; i++) { MeshRenderer val = FindFirstActiveMeshRenderer(root.GetChild(i), depth + 1, ref visited); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Type RequireType(Assembly assembly, string fullName) { return assembly.GetType(fullName, throwOnError: false, ignoreCase: false) ?? throw new TypeLoadException("Feature assembly '" + assembly.GetName().Name + "' does not contain required fallback component '" + fullName + "'."); } private static void SetField(object target, string fieldName, object value) { FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)field == null) { throw new MissingFieldException(target.GetType().FullName, fieldName); } if (!field.FieldType.IsInstanceOfType(value)) { throw new InvalidOperationException("Cannot assign '" + value.GetType().FullName + "' to '" + target.GetType().FullName + "." + fieldName + "'."); } field.SetValue(target, value); } private static void SetProperty(object target, string propertyName, object value) { PropertyInfo property = target.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public); if ((object)property == null || !property.CanWrite) { throw new MissingMemberException(target.GetType().FullName, propertyName); } property.SetValue(target, value, null); } private void Cache(string path, Object value) { if (!Object.op_Implicit(value)) { throw new InvalidOperationException("Fallback asset '" + path + "' is null."); } generated[path] = value; } private static string FriendlyName(string value) { if (string.IsNullOrEmpty(value)) { return "Chaos Content"; } StringBuilder stringBuilder = new StringBuilder(value.Length + 6); for (int i = 0; i < value.Length; i++) { if (i > 0 && char.IsUpper(value[i]) && char.IsLower(value[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(value[i]); } return stringBuilder.ToString(); } } public sealed class RuntimeFallbackItem : GrabbableObject { } public static class KnownLethalContent { private static readonly IReadOnlyList definitions = Array.AsReadOnly(new LethalContentDefinition[11] { Enemy("NewtonsApple", "com.chaossuite.newtonsapple", 18), PlainItem("NewtonsApple", "com.chaossuite.newtonsapple", "BlackAppleCoreItem"), Scrap("JobApplication", "com.chaossuite.jobapplication", 14), Prefab("JobApplication", "com.chaossuite.jobapplication", "ApplicantEnemy"), Prefab("JobApplication", "com.chaossuite.jobapplication", "PaperEmployee"), Enemy("Professor", "com.chaossuite.professor", 12), Enemy("Webhead", "com.chaossuite.webhead", 15), Enemy("HorrorBowler", "com.chaossuite.horrorbowler", 9), Scrap("MonkeysPaw", "com.chaossuite.monkeyspaw", 8), PlainItem("MonkeysPaw", "com.chaossuite.monkeyspaw", "CursedFortuneItem"), Enemy("Relocator", "com.chaossuite.relocator", 11) }); public static readonly IReadOnlyList Modules = Array.AsReadOnly(new string[8] { "MasklessMimic", "NewtonsApple", "JobApplication", "Professor", "Webhead", "HorrorBowler", "MonkeysPaw", "Relocator" }); public static readonly IReadOnlyList FallbackComponents = Array.AsReadOnly(new FallbackComponentDefinition[9] { new FallbackComponentDefinition("NewtonsApple", "NewtonsAppleEnemy", "ChaosSuite.NewtonsApple.NewtonsAppleEnemy"), new FallbackComponentDefinition("JobApplication", "JobApplication", "ChaosSuite.JobApplication.JobApplicationItem"), new FallbackComponentDefinition("JobApplication", "ApplicantEnemy", "ChaosSuite.JobApplication.ApplicantEnemyAI"), new FallbackComponentDefinition("JobApplication", "PaperEmployee", "ChaosSuite.JobApplication.PaperEmployeeAI"), new FallbackComponentDefinition("Professor", "ProfessorEnemy", "ChaosSuite.Professor.ProfessorEnemyAI"), new FallbackComponentDefinition("Webhead", "WebheadEnemy", "ChaosSuite.Webhead.WebheadEnemyAI"), new FallbackComponentDefinition("HorrorBowler", "HorrorBowlerEnemy", "ChaosSuite.HorrorBowler.HorrorBowlerEnemyAI"), new FallbackComponentDefinition("MonkeysPaw", "MonkeysPaw", "ChaosSuite.MonkeysPaw.MonkeysPawItem"), new FallbackComponentDefinition("Relocator", "RelocatorEnemy", "ChaosSuite.Quagmire.RelocatorEnemyAI") }); public static IReadOnlyList Definitions => definitions; private static LethalContentDefinition Enemy(string module, string guid, int rarity) { return new LethalContentDefinition(module, guid, LethalContentKind.NaturalEnemy, ChaosAssetPaths.EnemyType(module), rarity); } private static LethalContentDefinition Scrap(string module, string guid, int rarity) { return new LethalContentDefinition(module, guid, LethalContentKind.ScrapItem, ChaosAssetPaths.ItemDefinition(module), rarity); } private static LethalContentDefinition PlainItem(string module, string guid, string itemName) { return new LethalContentDefinition(module, guid, LethalContentKind.PlainItem, ChaosAssetPaths.ItemDefinition(module, itemName), 0); } private static LethalContentDefinition Prefab(string module, string guid, string prefabName) { return new LethalContentDefinition(module, guid, LethalContentKind.NetworkPrefab, ChaosAssetPaths.NetworkPrefab(module, prefabName), 0); } } internal static class LifecyclePatches { private static readonly MethodInfo ClearRoundMethod = AccessTools.Method(typeof(LifecyclePatches), "ClearRound", (Type[])null, (Type[])null); private static readonly MethodInfo ClearLevelMethod = AccessTools.Method(typeof(LifecyclePatches), "ClearLevel", (Type[])null, (Type[])null); private static readonly MethodInfo ClearMenuMethod = AccessTools.Method(typeof(LifecyclePatches), "ClearMenu", (Type[])null, (Type[])null); private static readonly MethodInfo ClientDisconnectedMethod = AccessTools.Method(typeof(LifecyclePatches), "ClientDisconnected", (Type[])null, (Type[])null); private static readonly MethodInfo PlayerRemovedMethod = AccessTools.Method(typeof(LifecyclePatches), "PlayerRemoved", (Type[])null, (Type[])null); private static readonly MethodInfo NetworkObjectDespawnedMethod = AccessTools.Method(typeof(LifecyclePatches), "NetworkObjectDespawned", (Type[])null, (Type[])null); private static readonly MethodInfo PlayerTeleportedMethod = AccessTools.Method(typeof(LifecyclePatches), "PlayerTeleported", (Type[])null, (Type[])null); private static EffectCleanupRegistry? Registry => ChaosSuiteRuntimePlugin.Instance?.Cleanup; internal static int Install(Harmony harmony, ManualLogSource log) { //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Expected O, but got Unknown int num = 0; num += PatchPrefix(harmony, log, typeof(StartOfRound), "ShipLeave", Type.EmptyTypes, ClearRoundMethod); num += PatchPrefix(harmony, log, typeof(StartOfRound), "ChangeLevel", new Type[1] { typeof(int) }, ClearLevelMethod); num += PatchPrefix(harmony, log, typeof(GameNetworkManager), "Disconnect", Type.EmptyTypes, ClearMenuMethod); num += PatchPrefix(harmony, log, typeof(StartOfRound), "OnDestroy", Type.EmptyTypes, ClearMenuMethod); num += PatchPostfix(harmony, log, typeof(StartOfRound), "OnClientDisconnect", new Type[1] { typeof(ulong) }, ClientDisconnectedMethod); num += PatchPostfix(harmony, log, typeof(PlayerControllerB), "KillPlayer", new Type[6] { typeof(Vector3), typeof(bool), typeof(CauseOfDeath), typeof(int), typeof(Vector3), typeof(bool) }, PlayerRemovedMethod); num += PatchPrefix(harmony, log, typeof(PlayerControllerB), "OnDestroy", Type.EmptyTypes, PlayerRemovedMethod); num += PatchPrefix(harmony, log, typeof(NetworkObject), "InvokeBehaviourNetworkDespawn", Type.EmptyTypes, NetworkObjectDespawnedMethod); MethodInfo[] array = (from method in AccessTools.GetDeclaredMethods(typeof(PlayerControllerB)) where method.Name == "TeleportPlayer" select method).ToArray(); if (array.Length == 0) { log.LogError((object)"Required lifecycle hook was not found: PlayerControllerB.TeleportPlayer. Teleporter cleanup safety is degraded for this game version."); } for (int num2 = 0; num2 < array.Length; num2++) { harmony.Patch((MethodBase)array[num2], (HarmonyMethod)null, new HarmonyMethod(PlayerTeleportedMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; log.LogDebug((object)("Installed lifecycle hook: " + array[num2].DeclaringType?.FullName + "." + array[num2].Name + ".")); } return num; } private static int PatchPrefix(Harmony harmony, ManualLogSource log, Type type, string name, Type[] arguments, MethodInfo callback) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown return Patch(harmony, log, type, name, arguments, new HarmonyMethod(callback), null); } private static int PatchPostfix(Harmony harmony, ManualLogSource log, Type type, string name, Type[] arguments, MethodInfo callback) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown return Patch(harmony, log, type, name, arguments, null, new HarmonyMethod(callback)); } private static int Patch(Harmony harmony, ManualLogSource log, Type type, string name, Type[] arguments, HarmonyMethod? prefix, HarmonyMethod? postfix) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, arguments, (Type[])null); if ((object)methodInfo == null) { log.LogError((object)("Required lifecycle hook was not found: " + type.FullName + "." + name + ". Runtime cleanup safety is degraded for this game version.")); return 0; } harmony.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogDebug((object)("Installed lifecycle hook: " + methodInfo.DeclaringType?.FullName + "." + methodInfo.Name + ".")); return 1; } private static void ClearRound() { ClearAll("ship departure"); } private static void ClearLevel() { ClearAll("level change"); } private static void ClearMenu() { ClearAll("disconnect or return to menu"); } private static void ClearAll(string reason) { Registry?.Clear(reason); ChaosSuiteRuntimePlugin.Instance?.Noise.Clear(); ChaosSuiteRuntimePlugin.Instance?.ThreatBudget.Clear(); } private static void ClientDisconnected(ulong clientId) { Registry?.ReleaseOwner(clientId, "client disconnected"); ChaosSuiteRuntimePlugin.Instance?.SynchronizedConfig.ForgetClient(clientId); } private static void PlayerRemoved(PlayerControllerB __instance) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) EffectCleanupRegistry registry = Registry; if (registry != null && Object.op_Implicit((Object)(object)__instance)) { registry.ReleaseAffectedOwner(__instance.actualClientId, "player death or despawn"); if (((NetworkBehaviour)__instance).NetworkObjectId != 0L) { registry.Release(new EntityId(((NetworkBehaviour)__instance).NetworkObjectId), "player death or despawn"); } } } private static void NetworkObjectDespawned(NetworkObject __instance) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)__instance) && __instance.NetworkObjectId != 0L) { Registry?.Release(new EntityId(__instance.NetworkObjectId), "network object despawned"); } } private static void PlayerTeleported(PlayerControllerB __instance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (TeleportCleanupGuard.IsSuppressed) { return; } EffectCleanupRegistry registry = Registry; if (registry != null && Object.op_Implicit((Object)(object)__instance)) { registry.ReleaseAffectedOwner(__instance.actualClientId, "player teleported"); if (((NetworkBehaviour)__instance).NetworkObjectId != 0L) { registry.Release(new EntityId(((NetworkBehaviour)__instance).NetworkObjectId), "player teleported"); } } } } public static class ConfigSnapshotCodec { private const uint Magic = 1129530182u; private const ushort Version = 1; public const int MaximumEntries = 256; public const int MaximumPacketBytes = 32768; private const int MaximumKeyBytes = 256; private const int MaximumValueBytes = 4096; public static byte[] Encode(IReadOnlyDictionary values) { if (values == null) { throw new ArgumentNullException("values"); } if (values.Count > 256) { throw new ArgumentException($"A config snapshot may contain at most {256} entries.", "values"); } using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true)) { binaryWriter.Write(1129530182u); binaryWriter.Write((ushort)1); binaryWriter.Write((ushort)values.Count); List list = new List(values.Keys); list.Sort(StringComparer.Ordinal); foreach (string item in list) { WriteString(binaryWriter, item, 256); WriteString(binaryWriter, values[item], 4096); } } if (memoryStream.Length > 32768) { throw new InvalidDataException($"Encoded config snapshot exceeds {32768} bytes."); } return memoryStream.ToArray(); } public static IReadOnlyDictionary Decode(byte[] payload) { if (payload == null) { throw new ArgumentNullException("payload"); } if (payload.Length > 32768) { throw new InvalidDataException("Config snapshot is too large."); } using MemoryStream memoryStream = new MemoryStream(payload, writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: false); if (binaryReader.ReadUInt32() != 1129530182) { throw new InvalidDataException("Config snapshot magic is invalid."); } if (binaryReader.ReadUInt16() != 1) { throw new InvalidDataException("Config snapshot protocol version is unsupported."); } ushort num = binaryReader.ReadUInt16(); if (num > 256) { throw new InvalidDataException("Config snapshot contains too many entries."); } Dictionary dictionary = new Dictionary(num, StringComparer.Ordinal); for (int i = 0; i < num; i++) { string text = ReadString(binaryReader, 256); string value = ReadString(binaryReader, 4096); if (!dictionary.TryAdd(text, value)) { throw new InvalidDataException("Config snapshot repeats key '" + text + "'."); } } if (memoryStream.Position != memoryStream.Length) { throw new InvalidDataException("Config snapshot has trailing data."); } return dictionary; } private static void WriteString(BinaryWriter writer, string value, int maximumBytes) { if (value == null) { throw new ArgumentNullException("value"); } byte[] bytes = Encoding.UTF8.GetBytes(value); if (bytes.Length > maximumBytes) { throw new InvalidDataException($"Config text exceeds its {maximumBytes}-byte limit."); } writer.Write((ushort)bytes.Length); writer.Write(bytes); } private static string ReadString(BinaryReader reader, int maximumBytes) { ushort num = reader.ReadUInt16(); if (num > maximumBytes) { throw new InvalidDataException("Config text exceeds its length limit."); } byte[] array = reader.ReadBytes(num); if (array.Length != num) { throw new EndOfStreamException("Config snapshot ended inside a text value."); } return new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(array); } } public sealed class SynchronizedConfigEntry { private readonly ConfigEntry localEntry; private T effectiveValue; public T Value => effectiveValue; public T LocalValue => localEntry.Value; internal SynchronizedConfigEntry(ConfigEntry localEntry) { this.localEntry = localEntry; effectiveValue = localEntry.Value; } internal string SerializeLocal() { return TomlTypeConverter.ConvertToString((object)localEntry.Value, typeof(T)); } internal void UseLocal() { effectiveValue = localEntry.Value; } internal void ApplyRemote(string value) { effectiveValue = (T)TomlTypeConverter.ConvertToValue(value, typeof(T)); } internal void Unsubscribe(EventHandler handler) { localEntry.SettingChanged -= handler; } } public sealed class SynchronizedConfigService : IDisposable { private interface IBinding { string SerializeLocal(); void ApplyRemote(string value); void UseLocal(); void Unsubscribe(EventHandler handler); } private sealed class Binding : IBinding { private readonly SynchronizedConfigEntry entry; internal Binding(SynchronizedConfigEntry entry) { this.entry = entry; } public string SerializeLocal() { return entry.SerializeLocal(); } public void ApplyRemote(string value) { entry.ApplyRemote(value); } public void UseLocal() { entry.UseLocal(); } public void Unsubscribe(EventHandler handler) { entry.Unsubscribe(handler); } } private const string MessageName = "ChaosSuite.Config.v1"; private const byte RequestMessage = 1; private const byte SnapshotMessage = 2; private const byte AcknowledgementMessage = 3; private const double RequestMinimumInterval = 1.0; private const double RejectionLogMinimumInterval = 2.0; private readonly ManualLogSource log; private readonly Dictionary bindings = new Dictionary(StringComparer.Ordinal); private readonly HashSet synchronizedClients = new HashSet(); private readonly RateLimiter requestLimiter = new RateLimiter(); private readonly RateLimiter rejectionLogLimiter = new RateLimiter(); private NetworkManager? attachedManager; private bool dirty; private float nextBroadcastTime; internal SynchronizedConfigService(ManualLogSource log) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown this.log = log; } public SynchronizedConfigEntry Register(string key, ConfigEntry entry) { ValidateKey(key); if (entry == null) { throw new ArgumentNullException("entry"); } if (!TomlTypeConverter.CanConvert(typeof(T))) { throw new NotSupportedException("BepInEx cannot serialize synchronized config type " + typeof(T).FullName + "."); } SynchronizedConfigEntry synchronizedConfigEntry = new SynchronizedConfigEntry(entry); if (!bindings.TryAdd(key, new Binding(synchronizedConfigEntry))) { throw new InvalidOperationException("Synchronized config key '" + key + "' is already registered."); } entry.SettingChanged += LocalSettingChanged; dirty = true; return synchronizedConfigEntry; } public void Tick() { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !Object.op_Implicit((Object)(object)singleton) || !singleton.IsListening) { Detach(); return; } if (singleton != attachedManager) { Attach(singleton); } if (singleton.IsServer && dirty && Time.unscaledTime >= nextBroadcastTime) { BroadcastSnapshot(); nextBroadcastTime = Time.unscaledTime + 0.25f; } } public void ForgetClient(ulong clientId) { //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_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) EntityId val = ClientRateKey(clientId); requestLimiter.Clear(val); rejectionLogLimiter.Clear(val); if (synchronizedClients.Remove(clientId)) { log.LogDebug((object)$"Released synchronized-config session state for client {clientId}."); } } public bool IsClientSynchronized(ulong clientId) { return synchronizedClients.Contains(clientId); } public void Dispose() { foreach (IBinding value in bindings.Values) { value.Unsubscribe(LocalSettingChanged); } bindings.Clear(); Detach(); } private void Attach(NetworkManager manager) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown Detach(); attachedManager = manager; manager.CustomMessagingManager.RegisterNamedMessageHandler("ChaosSuite.Config.v1", new HandleNamedMessageDelegate(ReceiveMessage)); manager.OnClientConnectedCallback += ClientConnected; manager.OnClientDisconnectCallback += ClientDisconnected; foreach (IBinding value in bindings.Values) { value.UseLocal(); } if (manager.IsClient && !manager.IsServer) { SendRequest(); } if (manager.IsServer) { dirty = true; } } private void Detach() { NetworkManager val = attachedManager; if (val != null && Object.op_Implicit((Object)(object)val)) { val.CustomMessagingManager.UnregisterNamedMessageHandler("ChaosSuite.Config.v1"); val.OnClientConnectedCallback -= ClientConnected; val.OnClientDisconnectCallback -= ClientDisconnected; } attachedManager = null; synchronizedClients.Clear(); requestLimiter.ClearAll(); rejectionLogLimiter.ClearAll(); foreach (IBinding value in bindings.Values) { value.UseLocal(); } } private void LocalSettingChanged(object sender, EventArgs args) { NetworkManager val = attachedManager; if (val != null && Object.op_Implicit((Object)(object)val) && !val.IsServer) { return; } foreach (IBinding value in bindings.Values) { value.UseLocal(); } dirty = true; } private void ClientConnected(ulong clientId) { NetworkManager val = attachedManager; if (val != null && Object.op_Implicit((Object)(object)val)) { if (val.IsServer && clientId != 0L) { SendSnapshot(clientId); } else if (!val.IsServer && clientId == val.LocalClientId) { SendRequest(); } } } private void ClientDisconnected(ulong clientId) { ForgetClient(clientId); NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val) || clientId != val.LocalClientId) { return; } foreach (IBinding value in bindings.Values) { value.UseLocal(); } } private void ReceiveMessage(ulong senderId, FastBufferReader reader) { //IL_010a: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val)) { return; } try { byte b = default(byte); ((FastBufferReader)(ref reader)).ReadByteSafe(ref b); switch (b) { case 1: if (val.IsServer && val.ConnectedClients.ContainsKey(senderId) && requestLimiter.TryAcquire(ClientRateKey(senderId), Time.unscaledTimeAsDouble, 1.0)) { SendSnapshot(senderId); } break; case 3: if (val.IsServer && val.ConnectedClients.ContainsKey(senderId)) { synchronizedClients.Add(senderId); } break; case 2: if (!val.IsServer && senderId == 0L) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); if (num < 0 || num > 32768 || num > ((FastBufferReader)(ref reader)).Length - ((FastBufferReader)(ref reader)).Position) { throw new InvalidDataException("Config snapshot payload length is invalid."); } byte[] payload = null; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref payload, num, 0); ApplySnapshot(ConfigSnapshotCodec.Decode(payload)); SendAcknowledgement(); } break; } } catch (Exception ex) { if (rejectionLogLimiter.TryAcquire(ClientRateKey(senderId), Time.unscaledTimeAsDouble, 2.0)) { log.LogWarning((object)$"Rejected synchronized config message from client {senderId}: {ex.Message}"); } } } private void ApplySnapshot(IReadOnlyDictionary values) { foreach (IBinding value2 in bindings.Values) { value2.UseLocal(); } foreach (KeyValuePair value3 in values) { if (bindings.TryGetValue(value3.Key, out IBinding value)) { try { value.ApplyRemote(value3.Value); } catch (Exception ex) { log.LogWarning((object)("Rejected synchronized config value '" + value3.Key + "': " + ex.Message)); } } } log.LogDebug((object)$"Applied {values.Count} host config values."); } private unsafe void SendRequest() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val) || !val.IsClient || val.IsServer) { return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(1, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteByteSafe((byte)1); val.CustomMessagingManager.SendNamedMessage("ChaosSuite.Config.v1", 0uL, val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe void SendAcknowledgement() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val) || !val.IsClient || val.IsServer) { return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(1, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteByteSafe((byte)3); val.CustomMessagingManager.SendNamedMessage("ChaosSuite.Config.v1", 0uL, val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private void BroadcastSnapshot() { NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val) || !val.IsServer) { return; } foreach (ulong connectedClientsId in val.ConnectedClientsIds) { if (connectedClientsId != 0L) { SendSnapshot(connectedClientsId); } } dirty = false; } private unsafe void SendSnapshot(ulong clientId) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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) NetworkManager val = attachedManager; if (val == null || !Object.op_Implicit((Object)(object)val) || !val.IsServer || !val.ConnectedClients.ContainsKey(clientId)) { return; } Dictionary dictionary = new Dictionary(bindings.Count, StringComparer.Ordinal); foreach (KeyValuePair binding in bindings) { dictionary.Add(binding.Key, binding.Value.SerializeLocal()); } byte[] array = ConfigSnapshotCodec.Encode(dictionary); FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(array.Length + 8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteByteSafe((byte)2); int num = array.Length; ((FastBufferWriter)(ref val2)).WriteValueSafe(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteBytesSafe(array, array.Length, 0); val.CustomMessagingManager.SendNamedMessage("ChaosSuite.Config.v1", clientId, val2, (NetworkDelivery)3); synchronizedClients.Remove(clientId); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private static void ValidateKey(string key) { if (string.IsNullOrWhiteSpace(key) || key.Length > 128) { throw new ArgumentException("Synchronized config keys must contain 1-128 characters.", "key"); } } private static EntityId ClientRateKey(ulong clientId) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return new EntityId(clientId + 1); } } } namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } }