Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ArtifactOfParallel v1.0.1
ArtifactOfParallel.dll
Decompiled 20 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HG; using HG.Reflection; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using On.RoR2; using On.RoR2.Projectile; using On.RoR2.UI; using R2API; using R2API.Utils; using RiskOfOptions; using RiskOfOptions.Options; using RoR2; using RoR2.Hologram; using RoR2.Projectile; using RoR2.UI; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Networking; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: OptIn] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ArtifactOfParallel")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+20bf3827aba0faf20dbbe0d748a808e9db9ca520")] [assembly: AssemblyProduct("ArtifactOfParallel")] [assembly: AssemblyTitle("ArtifactOfParallel")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ArtifactOfParallel { [BepInPlugin("kaitorque.ArtifactOfParallel", "ArtifactOfParallel", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class ArtifactOfParallelPlugin : BaseUnityPlugin { public const string PluginGuid = "kaitorque.ArtifactOfParallel"; public const string PluginName = "ArtifactOfParallel"; public const string PluginVersion = "1.0.1"; internal static ManualLogSource Log { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); DebugLog.Init(); ParallelArtifact.Register(); PrivatePrefabs.Init(); InteractionHooks.Init(); SceneModelCleanup.Init(); LootAssigner.Init(); OwnershipTracker.Init(); RiskOfOptionsCompat.TryInit(); } private void FixedUpdate() { OwnershipTracker.FixedUpdate(); } } internal static class DebugLog { private static readonly HashSet<string> logged = new HashSet<string>(); public static bool Enabled => ModConfig.DebugLogging.Value; public static void Init() { Stage.onStageStartGlobal += delegate { logged.Clear(); }; } public static void Info(string message) { if (Enabled) { ArtifactOfParallelPlugin.Log.LogInfo((object)message); } } public static void Once(string key, string message) { if (Enabled && logged.Add(key)) { ArtifactOfParallelPlugin.Log.LogInfo((object)message); } } } internal static class InteractionHooks { [StructLayout(LayoutKind.Sequential, Size = 1)] private readonly struct DrifterScope : IDisposable { public DrifterScope(CharacterBody body) { drifterActor = ParallelOwner.UserOf(body); hasDrifterActor = true; } public void Dispose() { drifterActor = null; hasDrifterActor = false; } } [CompilerGenerated] private static class <>O { public static hook_GetInteractability <0>__PurchaseInteraction_GetInteractability; public static hook_OnInteractionBegin <1>__PurchaseInteraction_OnInteractionBegin; public static hook_AssignPassenger <2>__DrifterBagController_AssignPassenger; public static hook_TransmuteTargetObject <3>__TinkerProjectile_TransmuteTargetObject; public static hook_RebuildPing <4>__PingIndicator_RebuildPing; } private static Hook isTargetableHook; private static NetworkUser drifterActor; private static bool hasDrifterActor; private static readonly FieldInfo PingTargetRenderers = typeof(PingIndicator).GetField("pingTargetRenderers", BindingFlags.Instance | BindingFlags.NonPublic); public static void Init() { //IL_0010: 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_001b: Expected O, but got Unknown //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: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0070: 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_007b: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0090: 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 object obj = <>O.<0>__PurchaseInteraction_GetInteractability; if (obj == null) { hook_GetInteractability val = PurchaseInteraction_GetInteractability; <>O.<0>__PurchaseInteraction_GetInteractability = val; obj = (object)val; } PurchaseInteraction.GetInteractability += (hook_GetInteractability)obj; object obj2 = <>O.<1>__PurchaseInteraction_OnInteractionBegin; if (obj2 == null) { hook_OnInteractionBegin val2 = PurchaseInteraction_OnInteractionBegin; <>O.<1>__PurchaseInteraction_OnInteractionBegin = val2; obj2 = (object)val2; } PurchaseInteraction.OnInteractionBegin += (hook_OnInteractionBegin)obj2; object obj3 = <>O.<2>__DrifterBagController_AssignPassenger; if (obj3 == null) { hook_AssignPassenger val3 = DrifterBagController_AssignPassenger; <>O.<2>__DrifterBagController_AssignPassenger = val3; obj3 = (object)val3; } DrifterBagController.AssignPassenger += (hook_AssignPassenger)obj3; object obj4 = <>O.<3>__TinkerProjectile_TransmuteTargetObject; if (obj4 == null) { hook_TransmuteTargetObject val4 = TinkerProjectile_TransmuteTargetObject; <>O.<3>__TinkerProjectile_TransmuteTargetObject = val4; obj4 = (object)val4; } TinkerProjectile.TransmuteTargetObject += (hook_TransmuteTargetObject)obj4; object obj5 = <>O.<4>__PingIndicator_RebuildPing; if (obj5 == null) { hook_RebuildPing val5 = PingIndicator_RebuildPing; <>O.<4>__PingIndicator_RebuildPing = val5; obj5 = (object)val5; } PingIndicator.RebuildPing += (hook_RebuildPing)obj5; isTargetableHook = new Hook((MethodBase)typeof(SpecialObjectAttributes).GetProperty("isTargetable").GetGetMethod(), (Delegate)new Func<Func<SpecialObjectAttributes, bool>, SpecialObjectAttributes, bool>(SpecialObjectAttributes_isTargetable)); } private static Interactability PurchaseInteraction_GetInteractability(orig_GetInteractability orig, PurchaseInteraction self, Interactor activator) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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_0104: Unknown result type (might be due to invalid IL or missing references) ParallelOwner parallelOwner = default(ParallelOwner); if (((Component)self).TryGetComponent<ParallelOwner>(ref parallelOwner)) { if (!parallelOwner.CanBeUsedBy(activator)) { if (DebugLog.Enabled) { DebugLog.Once($"deny:{((Object)self).GetInstanceID()}:{ActorName(activator)}", "Interact: refused " + ActorName(activator) + " on " + ((Object)self).name + " (owner " + parallelOwner.OwnerName() + ")"); } return (Interactability)0; } if (parallelOwner.IsPrivate && Object.op_Implicit((Object)(object)activator) && activator.isRemoteOp && !self.allowRemoteOpInteraction) { if (DebugLog.Enabled) { DebugLog.Once($"remoteop:{((Object)self).GetInstanceID()}", "Interact: allowed " + ActorName(activator) + "'s Remote Operation drone on their own " + ((Object)self).name); } self.allowRemoteOpInteraction = true; try { return orig.Invoke(self, activator); } finally { self.allowRemoteOpInteraction = false; } } } return orig.Invoke(self, activator); } private static void PurchaseInteraction_OnInteractionBegin(orig_OnInteractionBegin orig, PurchaseInteraction self, Interactor activator) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) ParallelOwner parallelOwner = default(ParallelOwner); if (DebugLog.Enabled && ((Component)self).TryGetComponent<ParallelOwner>(ref parallelOwner)) { DebugLog.Info("Interact: " + ActorName(activator) + ((Object.op_Implicit((Object)(object)activator) && activator.isRemoteOp) ? " (Remote Operation)" : "") + " " + string.Format("used {0} at {1} (owner {2}, {3})", ((Object)self).name, ((Component)self).transform.position, parallelOwner.OwnerName(), parallelOwner.IsPrivate ? "private" : "shared")); } orig.Invoke(self, activator); } private static string ActorName(Interactor activator) { NetworkUser val = ParallelOwner.UserOf(Object.op_Implicit((Object)(object)activator) ? ((Component)activator).GetComponent<CharacterBody>() : null); if (!Object.op_Implicit((Object)(object)val)) { if (!Object.op_Implicit((Object)(object)activator)) { return "nobody"; } return ((Object)activator).name; } return val.userName; } private static void PingIndicator_RebuildPing(orig_RebuildPing orig, PingIndicator self) { orig.Invoke(self); ParallelOwner parallelOwner = (Object.op_Implicit((Object)(object)self.pingTarget) ? self.pingTarget.GetComponentInParent<ParallelOwner>() : null); if (Object.op_Implicit((Object)(object)parallelOwner) && parallelOwner.IsLocallyHidden && PingTargetRenderers.GetValue(self) is List<Renderer> list) { list.Clear(); DebugLog.Info("Ping: removed the outline from hidden " + ((Object)parallelOwner).name + " (owner " + parallelOwner.OwnerName() + "); the marker stays"); } } private static bool SpecialObjectAttributes_isTargetable(Func<SpecialObjectAttributes, bool> orig, SpecialObjectAttributes self) { bool flag = orig(self); ParallelOwner parallelOwner = default(ParallelOwner); if (!flag || !((Component)self).TryGetComponent<ParallelOwner>(ref parallelOwner) || !parallelOwner.IsPrivate) { return flag; } bool flag2 = (hasDrifterActor ? parallelOwner.CanBeUsedBy(drifterActor) : parallelOwner.IsVisibleToLocalPlayer()); if (!flag2 && DebugLog.Enabled) { string text = ((!hasDrifterActor) ? "local player" : (Object.op_Implicit((Object)(object)drifterActor) ? drifterActor.userName : "unknown player")); DebugLog.Once($"drifter:{((Object)self).GetInstanceID()}:{text}:{hasDrifterActor}", "Drifter: " + ((Object)self).name + " (owner " + parallelOwner.OwnerName() + ") can't be grabbed or Tinkered by " + text + (hasDrifterActor ? " (server check)" : " (targeting on this game)")); } return flag2; } private static void DrifterBagController_AssignPassenger(orig_AssignPassenger orig, DrifterBagController self, GameObject passengerObject) { using (new DrifterScope(((Component)self).GetComponent<CharacterBody>())) { orig.Invoke(self, passengerObject); } } private static void TinkerProjectile_TransmuteTargetObject(orig_TransmuteTargetObject orig, TinkerProjectile self, GameObject targetObject) { ProjectileController val2 = default(ProjectileController); GameObject val = (((Component)self).TryGetComponent<ProjectileController>(ref val2) ? val2.owner : null); using (new DrifterScope(Object.op_Implicit((Object)(object)val) ? val.GetComponent<CharacterBody>() : null)) { orig.Invoke(self, targetObject); } } } internal static class LootAssigner { private struct Spawned { public GameObject obj; public int value; } private struct Fixed { public GameObject obj; public InteractableSpawnCard card; } [CompilerGenerated] private static class <>O { public static Action<SceneDirector> <0>__OnPrePopulate; public static Action<SceneDirector, GameObject, DirectorCard> <1>__OnSpawnInteractable; public static Action<SceneDirector> <2>__OnPostPopulate; public static hook_TrySpawnObject <3>__DirectorCore_TrySpawnObject; } private static readonly List<Spawned> privateObjects = new List<Spawned>(); private static readonly Dictionary<string, List<GameObject>> itemBoxes = new Dictionary<string, List<GameObject>>(); private static readonly SortedDictionary<string, (int count, int value)> sharedCounts = new SortedDictionary<string, (int, int)>(); private static readonly SortedDictionary<string, (int count, int value)> privateCounts = new SortedDictionary<string, (int, int)>(); private static int sharedValue; private static bool populating; public static void Init() { //IL_0070: 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_007b: Expected O, but got Unknown SceneDirector.onPrePopulateSceneServer += OnPrePopulate; SceneDirector.onSpawnInteractableServer += OnSpawnInteractable; SceneDirector.onPostPopulateSceneServer += OnPostPopulate; object obj = <>O.<3>__DirectorCore_TrySpawnObject; if (obj == null) { hook_TrySpawnObject val = DirectorCore_TrySpawnObject; <>O.<3>__DirectorCore_TrySpawnObject = val; obj = (object)val; } DirectorCore.TrySpawnObject += (hook_TrySpawnObject)obj; } private static void OnPrePopulate(SceneDirector director) { privateObjects.Clear(); itemBoxes.Clear(); sharedCounts.Clear(); privateCounts.Clear(); sharedValue = 0; populating = true; } private static void OnSpawnInteractable(SceneDirector director, GameObject obj, DirectorCard card) { if (!Object.op_Implicit((Object)(object)obj) || card == null) { return; } bool flag = PrivatePrefabs.IsPrivate(card.GetSpawnCard()) && Object.op_Implicit((Object)(object)obj.GetComponent<ParallelOwner>()); if (DebugLog.Enabled) { CountSpawn((flag && ParallelArtifact.Enabled) ? privateCounts : sharedCounts, card); } if (ParallelArtifact.Enabled) { if (flag) { privateObjects.Add(new Spawned { obj = obj, value = card.cost }); } else { sharedValue += card.cost; } } } private static GameObject DirectorCore_TrySpawnObject(orig_TrySpawnObject orig, DirectorCore self, DirectorSpawnRequest request) { GameObject val = orig.Invoke(self, request); if (populating && request != null && PrivatePrefabs.IsItemBox(request.spawnCard)) { string name = ((Object)request.spawnCard).name; if (!itemBoxes.TryGetValue(name, out var value)) { value = (itemBoxes[name] = new List<GameObject>()); } value.Add(val); } return val; } private static void OnPostPopulate(SceneDirector director) { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown populating = false; List<Spawned> list = privateObjects.Where((Spawned spawned) => Object.op_Implicit((Object)(object)spawned.obj)).ToList(); privateObjects.Clear(); LogSpawnCounts(); if (!ParallelArtifact.Enabled) { return; } DebugLog.Info("Settings: " + ModConfig.Summary()); List<NetworkUser> list2 = NetworkUser.readOnlyInstancesList.Where((NetworkUser user) => Object.op_Implicit((Object)(object)user) && Object.op_Implicit((Object)(object)user.master)).ToList(); int num = list.Sum((Spawned spawned) => spawned.value); StringBuilder stringBuilder = new StringBuilder(); object[] array = new object[4]; SceneDef mostRecentSceneDef = SceneCatalog.mostRecentSceneDef; array[0] = ((mostRecentSceneDef != null) ? mostRecentSceneDef.cachedName : null); array[1] = list2.Count; array[2] = sharedValue; array[3] = num; stringBuilder.Append(string.Format("Stage {0}: players={1} shared={2} private={3}", array)); if (list2.Count <= 1) { Log(stringBuilder.Append(" (not split: fewer than 2 players)")); return; } Xoroshiro128Plus rng = new Xoroshiro128Plus(Run.instance.seed ^ (ulong)((Run.instance.stageClearCount + 1) * -7046029254386353131L)); Shuffle(list2, rng); List<Fixed> list3 = FindFixedObjects(); int num2 = list3.Sum((Fixed item) => ((SpawnCard)item.card).directorCreditCost); float num3 = (float)(num + num2) / (float)list2.Count; bool flag = list3.Count > 0 && (float)num2 <= num3; if (!flag) { num3 = (float)num / (float)list2.Count; } stringBuilder.Append(string.Format(" fixed={0} ({1} objects, {2})", num2, list3.Count, (list3.Count == 0) ? "none" : (flag ? "copied" : "shared"))); stringBuilder.Append($" target={num3:0.#}"); int[] array2 = new int[list2.Count]; int[] array3 = new int[list2.Count]; if (flag) { CopyFixedObjects(list3, list2); for (int num4 = 0; num4 < list2.Count; num4++) { array2[num4] = num2; array3[num4] = list3.Count; } } Shuffle(list, rng); list = list.OrderByDescending((Spawned spawned) => spawned.value).ToList(); int num5 = 0; int num6 = 0; List<string>[] array4 = new List<string>[list2.Count]; for (int num7 = 0; num7 < array4.Length; num7++) { array4[num7] = new List<string>(); } List<string> list4 = new List<string>(); foreach (Spawned item in list) { int num8 = 0; for (int num9 = 1; num9 < array2.Length; num9++) { if (array2[num9] < array2[num8]) { num8 = num9; } } if ((float)(array2[num8] + item.value) <= num3 + (float)item.value * 0.5f) { item.obj.GetComponent<ParallelOwner>().SetOwner(list2[num8]); array2[num8] += item.value; array3[num8]++; array4[num8].Add(ShortName(item)); } else { num5 += item.value; num6++; list4.Add(ShortName(item)); NetworkServer.Destroy(item.obj); } } for (int num10 = 0; num10 < list2.Count; num10++) { stringBuilder.Append(string.Format("\n {0}: value={1} objects={2} [{3}]", list2[num10].userName, array2[num10], array3[num10], string.Join(", ", array4[num10]))); } stringBuilder.Append(string.Format("\n removed: value={0} objects={1} [{2}]", num5, num6, string.Join(", ", list4))); AssignItemBoxes(stringBuilder); Log(stringBuilder); } private static List<Fixed> FindFixedObjects() { //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) List<Fixed> list = new List<Fixed>(); PurchaseInteraction[] array = Object.FindObjectsOfType<PurchaseInteraction>(); foreach (PurchaseInteraction val in array) { if (!val.available || Object.op_Implicit((Object)(object)((Component)val).GetComponent<ParallelOwner>())) { continue; } NetworkIdentity component = ((Component)val).GetComponent<NetworkIdentity>(); if (!Object.op_Implicit((Object)(object)component)) { continue; } NetworkSceneId sceneId = component.sceneId; if (!((NetworkSceneId)(ref sceneId)).IsEmpty()) { InteractableSpawnCard val2 = PrivatePrefabs.FindCardForSceneObject(val); if (Object.op_Implicit((Object)(object)val2)) { list.Add(new Fixed { obj = ((Component)val).gameObject, card = val2 }); } } } return list; } private static void CopyFixedObjects(List<Fixed> fixedObjects, List<NetworkUser> players) { //IL_003f: 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) //IL_0094: Unknown result type (might be due to invalid IL or missing references) foreach (Fixed fixedObject in fixedObjects) { Transform transform = fixedObject.obj.transform; foreach (NetworkUser player in players) { GameObject obj = Object.Instantiate<GameObject>(((SpawnCard)fixedObject.card).prefab, transform.position, transform.rotation); obj.GetComponent<ParallelOwner>().SetOwner(player); NetworkServer.Spawn(obj); } NetworkIdentity component = fixedObject.obj.GetComponent<NetworkIdentity>(); if (Object.op_Implicit((Object)(object)component)) { NetworkInstanceId netId = component.netId; if (!((NetworkInstanceId)(ref netId)).IsEmpty()) { NetworkServer.Destroy(fixedObject.obj); continue; } } Object.Destroy((Object)(object)fixedObject.obj); } } private static void AssignItemBoxes(StringBuilder log) { if (ModConfig.PrivateItemBoxes.Value) { AssignItemBoxes(log, "iscLockbox", Items.TreasureCache); AssignItemBoxes(log, "iscLockboxVoid", Items.TreasureCacheVoid); AssignItemBoxes(log, "iscFreeChest", Items.FreeChest); } } private static void AssignItemBoxes(StringBuilder log, string cardName, ItemDef item) { if (!Object.op_Implicit((Object)(object)item) || !itemBoxes.TryGetValue(cardName, out var value)) { return; } List<CharacterMaster> list = CharacterMaster.readOnlyInstancesList.Where((CharacterMaster master) => Object.op_Implicit((Object)(object)master.inventory) && master.inventory.GetItemCountEffective(item) > 0).ToList(); ParallelOwner parallelOwner = default(ParallelOwner); for (int num = 0; num < value.Count; num++) { GameObject val = value[num]; if (Object.op_Implicit((Object)(object)val) && val.TryGetComponent<ParallelOwner>(ref parallelOwner)) { NetworkUser val2 = ((num < list.Count) ? PlayerFor(list[num]) : null); if (Object.op_Implicit((Object)(object)val2)) { parallelOwner.SetOwner(val2); } log.Append("\n " + cardName + ": " + (Object.op_Implicit((Object)(object)val2) ? val2.userName : "shared")); } } } private static NetworkUser PlayerFor(CharacterMaster master) { if (Object.op_Implicit((Object)(object)master.playerCharacterMasterController)) { return master.playerCharacterMasterController.networkUser; } CharacterMaster val = (Object.op_Implicit((Object)(object)master.minionOwnership) ? master.minionOwnership.ownerMaster : null); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.playerCharacterMasterController)) { return null; } return val.playerCharacterMasterController.networkUser; } private static void Shuffle<T>(List<T> list, Xoroshiro128Plus rng) { for (int num = list.Count - 1; num > 0; num--) { int num2 = rng.RangeInt(0, num + 1); int index = num; int index2 = num2; T value = list[num2]; T value2 = list[num]; list[index] = value; list[index2] = value2; } } private static string ShortName(Spawned spawned) { return string.Format("{0} {1}", ((Object)spawned.obj).name.Replace("(Clone)", ""), spawned.value); } private static void CountSpawn(SortedDictionary<string, (int count, int value)> counts, DirectorCard card) { SpawnCard spawnCard = card.GetSpawnCard(); string key = (Object.op_Implicit((Object)(object)spawnCard) ? ((Object)spawnCard).name : "unknown"); counts.TryGetValue(key, out (int, int) value); counts[key] = (value.Item1 + 1, value.Item2 + card.cost); } private static void LogSpawnCounts() { if (DebugLog.Enabled) { StringBuilder stringBuilder = new StringBuilder(); string[] obj = new string[6] { "Director spawns on ", null, null, null, null, null }; SceneDef mostRecentSceneDef = SceneCatalog.mostRecentSceneDef; obj[1] = ((mostRecentSceneDef != null) ? mostRecentSceneDef.cachedName : null); obj[2] = " (artifact "; obj[3] = (ParallelArtifact.Enabled ? "on" : "off"); obj[4] = ", "; obj[5] = $"{NetworkUser.readOnlyInstancesList.Count((NetworkUser user) => Object.op_Implicit((Object)(object)user) && Object.op_Implicit((Object)(object)user.master))} players, before the split):"; stringBuilder.Append(string.Concat(obj)); AppendCounts(stringBuilder, "shared", sharedCounts); AppendCounts(stringBuilder, "private", privateCounts); DebugLog.Info(stringBuilder.ToString()); } } private static void AppendCounts(StringBuilder log, string label, SortedDictionary<string, (int count, int value)> counts) { log.Append($"\n {label}: {counts.Values.Sum(((int count, int value) entry) => entry.count)} objects, value {counts.Values.Sum(((int count, int value) entry) => entry.value)}"); foreach (KeyValuePair<string, (int, int)> count in counts) { log.Append($"\n {count.Key} x{count.Value.Item1} (value {count.Value.Item2})"); } } private static void Log(StringBuilder text) { DebugLog.Info(text.ToString()); } } internal static class ModConfig { public static ConfigEntry<bool> PrivateChests { get; private set; } public static ConfigEntry<bool> PrivateEquipmentBarrels { get; private set; } public static ConfigEntry<bool> PrivateLunarPods { get; private set; } public static ConfigEntry<bool> PrivateVoidCradles { get; private set; } public static ConfigEntry<bool> PrivateShrineOfChance { get; private set; } public static ConfigEntry<bool> PrivateMultishops { get; private set; } public static ConfigEntry<bool> PrivateItemBoxes { get; private set; } public static ConfigEntry<bool> DebugLogging { get; private set; } public static void Init(ConfigFile config) { PrivateChests = config.Bind<bool>("Private", "Chests", true, "Small, large, legendary, category and adaptive chests are split between players."); PrivateEquipmentBarrels = config.Bind<bool>("Private", "EquipmentBarrels", true, "Equipment barrels are split between players."); PrivateLunarPods = config.Bind<bool>("Private", "LunarPods", true, "Lunar pods are split between players."); PrivateVoidCradles = config.Bind<bool>("Private", "VoidCradles", true, "Void cradles and void potentials are split between players."); PrivateShrineOfChance = config.Bind<bool>("Private", "ShrineOfChance", true, "Shrines of Chance are split between players."); PrivateMultishops = config.Bind<bool>("Private", "Multishops", true, "Multishop terminals are split between players."); PrivateItemBoxes = config.Bind<bool>("Private", "ItemBoxes", true, "Rusted Key lockboxes, Encrusted Key caches and Shipping Request Form chests belong to the player whose item spawned them."); DebugLogging = config.Bind<bool>("Debug", "Logging", false, "Log each stage's loot split, ownership changes, and interaction, Drifter, ping and hiding checks. For testing."); } public static string Summary() { return $"Chests={PrivateChests.Value} EquipmentBarrels={PrivateEquipmentBarrels.Value} LunarPods={PrivateLunarPods.Value} " + $"VoidCradles={PrivateVoidCradles.Value} ShrineOfChance={PrivateShrineOfChance.Value} Multishops={PrivateMultishops.Value} " + $"ItemBoxes={PrivateItemBoxes.Value}"; } } internal static class OwnershipTracker { private const float Interval = 0.25f; private const float DeathDelay = 1f; private static readonly HashSet<NetworkUserId> aliveThisStage = new HashSet<NetworkUserId>(); private static readonly Dictionary<NetworkUserId, float> deadSince = new Dictionary<NetworkUserId, float>(); private static readonly Dictionary<NetworkUserId, bool> lastShared = new Dictionary<NetworkUserId, bool>(); private static readonly Dictionary<NetworkUserId, (NetworkUser user, bool shared)> states = new Dictionary<NetworkUserId, (NetworkUser, bool)>(); private static readonly Dictionary<NetworkUserId, string> names = new Dictionary<NetworkUserId, string>(); private static readonly List<ParallelOwner> buffer = new List<ParallelOwner>(); private static float timer; public static void Init() { SceneDirector.onPrePopulateSceneServer += delegate { aliveThisStage.Clear(); deadSince.Clear(); lastShared.Clear(); }; } public static void FixedUpdate() { //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_0096: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) timer -= Time.fixedDeltaTime; if (timer > 0f) { return; } timer = 0.25f; if (!NetworkServer.active || !ParallelArtifact.Enabled || ParallelOwner.Instances.Count == 0) { return; } states.Clear(); buffer.Clear(); buffer.AddRange(ParallelOwner.Instances); foreach (ParallelOwner item in buffer) { if (Object.op_Implicit((Object)(object)item) && item.HasOwner) { NetworkUserId ownerId = item.OwnerId; if (!states.TryGetValue(ownerId, out (NetworkUser, bool) value)) { value = Evaluate(ownerId); states[ownerId] = value; LogChange(ownerId, value); } item.ServerUpdateState(value.Item1, value.Item2); } } } private static (NetworkUser user, bool shared) Evaluate(NetworkUserId id) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_0066: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: 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_00be: Unknown result type (might be due to invalid IL or missing references) NetworkUser val = null; foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances)) { NetworkUserId id2 = readOnlyInstances.id; if (((NetworkUserId)(ref id2)).Equals(id)) { val = readOnlyInstances; break; } } } if (!Object.op_Implicit((Object)(object)val)) { return (user: null, shared: true); } names[id] = val.userName; CharacterMaster master = val.master; CharacterBody val2 = (Object.op_Implicit((Object)(object)master) ? master.GetBody() : null); if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.healthComponent) && val2.healthComponent.alive) { aliveThisStage.Add(id); deadSince.Remove(id); return (user: val, shared: false); } if (!aliveThisStage.Contains(id)) { return (user: val, shared: false); } if (!deadSince.TryGetValue(id, out var value)) { value = (deadSince[id] = Time.time); } return (user: val, shared: Time.time - value >= 1f); } private static void LogChange(NetworkUserId id, (NetworkUser user, bool shared) state) { //IL_0005: 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_0054: Unknown result type (might be due to invalid IL or missing references) if (!lastShared.TryGetValue(id, out var value) || value != state.shared) { lastShared[id] = state.shared; if (DebugLog.Enabled && (value || state.shared)) { string value2; string text = (Object.op_Implicit((Object)(object)state.user) ? state.user.userName : (names.TryGetValue(id, out value2) ? value2 : "unknown player")); string text2 = ((!Object.op_Implicit((Object)(object)state.user)) ? "disconnected" : (state.shared ? "dead" : "back")); ArtifactOfParallelPlugin.Log.LogInfo((object)("Owner " + text + " " + text2 + ": objects now " + (state.shared ? "shared" : "private"))); } } } } public static class ParallelApi { public static event Action<GameObject, bool> LocalVisibilityChanged; public static bool IsVisibleToLocalPlayer(GameObject obj) { if (!Object.op_Implicit((Object)(object)obj)) { return false; } ParallelOwner componentInParent = obj.GetComponentInParent<ParallelOwner>(); if (Object.op_Implicit((Object)(object)componentInParent)) { return !componentInParent.IsLocallyHidden; } return true; } internal static void RaiseLocalVisibilityChanged(GameObject obj, bool visible) { if (DebugLog.Enabled) { Action<GameObject, bool> localVisibilityChanged = ParallelApi.LocalVisibilityChanged; int num = ((localVisibilityChanged != null) ? localVisibilityChanged.GetInvocationList().Length : 0); DebugLog.Info(string.Format("ParallelApi: {0} visible={1}, {2} listeners", Object.op_Implicit((Object)(object)obj) ? ((Object)obj).name : "null", visible, num)); } if (ParallelApi.LocalVisibilityChanged == null) { return; } try { ParallelApi.LocalVisibilityChanged(obj, visible); } catch (Exception arg) { ArtifactOfParallelPlugin.Log.LogError((object)$"ParallelApi.LocalVisibilityChanged handler failed: {arg}"); } } } internal static class ParallelArtifact { private const string NameToken = "KAITORQUE_ARTIFACT_PARALLEL_NAME"; private const string DescriptionToken = "KAITORQUE_ARTIFACT_PARALLEL_DESC"; public static ArtifactDef Def { get; private set; } public static bool Enabled { get { if (Object.op_Implicit((Object)(object)Def) && Object.op_Implicit((Object)(object)RunArtifactManager.instance)) { return RunArtifactManager.instance.IsArtifactEnabled(Def); } return false; } } public static void Register() { LanguageAPI.Add("KAITORQUE_ARTIFACT_PARALLEL_NAME", "Artifact of Parallel"); LanguageAPI.Add("KAITORQUE_ARTIFACT_PARALLEL_DESC", "Chests, shrines and shops are split between players. You only see your own; anything that drops is shared. Total loot stays the same."); Def = ScriptableObject.CreateInstance<ArtifactDef>(); Def.cachedName = "KaitorqueParallel"; Def.nameToken = "KAITORQUE_ARTIFACT_PARALLEL_NAME"; Def.descriptionToken = "KAITORQUE_ARTIFACT_PARALLEL_DESC"; Def.smallIconSelectedSprite = LoadSprite("texArtifactParallelEnabled.png"); Def.smallIconDeselectedSprite = LoadSprite("texArtifactParallelDisabled.png"); if (!ContentAddition.AddArtifactDef(Def)) { ArtifactOfParallelPlugin.Log.LogError((object)"Failed to register the artifact."); } } private static Sprite LoadSprite(string fileName) { //IL_0054: 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_0061: Expected O, but got Unknown //IL_0082: 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) using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ArtifactOfParallel." + fileName); if (stream == null) { ArtifactOfParallelPlugin.Log.LogError((object)("Missing embedded icon " + fileName + ".")); return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1 }; ImageConversion.LoadImage(val, array); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } } public class ParallelOwner : NetworkBehaviour { private const uint OwnerDirtyBit = 1u; private NetworkUserId ownerId; private bool hasOwner; private NetworkInstanceId ownerNetId = NetworkInstanceId.Invalid; private bool isPrivate; private const float RescanInterval = 0.25f; private readonly HashSet<Behaviour> hiddenBehaviours = new HashSet<Behaviour>(); private readonly HashSet<Renderer> hiddenRenderers = new HashSet<Renderer>(); private readonly HashSet<Collider> hiddenColliders = new HashSet<Collider>(); private bool locallyHidden; private float rescanTimer; private static readonly List<ParallelOwner> instances = new List<ParallelOwner>(); public static IReadOnlyList<ParallelOwner> Instances => instances; public bool IsPrivate => isPrivate; public bool HasOwner => hasOwner; public NetworkUserId OwnerId => ownerId; public NetworkInstanceId OwnerNetId => ownerNetId; private static bool IsMidStage { get { //IL_001d: 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) if (Object.op_Implicit((Object)(object)Run.instance) && Object.op_Implicit((Object)(object)Stage.instance)) { FixedTimeStamp entryTime = Stage.instance.entryTime; return ((FixedTimeStamp)(ref entryTime)).timeSince > 2f; } return false; } } public bool IsLocallyHidden => locallyHidden; private void OnEnable() { instances.Add(this); } private void OnDisable() { instances.Remove(this); } public unsafe void SetOwner(NetworkUser owner) { //IL_0021: 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_001d: 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_0042: 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_0057: 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) hasOwner = Object.op_Implicit((Object)(object)owner); ownerId = (NetworkUserId)(Object.op_Implicit((Object)(object)owner) ? owner.id : default(NetworkUserId)); ApplyState(hasOwner, Object.op_Implicit((Object)(object)owner) ? ((NetworkBehaviour)owner).netId : NetworkInstanceId.Invalid); MultiShopController val = default(MultiShopController); if (!((Component)this).TryGetComponent<MultiShopController>(ref val)) { return; } ReadOnlyArray<GameObject> terminalGameObjectsList = val.terminalGameObjectsList; ParallelOwner parallelOwner = default(ParallelOwner); for (int i = 0; i < terminalGameObjectsList.Length; i++) { if (Object.op_Implicit((Object)Unsafe.Read<object>((void*)terminalGameObjectsList[i])) && ((GameObject)Unsafe.Read<object>((void*)terminalGameObjectsList[i])).TryGetComponent<ParallelOwner>(ref parallelOwner)) { parallelOwner.SetOwner(owner); } } } public void ServerUpdateState(NetworkUser currentOwner, bool shared) { //IL_002e: 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_0033: 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_003d: 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) if (hasOwner && !IsUsed()) { bool flag = !shared && Object.op_Implicit((Object)(object)currentOwner); NetworkInstanceId val = (Object.op_Implicit((Object)(object)currentOwner) ? ((NetworkBehaviour)currentOwner).netId : NetworkInstanceId.Invalid); if (flag != isPrivate || val != ownerNetId) { ApplyState(flag, val); } } } private void ApplyState(bool newPrivate, NetworkInstanceId newOwnerNetId) { //IL_0008: 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) isPrivate = newPrivate; ownerNetId = newOwnerNetId; if (((NetworkBehaviour)this).isServer) { ((NetworkBehaviour)this).SetDirtyBit(1u); ((Component)this).GetComponent<NetworkIdentity>().RebuildObservers(false); } RefreshLocalVisibility(); } private unsafe bool IsUsed() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) PurchaseInteraction val = default(PurchaseInteraction); if (((Component)this).TryGetComponent<PurchaseInteraction>(ref val)) { return !val.available; } MultiShopController val2 = default(MultiShopController); if (((Component)this).TryGetComponent<MultiShopController>(ref val2)) { ReadOnlyArray<GameObject> terminalGameObjectsList = val2.terminalGameObjectsList; PurchaseInteraction val3 = default(PurchaseInteraction); for (int i = 0; i < terminalGameObjectsList.Length; i++) { if (Object.op_Implicit((Object)Unsafe.Read<object>((void*)terminalGameObjectsList[i])) && ((GameObject)Unsafe.Read<object>((void*)terminalGameObjectsList[i])).TryGetComponent<PurchaseInteraction>(ref val3) && val3.available) { return false; } } return terminalGameObjectsList.Length > 0; } return false; } public bool IsOwnedBy(NetworkUser user) { //IL_0012: 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_002c: 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_0035: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)user)) { return false; } if (!NetworkServer.active) { return ((NetworkBehaviour)user).netId == ownerNetId; } if (hasOwner) { NetworkUserId id = user.id; return ((NetworkUserId)(ref id)).Equals(ownerId); } return false; } public bool CanBeUsedBy(Interactor activator) { return CanBeUsedBy(UserOf(Object.op_Implicit((Object)(object)activator) ? ((Component)activator).GetComponent<CharacterBody>() : null)); } public bool CanBeUsedBy(NetworkUser user) { if (isPrivate) { return IsOwnedBy(user); } return true; } public static NetworkUser UserOf(CharacterBody body) { CharacterMaster val = (Object.op_Implicit((Object)(object)body) ? body.master : null); PlayerCharacterMasterController val2 = (Object.op_Implicit((Object)(object)val) ? val.playerCharacterMasterController : null); if (!Object.op_Implicit((Object)(object)val2)) { return null; } return val2.networkUser; } public bool IsVisibleToLocalPlayer() { if (!isPrivate) { return true; } foreach (NetworkUser readOnlyLocalPlayers in NetworkUser.readOnlyLocalPlayersList) { if (IsOwnedBy(readOnlyLocalPlayers)) { return true; } } return false; } public string OwnerName() { //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) foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (IsOwnedBy(readOnlyInstances)) { return readOnlyInstances.userName; } } if (!hasOwner && !(ownerNetId != NetworkInstanceId.Invalid)) { return "none"; } return "absent player"; } public string Describe() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) string text = OwnerName(); int num = 0; Renderer[] array = Parts<Renderer>(includeInactive: false); foreach (Renderer val in array) { if (val.enabled && !val.forceRenderingOff) { num++; } } NetworkIdentity component = ((Component)this).GetComponent<NetworkIdentity>(); string text2 = ((NetworkServer.active && Object.op_Implicit((Object)(object)component) && component.observers != null) ? component.observers.Count.ToString() : "-"); return $"{((Object)this).name} netId={((NetworkBehaviour)this).netId} private={isPrivate} owner={text} ownerNetId={ownerNetId} " + $"visibleToLocal={IsVisibleToLocalPlayer()} hidden={locallyHidden} renderersOn={num} observers={text2} pos={((Component)this).transform.position}"; } public override bool OnCheckObserver(NetworkConnection conn) { if (isPrivate) { return IsOwnerConnection(conn); } return true; } public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initialize) { AddObservers(NetworkServer.connections, observers); AddObservers(NetworkServer.localConnections, observers); return true; } public override void OnSetLocalVisibility(bool vis) { RefreshLocalVisibility(); } private void AddObservers(IList<NetworkConnection> connections, HashSet<NetworkConnection> observers) { foreach (NetworkConnection connection in connections) { if (connection != null && connection.isReady && ((NetworkBehaviour)this).OnCheckObserver(connection)) { observers.Add(connection); } } } private bool IsOwnerConnection(NetworkConnection conn) { foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (((NetworkBehaviour)readOnlyInstances).connectionToClient == conn && IsOwnedBy(readOnlyInstances)) { return true; } } return false; } private void Start() { RefreshLocalVisibility(); } public void RefreshLocalVisibility() { bool flag = NetworkUser.readOnlyLocalPlayersList.Count > 0 && !IsVisibleToLocalPlayer(); if (flag == locallyHidden) { return; } locallyHidden = flag; if (DebugLog.Enabled) { DebugLog.Info("Visibility: " + ((Object)this).name + " (owner " + OwnerName() + ") is now " + (flag ? "hidden" : "visible") + " here"); } if (flag) { Hide(); } else { Show(); } PingIndicator[] array = PingIndicator.instancesList.ToArray(); foreach (PingIndicator val in array) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.pingTarget) && (Object)(object)val.pingTarget.GetComponentInParent<ParallelOwner>() == (Object)(object)this) { val.RebuildPing(); } } ParallelApi.RaiseLocalVisibilityChanged(((Component)this).gameObject, !flag); } public override void OnStartClient() { if (!NetworkServer.active && IsMidStage) { ParallelApi.RaiseLocalVisibilityChanged(((Component)this).gameObject, visible: true); } } public override void OnNetworkDestroy() { if (!NetworkServer.active && IsMidStage) { ParallelApi.RaiseLocalVisibilityChanged(((Component)this).gameObject, visible: false); } } private void LateUpdate() { if (!locallyHidden) { return; } rescanTimer -= Time.deltaTime; if (rescanTimer <= 0f) { rescanTimer = 0.25f; Hide(); } int num = 0; foreach (Renderer hiddenRenderer in hiddenRenderers) { if (Object.op_Implicit((Object)(object)hiddenRenderer) && !hiddenRenderer.forceRenderingOff) { hiddenRenderer.forceRenderingOff = true; num++; } } foreach (Collider hiddenCollider in hiddenColliders) { if (Object.op_Implicit((Object)(object)hiddenCollider) && hiddenCollider.enabled) { hiddenCollider.enabled = false; num++; } } foreach (Behaviour hiddenBehaviour in hiddenBehaviours) { if (Object.op_Implicit((Object)(object)hiddenBehaviour) && hiddenBehaviour.enabled) { hiddenBehaviour.enabled = false; num++; HologramProjector val = (HologramProjector)(object)((hiddenBehaviour is HologramProjector) ? hiddenBehaviour : null); if (val != null && Object.op_Implicit((Object)(object)val.hologramContentInstance)) { Object.Destroy((Object)(object)val.hologramContentInstance); val.hologramContentInstance = null; } } } if (num > 0 && DebugLog.Enabled) { DebugLog.Once("rehide:" + ((Object)this).GetInstanceID(), $"Hiding: {((Object)this).name} (owner {OwnerName()}) had {num} hidden parts switched back on (e.g. Drifter grab or throw); hid them again."); } } private T[] Parts<T>(bool includeInactive = false) where T : Component { T[] componentsInChildren = ((Component)this).GetComponentsInChildren<T>(includeInactive); ModelLocator component = ((Component)this).GetComponent<ModelLocator>(); Transform val = (Object.op_Implicit((Object)(object)component) ? component.modelTransform : null); if (!Object.op_Implicit((Object)(object)val) || val.IsChildOf(((Component)this).transform)) { return componentsInChildren; } T[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<T>(includeInactive); if (componentsInChildren2.Length == 0) { return componentsInChildren; } T[] array = new T[componentsInChildren.Length + componentsInChildren2.Length]; componentsInChildren.CopyTo(array, 0); componentsInChildren2.CopyTo(array, componentsInChildren.Length); return array; } private void Hide() { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; Renderer[] array = Parts<Renderer>(includeInactive: true); foreach (Renderer val in array) { if (hiddenRenderers.Add(val)) { val.forceRenderingOff = true; num++; } } Collider[] array2 = Parts<Collider>(includeInactive: false); foreach (Collider val2 in array2) { if (val2.enabled && hiddenColliders.Add(val2)) { val2.enabled = false; num2++; } } Light[] array3 = Parts<Light>(includeInactive: false); foreach (Light val3 in array3) { if (((Behaviour)val3).enabled) { hiddenBehaviours.Add((Behaviour)(object)val3); ((Behaviour)val3).enabled = false; num3++; } } Highlight[] array4 = Parts<Highlight>(includeInactive: false); foreach (Highlight val4 in array4) { val4.isOn = false; if (((Behaviour)val4).enabled && hiddenBehaviours.Add((Behaviour)(object)val4)) { ((Behaviour)val4).enabled = false; } } HologramProjector[] array5 = Parts<HologramProjector>(includeInactive: false); foreach (HologramProjector val5 in array5) { if (Object.op_Implicit((Object)(object)val5.hologramContentInstance)) { Object.Destroy((Object)(object)val5.hologramContentInstance); val5.hologramContentInstance = null; num4++; } if (((Behaviour)val5).enabled && hiddenBehaviours.Add((Behaviour)(object)val5)) { ((Behaviour)val5).enabled = false; } } if (num + num2 + num3 + num4 > 0 && DebugLog.Enabled) { DebugLog.Info($"Hiding: {((Object)this).name} (owner {OwnerName()}) renderers+{num} colliders+{num2} lights+{num3} priceLabels+{num4}"); } } private void Show() { foreach (Renderer hiddenRenderer in hiddenRenderers) { if (Object.op_Implicit((Object)(object)hiddenRenderer)) { hiddenRenderer.forceRenderingOff = false; } } foreach (Collider hiddenCollider in hiddenColliders) { if (Object.op_Implicit((Object)(object)hiddenCollider)) { hiddenCollider.enabled = true; } } foreach (Behaviour hiddenBehaviour in hiddenBehaviours) { if (Object.op_Implicit((Object)(object)hiddenBehaviour)) { hiddenBehaviour.enabled = true; } } hiddenRenderers.Clear(); hiddenColliders.Clear(); hiddenBehaviours.Clear(); } public override bool OnSerialize(NetworkWriter writer, bool initialState) { if (initialState) { WriteState(writer); return true; } uint syncVarDirtyBits = ((NetworkBehaviour)this).syncVarDirtyBits; writer.WritePackedUInt32(syncVarDirtyBits); if (syncVarDirtyBits != 0) { WriteState(writer); } return syncVarDirtyBits != 0; } public override void OnDeserialize(NetworkReader reader, bool initialState) { //IL_000d: 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) if (initialState || reader.ReadPackedUInt32() != 0) { ownerNetId = reader.ReadNetworkId(); isPrivate = reader.ReadBoolean(); RefreshLocalVisibility(); } } private void WriteState(NetworkWriter writer) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) writer.Write(ownerNetId); writer.Write(isPrivate); } } internal enum PrivateType { Chest, EquipmentBarrel, LunarPod, VoidCradle, ShrineOfChance, Multishop, ItemBox } internal static class PrivatePrefabs { public const string SmallChest = "RoR2/Base/Chest1/iscChest1.asset"; private static readonly (string key, PrivateType type)[] SpawnCards = new(string, PrivateType)[25] { ("RoR2/Base/Chest1/iscChest1.asset", PrivateType.Chest), ("RoR2/Base/Chest1StealthedVariant/iscChest1Stealthed.asset", PrivateType.Chest), ("RoR2/Base/Chest2/iscChest2.asset", PrivateType.Chest), ("RoR2/Base/GoldChest/iscGoldChest.asset", PrivateType.Chest), ("RoR2/Base/CategoryChest/iscCategoryChestDamage.asset", PrivateType.Chest), ("RoR2/Base/CategoryChest/iscCategoryChestHealing.asset", PrivateType.Chest), ("RoR2/Base/CategoryChest/iscCategoryChestUtility.asset", PrivateType.Chest), ("RoR2/DLC1/CategoryChest2/iscCategoryChest2Damage.asset", PrivateType.Chest), ("RoR2/DLC1/CategoryChest2/iscCategoryChest2Healing.asset", PrivateType.Chest), ("RoR2/DLC1/CategoryChest2/iscCategoryChest2Utility.asset", PrivateType.Chest), ("RoR2/Base/CasinoChest/iscCasinoChest.asset", PrivateType.Chest), ("RoR2/Base/EquipmentBarrel/iscEquipmentBarrel.asset", PrivateType.EquipmentBarrel), ("RoR2/Base/LunarChest/iscLunarChest.asset", PrivateType.LunarPod), ("RoR2/DLC1/VoidChest/iscVoidChest.asset", PrivateType.VoidCradle), ("RoR2/DLC1/VoidChest/iscVoidChestSacrificeOn.asset", PrivateType.VoidCradle), ("RoR2/DLC1/VoidTriple/iscVoidTriple.asset", PrivateType.VoidCradle), ("RoR2/Base/ShrineChance/iscShrineChance.asset", PrivateType.ShrineOfChance), ("RoR2/Base/ShrineChance/iscShrineChanceSandy.asset", PrivateType.ShrineOfChance), ("RoR2/Base/ShrineChance/iscShrineChanceSnowy.asset", PrivateType.ShrineOfChance), ("RoR2/Base/TripleShop/iscTripleShop.asset", PrivateType.Multishop), ("RoR2/Base/TripleShopLarge/iscTripleShopLarge.asset", PrivateType.Multishop), ("RoR2/Base/TripleShopEquipment/iscTripleShopEquipment.asset", PrivateType.Multishop), ("RoR2/Junk/TreasureCache/iscLockbox.asset", PrivateType.ItemBox), ("RoR2/DLC1/TreasureCacheVoid/iscLockboxVoid.asset", PrivateType.ItemBox), ("RoR2/DLC1/FreeChest/iscFreeChest.asset", PrivateType.ItemBox) }; private static readonly Dictionary<SpawnCard, PrivateType> typeByCard = new Dictionary<SpawnCard, PrivateType>(); private static readonly Dictionary<string, InteractableSpawnCard> cardByNameToken = new Dictionary<string, InteractableSpawnCard>(); public static void Init() { //IL_0022: 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) (string, PrivateType)[] spawnCards = SpawnCards; MultiShopController val2 = default(MultiShopController); PurchaseInteraction val3 = default(PurchaseInteraction); for (int i = 0; i < spawnCards.Length; i++) { (string, PrivateType) tuple = spawnCards[i]; string item = tuple.Item1; PrivateType item2 = tuple.Item2; InteractableSpawnCard val = Addressables.LoadAssetAsync<InteractableSpawnCard>((object)item).WaitForCompletion(); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((SpawnCard)val).prefab)) { ArtifactOfParallelPlugin.Log.LogError((object)("Spawn card " + item + " not found.")); continue; } AddOwner(((SpawnCard)val).prefab); if (((SpawnCard)val).prefab.TryGetComponent<MultiShopController>(ref val2) && Object.op_Implicit((Object)(object)val2.terminalPrefab)) { AddOwner(val2.terminalPrefab); } typeByCard[(SpawnCard)(object)val] = item2; if (item2 != PrivateType.ItemBox && ((SpawnCard)val).prefab.TryGetComponent<PurchaseInteraction>(ref val3) && !string.IsNullOrEmpty(val3.displayNameToken) && !cardByNameToken.ContainsKey(val3.displayNameToken)) { cardByNameToken[val3.displayNameToken] = val; } } } private static void AddOwner(GameObject prefab) { if (!Object.op_Implicit((Object)(object)prefab.GetComponent<ParallelOwner>())) { prefab.AddComponent<ParallelOwner>(); } } public static InteractableSpawnCard Load(string key) { //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) return Addressables.LoadAssetAsync<InteractableSpawnCard>((object)key).WaitForCompletion(); } public static bool IsPrivate(SpawnCard card) { if (Object.op_Implicit((Object)(object)card) && typeByCard.TryGetValue(card, out var value)) { return IsEnabled(value); } return false; } public static bool IsItemBox(SpawnCard card) { if (Object.op_Implicit((Object)(object)card) && typeByCard.TryGetValue(card, out var value)) { return value == PrivateType.ItemBox; } return false; } public static InteractableSpawnCard FindCardForSceneObject(PurchaseInteraction purchase) { if (Object.op_Implicit((Object)(object)purchase) && !string.IsNullOrEmpty(purchase.displayNameToken) && cardByNameToken.TryGetValue(purchase.displayNameToken, out var value) && IsPrivate((SpawnCard)(object)value)) { return value; } return null; } public static bool IsPrivateType(PurchaseInteraction purchase) { if (Object.op_Implicit((Object)(object)purchase) && !string.IsNullOrEmpty(purchase.displayNameToken)) { return cardByNameToken.ContainsKey(purchase.displayNameToken); } return false; } private static bool IsEnabled(PrivateType type) { return ((ConfigEntry<bool>)(type switch { PrivateType.Chest => ModConfig.PrivateChests, PrivateType.EquipmentBarrel => ModConfig.PrivateEquipmentBarrels, PrivateType.LunarPod => ModConfig.PrivateLunarPods, PrivateType.VoidCradle => ModConfig.PrivateVoidCradles, PrivateType.ShrineOfChance => ModConfig.PrivateShrineOfChance, PrivateType.Multishop => ModConfig.PrivateMultishops, PrivateType.ItemBox => ModConfig.PrivateItemBoxes, _ => null, }))?.Value ?? false; } } internal static class RiskOfOptionsCompat { public const string Guid = "com.rune580.riskofoptions"; public static void TryInit() { if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions")) { Init(); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void Init() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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 //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown ModSettingsManager.SetModDescription("Splits chests, shrines and shops between players. The host's settings apply, read at the start of each stage."); if (Object.op_Implicit((Object)(object)ParallelArtifact.Def) && Object.op_Implicit((Object)(object)ParallelArtifact.Def.smallIconSelectedSprite)) { ModSettingsManager.SetModIcon(ParallelArtifact.Def.smallIconSelectedSprite); } ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateChests)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateEquipmentBarrels)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateLunarPods)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateVoidCradles)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateShrineOfChance)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateMultishops)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.PrivateItemBoxes)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModConfig.DebugLogging)); DebugLog.Info("Risk Of Options settings page added."); } } internal static class SceneModelCleanup { [CompilerGenerated] private static class <>O { public static hook_Start <0>__ModelLocator_Start; } public static void Init() { //IL_0010: 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_001b: Expected O, but got Unknown object obj = <>O.<0>__ModelLocator_Start; if (obj == null) { hook_Start val = ModelLocator_Start; <>O.<0>__ModelLocator_Start = val; obj = (object)val; } ModelLocator.Start += (hook_Start)obj; } private static void ModelLocator_Start(orig_Start orig, ModelLocator self) { //IL_003f: 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) orig.Invoke(self); if (NetworkServer.active || !ParallelArtifact.Enabled) { return; } Transform modelTransform = self.modelTransform; NetworkIdentity val = default(NetworkIdentity); if (Object.op_Implicit((Object)(object)modelTransform) && !modelTransform.IsChildOf(((Component)self).transform) && ((Component)self).TryGetComponent<NetworkIdentity>(ref val)) { NetworkSceneId sceneId = val.sceneId; PurchaseInteraction purchase = default(PurchaseInteraction); if (!((NetworkSceneId)(ref sceneId)).IsEmpty() && ((Component)self).TryGetComponent<PurchaseInteraction>(ref purchase) && PrivatePrefabs.IsPrivateType(purchase)) { ((Component)self).gameObject.AddComponent<DetachedModelFollower>().model = ((Component)modelTransform).gameObject; } } } } internal class DetachedModelFollower : MonoBehaviour { public GameObject model; private void OnEnable() { if (Object.op_Implicit((Object)(object)model)) { model.SetActive(true); } } private void OnDisable() { //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_0040: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)model)) { Scene scene = ((Component)this).gameObject.scene; if (((Scene)(ref scene)).isLoaded) { model.SetActive(false); DebugLog.Info($"Scene object: hid the leftover model of removed {((Object)this).name} at {((Component)this).transform.position}"); } } } } internal static class TestCommands { private const float Spacing = 3f; [ConCommand(/*Could not decode attribute arguments.*/)] private static void SpawnTestChests(ConCommandArgs args) { //IL_007f: 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_0084: 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_008e: 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_0097: Unknown result type (might be due to invalid IL or missing references) //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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_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) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018c: 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_01de: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) CharacterBody senderBody = ((ConCommandArgs)(ref args)).senderBody; if (!NetworkServer.active || !Object.op_Implicit((Object)(object)senderBody)) { Debug.Log((object)"parallel_test_chests: needs a living character on the host's run."); return; } InteractableSpawnCard val = PrivatePrefabs.Load("RoR2/Base/Chest1/iscChest1.asset"); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((SpawnCard)val).prefab) || !Object.op_Implicit((Object)(object)((SpawnCard)val).prefab.GetComponent<ParallelOwner>())) { Debug.Log((object)"parallel_test_chests: small chest spawn card failed to load; see the startup log."); return; } Vector3 val2 = Vector3.ProjectOnPlane(Object.op_Implicit((Object)(object)senderBody.inputBank) ? senderBody.inputBank.aimDirection : ((Component)senderBody).transform.forward, Vector3.up); Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3 val3 = Vector3.Cross(Vector3.up, normalized); Vector3 val4 = senderBody.footPosition + normalized * 6f; Quaternion val5 = Quaternion.LookRotation(-normalized, Vector3.up); int num = 0; foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances.master)) { num++; } } int num2 = 0; RaycastHit val8 = default(RaycastHit); foreach (NetworkUser readOnlyInstances2 in NetworkUser.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances2.master)) { Vector3 val6 = val4 + val3 * (((float)num2 - (float)(num - 1) * 0.5f) * 3f); Vector3 val7 = val6 + Vector3.up * 5f; Vector3 down = Vector3.down; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val7, down, ref val8, 20f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask))) { val6 = ((RaycastHit)(ref val8)).point; } GameObject obj = Object.Instantiate<GameObject>(((SpawnCard)val).prefab, val6, val5); obj.GetComponent<ParallelOwner>().SetOwner(readOnlyInstances2); NetworkServer.Spawn(obj); num2++; ArtifactOfParallelPlugin.Log.LogInfo((object)$"Test chest for {readOnlyInstances2.userName} at {val6}"); } } Debug.Log((object)$"parallel_test_chests: spawned {num2} chests."); } [ConCommand(commandName = "parallel_debug", helpText = "Lists the private objects that exist on this game, their owner and whether they're hidden here.")] private static void DebugList(ConCommandArgs args) { ParallelOwner[] array = Object.FindObjectsOfType<ParallelOwner>(); ArtifactOfParallelPlugin.Log.LogInfo((object)$"parallel_debug: server={NetworkServer.active} client={NetworkClient.active} objects={array.Length}"); ParallelOwner[] array2 = array; foreach (ParallelOwner parallelOwner in array2) { ArtifactOfParallelPlugin.Log.LogInfo((object)(" " + parallelOwner.Describe())); } Debug.Log((object)$"parallel_debug: {array.Length} private-type objects here; details in the BepInEx log."); } } }