using System;
using System.Collections.Generic;
using System.Linq;
namespace StardewModdingAPI.Framework.Events
{
/// The base implementation for an event wrapper which intercepts and logs errors in handler code.
internal abstract class ManagedEventBase
{
/*********
** Properties
*********/
/// A human-readable name for the event.
private readonly string EventName;
/// Writes messages to the log.
private readonly IMonitor Monitor;
/// The mod registry with which to identify mods.
protected readonly ModRegistry ModRegistry;
/// The display names for the mods which added each delegate.
private readonly IDictionary SourceMods = new Dictionary();
/// The cached invocation list.
protected TEventHandler[] CachedInvocationList { get; private set; }
/*********
** Public methods
*********/
/// Get whether anything is listening to the event.
public bool HasListeners()
{
return this.CachedInvocationList?.Length > 0;
}
/*********
** Protected methods
*********/
/// Construct an instance.
/// A human-readable name for the event.
/// Writes messages to the log.
/// The mod registry with which to identify mods.
protected ManagedEventBase(string eventName, IMonitor monitor, ModRegistry modRegistry)
{
this.EventName = eventName;
this.Monitor = monitor;
this.ModRegistry = modRegistry;
}
/// Track an event handler.
/// The mod which added the handler.
/// The event handler.
/// The updated event invocation list.
protected void AddTracking(IModMetadata mod, TEventHandler handler, IEnumerable invocationList)
{
this.SourceMods[handler] = mod;
this.CachedInvocationList = invocationList?.ToArray() ?? new TEventHandler[0];
}
/// Remove tracking for an event handler.
/// The event handler.
/// The updated event invocation list.
protected void RemoveTracking(TEventHandler handler, IEnumerable invocationList)
{
this.CachedInvocationList = invocationList?.ToArray() ?? new TEventHandler[0];
if (!this.CachedInvocationList.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once)
this.SourceMods.Remove(handler);
}
/// Get the mod which registered the given event handler, if available.
/// The event handler.
protected IModMetadata GetSourceMod(TEventHandler handler)
{
return this.SourceMods.TryGetValue(handler, out IModMetadata mod)
? mod
: null;
}
/// Log an exception from an event handler.
/// The event handler instance.
/// The exception that was raised.
protected void LogError(TEventHandler handler, Exception ex)
{
IModMetadata mod = this.GetSourceMod(handler);
if (mod != null)
mod.LogAsMod($"This mod failed in the {this.EventName} event. Technical details: \n{ex.GetLogSummary()}", LogLevel.Error);
else
this.Monitor.Log($"A mod failed in the {this.EventName} event. Technical details: \n{ex.GetLogSummary()}", LogLevel.Error);
}
}
}