Skip to content

Commit

Permalink
Add link restoring method to SyncValue and automatically dispose them…
Browse files Browse the repository at this point in the history
… from sync object
  • Loading branch information
Banane9 committed Mar 7, 2025
1 parent 1e55843 commit f416aeb
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 21 deletions.
46 changes: 32 additions & 14 deletions MonkeyLoader/Sync/MonkeySyncObject.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using EnumerableToolkit;
using HarmonyLib;
using MonkeyLoader.Meta;
using MonkeyLoader.Patching;
using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand Down Expand Up @@ -100,6 +99,11 @@ public abstract class MonkeySyncObject<TSyncObject, TSyncValue, TLink> : IUnlink
/// </summary>
protected static readonly Dictionary<string, Func<TSyncObject, TSyncValue>> propertyAccessorsByName = new(StringComparer.Ordinal);

/// <summary>
/// The <typeparamref name="TSyncValue"/> instances associated with this sync object.
/// </summary>
protected readonly HashSet<TSyncValue> syncValues = [];

private bool _disposedValue;

/// <inheritdoc/>
Expand Down Expand Up @@ -217,19 +221,26 @@ protected virtual bool EstablishLink(bool fromRemote)
/// Creates a link for the given sync value of the given name.
/// </summary>
/// <remarks>
/// <i>By default:</i> Calls
/// <c><paramref name="syncValue"/>.<see cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor">EstablishLinkFor</see>()</c>.
/// <i>By default:</i> Adds the given sync value to the <see cref="syncValues">set of instances</see> and calls
/// <c><paramref name="syncValue"/>.<see cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor">EstablishLinkFor</see>()</c>.
/// </remarks>
/// <param name="syncValue">The sync value to link.</param>
/// <param name="propertyName">The name of the sync value to link.</param>
/// <param name="fromRemote">Whether the link is being established from the remote side.</param>
/// <returns><c>true</c> if the link was successfully created; otherwise, <c>false</c>.</returns>
protected virtual bool EstablishLinkFor(TSyncValue syncValue, string propertyName, bool fromRemote)
=> syncValue.EstablishLinkFor(this, propertyName, fromRemote);
{
syncValues.Add(syncValue);
return syncValue.EstablishLinkFor(this, propertyName, fromRemote);
}

/// <summary>
/// Creates a link for the given sync method of the given name.
/// </summary>
/// <remarks>
/// Any <typeparamref name="TSyncValue"/>s created for this
/// must be added to the <see cref="syncValues">set of instances</see>.
/// </remarks>
/// <param name="syncMethod">The sync method to link.</param>
/// <param name="methodName">The name of the sync method to link.</param>
/// <param name="fromRemote">Whether the link is being established from the remote side.</param>
Expand All @@ -240,10 +251,15 @@ protected virtual bool EstablishLinkFor(TSyncValue syncValue, string propertyNam
/// Cleans up any managed resources as part of <see cref="Dispose()">disposing</see>.
/// </summary>
/// <remarks>
/// <i>By default:</i> Disposes the <see cref="LinkObject">LinkObject</see> if it's <see cref="IDisposable"/>.
/// <i>By default:</i> Disposes all <typeparamref name="TSyncValue"/> instances
/// that were added to the <see cref="syncValues">set of instances</see>,
/// and the <see cref="LinkObject">LinkObject</see> if it's <see cref="IDisposable"/>.
/// </remarks>
protected virtual void OnDisposing()
{
foreach (var syncValue in syncValues)
syncValue.Dispose();

if (LinkObject is IDisposable disposable)
disposable.Dispose();
}
Expand Down Expand Up @@ -294,9 +310,9 @@ protected void OnPropertyChanged(string propertyName)
}

/// <remarks><para>
/// <i>By default:</i> Calls <see cref="TryRestoreLinkFor(TSyncValue, string)">TryRestoreLinkFor</see>
/// <i>By default:</i> Calls <see cref="TryRestoreLinkFor(TSyncValue)">TryRestoreLinkFor</see>
/// for every readable <typeparamref name="TSyncValue"/> instance property and
/// <see cref="TryRestoreLinkFor(TSyncValue, string)">its overload</see> for every
/// <see cref="TryRestoreLinkFor(Action{TSyncObject}, string)">its overload</see> for every
/// <see cref="MonkeySyncMethodAttribute">MonkeySync method</see> on <typeparamref name="TSyncObject"/>.<br/>
/// The detected properties are stored in <see cref="propertyAccessorsByName">propertyAccessorsByName</see>,
/// while the detected methods are stored in <see cref="methodsByName">methodsByName</see>.
Expand All @@ -312,24 +328,26 @@ protected virtual bool TryRestoreLink()
{
var success = true;

foreach (var syncValueProperty in propertyAccessorsByName)
success &= TryRestoreLinkFor(syncValueProperty.Value((TSyncObject)this), syncValueProperty.Key);
foreach (var syncValues in propertyAccessorsByName.Values)
success &= TryRestoreLinkFor(syncValues((TSyncObject)this));

foreach (var syncMethod in methodsByName)
success &= TryRestoreLinkFor(syncMethod.Value, syncMethod.Key);

return success;
}

// Implement the sync value one through a method on sync values

/// <summary>
/// Tries to restore the link for the given sync value of the given name.
/// Tries to restore the link for the given sync value.
/// </summary>
/// <remarks>
/// <i>By default:</i> Calls
/// <c><paramref name="syncValue"/>.<see cref="ILinkedMonkeySyncValue{TLink}.TryRestoreLink">TryRestoreLink</see>()</c>.
/// </remarks>
/// <param name="syncValue">The sync value to link.</param>
/// <param name="propertyName">The name of the sync value to link.</param>
/// <returns><c>true</c> if the link was successfully restored; otherwise, <c>false</c>.</returns>
protected abstract bool TryRestoreLinkFor(TSyncValue syncValue, string propertyName);
protected virtual bool TryRestoreLinkFor(TSyncValue syncValue)
=> syncValue.TryRestoreLink();

/// <summary>
/// Tries to restore the link for the given sync method of the given name.
Expand Down
73 changes: 66 additions & 7 deletions MonkeyLoader/Sync/MonkeySyncValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ namespace MonkeyLoader.Sync
/// Defines the non-generic interface for <see cref="ILinkedMonkeySyncValue{TLink, T}"/>s.
/// </summary>
/// <inheritdoc cref="ILinkedMonkeySyncValue{TLink, T}"/>
public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged, IDisposable
{
/// <summary>
/// Gets the <see cref="MonkeySyncObject{TSyncObject, TSyncValue, TLink}.LinkObject">LinkObject</see>
/// of the <see cref="ILinkedMonkeySyncValue{TLink}.SyncObject">SyncObject</see> that this value belongs to.
/// </summary>
public TLink LinkObject { get; }

/// <summary>
/// Gets the property name of this sync value.
/// </summary>
Expand All @@ -35,6 +41,12 @@ public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
/// the wrapped <see cref="Value">Value</see>.
/// </summary>
public Type ValueType { get; }

/// <summary>
/// Tries to restore the link of this sync value.
/// </summary>
/// <returns><c>true</c> if the link was successfully restored; otherwise, <c>false</c>.</returns>
public bool TryRestoreLink();
}

/// <summary>
Expand All @@ -45,12 +57,6 @@ public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
public interface ILinkedMonkeySyncValue<out TLink, T> : INotifyValueChanged<T>,
IReadOnlyMonkeySyncValue<TLink, T>, IWriteOnlyMonkeySyncValue<TLink, T>
{
/// <summary>
/// Gets the <see cref="MonkeySyncObject{TSyncObject, TSyncValue, TLink}.LinkObject">LinkObject</see>
/// of the <see cref="ILinkedMonkeySyncValue{TLink}.SyncObject">SyncObject</see> that this value belongs to.
/// </summary>
public TLink LinkObject { get; }

/// <inheritdoc cref="ILinkedMonkeySyncValue{TLink}.Value"/>
public new T Value { get; set; }
}
Expand Down Expand Up @@ -109,6 +115,7 @@ public abstract class MonkeySyncValue<TLink, T> : IUnlinkedMonkeySyncValue<TLink
{
private static readonly Type _valueType = typeof(T);

private bool _disposedValue;
private ValueChangedEventHandler? _untypedChanged;
private T _value;

Expand Down Expand Up @@ -154,12 +161,28 @@ public MonkeySyncValue(T value)
Value = value;
}

/// <summary>
/// Ensures any unmanaged resources are <see cref="Dispose(bool)">disposed</see>.
/// </summary>
~MonkeySyncValue()
{
Dispose(false);
}

/// <summary>
/// Unwraps the <see cref="Value">Value</see> from the given sync object.
/// </summary>
/// <param name="syncValue">The sync object to unwrap.</param>
public static implicit operator T(MonkeySyncValue<TLink, T> syncValue) => syncValue.Value;

/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in the 'OnDisposing()' or 'OnFinalizing()' methods
Dispose(true);
GC.SuppressFinalize(this);
}

/// <remarks>
/// Sets this sync value's <see cref="SyncObject">SyncObject</see>
/// and <see cref="Name">Name</see> to the ones provided.<br/>
Expand All @@ -177,6 +200,9 @@ public bool EstablishLinkFor(ILinkedMonkeySyncObject<TLink> syncObject, string p
/// <inheritdoc/>
public override string ToString() => Value?.ToString() ?? "";

/// <inheritdoc/>
public abstract bool TryRestoreLink();

/// <remarks>
/// Handles the aspects of establishing a link that are
/// particular to <typeparamref name="TLink"/>s as a link object.<br/>
Expand All @@ -187,6 +213,39 @@ public bool EstablishLinkFor(ILinkedMonkeySyncObject<TLink> syncObject, string p
/// <inheritdoc cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor"/>
protected abstract bool EstablishLinkInternal(bool fromRemote);

/// <summary>
/// Cleans up any managed resources as part of <see cref="Dispose()">disposing</see>.
/// </summary>
/// <remarks>
/// <i>By default:</i> Sets the <see cref="SyncObject">SyncObject</see> to <c>null</c>
/// and the internal <see cref="Value">value</see> to its <c>default</c>.
/// </remarks>
protected virtual void OnDisposing()
{
SyncObject = null!;
_value = default!;
}

/// <summary>
/// Cleans up any unmanaged resources as part of
/// <see cref="Dispose()">disposing</see> or finalization.
/// </summary>
protected virtual void OnFinalizing()
{ }

private void Dispose(bool disposing)
{
if (_disposedValue)
return;

if (disposing)
OnDisposing();

OnFinalizing();

_disposedValue = true;
}

/// <summary>
/// Handles the value of this config item potentially having changed.
/// If the <paramref name="oldValue"/> and new value are different:<br/>
Expand Down

0 comments on commit f416aeb

Please sign in to comment.