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 SharedDiscovery v0.1.0
plugins\SharedDiscovery.Core.dll
Decompiled a month 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.Versioning; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SharedDiscovery.Core.Discovery; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("SharedDiscovery.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a98234877f9b3ac258bc4c93ecb7a728180ef2ef")] [assembly: AssemblyProduct("SharedDiscovery.Core")] [assembly: AssemblyTitle("SharedDiscovery.Core")] [assembly: AssemblyVersion("1.0.0.0")] 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; } } } namespace SharedDiscovery.Core.Persistence { public sealed class WorldDiscoveryData { public int Version { get; set; } = 1; public List<string> Discoveries { get; set; } = new List<string>(); } public sealed class WorldDiscoveryStore { private const int CurrentVersion = 1; private readonly string _worldsDirectory; private readonly Action<string> _info; private readonly Action<string> _warning; public WorldDiscoveryStore(string worldsDirectory, Action<string>? info = null, Action<string>? warning = null) { _worldsDirectory = worldsDirectory ?? throw new ArgumentNullException("worldsDirectory"); _info = info ?? ((Action<string>)delegate { }); _warning = warning ?? ((Action<string>)delegate { }); } public IReadOnlyList<string> Load(string worldId) { //IL_00fd: Expected O, but got Unknown string path = GetPath(worldId); try { if (!File.Exists(path)) { _info("No SharedDiscovery state exists yet for world '" + worldId + "'."); return Array.Empty<string>(); } string text = File.ReadAllText(path); if (string.IsNullOrWhiteSpace(text)) { _warning("SharedDiscovery state for world '" + worldId + "' is empty; starting with no discoveries."); return Array.Empty<string>(); } WorldDiscoveryData worldDiscoveryData = JsonConvert.DeserializeObject<WorldDiscoveryData>(text); if (worldDiscoveryData?.Discoveries == null) { _warning("SharedDiscovery state for world '" + worldId + "' did not contain discoveries; starting empty."); return Array.Empty<string>(); } return worldDiscoveryData.Discoveries.Where(DiscoveryValidator.IsValidItemId).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string value) => value, StringComparer.Ordinal) .ToArray(); } catch (JsonException ex) { JsonException ex2 = ex; _warning("Could not parse SharedDiscovery state for world '" + worldId + "': " + ((Exception)(object)ex2).Message); return Array.Empty<string>(); } catch (IOException ex3) { _warning("Could not read SharedDiscovery state for world '" + worldId + "': " + ex3.Message); return Array.Empty<string>(); } catch (UnauthorizedAccessException ex4) { _warning("Could not access SharedDiscovery state for world '" + worldId + "': " + ex4.Message); return Array.Empty<string>(); } } public bool Save(string worldId, IEnumerable<string> discoveries) { string path = GetPath(worldId); try { Directory.CreateDirectory(_worldsDirectory); string contents = JsonConvert.SerializeObject((object)new WorldDiscoveryData { Version = 1, Discoveries = discoveries.Where(DiscoveryValidator.IsValidItemId).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string value) => value, StringComparer.Ordinal) .ToList() }, (Formatting)1); string text = path + ".tmp"; File.WriteAllText(text, contents); if (File.Exists(path)) { File.Delete(path); } File.Move(text, path); return true; } catch (IOException ex) { _warning("Could not save SharedDiscovery state for world '" + worldId + "': " + ex.Message); return false; } catch (UnauthorizedAccessException ex2) { _warning("Could not access SharedDiscovery state for world '" + worldId + "': " + ex2.Message); return false; } } private string GetPath(string worldId) { string text = (DiscoveryValidator.IsValidItemId(worldId) ? worldId : "unknown-world"); return Path.Combine(_worldsDirectory, text + ".json"); } } } namespace SharedDiscovery.Core.Network { public sealed class DiscoverySnapshot { public IReadOnlyList<string> ItemIds { get; } public DiscoverySnapshot(IEnumerable<string> itemIds) { ItemIds = (itemIds ?? Enumerable.Empty<string>()).Where(DiscoveryValidator.IsValidItemId).Distinct().OrderBy<string, string>((string value) => value, StringComparer.Ordinal) .ToArray(); } } } namespace SharedDiscovery.Core.Discovery { public static class DiscoveryOriginPolicy { public static bool ShouldApplyToHost(long sender, bool isNewDiscovery) { return sender != 0 && isNewDiscovery; } } public sealed class DiscoveryRegistry { private readonly HashSet<string> _discoveries; public int Count => _discoveries.Count; public DiscoveryRegistry() { _discoveries = new HashSet<string>(StringComparer.Ordinal); } public bool Add(string itemId) { if (!DiscoveryValidator.IsValidItemId(itemId)) { return false; } return _discoveries.Add(itemId); } public bool Contains(string itemId) { if (DiscoveryValidator.IsValidItemId(itemId)) { return _discoveries.Contains(itemId); } return false; } public void ReplaceWith(IEnumerable<string> itemIds) { _discoveries.Clear(); foreach (string item in itemIds ?? Enumerable.Empty<string>()) { Add(item); } } public void Clear() { _discoveries.Clear(); } public IReadOnlyList<string> Snapshot() { return _discoveries.OrderBy<string, string>((string value) => value, StringComparer.Ordinal).ToArray(); } } public static class DiscoveryValidator { public const int MaxItemIdLength = 128; public static bool IsValidItemId(string? itemId) { if (itemId == null || itemId.Trim().Length == 0) { return false; } if (itemId.Length > 128) { return false; } foreach (char c in itemId) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.') { return false; } } return true; } } }
plugins\SharedDiscovery.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SharedDiscovery.Config; using SharedDiscovery.Core.Discovery; using SharedDiscovery.Core.Persistence; using SharedDiscovery.Discovery; using SharedDiscovery.Network; using SharedDiscovery.Persistence; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("SharedDiscovery")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a98234877f9b3ac258bc4c93ecb7a728180ef2ef")] [assembly: AssemblyProduct("SharedDiscovery")] [assembly: AssemblyTitle("SharedDiscovery")] [assembly: AssemblyVersion("1.0.0.0")] 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; } } } namespace SharedDiscovery { [BepInPlugin("lorenzo.valheim.shareddiscovery", "SharedDiscovery", "0.1.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "lorenzo.valheim.shareddiscovery"; public const string Name = "SharedDiscovery"; public const string Version = "0.1.0"; private Harmony? _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static DiscoveryService Discovery { get; private set; } internal static VanillaDiscoveryService VanillaDiscovery { get; private set; } internal static DiscoveryNetwork Network { get; private set; } internal static DiscoverySessionState Session { get; private set; } private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); WorldDiscoveryStoreAdapter store = new WorldDiscoveryStoreAdapter(); Session = new DiscoverySessionState(); Discovery = new DiscoveryService(store); VanillaDiscovery = new VanillaDiscoveryService(); Network = new DiscoveryNetwork(Discovery, VanillaDiscovery); _harmony = new Harmony("lorenzo.valheim.shareddiscovery"); _harmony.PatchAll(); Network.TryRegister(); Log.LogInfo((object)"SharedDiscovery 0.1.0 loaded."); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } ResetSessionState(save: true); Log.LogInfo((object)"SharedDiscovery unloaded."); } internal static void ResetSessionState(bool save) { if (save) { Discovery.SaveCurrentWorld(); } Session.Reset(); Discovery.Clear(); VanillaDiscovery.ClearPending(); Network.ResetSession(); } internal static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Log.LogInfo((object)message); } } } } namespace SharedDiscovery.Persistence { internal sealed class WorldDiscoveryStoreAdapter { private readonly WorldDiscoveryStore _store; public WorldDiscoveryStoreAdapter() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "SharedDiscovery", "worlds"); _store = new WorldDiscoveryStore(text, (Action<string>)Plugin.Log.LogInfo, (Action<string>)Plugin.Log.LogWarning); } public IReadOnlyList<string> Load(string worldId) { return _store.Load(worldId); } public bool Save(string worldId, IEnumerable<string> discoveries) { return _store.Save(worldId, discoveries); } } } namespace SharedDiscovery.Patches { [HarmonyPatch(typeof(Player), "AddKnownItem")] internal static class KnownItemPatch { private static void Prefix(Player __instance, ItemData item, ref bool __state) { __state = true; if (ModConfig.Enabled.Value && !((Object)(object)__instance == (Object)null) && item?.m_shared != null) { __state = __instance.IsMaterialKnown(item.m_shared.m_name); } } private static void Postfix(Player __instance, ItemData item, bool __state) { if (!ModConfig.Enabled.Value || __state) { return; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || item?.m_shared == null) { Plugin.DebugLog("Ignoring local AddKnownItem '" + (item?.m_shared?.m_name ?? "<unknown>") + "': not local player."); return; } if (VanillaDiscoveryService.IsApplyingSharedDiscovery) { Plugin.DebugLog("Ignoring local AddKnownItem '" + item.m_shared.m_name + "': remote-application guard active."); return; } if (!__instance.IsMaterialKnown(item.m_shared.m_name)) { Plugin.DebugLog("Ignoring local AddKnownItem '" + item.m_shared.m_name + "': vanilla did not retain knowledge."); return; } if (!Plugin.Session.AcceptLocalDiscoveries) { Plugin.DebugLog("Ignoring local AddKnownItem '" + item.m_shared.m_name + "': local discoveries not accepted."); return; } if (!Plugin.Session.IsLocalPlayerReady) { Plugin.DebugLog("Ignoring local AddKnownItem '" + item.m_shared.m_name + "': session not ready."); return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && !Plugin.Discovery.IsWorldLoaded) { Plugin.DebugLog("Ignoring local AddKnownItem '" + item.m_shared.m_name + "': no active world state."); return; } try { string candidatePrefabName = VanillaItemIdentifier.GetCandidatePrefabName(item); Plugin.DebugLog("Discovery candidate: sharedName='" + item.m_shared.m_name + "' dropPrefab='" + (candidatePrefabName ?? "<none>") + "'."); string prefabItemId = VanillaItemIdentifier.GetPrefabItemId(item); if (prefabItemId == null) { Plugin.DebugLog("Failed to resolve stable prefab ID: sharedName='" + item.m_shared.m_name + "' dropPrefab='" + (candidatePrefabName ?? "<none>") + "'."); } else if (Plugin.Session.IsRestoredItem(prefabItemId)) { Plugin.DebugLog("Ignoring restored startup item '" + prefabItemId + "'."); } else { Plugin.DebugLog(prefabItemId + " is not part of restored startup baseline; processing discovery."); Plugin.DebugLog("Resolved stable prefab ID: " + prefabItemId); Plugin.Network.SubmitLocalDiscovery(prefabItemId); } } catch (Exception arg) { Plugin.Log.LogError((object)$"SharedDiscovery failed to process discovered item '{item.m_shared.m_name}': {arg}"); } } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class ZNetAwakePatch { private static void Postfix() { Plugin.Network.TryRegister(); } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNetDestroyPatch { private static void Prefix() { Plugin.ResetSessionState(save: true); } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class PlayerSpawnedPatch { private static void Postfix(Player __instance) { if (ModConfig.Enabled.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Plugin.Network.TryRegister(); Plugin.Session.CaptureInitialRestoredItems(__instance); if (((Character)__instance).InIntro()) { Plugin.DebugLog("Local player spawned in intro; waiting for Valkyrie.DropPlayer before requesting snapshot."); } else { MarkPlayerReady(__instance); } } } internal static void MarkPlayerReady(Player player) { if (Plugin.Session.MarkLocalPlayerReady(player)) { Plugin.Network.RequestSnapshot(); } } } } namespace SharedDiscovery.Network { internal static class DiscoveryMessages { public const string SubmitDiscovery = "SharedDiscovery_SubmitDiscovery"; public const string ApplyDiscovery = "SharedDiscovery_ApplyDiscovery"; public const string RequestSnapshot = "SharedDiscovery_RequestSnapshot"; public const string Snapshot = "SharedDiscovery_Snapshot"; } internal sealed class DiscoveryNetwork { private readonly DiscoveryService _discovery; private readonly VanillaDiscoveryService _vanillaDiscovery; private bool _registered; private int _sessionGeneration; public DiscoveryNetwork(DiscoveryService discovery, VanillaDiscoveryService vanillaDiscovery) { _discovery = discovery; _vanillaDiscovery = vanillaDiscovery; } public void TryRegister() { if (!_registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<string>("SharedDiscovery_SubmitDiscovery", (Action<long, string>)RPC_SubmitDiscovery); ZRoutedRpc.instance.Register<string>("SharedDiscovery_ApplyDiscovery", (Action<long, string>)RPC_ApplyDiscovery); ZRoutedRpc.instance.Register("SharedDiscovery_RequestSnapshot", (Action<long>)RPC_RequestSnapshot); ZRoutedRpc.instance.Register<ZPackage>("SharedDiscovery_Snapshot", (Action<long, ZPackage>)RPC_Snapshot); _registered = true; Plugin.DebugLog($"Registered SharedDiscovery routed RPC handlers. Session network generation: {_sessionGeneration}."); } } public void ResetSession() { _registered = false; _sessionGeneration++; Plugin.DebugLog($"Session network generation: {_sessionGeneration}."); } public void SubmitLocalDiscovery(string itemId) { if (!DiscoveryValidator.IsValidItemId(itemId) || ZRoutedRpc.instance == null) { return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { Plugin.DebugLog("Sending discovery '" + itemId + "' to server."); if (_discovery.TryAddServerDiscovery(itemId)) { BroadcastDiscovery(itemId); } } else { Plugin.DebugLog("Sending discovery '" + itemId + "' to server."); ZRoutedRpc.instance.InvokeRoutedRPC("SharedDiscovery_SubmitDiscovery", new object[1] { itemId }); } } public void RequestSnapshot() { if (ZRoutedRpc.instance != null && WorldContext.TryGetCurrentWorldId(out string worldId)) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { Plugin.DebugLog("Snapshot request handled locally for world UID " + worldId + "."); SendSnapshot(0L); } else { Plugin.DebugLog("Snapshot request sent for world UID " + worldId + "."); ZRoutedRpc.instance.InvokeRoutedRPC("SharedDiscovery_RequestSnapshot", new object[0]); } } } public void SendSnapshot(long peerId) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && _discovery.EnsureWorldLoaded()) { ZPackage val = WriteSnapshot(_discovery.Snapshot); Plugin.DebugLog($"Sending snapshot with {_discovery.Snapshot.Count} discovery ID(s)."); if (peerId == 0L) { RPC_Snapshot(0L, new ZPackage(val.GetArray())); return; } Plugin.DebugLog($"Snapshot response sent to peer {peerId}."); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "SharedDiscovery_Snapshot", new object[1] { val }); } } private void RPC_SubmitDiscovery(long sender, string itemId) { Plugin.DebugLog($"Client discovery '{itemId}' received from peer {sender}."); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!IsValidClientSender(sender)) { Plugin.Log.LogWarning((object)$"Rejected discovery from unknown peer {sender}."); } else if (!DiscoveryValidator.IsValidItemId(itemId)) { Plugin.Log.LogWarning((object)$"Rejected invalid discovery from peer {sender}."); } else if (!VanillaItemExists(itemId)) { Plugin.Log.LogWarning((object)$"Rejected discovery for missing item prefab from peer {sender}: {itemId}"); } else if (_discovery.TryAddServerDiscovery(itemId)) { if (DiscoveryOriginPolicy.ShouldApplyToHost(sender, true)) { Plugin.DebugLog("Applying client-originated discovery '" + itemId + "' to host local player."); _vanillaDiscovery.ApplyOrQueue(itemId); } BroadcastDiscovery(itemId); } } private void RPC_ApplyDiscovery(long sender, string itemId) { ReceiveRemoteDiscovery(itemId, live: true); } private void RPC_RequestSnapshot(long sender) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { if (!IsValidClientSender(sender)) { Plugin.Log.LogWarning((object)$"Rejected snapshot request from unknown peer {sender}."); return; } Plugin.DebugLog("Snapshot request received from client."); SendSnapshot(sender); } } private void RPC_Snapshot(long sender, ZPackage package) { List<string> list = ReadSnapshot(package); Plugin.DebugLog($"Snapshot received with {list.Count} discovery ID(s)."); foreach (string item in list) { ReceiveRemoteDiscovery(item, live: false); } } private void ReceiveRemoteDiscovery(string itemId, bool live) { if (!DiscoveryValidator.IsValidItemId(itemId)) { Plugin.Log.LogWarning((object)("Rejected invalid discovery " + (live ? "broadcast" : "snapshot") + ".")); return; } if (live) { Plugin.DebugLog("Live discovery RPC received: " + itemId); } _vanillaDiscovery.ApplyOrQueue(itemId); } private void BroadcastDiscovery(string itemId) { if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null) { return; } List<ZNetPeer> list = new List<ZNetPeer>(); foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers()) { if (connectedPeer != null && !connectedPeer.m_server && connectedPeer.m_uid != 0L) { list.Add(connectedPeer); } } Plugin.DebugLog($"Broadcasting discovery '{itemId}' to {list.Count} remote peer(s)."); foreach (ZNetPeer item in list) { Plugin.DebugLog($"Sending live discovery '{itemId}' to peer routed ID {item.m_uid}."); ZRoutedRpc.instance.InvokeRoutedRPC(item.m_uid, "SharedDiscovery_ApplyDiscovery", new object[1] { itemId }); } } private static ZPackage WriteSnapshot(IReadOnlyList<string> itemIds) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(itemIds.Count); foreach (string itemId in itemIds) { val.Write(itemId); } return val; } private static List<string> ReadSnapshot(ZPackage package) { List<string> list = new List<string>(); int num; try { num = package.ReadInt(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Rejected unreadable shared discovery snapshot: " + ex.Message)); return list; } if (num < 0 || num > 4096) { Plugin.Log.LogWarning((object)$"Rejected shared discovery snapshot with invalid size: {num}"); return list; } for (int i = 0; i < num; i++) { string text; try { text = package.ReadString(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Stopped reading malformed shared discovery snapshot: " + ex2.Message)); break; } if (DiscoveryValidator.IsValidItemId(text)) { list.Add(text); } } return list; } private static bool IsValidClientSender(long sender) { if (sender == 0L || ZRoutedRpc.instance == null) { return false; } if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.GetPeer(sender) != null; } return false; } private static bool VanillaItemExists(string itemId) { if ((Object)(object)ObjectDB.instance != (Object)null) { return (Object)(object)ObjectDB.instance.GetItemPrefab(itemId) != (Object)null; } return false; } } } namespace SharedDiscovery.Discovery { internal sealed class DiscoveryService { private readonly DiscoveryRegistry _registry = new DiscoveryRegistry(); private readonly WorldDiscoveryStoreAdapter _store; private string? _worldId; public IReadOnlyList<string> Snapshot => _registry.Snapshot(); public bool IsWorldLoaded => _worldId != null; public DiscoveryService(WorldDiscoveryStoreAdapter store) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown _store = store; } public bool EnsureWorldLoaded() { if (!ModConfig.Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if (!WorldContext.TryGetCurrentWorldId(out string worldId)) { return false; } if (_worldId == worldId) { return true; } Plugin.DebugLog("Loading authoritative SharedDiscovery registry for UID " + worldId + "."); _registry.ReplaceWith((IEnumerable<string>)_store.Load(worldId)); _worldId = worldId; Plugin.Log.LogInfo((object)$"Loaded world state '{worldId}': {_registry.Count} discoveries."); return true; } public void Clear() { _registry.Clear(); _worldId = null; } public bool TryAddServerDiscovery(string itemId) { if (!ModConfig.Enabled.Value || !DiscoveryValidator.IsValidItemId(itemId) || !EnsureWorldLoaded()) { return false; } if (!_registry.Add(itemId)) { return false; } Plugin.DebugLog("Server registered global discovery '" + itemId + "'."); SaveCurrentWorld(); Plugin.Log.LogInfo((object)("New global discovery: " + itemId)); return true; } public bool Contains(string itemId) { return _registry.Contains(itemId); } public void SaveCurrentWorld() { if (_worldId != null) { _store.Save(_worldId, _registry.Snapshot()); Plugin.DebugLog($"Saved world state '{_worldId}': {_registry.Count} discoveries."); } } } internal sealed class DiscoverySessionState { private readonly HashSet<string> _restoredItemIds = new HashSet<string>(StringComparer.Ordinal); private Player? _readyPlayer; public bool AcceptLocalDiscoveries { get; private set; } public bool IsLocalPlayerReady { get; private set; } public bool CanApplyRemoteDiscoveries { get; private set; } public void CaptureInitialRestoredItems(Player player) { _restoredItemIds.Clear(); if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null) { return; } foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { string prefabItemId = VanillaItemIdentifier.GetPrefabItemId(allItem); if (prefabItemId != null && _restoredItemIds.Add(prefabItemId)) { Plugin.DebugLog("Initial restored item baseline: " + prefabItemId); } } } public bool MarkLocalPlayerReady(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } if (IsLocalPlayerReady && (Object)(object)_readyPlayer == (Object)(object)player) { return false; } _readyPlayer = player; IsLocalPlayerReady = true; CanApplyRemoteDiscoveries = true; AcceptLocalDiscoveries = true; Plugin.DebugLog("Local player is ready for remote discovery application."); return true; } public bool IsRestoredItem(string itemId) { return _restoredItemIds.Contains(itemId); } public void Reset() { AcceptLocalDiscoveries = false; IsLocalPlayerReady = false; CanApplyRemoteDiscoveries = false; _readyPlayer = null; _restoredItemIds.Clear(); VanillaDiscoveryService.ResetApplyingSharedDiscoveryGuard(); } } internal sealed class VanillaDiscoveryService { private readonly Queue<string> _pending = new Queue<string>(); private readonly HashSet<string> _pendingSet = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<string, int> _attempts = new Dictionary<string, int>(StringComparer.Ordinal); private bool _processing; private int _sessionGeneration; public static bool IsApplyingSharedDiscovery { get; private set; } public static void ResetApplyingSharedDiscoveryGuard() { IsApplyingSharedDiscovery = false; } public void ClearPending() { _pending.Clear(); _pendingSet.Clear(); _attempts.Clear(); _processing = false; _sessionGeneration++; } public void ApplyOrQueue(string itemId) { if (ModConfig.Enabled.Value && DiscoveryValidator.IsValidItemId(itemId) && !PlayerAlreadyKnows(itemId)) { if (_pendingSet.Add(itemId)) { _pending.Enqueue(itemId); Plugin.DebugLog("Queued remote discovery: " + itemId); } if (!_processing && (Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(ProcessPending(_sessionGeneration)); } } } public bool PlayerAlreadyKnows(string itemId) { Player localPlayer = Player.m_localPlayer; ItemDrop val = FindItemDrop(itemId); if ((Object)(object)localPlayer != (Object)null && (Object)(object)val != (Object)null) { return localPlayer.IsMaterialKnown(val.m_itemData.m_shared.m_name); } return false; } public void ApplySnapshot(IEnumerable<string> itemIds) { foreach (string itemId in itemIds) { ApplyOrQueue(itemId); } } private IEnumerator ProcessPending(int generation) { _processing = true; while (generation == _sessionGeneration && _pending.Count > 0) { string text = _pending.Dequeue(); _pendingSet.Remove(text); Plugin.DebugLog("Applying pending remote discovery: " + text); if (!TryApply(text)) { if (!Plugin.Session.CanApplyRemoteDiscoveries || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)MessageHud.instance == (Object)null) { Plugin.DebugLog("Deferred remote discovery '" + text + "': final player not ready."); } int value; int num = ((!_attempts.TryGetValue(text, out value)) ? 1 : (value + 1)); _attempts[text] = num; if (num >= 30) { Plugin.Log.LogWarning((object)("Shared discovery item prefab was not available after retries: " + text)); _attempts.Remove(text); } else { _pending.Enqueue(text); _pendingSet.Add(text); yield return (object)new WaitForSeconds(1f); } } else { _attempts.Remove(text); yield return null; } } if (generation == _sessionGeneration) { _processing = false; } } private static bool TryApply(string itemId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || !Plugin.Session.IsLocalPlayerReady || (Object)(object)MessageHud.instance == (Object)null) { return false; } ItemDrop val = FindItemDrop(itemId); if ((Object)(object)val == (Object)null) { return false; } string name = val.m_itemData.m_shared.m_name; if (localPlayer.IsMaterialKnown(name)) { return true; } try { IsApplyingSharedDiscovery = true; localPlayer.AddKnownItem(val.m_itemData); Plugin.DebugLog("Applied remote discovery through Player.AddKnownItem: " + itemId); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to refresh vanilla knowledge after remote AddKnownItem '{itemId}': {arg}"); return false; } finally { IsApplyingSharedDiscovery = false; } return true; } private static ItemDrop FindItemDrop(string itemId) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemId); if ((Object)(object)itemPrefab == (Object)null) { return null; } return itemPrefab.GetComponent<ItemDrop>(); } } internal static class VanillaItemIdentifier { public static string? GetPrefabItemId(ItemData item) { if (item == null || (Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject val = item.m_dropPrefab; if ((Object)(object)val == (Object)null && item.m_shared != null) { val = ObjectDB.instance.GetItemPrefab(item.m_shared); } string normalizedPrefabName = GetNormalizedPrefabName(val); if (normalizedPrefabName == null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(normalizedPrefabName); if (!((Object)(object)itemPrefab != (Object)null) || !((Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null)) { return null; } return normalizedPrefabName; } public static string? GetCandidatePrefabName(ItemData item) { if (item == null) { return null; } GameObject val = item.m_dropPrefab; if ((Object)(object)val == (Object)null && item.m_shared != null && (Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(item.m_shared); } return GetNormalizedPrefabName(val); } private static string? GetNormalizedPrefabName(GameObject? prefab) { if ((Object)(object)prefab == (Object)null) { return null; } string text = StripCloneSuffix(((Object)prefab).name); if (!DiscoveryValidator.IsValidItemId(text)) { return null; } return text; } private static string StripCloneSuffix(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } } internal static class WorldContext { public static bool TryGetCurrentWorldId(out string worldId) { worldId = string.Empty; ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } if (instance.GetWorld() == null) { return false; } long worldUID = instance.GetWorldUID(); if (worldUID == 0L) { return false; } string text = worldUID.ToString(CultureInfo.InvariantCulture); if (!DiscoveryValidator.IsValidItemId(text)) { return false; } worldId = text; return true; } } } namespace SharedDiscovery.Config { internal static class ModConfig { public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> DebugLogging { get; private set; } public static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("General", "Enabled", true, "Enable shared item discoveries for the current world."); DebugLogging = config.Bind<bool>("General", "DebugLogging", false, "Enable verbose SharedDiscovery debug logs."); } } }