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 BasicItemSync v0.1.0
BasicItemSync.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; 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.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BasicItemSync.Data; using BasicItemSync.Modules.Hooks; using BasicItemSync.Modules.Network.Client; using BasicItemSync.Modules.Network.Server; using BepInEx; using GenericVariableExtension; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SSMP.Api.Client; using SSMP.Api.Client.Networking; using SSMP.Api.Command; using SSMP.Api.Command.Server; using SSMP.Api.Server; using SSMP.Api.Server.Networking; using SSMP.Logging; using SSMP.Networking.Packet; using SSMP.Networking.Packet.Data; using Silksong.AssetHelper.ManagedAssets; using TeamCherry.Localization; using UnityEngine; using UnityEngine.SceneManagement; [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("BasicItemSync")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+40f31cdea2e2ed2295a61cb322755c47cd9a53b8")] [assembly: AssemblyProduct("BasicItemSync")] [assembly: AssemblyTitle("BasicItemSync")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/BobbyTheCatfish/BasicItemSync")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BasicItemSync { internal class Common { internal const string AddonName = "BasicItemSync"; internal const string AddonVersion = "0.1.0"; internal const int AddonApiVersion = 1; public static bool FlagAllowed(SyncServerSettings settings, FlagType flag) { if (ServerAddon.Settings.KillSwitch) { return false; } return flag switch { FlagType.Ability => settings.SyncAbilities, FlagType.Map => settings.SyncMaps, FlagType.Pin => settings.SyncPins, FlagType.Bellshrine => settings.SyncProgression, FlagType.Boss => settings.SyncBattles, FlagType.Arena => settings.SyncBattles, FlagType.Progression => settings.SyncProgression, FlagType.Flea => settings.SyncCollectables, FlagType.Collectable => settings.SyncCollectables, FlagType.Bellway => settings.SyncTransit, FlagType.Ventrica => settings.SyncTransit, FlagType.Currency => settings.SyncCurrency, FlagType.Mask => settings.SyncUpgrades, FlagType.Spool => settings.SyncUpgrades, FlagType.Pouch => settings.SyncUpgrades, FlagType.CraftingKit => settings.SyncUpgrades, FlagType.Quest => settings.SyncQuests, FlagType.Tool => settings.SyncTools, FlagType.Crest => settings.SyncTools, FlagType.SilkHeart => settings.SyncUpgrades, FlagType.Needle => settings.SyncUpgrades, FlagType.Bench => settings.SyncProgression, FlagType.Shortcut => true, FlagType.QuestItem => settings.SyncQuestItems, FlagType.DoNotSync => false, _ => false, }; } } internal enum FlagType { Ability, Map, Pin, Bellshrine, Boss, Arena, Progression, Collectable, Bellway, Ventrica, Mask, Spool, Pouch, CraftingKit, SilkHeart, Needle, Quest, QuestItem, Tool, Crest, Currency, Shortcut, Bench, Flea, DoNotSync } internal enum InternalCurrencyType { Rosary, ShellShard } internal enum Packets { BoolPlayerData, IntPlayerData, FloatPlayerData, Collectable, Currency, Quest, Tool, Upgrade, Settings, PersistentBool, PersistentInt } internal class FakeLogger : ILogger { public void Debug(string message) { } public void Error(string message) { } public void Info(string message) { } public void Message(string message) { } public void Warn(string message) { } } internal static class Log { private static ILogger logger = (ILogger)(object)new FakeLogger(); public static void SetLogger(ILogger log) { logger = log; } public static void LogInfo(params object[] data) { foreach (object obj in data) { logger.Info(obj.ToString()); } } public static void LogWarning(params object[] data) { foreach (object obj in data) { logger.Warn(obj.ToString()); } } public static void LogError(params object[] data) { foreach (object obj in data) { logger.Error(obj.ToString()); } } public static void LogFatal(params object[] data) { foreach (object obj in data) { logger.Error(obj.ToString()); } } public static void LogDebug(params object[] data) { } public static void LogMessage(params object[] data) { foreach (object obj in data) { logger.Message(obj.ToString()); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("io.github.bobbythecatfish.basicitemsync", "BasicItemSync", "0.1.0")] public class SyncPlugin : BaseUnityPlugin { private static List<Action> CurrentFrameActions = new List<Action>(); private static List<Action> NextFrameActions = new List<Action>(); internal static SyncPlugin Instance; internal static Dictionary<string, ManagedAsset<FakeCollectable>> Collectables = new Dictionary<string, ManagedAsset<FakeCollectable>>(); public const string Id = "io.github.bobbythecatfish.basicitemsync"; public static string Name => "BasicItemSync"; public static string Version => "0.1.0"; private void Awake() { Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " (io.github.bobbythecatfish.basicitemsync) has loaded!")); ClientAddon.RegisterAddon((ClientAddon)(object)new ClientAddon()); ServerAddon.RegisterAddon((ServerAddon)(object)new ServerAddon()); Collectables["Mask"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Heart Piece.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Spool"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Silk Spool.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Pouch"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Tool Pouch Pickup.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["CraftKit"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Tool Kit Pickup.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["SilkHeart"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Silk Heart.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Collectables["Needle"] = ManagedAsset<FakeCollectable>.FromNonSceneAsset("Assets/Data Assets/Collectables/Fake Collectables/Needle Upgrade.asset", "dataassets_assets_assets/dataassets/collectables/fakecollectables"); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } internal static void AddNextFrameAction(Action action) { NextFrameActions.Add(action); } private void Update() { if (CurrentFrameActions.Count > 0) { Action[] array = CurrentFrameActions.ToArray(); CurrentFrameActions.Clear(); Action[] array2 = array; for (int i = 0; i < array2.Length; i++) { array2[i](); } } } private void LateUpdate() { if (NextFrameActions.Count > 0) { CurrentFrameActions = NextFrameActions.ToList(); NextFrameActions.Clear(); } } } } namespace BasicItemSync.Modules { internal class BattleManager { private static BattleScene? GetBattleScene(string keyName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); DeactivateIfPlayerdataTrue val2 = default(DeactivateIfPlayerdataTrue); for (int i = 0; i < rootGameObjects.Length; i++) { BattleScene[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<BattleScene>(); foreach (BattleScene val in componentsInChildren) { if (((Component)val).TryGetComponent<DeactivateIfPlayerdataTrue>(ref val2) && val2.boolName == keyName) { return val; } } } return null; } public static void DefeatBattleScene(string keyName) { if (keyName == "defeatedBellBeast") { OnBellBeast(); return; } BattleScene battleScene = GetBattleScene(keyName); if (!Object.op_Implicit((Object)(object)battleScene)) { return; } if (battleScene.started) { int currentWave = battleScene.currentWave; battleScene.DoEndBattle(); HealthManager[] componentsInChildren = ((Component)battleScene.waves[currentWave]).gameObject.GetComponentsInChildren<HealthManager>(); foreach (HealthManager val in componentsInChildren) { if (!val.isDead) { val.Die((float?)0f, (AttackTypes)0, true); } } } else { ((Component)battleScene).gameObject.SetActive(false); } } private static void OnBellBeast() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_007e: 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_008c: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name == "Bone_05") { Scene sceneByName = SceneManager.GetSceneByName("Bone_05_boss"); if (((Scene)(ref sceneByName)).IsValid()) { GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { rootGameObjects[i].SetActive(false); } } } else if (((Scene)(ref activeScene)).name == "Memory_Silk_Heart_BellBeast") { SceneLoadInfo val = new SceneLoadInfo { SceneName = "Bone_05", EntryGateName = "bot1", PreventCameraFadeOut = true, WaitForSceneTransitionCameraFade = false, Visualization = (SceneLoadVisualizations)0, AlwaysUnloadUnusedAssets = true, IsFirstLevelForPlayer = false }; HeroController.instance.SetSilkRegenBlocked(false); PlayerData.instance.disableInventory = false; ToolItemManager.SetIsInCutscene(false); GameManager.instance.BeginSceneTransition(val); } } } internal class ClientState { public static string LastItem = ""; public static string LastPlayerData = ""; public static string LastPersistent = ""; public static int LastCurrency = 0; public static FlagType LastUpgrade = FlagType.Currency; public static bool WasItemReceived(string item) { return LastItem == item; } public static bool WasCurrencyReceived(int amount) { return LastCurrency == amount; } public static bool WasUpgradeReceived(FlagType flagType) { return LastUpgrade == flagType; } public static bool WasPersistentReceived(string key) { return LastPersistent == key; } } internal class ObjectHelper { public static PersistentItem<T>? FindPersistent<T>(string sceneName, string id) where T : IEquatable<T> { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { PersistentItem<T>[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<PersistentItem<T>>(); foreach (PersistentItem<T> val in componentsInChildren) { if (val.ItemData.ID == id && val.ItemData.SceneName == sceneName) { return val; } } } return null; } } internal class PersistentHandler { public static void SetPersistentIntData(string sceneName, string keyName, int status, FlagType flagType) { //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) PersistentItemData<int> value = new PersistentItemData<int> { SceneName = sceneName, ID = keyName, IsSemiPersistent = false, Value = status }; PersistentItemData<int> val = default(PersistentItemData<int>); if (SceneData.instance.PersistentInts.TryGetValue(sceneName, keyName, ref val)) { val.Value = status; } else { SceneData.instance.PersistentInts.SetValue(value); } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (flagType == FlagType.Currency && name != sceneName) { return; } PersistentItem<int> val2 = ObjectHelper.FindPersistent<int>(sceneName, keyName); if (!((Object)(object)val2 == (Object)null)) { PersistentIntItem val3 = (PersistentIntItem)(object)((val2 is PersistentIntItem) ? val2 : null); if (val3 != null) { ((PersistentItem<int>)(object)val3).ItemData.Value = status; ((PersistentItem<int>)(object)val3).Start(); } } } public static void SetPersistentBoolData(string sceneName, string keyName, bool status, bool forceDisable = false) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00e2: 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) //IL_0106: Unknown result type (might be due to invalid IL or missing references) PersistentItemData<bool> value = new PersistentItemData<bool> { SceneName = sceneName, ID = keyName, IsSemiPersistent = false, Value = status }; PersistentItemData<bool> val = default(PersistentItemData<bool>); if (SceneData.instance.PersistentBools.TryGetValue(sceneName, keyName, ref val)) { val.Value = status; } else { SceneData.instance.PersistentBools.SetValue(value); } PersistentItem<bool> val2 = ObjectHelper.FindPersistent<bool>(sceneName, keyName); if ((Object)(object)val2 == (Object)null) { return; } PersistentBoolItem val3 = (PersistentBoolItem)(object)((val2 is PersistentBoolItem) ? val2 : null); if (val3 == null) { return; } Lever val4 = default(Lever); Lever_tk2d val5 = default(Lever_tk2d); PersistentBoolItemResponder val6 = default(PersistentBoolItemResponder); if (((Component)val3).TryGetComponent<Lever>(ref val4)) { TrackTriggerObjects canHitTrigger = val4.canHitTrigger; val4.canHitTrigger = null; val4.Hit(new HitInstance { IsFirstHit = true, IsNailTag = true, Direction = 0f }); val4.canHitTrigger = canHitTrigger; } else if (((Component)val3).TryGetComponent<Lever_tk2d>(ref val5)) { TrackTriggerObjects canHitTrigger2 = val5.canHitTrigger; val5.canHitTrigger = null; val5.Hit(new HitInstance { IsFirstHit = true, IsNailTag = true, Direction = 0f }); val5.canHitTrigger = canHitTrigger2; } else if (Object.op_Implicit((Object)(object)((PersistentItem<bool>)(object)val3).fsm)) { ((PersistentItem<bool>)(object)val3).SetValueOnFSM(((PersistentItem<bool>)(object)val3).fsm, status); ((PersistentItem<bool>)(object)val3).fsm.SendEvent("QUICK BREAK"); ((PersistentItem<bool>)(object)val3).fsm.SendEvent("ACTIVATE"); ((PersistentItem<bool>)(object)val3).fsm.SendEvent("DESTROY"); } else if (((Component)val3).TryGetComponent<PersistentBoolItemResponder>(ref val6)) { val6.InvokeEvents(status); } if (val3.disableIfActivated || forceDisable) { if (Object.op_Implicit((Object)(object)val3.disablePrefabIfActivated)) { val3.disablePrefabIfActivated.SetActive(false); } ((Component)val3).gameObject.SetActive(false); } } } internal class QuestHandler { public static void EndQuest(string name) { FullQuestBase quest = QuestManager.GetQuest(name); if (Object.op_Implicit((Object)(object)quest)) { quest.SilentlyComplete(); quest.ConsumeTarget(); UI.ShowPopup((ICollectableUIMsgItem)(object)new QuestPopupItem(quest)); } } public static void EndQuestSilent(string name) { FullQuestBase quest = QuestManager.GetQuest(name); if (Object.op_Implicit((Object)(object)quest)) { quest.SilentlyComplete(); } } } internal class UI { public static void ShowPopup(ICollectableUIMsgItem item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) CollectableUIMsg.Spawn(item, Color.white, (CollectableUIMsg)null, false); } } internal class QuestPopupItem : ICollectableUIMsgItem, IUIMsgPopupItem { public FullQuestBase Quest; public QuestPopupItem(FullQuestBase quest) { Quest = quest; } public Object GetRepresentingObject() { return (Object)(object)Quest; } public float GetUIMsgIconScale() { return 1f; } public string GetUIMsgName() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return Language.Get(((BasicQuestBase)Quest).DisplayName.Key, ((BasicQuestBase)Quest).DisplayName.Sheet); } public Sprite GetUIMsgSprite() { return ((BasicQuestBase)Quest).QuestType.Icon; } public bool HasUpgradeIcon() { return false; } } internal class Upgrader { private static FakeCollectable? MaskCollectable; private static FakeCollectable? SpoolCollectable; private static FakeCollectable? PouchCollectable; private static FakeCollectable? CraftingKitCollectable; private static FakeCollectable? SilkHeartCollectable; private static FakeCollectable? NeedleUpgrade; public static bool UpgradeMask(string sceneName) { string text = "Heart Piece"; if (sceneName == "Bone_East_LavaChallenge") { text += " (1)"; } PersistentHandler.SetPersistentBoolData(sceneName, text, status: true, forceDisable: true); PlayerData instance = PlayerData.instance; instance.heartPieces++; if (PlayerData.instance.heartPieces >= 4) { PlayerData.instance.heartPieces = 0; HeroController.instance.AddToMaxHealth(1); HeroController.instance.MaxHealth(); EventRegister.SendEvent("MAX HP UP", (GameObject)null); } LoadAndDisplay(ref MaskCollectable, "Mask", "UI", "SHOP_SHELLFRAG_NAME"); return Save(); } public static bool UpgradeSpool(string sceneName) { PersistentHandler.SetPersistentBoolData(sceneName, "Silk Spool", status: true, forceDisable: true); PlayerData instance = PlayerData.instance; instance.silkSpoolParts++; if (PlayerData.instance.silkSpoolParts >= 2) { PlayerData.instance.silkSpoolParts = 0; HeroController.instance.AddToMaxSilk(1); EventRegister.SendEvent("SPOOL MAX UP", (GameObject)null); } LoadAndDisplay(ref SpoolCollectable, "Spool", "UI", "SHOP_SPOOL_SEGMENT_NAME"); return Save(); } public static bool GiveCollectable(string persistentScene, string persistentKey, string itemKey) { if (!string.IsNullOrEmpty(persistentScene) && !string.IsNullOrEmpty(persistentKey)) { PersistentHandler.SetPersistentBoolData(persistentScene, persistentKey, status: true, forceDisable: true); } CollectableItem itemByName = CollectableItemManager.GetItemByName(itemKey); itemByName.AddAmount(1); UI.ShowPopup((ICollectableUIMsgItem)(object)itemByName); return Save(); } public static bool UpgradePouch() { PlayerData instance = PlayerData.instance; instance.ToolPouchUpgrades++; LoadAndDisplay(ref PouchCollectable, "Pouch", "UI", "INV_NAME_TOOLPOUCH"); return Save(); } public static bool UpgradeCraftingKit() { PlayerData instance = PlayerData.instance; instance.ToolKitUpgrades++; LoadAndDisplay(ref CraftingKitCollectable, "CraftKit", "UI", "INV_MSG_TOOLKIT"); return Save(); } public static bool UpgradeSilkHeart(string scene) { if (HeroControllerHook.LastCollectedScene == scene) { return false; } HeroControllerHook.LastCollectedScene = scene; PlayerData instance = PlayerData.instance; instance.silkRegenMax++; LoadAndDisplay(ref SilkHeartCollectable, "SilkHeart", "UI", "INV_MSG_THREAD_HEART"); return Save(); } public static bool UpgradeNeedle() { PlayerData instance = PlayerData.instance; instance.nailUpgrades++; LoadAndDisplay(ref NeedleUpgrade, "Needle", "UI", "INV_MSG_NEEDLE_UPGRADE"); return Save(); } private static void LoadAndDisplay(ref FakeCollectable? assetRef, string dictKey, string langSheet, string langKey) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)assetRef == (Object)null) { if (!SyncPlugin.Collectables.TryGetValue(dictKey, out ManagedAsset<FakeCollectable> value)) { return; } if (!((ManagedAssetBase<FakeCollectable>)(object)value).IsLoaded) { ((MonoBehaviour)SyncPlugin.Instance).StartCoroutine(Coroutine(value)); return; } assetRef = ((ManagedAssetBase<FakeCollectable>)(object)value).Handle.Result; } UI.ShowPopup((ICollectableUIMsgItem)(object)assetRef); IEnumerator Coroutine(ManagedAsset<FakeCollectable> asset) { if (asset != null) { ((ManagedAssetBase<FakeCollectable>)(object)asset).Load(); yield return ((ManagedAssetBase<FakeCollectable>)(object)asset).Handle; if (((ManagedAssetBase<FakeCollectable>)(object)asset).Handle.OperationException != null) { Debug.LogError((object)$"Error loading asset: {((ManagedAssetBase<FakeCollectable>)(object)asset).Handle.OperationException}"); } else { FakeCollectable result = ((ManagedAssetBase<FakeCollectable>)(object)asset).Handle.Result; result.uiMsgName = new LocalisedString { Sheet = langSheet, Key = langKey }; UI.ShowPopup((ICollectableUIMsgItem)(object)result); } } } } public static bool Save() { if (!Object.op_Implicit((Object)(object)HeroController.SilentInstance)) { Log.LogError("[CLI: SAVE] Not in save file. Cannot save data."); return false; } GameManager.instance.SaveGame((Action<bool>)delegate { }); return true; } public static bool NoOp() { return false; } } } namespace BasicItemSync.Modules.Network { internal class ClientPacket : IPacketData { public virtual bool IsReliable => true; public virtual bool DropReliableDataIfNewerExists => false; public virtual void ReadData(IPacket packet) { } public virtual void WriteData(IPacket packet) { } } internal class SendFlagPacket : ClientPacket { public string Key = ""; public string Name = ""; public FlagType FlagType; public override void WriteData(IPacket packet) { packet.Write(Key); packet.Write(Name); packet.Write((int)FlagType); } public override void ReadData(IPacket packet) { Key = packet.ReadString(); Name = packet.ReadString(); FlagType = (FlagType)packet.ReadInt(); } } internal class SendBoolItemPacket : SendFlagPacket { public bool State = true; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadBool(); } } internal class SendUpgradeItemPacket : SendFlagPacket { private FlagType UpgradeType; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write((int)UpgradeType); } public override void ReadData(IPacket packet) { base.ReadData(packet); UpgradeType = (FlagType)packet.ReadInt(); } } internal class SendPersistentPacket : SendFlagPacket { public string PersistentScene = ""; public string PersistentObject = ""; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(PersistentScene); packet.Write(PersistentObject); } public override void ReadData(IPacket packet) { base.ReadData(packet); PersistentScene = packet.ReadString(); PersistentObject = packet.ReadString(); } } internal class SendPersistentBoolPacket : SendPersistentPacket { public bool State = true; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadBool(); } } internal class SendPersistentIntPacket : SendPersistentPacket { public int State; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(State); } public override void ReadData(IPacket packet) { base.ReadData(packet); State = packet.ReadInt(); } } internal class SendIntItemPacket : SendFlagPacket { public int Number; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(Number); } public override void ReadData(IPacket packet) { base.ReadData(packet); Number = packet.ReadInt(); } } internal class SendFloatItemPacket : SendFlagPacket { public float Number; public override void WriteData(IPacket packet) { base.WriteData(packet); packet.Write(Number); } public override void ReadData(IPacket packet) { base.ReadData(packet); Number = packet.ReadFloat(); } } internal class SendCurrencyPacket : ClientPacket { public InternalCurrencyType CurrencyType; public int Amount; public override bool IsReliable => false; public override void WriteData(IPacket packet) { packet.Write((byte)CurrencyType); packet.Write(Amount); } public override void ReadData(IPacket packet) { CurrencyType = (InternalCurrencyType)packet.ReadByte(); Amount = packet.ReadInt(); } } internal class SettingsUpdatePacket : IPacketData { public SyncServerSettings Settings; public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public void WriteData(IPacket packet) { List<bool> list = Settings.ToValues(); packet.Write(list.Count); foreach (bool item in list) { packet.Write(item); } } public void ReadData(IPacket packet) { int num = packet.ReadInt(); List<bool> list = new List<bool>(); for (int i = 0; i < num; i++) { list.Add(packet.ReadBool()); } Settings = SyncServerSettings.PopulateFromValues(list); } } internal class SendQuestItemPacket : SendBoolItemPacket { } internal static class PacketInstantiate { internal static IPacketData Instantiate(Packets packetID) { Log.LogDebug($"[PACKETS] Received {packetID}"); return (IPacketData)(packetID switch { Packets.BoolPlayerData => new PacketDataCollection<SendBoolItemPacket>(), Packets.IntPlayerData => new PacketDataCollection<SendIntItemPacket>(), Packets.FloatPlayerData => new PacketDataCollection<SendFloatItemPacket>(), Packets.Currency => new PacketDataCollection<SendCurrencyPacket>(), Packets.Quest => new PacketDataCollection<SendBoolItemPacket>(), Packets.Tool => new PacketDataCollection<SendBoolItemPacket>(), Packets.Upgrade => new PacketDataCollection<SendFlagPacket>(), Packets.Collectable => new PacketDataCollection<SendPersistentBoolPacket>(), Packets.PersistentBool => new PacketDataCollection<SendPersistentBoolPacket>(), Packets.PersistentInt => new PacketDataCollection<SendPersistentIntPacket>(), Packets.Settings => new SettingsUpdatePacket(), _ => new ErrorThrowingPacket(packetID, server: true), }); } } internal class ErrorThrowingPacket : ClientPacket { public ErrorThrowingPacket(Packets id, bool server) { Log.LogError(id.ToString(), server); throw new NotImplementedException(id.ToString()); } } } namespace BasicItemSync.Modules.Network.Server { internal class NetworkForwarder { private static IServerAddonNetworkReceiver<Packets> Receiver; private static IServerAddonNetworkSender<Packets> Sender; public static void Initialize() { Sender = ServerAddon.api.NetServer.GetNetworkSender<Packets>((ServerAddon)(object)ServerAddon.Instance); Receiver = ServerAddon.api.NetServer.GetNetworkReceiver<Packets>((ServerAddon)(object)ServerAddon.Instance, (Func<Packets, IPacketData>)PacketInstantiate.Instantiate); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.BoolPlayerData, (GenericServerPacketHandler<SendBoolItemPacket>)OnBool); Receiver.RegisterPacketHandler<SendIntItemPacket>(Packets.IntPlayerData, (GenericServerPacketHandler<SendIntItemPacket>)OnInt); Receiver.RegisterPacketHandler<SendFloatItemPacket>(Packets.FloatPlayerData, (GenericServerPacketHandler<SendFloatItemPacket>)OnFloat); Receiver.RegisterPacketHandler<SendCurrencyPacket>(Packets.Currency, (GenericServerPacketHandler<SendCurrencyPacket>)OnCurrency); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.Quest, (GenericServerPacketHandler<SendBoolItemPacket>)OnQuest); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.Tool, (GenericServerPacketHandler<SendBoolItemPacket>)OnTool); Receiver.RegisterPacketHandler<SendFlagPacket>(Packets.Upgrade, (GenericServerPacketHandler<SendFlagPacket>)OnUpgrade); Receiver.RegisterPacketHandler<SendPersistentBoolPacket>(Packets.Collectable, (GenericServerPacketHandler<SendPersistentBoolPacket>)OnCollectable); Receiver.RegisterPacketHandler<SendPersistentBoolPacket>(Packets.PersistentBool, (GenericServerPacketHandler<SendPersistentBoolPacket>)OnPersistentBool); Receiver.RegisterPacketHandler<SendPersistentIntPacket>(Packets.PersistentInt, (GenericServerPacketHandler<SendPersistentIntPacket>)OnPersistentInt); } private static void Broadcast(ushort senderId, Packets type, ClientPacket packet) { if (Sender == null) { return; } foreach (IServerPlayer player in ServerAddon.api.ServerManager.Players) { if (player.Id != senderId) { Sender.SendCollectionData<ClientPacket>(type, packet, player.Id); } } } public static void SendSettingsUpdate() { SettingsUpdatePacket settingsUpdatePacket = new SettingsUpdatePacket { Settings = ServerAddon.Settings }; ServerAddon.api.ServerManager.BroadcastMessage("Sync settings have been updated"); Sender.BroadcastSingleData(Packets.Settings, (IPacketData)(object)settingsUpdatePacket); } public static void SendSettingsUpdate(ushort id) { SettingsUpdatePacket settingsUpdatePacket = new SettingsUpdatePacket { Settings = ServerAddon.Settings }; Sender.SendSingleData(Packets.Settings, (IPacketData)(object)settingsUpdatePacket, id); } private static void OnCurrency(ushort id, SendCurrencyPacket packet) { if (ServerAddon.Settings.FlagAllowed(FlagType.Currency)) { Broadcast(id, Packets.Currency, packet); } } private static void OnBool(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.BoolPlayerData, isPD: true); } private static void OnInt(ushort id, SendIntItemPacket packet) { OnFlag(id, packet, Packets.IntPlayerData, isPD: true); } private static void OnFloat(ushort id, SendFloatItemPacket packet) { OnFlag(id, packet, Packets.FloatPlayerData, isPD: true); } private static void OnQuest(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.Quest, isPD: false); } private static void OnTool(ushort id, SendBoolItemPacket packet) { OnFlag(id, packet, Packets.Tool, isPD: false); } private static void OnUpgrade(ushort id, SendFlagPacket packet) { OnFlag(id, packet, Packets.Upgrade, isPD: false); } private static void OnCollectable(ushort id, SendPersistentBoolPacket packet) { OnFlag(id, packet, Packets.Collectable, isPD: false); } private static void OnPersistentBool(ushort id, SendPersistentBoolPacket packet) { OnFlag(id, packet, Packets.PersistentBool, isPD: true); } private static void OnPersistentInt(ushort id, SendPersistentIntPacket packet) { OnFlag(id, packet, Packets.PersistentInt, isPD: true); } private static void Announce(ushort id, SendFlagPacket packet, bool isPossiblySilent) { FlagType flagType = packet.FlagType; Log.LogDebug($"[SERVER] Received {flagType}"); if (!isPossiblySilent || !string.IsNullOrEmpty(packet.Name)) { string text = flagType switch { FlagType.Ability => "obtained $", FlagType.Map => "obtained $ Map", FlagType.Pin => "obtained $", FlagType.Bellshrine => "activated the $ Bellshrine", FlagType.Boss => "defeated $", FlagType.Arena => "defeated $", FlagType.Progression => "obtained the $", FlagType.Collectable => "collected a $", FlagType.Bellway => "unlocked the $ Bellway Station", FlagType.Ventrica => "unlocked the $ Ventrica", FlagType.Mask => "obtained the Mask Shard in $", FlagType.Spool => "obtained the Spool Fragment in $", FlagType.Pouch => "obtained a Tool Pouch from $", FlagType.CraftingKit => "obtained a Crafting Kit from $", FlagType.SilkHeart => "obtained a Silk Heart", FlagType.Needle => "upgraded their needle", FlagType.Quest => "finished the $ quest", FlagType.QuestItem => "", FlagType.Tool => "obtained $", FlagType.Crest => "obtained $ Crest", FlagType.Currency => "", FlagType.Bench => "unlocked a bench", FlagType.Flea => "saved a flea from $", _ => "", }; if (!string.IsNullOrEmpty(text)) { IServerPlayer player = ServerAddon.api.ServerManager.GetPlayer(id); string text2 = string.Concat(str2: text.Replace("$", string.IsNullOrEmpty(packet.Name) ? packet.Key : packet.Name), str0: ((player != null) ? player.Username : null) ?? "Unknown Player", str1: " "); ServerAddon.api.ServerManager.BroadcastMessage(text2); } } } private static void OnFlag(ushort id, SendFlagPacket packet, Packets type, bool isPD) { if (ServerAddon.Settings.FlagAllowed(packet.FlagType)) { Announce(id, packet, isPD); Broadcast(id, type, packet); } } } internal class ServerAddon : ServerAddon { public static IServerApi api; public static ServerAddon Instance; public static SyncServerSettings Settings; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "BasicItemSync"; protected override string Version => "0.1.0"; public override void Initialize(IServerApi serverApi) { Log.SetLogger(((ServerAddon)this).Logger); Log.LogInfo("Item Sync Server Addon enabled"); Settings = SyncServerSettings.ReadFromFile(); Instance = this; api = serverApi; NetworkForwarder.Initialize(); api.ServerManager.PlayerConnectEvent += OnPlayerConnect; } private void OnPlayerConnect(IServerPlayer player) { NetworkForwarder.SendSettingsUpdate(player.Id); } } internal class SettingCommand : IServerCommand, ICommand { private readonly SyncServerSettings Settings; public bool AuthorizedOnly => true; public string Trigger => "/sync"; public string[] Aliases => Array.Empty<string>(); public SettingCommand(SyncServerSettings settings) { Settings = settings; } public void Execute(ICommandSender commandSender, string[] arguments) { if (arguments.Length < 2) { SendUsage(); return; } string settingName = "Sync" + arguments[1]; FieldInfo fieldInfo = Extensions.GetAllFields(typeof(SyncServerSettings), BindingFlags.Public).FirstOrDefault((FieldInfo s) => s.Name.Equals(settingName, StringComparison.CurrentCultureIgnoreCase)); if (fieldInfo == null) { commandSender.SendMessage("Unknown setting '" + settingName); return; } if (arguments.Length == 2) { bool variable = VariableExtensionsGeneric.GetVariable<bool>((object)Settings, fieldInfo.Name); commandSender.SendMessage($"Setting '{fieldInfo.Name}' is currently {variable}"); return; } bool flag; if (arguments[2] == "true") { flag = true; } else { if (!(arguments[2] == "false")) { SendUsage(); return; } flag = false; } VariableExtensionsGeneric.SetVariable<bool>((object)Settings, fieldInfo.Name, flag); NetworkForwarder.SendSettingsUpdate(); void SendUsage() { commandSender.SendMessage("Invalid usage: " + Trigger + " <setting> <true/false>"); } } } internal class SyncServerSettings { public bool KillSwitch; public bool SyncCurrency = true; public bool SyncQuests = true; public bool SyncQuestItems; public bool SyncTools = true; public bool SyncAbilities = true; public bool SyncMaps = true; public bool SyncPins = true; public bool SyncBattles = true; public bool SyncProgression = true; public bool SyncCollectables = true; public bool SyncUpgrades = true; public bool SyncTransit = true; public bool SyncShortcuts = true; public bool FlagAllowed(FlagType flag) { if (ServerAddon.Settings.KillSwitch) { return false; } return flag switch { FlagType.Ability => SyncAbilities, FlagType.Map => SyncMaps, FlagType.Pin => SyncPins, FlagType.Bellshrine => SyncProgression, FlagType.Boss => SyncBattles, FlagType.Arena => SyncBattles, FlagType.Progression => SyncProgression, FlagType.Flea => SyncCollectables, FlagType.Collectable => SyncCollectables, FlagType.Bellway => SyncTransit, FlagType.Ventrica => SyncTransit, FlagType.Currency => SyncCurrency, FlagType.Mask => SyncUpgrades, FlagType.Spool => SyncUpgrades, FlagType.Pouch => SyncUpgrades, FlagType.CraftingKit => SyncUpgrades, FlagType.Quest => SyncQuests, FlagType.QuestItem => SyncQuestItems, FlagType.Tool => SyncTools, FlagType.Crest => SyncTools, FlagType.SilkHeart => SyncUpgrades, FlagType.Needle => SyncUpgrades, FlagType.Bench => SyncProgression, FlagType.Shortcut => SyncShortcuts, FlagType.DoNotSync => false, _ => false, }; } public List<bool> ToValues() { List<PropertyInfo> properties = GetProperties(); List<bool> list = new List<bool>(); foreach (PropertyInfo item in properties) { list.Add(VariableExtensionsGeneric.GetVariable<bool>((object)this, item.Name)); } return list; } private static List<PropertyInfo> GetProperties() { List<PropertyInfo> list = typeof(SyncServerSettings).GetProperties().ToList(); list.Sort((PropertyInfo a, PropertyInfo b) => a.Name.CompareTo(b.Name)); return list; } public void CopyFrom(SyncServerSettings settings) { foreach (PropertyInfo property in GetProperties()) { VariableExtensionsGeneric.SetVariable<bool>((object)this, property.Name, VariableExtensionsGeneric.GetVariable<bool>((object)settings, property.Name)); } } public static SyncServerSettings PopulateFromValues(List<bool> values) { SyncServerSettings syncServerSettings = new SyncServerSettings(); List<PropertyInfo> properties = GetProperties(); int num = Math.Min(properties.Count, values.Count); if (num != properties.Count) { Log.LogWarning("Mismatched server setting property count"); } for (int i = 0; i < num; i++) { PropertyInfo propertyInfo = properties[i]; bool flag = values[i]; propertyInfo.SetValue(syncServerSettings, flag); } return syncServerSettings; } public static SyncServerSettings ReadFromFile() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ServerSettings.json"); if (!File.Exists(text)) { Log.LogWarning("[SERVER: SETTINGS] " + text + " doesn't exist"); SyncServerSettings syncServerSettings = new SyncServerSettings(); syncServerSettings.SaveToFile(); return syncServerSettings; } try { return JsonConvert.DeserializeObject<SyncServerSettings>(File.ReadAllText(text)) ?? new SyncServerSettings(); } catch (Exception arg) { Log.LogError($"[SERVER: SETTINGS] Could not load server settings from file:\n{arg}"); return new SyncServerSettings(); } } public void SaveToFile() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ServerSettings.json"); if (!Directory.Exists(Path.GetDirectoryName(text))) { Log.LogWarning("[SERVER: SETTINGS] " + text + " directory doesn't exist"); return; } string text2 = JsonConvert.SerializeObject((object)this, (Formatting)1); if (text2 == null) { return; } try { File.WriteAllText(text, text2); } catch (Exception arg) { Log.LogError($"[SERVER: SETTINGS] Could not write server settings to file:\n{arg}"); } } } } namespace BasicItemSync.Modules.Network.Client { internal class ClientAddon : ClientAddon { public static IClientApi api; public static ClientAddon Instance; public static SyncServerSettings Settings = new SyncServerSettings(); private static readonly WaitForSeconds CurrencyTimer = new WaitForSeconds(4f); private static Coroutine? CurrencyRoutine; public override bool NeedsNetwork => true; public override uint ApiVersion => 1u; protected override string Name => "BasicItemSync"; protected override string Version => "0.1.0"; public override void Initialize(IClientApi clientApi) { Log.SetLogger(((ClientAddon)this).Logger); Log.LogInfo("Sync Client Addon enabled"); Settings = new SyncServerSettings(); Instance = this; api = clientApi; NetworkSender.Initialize(); NetworkReceiver.Initialize(); api.ClientManager.ConnectEvent += OnConnect; api.ClientManager.DisconnectEvent += OnDisconnect; } private void OnConnect() { EventHooks.Initialize(); Settings = new SyncServerSettings(); api.UiManager.ChatBox.AddMessage("BasicItemSync is in beta. Please report any bugs to the link on the mod page."); CurrencyRoutine = ((MonoBehaviour)SyncPlugin.Instance).StartCoroutine(CurrencySender()); } private void OnDisconnect() { EventHooks.Uninitialize(); if (CurrencyRoutine != null) { NetworkSender.SendPendingCurrency(); ((MonoBehaviour)SyncPlugin.Instance).StopCoroutine(CurrencyRoutine); } } public static void LocalChat(string message) { api.UiManager.ChatBox.AddMessage(message); } private static IEnumerator CurrencySender() { while (true) { NetworkSender.SendPendingCurrency(); yield return CurrencyTimer; } } } internal class NetworkReceiver { private static IClientAddonNetworkReceiver<Packets> Receiver; public static void Initialize() { Receiver = ClientAddon.api.NetClient.GetNetworkReceiver<Packets>((ClientAddon)(object)ClientAddon.Instance, (Func<Packets, IPacketData>)PacketInstantiate.Instantiate); Receiver.RegisterPacketHandler<SettingsUpdatePacket>(Packets.Settings, (GenericClientPacketHandler<SettingsUpdatePacket>)OnSettingsUpdate); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.BoolPlayerData, (GenericClientPacketHandler<SendBoolItemPacket>)OnBoolFlag); Receiver.RegisterPacketHandler<SendIntItemPacket>(Packets.IntPlayerData, (GenericClientPacketHandler<SendIntItemPacket>)OnIntFlag); Receiver.RegisterPacketHandler<SendFloatItemPacket>(Packets.FloatPlayerData, (GenericClientPacketHandler<SendFloatItemPacket>)OnFloatFlag); Receiver.RegisterPacketHandler<SendCurrencyPacket>(Packets.Currency, (GenericClientPacketHandler<SendCurrencyPacket>)OnCurrency); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.Quest, (GenericClientPacketHandler<SendBoolItemPacket>)OnQuestItem); Receiver.RegisterPacketHandler<SendBoolItemPacket>(Packets.Tool, (GenericClientPacketHandler<SendBoolItemPacket>)OnTool); Receiver.RegisterPacketHandler<SendFlagPacket>(Packets.Upgrade, (GenericClientPacketHandler<SendFlagPacket>)OnUpgrade); Receiver.RegisterPacketHandler<SendPersistentBoolPacket>(Packets.Collectable, (GenericClientPacketHandler<SendPersistentBoolPacket>)OnCollectable); Receiver.RegisterPacketHandler<SendPersistentBoolPacket>(Packets.PersistentBool, (GenericClientPacketHandler<SendPersistentBoolPacket>)OnPersistentBool); Receiver.RegisterPacketHandler<SendPersistentIntPacket>(Packets.PersistentInt, (GenericClientPacketHandler<SendPersistentIntPacket>)OnPersistentInt); } private static void OnIntFlag(SendIntItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetInt(packet.Key, packet.Number); ClientState.LastItem = ""; }); } private static void OnFloatFlag(SendFloatItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetFloat(packet.Key, packet.Number); ClientState.LastItem = ""; }); } private static void OnBoolFlag(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PlayerData.instance.SetBool(packet.Key, packet.State); ClientState.LastItem = ""; if (packet.FlagType == FlagType.Boss || packet.FlagType == FlagType.Arena) { BattleManager.DefeatBattleScene(packet.Key); } }); } private static void OnCurrency(SendCurrencyPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastCurrency = packet.Amount; if (packet.CurrencyType == InternalCurrencyType.Rosary) { CurrencyManager.AddGeo(packet.Amount); } else { CurrencyManager.AddShards(packet.Amount); } ClientState.LastCurrency = 0; }); } private static void OnQuestItem(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; QuestHandler.EndQuest(packet.Key); ClientState.LastItem = ""; }); } private static void OnTool(SendBoolItemPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; if (packet.FlagType == FlagType.Crest) { ToolCrest crestByName = ToolItemManager.GetCrestByName(packet.Key); if (Object.op_Implicit((Object)(object)crestByName)) { crestByName.Unlock(); } } else { ToolItem toolByName = ToolItemManager.GetToolByName(packet.Key); if (Object.op_Implicit((Object)(object)toolByName)) { PlayerData.instance.SeenToolGetPrompt = true; PlayerData.instance.SeenToolWeaponGetPrompt = true; if (packet.State) { toolByName.Unlock((Action)null, (PopupFlags)3); } else { toolByName.Lock(); } } } ClientState.LastItem = ""; }); } private static void OnUpgrade(SendFlagPacket packet) { if (!string.IsNullOrEmpty(packet.Name)) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Name; QuestHandler.EndQuestSilent(packet.Name); ClientState.LastItem = ""; }); } SyncPlugin.AddNextFrameAction(delegate { ClientState.LastUpgrade = packet.FlagType; switch (packet.FlagType) { case FlagType.Mask: Upgrader.UpgradeMask(packet.Key); break; case FlagType.Spool: Upgrader.UpgradeSpool(packet.Key); break; case FlagType.Pouch: Upgrader.UpgradePouch(); break; case FlagType.CraftingKit: Upgrader.UpgradeCraftingKit(); break; case FlagType.SilkHeart: Upgrader.UpgradeSilkHeart(packet.Key); break; case FlagType.Needle: Upgrader.UpgradeNeedle(); break; default: Upgrader.NoOp(); break; } ClientState.LastUpgrade = FlagType.DoNotSync; }); } private static void OnCollectable(SendPersistentBoolPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; Upgrader.GiveCollectable(packet.PersistentScene, packet.PersistentObject, packet.Key); ClientState.LastItem = ""; }); } private static void OnPersistentBool(SendPersistentBoolPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PersistentHandler.SetPersistentBoolData(packet.PersistentScene, packet.PersistentObject, packet.State); ClientState.LastItem = ""; }); } private static void OnPersistentInt(SendPersistentIntPacket packet) { SyncPlugin.AddNextFrameAction(delegate { ClientState.LastItem = packet.Key; PersistentHandler.SetPersistentIntData(packet.PersistentScene, packet.PersistentObject, packet.State, packet.FlagType); ClientState.LastItem = ""; }); } private static void OnSettingsUpdate(SettingsUpdatePacket packet) { ClientAddon.Settings.CopyFrom(packet.Settings); Log.LogInfo("[CLI] Received new settings"); } } internal class NetworkSender { private static IClientAddonNetworkSender<Packets>? Sender; private static int RosariesToSend = 0; private static int ShardsToSend = 0; private static Dictionary<(string, string), SendPersistentPacket> PersistentsToSend = new Dictionary<(string, string), SendPersistentPacket>(); public static void Initialize() { Sender = ClientAddon.api.NetClient.GetNetworkSender<Packets>((ClientAddon)(object)ClientAddon.Instance); } private static void SendData(Packets type, ClientPacket packet) { if (!ClientAddon.api.NetClient.IsConnected || Sender == null) { Log.LogDebug("Not connected"); } else if (!(packet is SendFlagPacket sendFlagPacket) || CanSync(sendFlagPacket.FlagType)) { Log.LogDebug($"[CLI] Sending {type}"); Sender.SendCollectionData<ClientPacket>(type, packet); } } private static bool CanSync(FlagType flagType) { if (!ClientAddon.Settings.FlagAllowed(flagType)) { Log.LogDebug($"[CLI] Syncing {flagType} is disabled."); return false; } return true; } public static void AddCurrency(InternalCurrencyType currencyType, int amount) { if (CanSync(FlagType.Currency)) { if (currencyType == InternalCurrencyType.Rosary) { RosariesToSend += amount; } else { ShardsToSend += amount; } } } public static void SendPendingCurrency() { if (RosariesToSend > 0) { SendData(Packets.Currency, new SendCurrencyPacket { CurrencyType = InternalCurrencyType.Rosary, Amount = RosariesToSend }); } if (ShardsToSend > 0) { SendData(Packets.Currency, new SendCurrencyPacket { CurrencyType = InternalCurrencyType.ShellShard, Amount = ShardsToSend }); } RosariesToSend = 0; ShardsToSend = 0; } public static void SendPendingPersistents() { if (PersistentsToSend.Count > 0) { foreach (SendPersistentPacket value in PersistentsToSend.Values) { SendData(Packets.PersistentBool, value); } } PersistentsToSend.Clear(); } public static void SendFlag(string playerDataKey, FlagType flagType, string name, bool state = true) { SendData(Packets.BoolPlayerData, new SendBoolItemPacket { Key = playerDataKey, Name = name, FlagType = flagType, State = state }); } public static void SendInt(string playerDataKey, FlagType flagType, string name, int state) { SendData(Packets.IntPlayerData, new SendIntItemPacket { Key = playerDataKey, Name = name, FlagType = flagType, Number = state }); } public static void SendQuestComplete(string internalName, string displayName) { SendData(Packets.Quest, new SendBoolItemPacket { Key = internalName, Name = displayName, FlagType = FlagType.Quest, State = true }); } public static void SendTool(string toolName, string displayName, bool state, bool isCrest) { SendData(Packets.Tool, new SendBoolItemPacket { Key = toolName, Name = displayName, FlagType = (isCrest ? FlagType.Crest : FlagType.Tool), State = state }); } public static void SendUpgrade(string sceneName, FlagType upgradeType) { SendData(Packets.Upgrade, new SendFlagPacket { Key = sceneName, Name = "", FlagType = upgradeType }); } public static void SendCollectable(string key, string itemName, string persistentKey, string persistentScene, FlagType flagType = FlagType.Collectable) { SendData(Packets.Collectable, new SendPersistentBoolPacket { Key = key, Name = itemName, FlagType = flagType, PersistentObject = persistentKey, PersistentScene = persistentScene }); } public static void AddPersistentIntData(string id, string scene, int value, FlagType flagType) { if (!PersistentsToSend.ContainsKey((scene, id))) { PersistentsToSend[(scene, id)] = new SendPersistentIntPacket { Key = "", Name = "", FlagType = flagType, PersistentObject = id, PersistentScene = scene, State = value }; if (PersistentsToSend.Count == 1) { SyncPlugin.AddNextFrameAction(SendPendingPersistents); } } } public static void AddPersistentBoolData(string id, string scene, bool value, FlagType flagType) { if (!PersistentsToSend.ContainsKey((scene, id))) { PersistentsToSend[(scene, id)] = new SendPersistentBoolPacket { Key = "", Name = "", FlagType = flagType, PersistentObject = id, PersistentScene = scene, State = value }; if (PersistentsToSend.Count == 1) { SyncPlugin.AddNextFrameAction(SendPendingPersistents); } } } } } namespace BasicItemSync.Modules.Hooks { [HarmonyPatch(typeof(CollectableItemPickup))] internal class CollectableItemPickupHook { private static Dictionary<string, FlagType> CollectableTypes = new Dictionary<string, FlagType> { { "Ant Trapper Item", FlagType.QuestItem }, { "Architect Key", FlagType.Progression }, { "Beastfly Remainss", FlagType.QuestItem }, { "Belltown House Key", FlagType.Progression }, { "Blue Goop Jar", FlagType.QuestItem }, { "Broken SilkShot", FlagType.Tool }, { "Broodmother Remains", FlagType.QuestItem }, { "Clover Heart", FlagType.Progression }, { "Cog Heart Pieces", FlagType.Progression }, { "Common Spine", FlagType.QuestItem }, { "Conchfly Remains", FlagType.QuestItem }, { "Coral Chunk", FlagType.QuestItem }, { "Coral Heart", FlagType.Progression }, { "Coral Ingredient", FlagType.QuestItem }, { "Courier Supplies", FlagType.DoNotSync }, { "Courier Supplies Mask Maker", FlagType.DoNotSync }, { "Courier Supplies Slave", FlagType.DoNotSync }, { "Craw Summons", FlagType.Progression }, { "Crawbell", FlagType.Collectable }, { "Crest Socket Unlocker", FlagType.Collectable }, { "Crow Feather", FlagType.QuestItem }, { "Crowman Memento", FlagType.Collectable }, { "Dock Demo Key", FlagType.DoNotSync }, { "Dock Key", FlagType.Progression }, { "Dresses", FlagType.DoNotSync }, { "Enemy Morsel Seared", FlagType.QuestItem }, { "Enemy Morsel Shredded", FlagType.QuestItem }, { "Enemy Morsel Speared", FlagType.QuestItem }, { "Extractor Machine Pins", FlagType.QuestItem }, { "Farsight", FlagType.Collectable }, { "Fine Pins", FlagType.QuestItem }, { "Fixer Idol", FlagType.Quest }, { "Flower Heart", FlagType.Progression }, { "Great Shard", FlagType.Collectable }, { "Grey Memento", FlagType.Collectable }, { "Growstone", FlagType.Quest }, { "Hunter Heart", FlagType.Collectable }, { "Hunter Memento", FlagType.Collectable }, { "Materium", FlagType.Collectable }, { "Memento Garmond", FlagType.Collectable }, { "Memento Seth", FlagType.Collectable }, { "Memento Surface", FlagType.Collectable }, { "Mossberry", FlagType.QuestItem }, { "Mossberry Stew", FlagType.QuestItem }, { "Pale Oil", FlagType.QuestItem }, { "Pickled Roach Egg", FlagType.QuestItem }, { "Pilgrim Rag", FlagType.QuestItem }, { "Plasmium", FlagType.QuestItem }, { "Plasmium Blood", FlagType.QuestItem }, { "Plasmium Gland", FlagType.Quest }, { "Pristine Core", FlagType.Progression }, { "Quill", FlagType.Collectable }, { "R Ancient Egg", FlagType.Collectable }, { "R Bone Record", FlagType.Collectable }, { "R Librarian Melody Cylinder", FlagType.Progression }, { "R Psalm Cylinder", FlagType.Collectable }, { "R Seal Chit", FlagType.Collectable }, { "R Weaver Record", FlagType.Collectable }, { "R Weaver Totem", FlagType.Collectable }, { "Roach Corpse Item", FlagType.QuestItem }, { "Rock Roller Item", FlagType.QuestItem }, { "Rosary_Set_Frayed", FlagType.Collectable }, { "Rosary_Set_Huge_White", FlagType.Collectable }, { "Rosary_Set_Large", FlagType.Collectable }, { "Rosary_Set_Medium", FlagType.Collectable }, { "Rosary_Set_Small", FlagType.Collectable }, { "Shard Pouch", FlagType.Collectable }, { "Shell Flower", FlagType.QuestItem }, { "Shining Cog", FlagType.QuestItem }, { "Silk Grub", FlagType.Collectable }, { "Silver Bellclapper", FlagType.QuestItem }, { "Simple Key", FlagType.Collectable }, { "Skull King Fragment", FlagType.QuestItem }, { "Slab Key", FlagType.Progression }, { "Snare Soul Bell Hermit", FlagType.Progression }, { "Snare Soul Churchkeeper", FlagType.Progression }, { "Snare Soul Swamp Bug", FlagType.Progression }, { "Song Pilgrim Cloak", FlagType.QuestItem }, { "Sprintmaster Memento", FlagType.Collectable }, { "Tool Metal", FlagType.Collectable }, { "Vintage Nectar", FlagType.QuestItem }, { "Ward Boss Key", FlagType.Progression }, { "Ward Key", FlagType.Progression }, { "White Flower", FlagType.Progression }, { "Wood Witch Item", FlagType.Progression } }; [HarmonyPatch("DoPickupAction")] [HarmonyPostfix] public static void DoPickupAction(CollectableItemPickup __instance, ref bool __result) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) string name = ((Object)__instance.Item).name; if (ClientState.WasItemReceived(name) || !__result || !Object.op_Implicit((Object)(object)__instance.Item)) { return; } if (!CollectableTypes.TryGetValue(((Object)__instance.Item).name, out var value)) { if (!(__instance.Item is CollectableRelic)) { return; } value = FlagType.Collectable; } if (value != FlagType.DoNotSync) { PlayerDataHook.BoolUpdated(__instance.playerDataBool, value: true, (Operation)0); string popupName = __instance.Item.GetPopupName(); string persistentKey; string persistentScene; if (Object.op_Implicit((Object)(object)__instance.persistent)) { persistentKey = ((PersistentItem<bool>)(object)__instance.persistent).ItemData.ID; persistentScene = ((PersistentItem<bool>)(object)__instance.persistent).ItemData.SceneName; } else { persistentKey = ((Object)((Component)__instance).gameObject).name; Scene activeScene = SceneManager.GetActiveScene(); persistentScene = ((Scene)(ref activeScene)).name; } NetworkSender.SendCollectable(name, popupName, persistentKey, persistentScene, value); } } } [HarmonyPatch(typeof(CurrencyManager))] internal class CurrencyManagerHook { [HarmonyPatch("ChangeCurrency")] [HarmonyPostfix] public static void ChangeCurrency(int amount, CurrencyType type) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (amount >= 1 && !ClientState.WasCurrencyReceived(amount)) { NetworkSender.AddCurrency(((int)type != 0) ? InternalCurrencyType.ShellShard : InternalCurrencyType.Rosary, amount); } } } internal class EventHooks { private static GameObject? HookObj; private static List<EventRegister> Hooks = new List<EventRegister>(); private static bool CollectedRecently = false; public static void Initialize() { AddHook("HEART PIECE COLLECTED", OnMask); AddHook("SILK SPOOL SAVE", OnSilkSpool); } public static void Uninitialize() { foreach (EventRegister hook in Hooks) { EventRegister.UnsubscribeEvent(hook); } if (Object.op_Implicit((Object)(object)HookObj)) { Object.Destroy((Object)(object)HookObj); } } private static void AddHook(string eventName, Action action) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)HookObj)) { HookObj = new GameObject("BasicItemSync Event Hooks"); Object.DontDestroyOnLoad((Object)(object)HookObj); } EventRegister val = HookObj.AddComponent<EventRegister>(); val.SubscribedEvent = eventName; ((EventBase)val).ReceivedEvent += action; EventRegister.SubscribeEvent(val); Hooks.Add(val); } private static void SendUpgrade(FlagType upgradeType) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!CollectedRecently && !ClientState.WasUpgradeReceived(upgradeType)) { CollectedRecently = true; Scene activeScene = SceneManager.GetActiveScene(); NetworkSender.SendUpgrade(((Scene)(ref activeScene)).name, upgradeType); SyncPlugin.AddNextFrameAction(delegate { CollectedRecently = false; }); } } private static void OnMask() { SendUpgrade(FlagType.Mask); } private static void OnSilkSpool() { SendUpgrade(FlagType.Spool); } private static void OnPouch() { SendUpgrade(FlagType.Pouch); } private static void OnCraftingKit() { SendUpgrade(FlagType.CraftingKit); } } [HarmonyPatch(typeof(HeroController))] internal class HeroControllerHook { public static string LastCollectedScene = ""; [HarmonyPatch("CocoonBroken", new Type[] { typeof(bool), typeof(bool) })] [HarmonyPrefix] public static void CocoonBrokenPrefix(out int __state) { __state = ClientState.LastCurrency; int heroCorpseMoneyPool = PlayerData.instance.HeroCorpseMoneyPool; if (heroCorpseMoneyPool != 0) { ClientState.LastCurrency = heroCorpseMoneyPool; } } [HarmonyPatch("CocoonBroken", new Type[] { typeof(bool), typeof(bool) })] [HarmonyPostfix] public static void CocoonBrokenPostfix(int __state) { ClientState.LastCurrency = __state; } [HarmonyPatch("AddToMaxSilkRegen")] [HarmonyPrefix] public static bool AddToMaxSilkRegen() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (ClientState.WasUpgradeReceived(FlagType.SilkHeart)) { return true; } Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name == LastCollectedScene) { return false; } activeScene = SceneManager.GetActiveScene(); NetworkSender.SendUpgrade(LastCollectedScene = ((Scene)(ref activeScene)).name, FlagType.SilkHeart); return true; } } public abstract class PersistentItemMock : MonoBehaviour { public delegate void SetValueEvent(bool value); public delegate void GetValueEvent(out bool value); [SerializeField] private PlayerDataTest saveCondition; private bool dontSave; private bool hasSetup; private bool isValueOverridden; private GameManager gm; private PlayMakerFSM fsm; private PersistentItemData<bool> itemData; private bool started; private bool disableIfActivated; private GameObject disablePrefabIfActivated; public PersistentItemData<bool> ItemData { get { EnsureSetup(); return itemData; } } protected bool DefaultValue => false; protected PersistentItemData<bool> SerializedItemData => itemData; public bool HasLoadedValue { get; private set; } public bool LoadedValue { get; private set; } public event SetValueEvent? OnSetSaveState; public event GetValueEvent? OnGetSaveState; public event Action? SemiPersistentReset; protected virtual void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (itemData == null) { itemData = new PersistentItemData<bool>(); } if (saveCondition == null) { saveCondition = new PlayerDataTest(); } gm = GameManager.instance; gm.SavePersistentObjects += SaveState; if (Object.op_Implicit((Object)(object)disablePrefabIfActivated)) { OnSetSaveState += delegate(bool value) { if (value) { disablePrefabIfActivated.SetActive(false); } }; } if (!disableIfActivated) { return; } OnSetSaveState += delegate(bool value) { if (value) { ((Component)this).gameObject.SetActive(false); } }; } private void OnEnable() { if (SerializedItemData.IsSemiPersistent) { gm.ResetSemiPersistentObjects += ResetState; } if (this.OnGetSaveState == null) { fsm = LookForMyFSM(); } } private void OnDisable() { if ((Object)(object)gm != (Object)null) { gm.ResetSemiPersistentObjects -= ResetState; } } private void OnDestroy() { if ((Object)(object)gm != (Object)null) { gm.SavePersistentObjects -= SaveState; } } private void Start() { started = true; EnsureSetup(); CheckIsValid(); bool flag = false; PersistentItemData<bool> val = default(PersistentItemData<bool>); if (SceneData.instance.PersistentBools.TryGetValue(itemData.SceneName, itemData.ID, ref val)) { itemData.Value = val.Value; flag = true; } if (flag) { this.OnSetSaveState?.Invoke(itemData.Value); if ((Object)(object)fsm == (Object)null) { fsm = LookForMyFSM(); } if ((Object)(object)fsm != (Object)null) { fsm.FsmVariables.FindFsmBool("Activated").Value = itemData.Value; } HasLoadedValue = true; LoadedValue = itemData.Value; } else { UpdateValue(); } } private void UpdateValue() { if (!isValueOverridden) { if (this.OnGetSaveState != null) { this.OnGetSaveState(out ItemData.Value); } else { UpdateActivatedFromFSM(); } } } private void CheckIsValid() { Type type = ((object)this).GetType(); if (((Component)this).GetComponents(type).Length > 1) { Debug.LogError((object)$"There is more than one component of type: <b>{type}</b> on <b>{((Object)((Component)this).gameObject).name}</b>, please remove one!", (Object)(object)this); } } public void SaveState() { if (!saveCondition.IsDefined || saveCondition.IsFulfilled) { SaveStateNoCondition(); } } public void SaveStateNoCondition() { EnsureSetup(); if (!isValueOverridden) { if (this.OnGetSaveState != null) { this.OnGetSaveState(out itemData.Value); } else { UpdateActivatedFromFSM(); } } HasLoadedValue = true; LoadedValue = itemData.Value; if (!dontSave) { SceneData.instance.PersistentBools.SetValue(itemData); } } private void ResetState() { if (!itemData.IsSemiPersistent) { return; } SaveState(); if (!itemData.Value.Equals(DefaultValue)) { itemData.Value = DefaultValue; this.SemiPersistentReset?.Invoke(); if ((Object)(object)fsm != (Object)null) { fsm.SendEvent("RESET"); } } } private void EnsureSetup() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!hasSetup) { hasSetup = true; itemData = SerializedItemData; if (string.IsNullOrEmpty(itemData.ID)) { itemData.ID = ((Object)this).name; } if (string.IsNullOrEmpty(itemData.SceneName)) { PersistentItemData<bool> obj = itemData; Scene scene = ((Component)this).gameObject.scene; obj.SceneName = GameManager.GetBaseSceneName(((Scene)(ref scene)).name); } } } private void UpdateActivatedFromFSM() { if ((Object)(object)fsm != (Object)null) { itemData.Value = fsm.FsmVariables.FindFsmBool("Activated").Value; } else { fsm = LookForMyFSM(); } } protected virtual PlayMakerFSM LookForMyFSM() { PlayMakerFSM[] components = ((Component)this).GetComponents<PlayMakerFSM>(); if (components == null) { return null; } return FSMUtility.FindFSMWithPersistentBool(components); } } [HarmonyPatch(typeof(PersistentItem<bool>))] internal class PersistentBoolItemHook { private static readonly Dictionary<string, FlagType> PersistentTypes = new Dictionary<string, FlagType> { { "moss vine cluster", FlagType.Shortcut }, { "abyss_collapse_wall_bot", FlagType.Shortcut }, { "shaman_collapse_wall", FlagType.Shortcut }, { "explode_wall", FlagType.Shortcut }, { "explode_wall 4", FlagType.Shortcut }, { "explode_wall fake", FlagType.Shortcut }, { "abyss_collapse_wall", FlagType.Shortcut }, { "weaver_lift_power_chamber", FlagType.Shortcut }, { "ant_lever_persistent", FlagType.Shortcut }, { "one way wall", FlagType.Shortcut }, { "collapser small", FlagType.Shortcut }, { "collapser small_bell_type", FlagType.Shortcut }, { "breakable wall aqueduct start", FlagType.Shortcut }, { "breakable wall", FlagType.Shortcut }, { "breakable wall bot mid", FlagType.Shortcut }, { "breakable wall_silhouette", FlagType.Shortcut }, { "drop_planks", FlagType.Shortcut }, { "inverse remasker", FlagType.Shortcut }, { "grouped remasker", FlagType.Shortcut }, { "grouped remasker_large", FlagType.Shortcut }, { "secret mask", FlagType.Shortcut }, { "secret mask new", FlagType.Shortcut }, { "secret mask new sharp ultra", FlagType.Shortcut }, { "secret mask new gate", FlagType.Shortcut }, { "slab_vent_mask", FlagType.Shortcut }, { "breakable window", FlagType.Shortcut }, { "breakable window arborium", FlagType.Shortcut }, { "coral crust wall sphere", FlagType.Shortcut }, { "crust wall", FlagType.Shortcut }, { "coral crust wall tall", FlagType.Shortcut }, { "coral crust wall mid", FlagType.Shortcut }, { "coral crust wall small", FlagType.Shortcut }, { "song_lever_side old", FlagType.Shortcut }, { "song_lever_side", FlagType.Shortcut }, { "song_lever_side right", FlagType.Shortcut }, { "bell wall tall", FlagType.Shortcut }, { "mines lift", FlagType.Shortcut }, { "act3_bellway_01_breakable_wall", FlagType.Shortcut }, { "wind rock", FlagType.Shortcut }, { "plank_wall_cluster", FlagType.Shortcut }, { "understore lever", FlagType.Shortcut }, { "gg_breakable_junk_pile_red", FlagType.Shortcut }, { "charm_break_wall", FlagType.Shortcut }, { "bone lever", FlagType.Shortcut }, { "chain drop platform", FlagType.Shortcut }, { "bone gate", FlagType.Shortcut }, { "fall_platform_barrels", FlagType.Shortcut }, { "lever", FlagType.Shortcut }, { "song_gate_small", FlagType.Shortcut }, { "trapdoor lever", FlagType.Shortcut }, { "plate", FlagType.Shortcut }, { "bone carriage", FlagType.Shortcut }, { "barrel plat lift", FlagType.Shortcut }, { "dockdashexploderock", FlagType.Shortcut }, { "explode_wall (7) - boneEast07_openedMidRoof", FlagType.Shortcut }, { "bone_east_10_church", FlagType.Shortcut }, { "toll door", FlagType.Shortcut }, { "toll door breakable cog", FlagType.Shortcut }, { "dropbomb rock", FlagType.Shortcut }, { "bone east 11 cross over group", FlagType.Shortcut }, { "coal bucket plat", FlagType.Shortcut }, { "explode_wall_norck", FlagType.Shortcut }, { "hornet_pressure_plate_small_persistent", FlagType.Shortcut }, { "clover gate", FlagType.Shortcut }, { "cog_lever", FlagType.Shortcut }, { "active", FlagType.Shortcut }, { "cog_junk_pile_break", FlagType.Shortcut }, { "pipe_vent_hatch broken", FlagType.Shortcut }, { "plank_wall_cluster_metal", FlagType.Shortcut }, { "slide_gate_ring", FlagType.Shortcut }, { "coral10rightsidegate", FlagType.Shortcut }, { "coral crust mask", FlagType.Shortcut }, { "coral_gate_large", FlagType.Shortcut }, { "stalactite group bottom", FlagType.Shortcut }, { "stalactite group top", FlagType.Shortcut }, { "breakable blocker struts", FlagType.Shortcut }, { "cradle_plat", FlagType.Shortcut }, { "cradle_spike_plat", FlagType.Shortcut }, { "aspid_sealed_gate_stone", FlagType.Shortcut }, { "harpoon ring pull switch", FlagType.Shortcut }, { "dock_pressure_plate_lock", FlagType.Shortcut }, { "greymoor_lever_simple_edited", FlagType.Shortcut }, { "Dust_02Gate", FlagType.Shortcut }, { "greymoor_lever_simple", FlagType.Shortcut }, { "break wall dustpen cage", FlagType.Shortcut }, { "plat collider", FlagType.Shortcut }, { "bell chain plat", FlagType.Shortcut }, { "greymoor stand lever - bridge", FlagType.Shortcut }, { "greymoor stand lever - crane", FlagType.Shortcut }, { "hornet_cage_rect_lift_greymoor", FlagType.Shortcut }, { "propeller", FlagType.Shortcut }, { "greymoor_breakable_plat", FlagType.Shortcut }, { "belltown_lever_drop_plat", FlagType.Shortcut }, { "greymoor_drop_propeller", FlagType.Shortcut }, { "grey_wood_chain_plat_mid_breakable", FlagType.Shortcut }, { "greymoor stand lever", FlagType.Shortcut }, { "greystore_floor_shortcut", FlagType.Shortcut }, { "bank_door", FlagType.Shortcut }, { "bot blocker", FlagType.Shortcut }, { "innerworks_lever", FlagType.Shortcut }, { "top shortcut", FlagType.Shortcut }, { "attic_ladder", FlagType.Shortcut }, { "understore propeller", FlagType.Shortcut }, { "gg_breakable_junk_pile_gold", FlagType.Shortcut }, { "understore big plate gate", FlagType.Shortcut }, { "barrel plat lift thin", FlagType.Shortcut }, { "plug_gate", FlagType.Shortcut }, { "stalactite group", FlagType.Shortcut }, { "vine platform", FlagType.Shortcut }, { "bone lever 2", FlagType.Shortcut }, { "thick silk vines", FlagType.Shortcut }, { "shellwood twig wall", FlagType.Shortcut }, { "hornet_cage_rect_lift_organ", FlagType.Shortcut }, { "organ_lift_broken_drop", FlagType.Shortcut }, { "one way wall crystal", FlagType.Shortcut }, { "junk_pile_break_peak", FlagType.Shortcut }, { "crystal break chunk", FlagType.Shortcut }, { "drop pillar_chunk", FlagType.Shortcut }, { "sack_multi_breakable", FlagType.Shortcut }, { "diving_bell_door_breakable", FlagType.Shortcut }, { "dock_sealed_gate", FlagType.Shortcut }, { "cage_mid_small_long_sway drop", FlagType.Shortcut }, { "breakablewallswampbenchshortcut", FlagType.Shortcut }, { "plank_wall_cluster_swamp", FlagType.Shortcut }, { "witch cluster vine", FlagType.Shortcut }, { "drop_planks_horizontal", FlagType.Shortcut }, { "gloom_lift_destroy", FlagType.Shortcut }, { "hornet_pressure_plate", FlagType.Shortcut }, { "shellwood hive", FlagType.Shortcut }, { "hornet_cage_rect_lift", FlagType.Shortcut }, { "plank solid 2", FlagType.Shortcut }, { "shellwood twig wall start", FlagType.Shortcut }, { "shellwood twig wall top", FlagType.Shortcut }, { "slab_jail_lever", FlagType.Shortcut }, { "door_slabcaged", FlagType.Shortcut }, { "slab lock", FlagType.Shortcut }, { "slab_cage_plat_thin_fall", FlagType.Shortcut }, { "slab lock l", FlagType.Shortcut }, { "slab lock r", FlagType.Shortcut }, { "breakable wall waterways", FlagType.Shortcut }, { "jail gate door - persistent", FlagType.Shortcut }, { "breakable floor_basic song_01", FlagType.Shortcut }, { "breakable wall entrance secret", FlagType.Shortcut }, { "breakable wall ruin lift", FlagType.Shortcut }, { "chain platform breaker", FlagType.Shortcut }, { "breakable wall second", FlagType.Shortcut }, { "propeller understore propeller", FlagType.Shortcut }, { "hatch", FlagType.Shortcut }, { "song_lever_side - merchant quest accepted", FlagType.Shortcut }, { "breakable floor_basic", FlagType.Shortcut }, { "cradle_pipe_trapdoor", FlagType.Shortcut }, { "one way vinewall", FlagType.Shortcut }, { "snail_collapse_wall", FlagType.Shortcut }, { "pipe_cog_lever", FlagType.Shortcut }, { "barrel plat lift dropper bottom", FlagType.Shortcut }, { "barrel plat lift dropper", FlagType.Shortcut }, { "collapser small understore grate", FlagType.Shortcut }, { "fall_platform_barrel wide", FlagType.Shortcut }, { "cog_05_shortcut", FlagType.Shortcut }, { "innerworks trap bridge large", FlagType.Shortcut }, { "architect shrine door", FlagType.Shortcut }, { "fall platform support pole", FlagType.Shortcut }, { "fall_platform_barrel", FlagType.Shortcut }, { "understore lever l main", FlagType.Shortcut }, { "under_lift_large", FlagType.Shortcut }, { "lock_machine", FlagType.Shortcut }, { "pipe_vent_hatch", FlagType.Shortcut }, { "ward_junk_pile_break", FlagType.Shortcut }, { "ward_junk_pile_break 1", FlagType.Shortcut }, { "ward_junk_pile_break 2", FlagType.Shortcut }, { "ward_junk_pile_break 3", FlagType.Shortcut }, { "ward_junk_pile_break 4", FlagType.Shortcut }, { "junk hatch", FlagType.Shortcut }, { "crest_shrine_break_tube", FlagType.Shortcut }, { "loom_room_jar2", FlagType.Shortcut }, { "ant_item_string", FlagType.Collectable }, { "tool_metal_deposit", FlagType.Collectable }, { "ladybug craft pickup", FlagType.CraftingKit }, { "silk grub large cocoon", FlagType.Collectable }, { "silk grub small cocoon 1", FlagType.Collectable }, { "silk grub small cocoon 2", FlagType.Collectable }, { "breakable hang sack memory locket", FlagType.Collectable }, { "collectable item pickup", FlagType.Collectable }, { "slab_item_chain", FlagType.Collectable }, { "breakable hang sack 3", FlagType.Collectable }, { "heart piece", FlagType.Mask }, { "library_glass_heart_piece", FlagType.Mask }, { "silk spool", FlagType.Spool }, { "lamp_hang_top", FlagType.Spool }, { "moss_berry_fruit", FlagType.QuestItem }, { "nectar pickup", FlagType.QuestItem }, { "bone chest", FlagType.Currency }, { "geo small persistent", FlagType.Currency }, { "geo mid persistent", FlagType.Currency }, { "ant chest", FlagType.Currency }, { "shell shard 01 persistent", FlagType.Currency }, { "shell shard 02 persistent", FlagType.Currency }, { "shell shard 03 persistent", FlagType.Currency }, { "city shard chest", FlagType.Currency }, { "pilgrim chest", FlagType.Currency }, { "craw_home_front", FlagType.Currency }, { "craw_home_right", FlagType.Currency }, { "craw_home_hang", FlagType.Currency }, { "chest", FlagType.Currency }, { "shell shard fossil shellwood", FlagType.Currency }, { "churchkeeper_rosary", FlagType.Currency }, { "cog_choir_cylinder", FlagType.Progression }, { "thread_memory_orb_source", FlagType.Progression }, { "shrine pressure plate l", FlagType.Progression }, { "shrine pressure plate TR", FlagType.Progression }, { "shrine pressure plate BR", FlagType.Progression }, { "plaque pressure plate", FlagType.Progression }, { "memory orb group", FlagType.Progression }, { "song knight tube", FlagType.Progression }, { "diving bell door", FlagType.Progression }, { "prince cell door", FlagType.Progression }, { "slab_break_weaver_gate_ring", FlagType.Progression }, { "organ webbed bench", FlagType.Bench }, { "fake bench collapser", FlagType.Bench } }; [HarmonyPatch("Start")] [HarmonyPostfix] private static void Start(PersistentItem<bool> __instance) { if (!Object.op_Implicit((Object)(object)__instance.fsm) || __instance.ItemData.Value) { return; } FsmState[] fsmStates = __instance.fsm.FsmStates; foreach (FsmState val in fsmStates) { int num = -1; int num2 = 0; while (num2 < val.Actions.Length) { FsmStateAction obj = val.Actions[num2]; SetFsmBool val2 = (SetFsmBool)(object)((obj is SetFsmBool) ? obj : null); if (val2 == null || !(val2.variableName.Value == "Activated")) { FsmStateAction obj2 = val.Actions[num2]; SetBoolValue val3 = (SetBoolValue)(object)((obj2 is SetBoolValue) ? obj2 : null); if (val3 == null || !(((NamedVariable)val3.boolVariable).Name == "Activated")) { num2++; continue; } } num = num2; break; } if (num != -1) { PersistentHook persistentHook = new PersistentHook(); ((FsmStateAction)persistentHook).Init(val); List<FsmStateAction> list = val.Actions.ToList(); list.Insert(num + 1, (FsmStateAction)(object)persistentHook); val.Actions = list.ToArray(); val.SaveActions(); break; } } } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPrefix] private static void SaveStateNoConditionPrefix(PersistentItem<bool> __instance, out bool __state) { __state = __instance.ItemData.Value; } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPostfix] public static void SaveStateNoConditionPostfix(PersistentItem<bool> __instance, bool __state) { if (__state == __instance.ItemData.Value) { Log.LogDebug($"persistent '{__instance.ItemData.ID}' value was the same ({__instance.ItemData.Value}), skipping"); return; } if (__instance.itemData.IsSemiPersistent || __instance.dontSave) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was semipersistent"); return; } string iD = __instance.itemData.ID; string sceneName = __instance.itemData.SceneName; bool value = __instance.itemData.Value; string text = Regex.Replace(iD.ToLower(), " ?\\((\\d+|Clone)\\)$", ""); Gate val = default(Gate); FlagType flagType; FlagType value2; if (((Component)__instance).TryGetComponent<Gate>(ref val)) { flagType = FlagType.Shortcut; } else if (PersistentTypes.TryGetValue(text, out value2)) { flagType = value2; } else if (text.StartsWith("remask")) { flagType = FlagType.Shortcut; } else if (text.StartsWith("black_thread_core")) { flagType = FlagType.Boss; } else if (text.StartsWith("bell_toll")) { flagType = FlagType.Bench; } else { if (!text.StartsWith("battle scene") && !text.StartsWith("black thread battle scene") && !text.StartsWith("boss scene")) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was not sent"); return; } flagType = FlagType.Arena; } Log.LogDebug($"[CLI: PERSISTENT] {text}, {flagType}"); NetworkSender.AddPersistentBoolData(iD, sceneName, value, flagType); } } [HarmonyPatch(typeof(Lever))] internal static class LeverHook { [HarmonyPatch("Hit")] [HarmonyPostfix] public static void Hit(Lever __instance, ref HitResponse __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__result.response != 0 && !__instance.doesNotActivate) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); if (Object.op_Implicit((Object)(object)__instance.persistent) && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponentInParent<DialDoorBridge>())) { ((PersistentItem<bool>)(object)__instance.persistent).ItemData.Value = true; PersistentBoolItemHook.SaveStateNoConditionPostfix((PersistentItem<bool>)(object)__instance.persistent, __state: false); } } } } [HarmonyPatch(typeof(Lever_tk2d))] internal static class Lever_tk2dHook { [HarmonyPatch("Hit")] [HarmonyPrefix] public static void Hit(Lever_tk2d __instance, HitInstance damageInstance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) HitInstance val = damageInstance; PersistentBoolItem val2 = default(PersistentBoolItem); if (((HitInstance)(ref val)).IsNailDamage && (!Object.op_Implicit((Object)(object)__instance.canHitTrigger) || __instance.canHitTrigger.IsInside) && !__instance.activated && ((Component)__instance).TryGetComponent<PersistentBoolItem>(ref val2)) { ((PersistentItem<bool>)(object)val2).ItemData.Value = true; PersistentBoolItemHook.SaveStateNoConditionPostfix((PersistentItem<bool>)(object)val2, __state: false); } } } internal class PersistentHook : FsmStateAction { public override void OnEnter() { GameObject gameObject = ((Component)((FsmStateAction)this).Fsm.Owner).gameObject; if (Object.op_Implicit((Object)(object)gameObject)) { PersistentBoolItem component = gameObject.GetComponent<PersistentBoolItem>(); if (Object.op_Implicit((Object)(object)component)) { ((PersistentItem<bool>)(object)component).SaveState(); ((FsmStateAction)this).Finish(); } } } } [HarmonyPatch(typeof(PersistentItem<int>))] internal class PersistentIntItemHook { private static readonly string[] CurrencyItems = new string[4] { "rosary", "shell", "song", "ant_" }; [HarmonyPatch("SaveStateNoCondition")] [HarmonyPrefix] private static void SaveStateNoCondition_Prefix(PersistentItem<int> __instance, out int __state) { __state = __instance.ItemData.Value; } [HarmonyPatch("SaveStateNoCondition")] [HarmonyPostfix] public static void SaveStateNoConditionPostfix(PersistentItem<int> __instance, int __state) { if (__state == __instance.ItemData.Value) { Log.LogDebug($"persistent '{__instance.ItemData.ID}' value was the same ({__instance.ItemData.Value}), skipping"); return; } if (__instance.itemData.IsSemiPersistent || __instance.dontSave) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was semipersistent"); return; } string iD = __instance.itemData.ID; string sceneName = __instance.itemData.SceneName; int value = __instance.itemData.Value; FlagType flagType = FlagType.DoNotSync; string text = iD.ToLower(); string[] currencyItems = CurrencyItems; foreach (string value2 in currencyItems) { if (text.StartsWith(value2)) { flagType = FlagType.Currency; break; } } if (flagType == FlagType.DoNotSync) { if (iD.StartsWith("bellshrine")) { flagType = FlagType.Bellshrine; } else if (iD.StartsWith("library sliding")) { flagType = FlagType.Shortcut; } } if (flagType == FlagType.DoNotSync) { Log.LogDebug("persistent '" + __instance.ItemData.ID + "' value was not sent"); } else { Log.LogDebug($"[CLI: PERSISTENT INT] {text}, {flagType}"); NetworkSender.AddPersistentIntData(iD, sceneName, value, flagType); } } } [HarmonyPatch(typeof(PlayerData))] internal static class PlayerDataHook { [HarmonyPatch("SetBool")] [HarmonyPrefix] private static void SetBool(string boolName, bool value) { BoolUpdated(boolName, value, (Operation)0); } [HarmonyPatch("SetInt")] [HarmonyPrefix] private static void SetInt(string intName, int value) { IntUpdated(intName, value, (Operation)3); } public static void BoolUpdated(string boolName, bool value, Operation operation = (Operation)0) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 if (string.IsNullOrEmpty(boolName)) { return; } Log.LogDebug("[CLI: PD BOOL] " + boolName); if (!ItemNames.BoolKeys.TryGetValue(boolName, out var value2) || ClientState.WasItemReceived(boolName)) { return; } bool state = value; bool flag = PlayerData.instance.GetBool(boolName); if ((int)operation != 0 || value != flag) { if ((int)operation == 1) { state = !flag; } NetworkSender.SendFlag(boolName, value2.Type, value2.Name, state); } } public static void IntUpdated(string intName, int value, Operation operation = (Operation)3) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if (string.IsNullOrEmpty(intName)) { return; } Log.LogDebug("[CLI: PD INT] " + intName); if (!ItemNames.IntKeys.TryGetValue(intName, out var value2)) { return; } int num = PlayerData.instance.GetInt(intName); int num2 = num; if ((int)operation == 3 && value == num) { return; } if ((int)operation == 0) { num2 += value; } else if ((int)operation == 1) { num2 -= value; } else { if ((int)operation != 2) { return; } num2 *= value; } NetworkSender.SendInt(intName, value2.Type, value2.Name, num2); } } [HarmonyPatch(typeof(SetPlayerDataBool))] internal static class SetPlayerDataBoolHook { [HarmonyPatch("SetBool")] [HarmonyPrefix] public static void SetBool(SetPlayerDataBool __instance) { string boolName = __instance.boolName; bool value = __instance.value; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(SetPlayerDataVariable))] internal static class SetPlayerDataVariableHook { [HarmonyPatch("OnEnter")] [HarmonyPrefix] public static void OnEnter(SetPlayerDataVariable __instance) { string value = __instance.VariableName.Value; object value2 = __instance.SetValue.GetValue(); if (value2 is bool value3) { PlayerDataHook.BoolUpdated(value, value3, (Operation)0); } else if (value2 is int value4) { PlayerDataHook.IntUpdated(value, value4, (Operation)3); } } } [HarmonyPatch(typeof(StateChangeSequence))] internal static class StateChangeSequenceHook { [HarmonyPatch("SetIsCompleteBool")] [HarmonyPrefix] public static void OnEnter(StateChangeSequence __instance) { string isCompleteBool = __instance.isCompleteBool; bool value = true; PlayerDataHook.BoolUpdated(isCompleteBool, value, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataCollectable))] internal static class PlayerDataCollectableHook { [HarmonyPatch("Get")] [HarmonyPrefix] public static void Get(PlayerDataCollectable __instance) { PlayerDataHook.BoolUpdated(__instance.linkedPDBool, value: true, (Operation)0); PlayerDataHook.IntUpdated(__instance.linkedPDInt, 1, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataBoolCollectable))] internal static class PlayerDataBoolCollectableHook { [HarmonyPatch("Get")] [HarmonyPostfix] public static void UnlockSequence(PlayerDataBoolCollectable __instance) { string boolName = __instance.boolName; bool value = true; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(CollectableItemBasic))] internal static class CollectableItemBasicHook { [HarmonyPatch("SetUniqueBool")] [HarmonyPrefix] private static void SetUniqueBool(CollectableItemBasic __instance) { PlayerDataHook.BoolUpdated(__instance.uniqueCollectBool, value: true, (Operation)0); } } [HarmonyPatch(typeof(CaravanTroupeHunter))] internal static class CaravanTroupeHunterHook { [HarmonyPatch("OnPurchasedItem")] [HarmonyPrefix] public static void OnPurchasedItem(CaravanTroupeHunter __instance, int itemIndex) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) string boolName = CaravanTroupeHunter.PdBools[__instance.currentListGroups[itemIndex]]; bool value = true; PlayerDataHook.BoolUpdated(boolName, value, (Operation)0); } } [HarmonyPatch(typeof(ConfigBase))] internal static class ControlReminderHook { [HarmonyPatch("DoAppear")] [HarmonyPrefix] public static void DoAppear(ConfigBase __instance) { string playerDataBool = __instance.PlayerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(CurrencyObjectBase))] internal static class CurrencyObjectBaseHook { [HarmonyPatch("CollectPopup")] [HarmonyPrefix] private static void CollectPopup(CurrencyObjectBase __instance) { PlayerDataHook.BoolUpdated(__instance.firstGetPDBool, value: true, (Operation)0); PlayerDataHook.BoolUpdated(__instance.popupPDBool, value: true, (Operation)0); } } [HarmonyPatch] internal static class GameManagerHook { private static MethodInfo TargetMethod() { return typeof(GameManager).GetMethod("SetPlayerDataVariable", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(typeof(bool)); } private static void Prefix(string fieldName, bool value) { PlayerDataHook.BoolUpdated(fieldName, value, (Operation)0); } } [HarmonyPatch(typeof(ItemReceptacle))] internal static class ItemReceptacleHook { [HarmonyPatch("UnlockSequence")] [HarmonyPrefix] public static void UnlockSequence(ItemReceptacle __instance) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(PersistentPressurePlate))] internal static class PersistentPressurePlateHook { [HarmonyPatch("Activate")] [HarmonyPrefix] public static void Activate(PersistentPressurePlate __instance) { string playerDataBool = __instance.playerDataBool; bool value = true; PlayerDataHook.BoolUpdated(playerDataBool, value, (Operation)0); } } [HarmonyPatch(typeof(PlayerDataBoolOperation))] internal static class PlayerDataBoolOperationHook { [HarmonyPatch("Execute")] [HarmonyPrefix] public static void Execute(PlayerDataBoolOperation __instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing refer