Skip to content

Commit

Permalink
[MTGOSDK/API] Client: Refresh ObjectProvider cache on reconnection
Browse files Browse the repository at this point in the history
  • Loading branch information
Qonfused committed May 29, 2024
1 parent 42c8f8a commit b6f730d
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 4 deletions.
14 changes: 13 additions & 1 deletion MTGOSDK/src/API/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ public Client(
RemoteClient.Port = Cast<ushort>(options.Port);
})
{
//
// If the RemoteClient was previously disposed (another Client instance was
// created and disposed), then we need to refresh all remote objects created
// through the ObjectProvider to ensure that references are still valid.
//
if (ObjectProvider.RequiresRefresh) ObjectProvider.Refresh();

//
// Ensure all deferred static fields in the queue are initialized.
//
Expand Down Expand Up @@ -241,7 +248,12 @@ public static bool ValidateVersion(bool assert = false)
/// <param name="disconnect">Whether to disconnect the client.</param>
public void ClearCaches(bool disconnect = false)
{
if (disconnect) RemoteClient.Dispose();
if (disconnect)
{
RemoteClient.Dispose();
ObjectProvider.RequiresRefresh = true;
}
m_currentUser = null;

Log.Debug("Disposing all pinned remote objects registered with the client.");
UserManager.Users.Clear();
Expand Down
13 changes: 12 additions & 1 deletion MTGOSDK/src/API/ObjectProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public static class ObjectProvider
/// </summary>
private static readonly ConcurrentDictionary<string, dynamic> s_instances = new();

/// <summary>
/// Whether the ObjectProvider cache requires a refresh.
/// </summary>
internal static bool RequiresRefresh = false;

/// <summary>
/// Returns an instance of the given type from the client's ObjectProvider.
/// </summary>
Expand All @@ -52,6 +57,7 @@ public static dynamic Get(
// If the client is disposed, return an empty instance to defer construction
if (RemoteClient.IsDisposed)
{
Log.Trace("Client is disposed. Returning empty instance of type {Type}", queryPath);
instance = new DynamicRemoteObject();
}
// Query using the ObjectProvider.Get<T>() method on the client
Expand Down Expand Up @@ -128,11 +134,16 @@ public static dynamic Get<T>(bool bindTypes = true) where T : class
/// </remarks>
public static void Refresh()
{
// Ensure that a connection to the MTGO client has been established
RemoteClient.EnsureInitialize();

Log.Debug("Refreshing ObjectProvider cache.");
foreach (var kvp in s_instances)
{
dynamic refObj = s_instances[kvp.Key];
s_instances.TryGetValue(kvp.Key, out dynamic refObj);
dynamic obj = Get(kvp.Key, useCache: false);
Swap(ref refObj, obj, bindTypes: true);
}
RequiresRefresh = false;
}
}
8 changes: 6 additions & 2 deletions MTGOSDK/src/Core/Reflection/DLRWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;

using MTGOSDK.Core.Logging;
using MTGOSDK.Core.Remoting;


Expand Down Expand Up @@ -126,13 +127,16 @@ internal virtual dynamic @base
/// constructor, triggering the lazy initialization of any deferred static
/// fields for immediate use.
/// </param>
/// <param name="raise">
/// Whether to raise an exception if the queue is empty (optional).
/// </param>
/// <exception cref="InvalidOperationException">
/// Thrown when the queue is empty and no deferred objects are available.
/// </exception>
public static void Construct(dynamic? _ref = null)
public static void Construct(dynamic? _ref = null, bool raise = false)
{
// Use any dereferenced object to lazy initialize and check the queue.
if (_ref is not null && DeferedQueue.IsEmpty)
if (_ref is not null && (raise && DeferedQueue.IsEmpty))
throw new InvalidOperationException(
$"{nameof(DLRWrapper<I>)}.Construct() called with no deferred objects.");

Expand Down

0 comments on commit b6f730d

Please sign in to comment.