/* * Copyright 2022 Peter Han * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections.Generic; namespace PeterHan.PLib.Core { /// /// Transparently provides the functionality of PRegistry, while the actual instance is /// from another mod's bootstrapper. /// internal sealed class PRemoteRegistry : IPLibRegistry { /// /// The prototype used for delegates to remote GetAllComponents. /// private delegate System.Collections.ICollection GetAllComponentsDelegate(string id); /// /// The prototype used for delegates to remote GetLatestVersion and GetSharedData. /// private delegate object GetObjectDelegate(string id); /// /// The prototype used for delegates to remote SetSharedData. /// private delegate void SetObjectDelegate(string id, object value); /// /// Points to the local registry's version of AddCandidateVersion. /// private readonly Action addCandidateVersion; /// /// Points to the local registry's version of GetAllComponents. /// private readonly GetAllComponentsDelegate getAllComponents; /// /// Points to the local registry's version of GetLatestVersion. /// private readonly GetObjectDelegate getLatestVersion; /// /// Points to the local registry's version of GetSharedData. /// private readonly GetObjectDelegate getSharedData; /// /// Points to the local registry's version of SetSharedData. /// private readonly SetObjectDelegate setSharedData; public IDictionary ModData { get; private set; } /// /// The components actually instantiated (latest version of each). /// private readonly IDictionary remoteComponents; /// /// Creates a remote registry wrapping the target object. /// /// The PRegistryComponent instance to wrap. internal PRemoteRegistry(object instance) { if (instance == null) throw new ArgumentNullException(nameof(instance)); remoteComponents = new Dictionary(32); if (!PPatchTools.TryGetPropertyValue(instance, nameof(ModData), out IDictionary modData)) throw new ArgumentException("Remote instance missing ModData"); ModData = modData; var type = instance.GetType(); addCandidateVersion = type.CreateDelegate>(nameof( PRegistryComponent.DoAddCandidateVersion), instance, typeof(object)); getAllComponents = type.CreateDelegate(nameof( PRegistryComponent.DoGetAllComponents), instance, typeof(string)); getLatestVersion = type.CreateDelegate(nameof( PRegistryComponent.DoGetLatestVersion), instance, typeof(string)); if (addCandidateVersion == null || getLatestVersion == null || getAllComponents == null) throw new ArgumentException("Remote instance missing candidate versions"); getSharedData = type.CreateDelegate(nameof(IPLibRegistry. GetSharedData), instance, typeof(string)); setSharedData = type.CreateDelegate(nameof(IPLibRegistry. SetSharedData), instance, typeof(string), typeof(object)); if (getSharedData == null || setSharedData == null) throw new ArgumentException("Remote instance missing shared data"); } public void AddCandidateVersion(PForwardedComponent instance) { addCandidateVersion.Invoke(instance); } public IEnumerable GetAllComponents(string id) { ICollection results = null; var all = getAllComponents.Invoke(id); if (all != null) { results = new List(all.Count); foreach (var component in all) if (component is PForwardedComponent local) results.Add(local); else results.Add(new PRemoteComponent(component)); } return results; } public PForwardedComponent GetLatestVersion(string id) { if (!remoteComponents.TryGetValue(id, out PForwardedComponent remoteComponent)) { // Attempt to resolve it object instantiated = getLatestVersion.Invoke(id); if (instantiated == null) { #if DEBUG PRegistry.LogPatchWarning("Unable to find a component matching: " + id); #endif remoteComponent = null; } else if (instantiated is PForwardedComponent inThisMod) // Running the current version remoteComponent = inThisMod; else remoteComponent = new PRemoteComponent(instantiated); remoteComponents.Add(id, remoteComponent); } return remoteComponent; } public object GetSharedData(string id) { return getSharedData.Invoke(id); } public void SetSharedData(string id, object data) { setSharedData.Invoke(id, data); } } }