using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace StardewModdingAPI.Framework
{
/// Provides extension methods for SMAPI's internal use.
internal static class InternalExtensions
{
/*********
** Properties
*********/
/// Tracks the installed mods.
private static ModRegistry ModRegistry;
/*********
** Public methods
*********/
/// Injects types required for backwards compatibility.
/// Tracks the installed mods.
internal static void Shim(ModRegistry modRegistry)
{
InternalExtensions.ModRegistry = modRegistry;
}
/****
** IMonitor
****/
/// Safely raise an event, and intercept any exceptions thrown by its handlers.
/// Encapsulates monitoring and logging.
/// The event name for error messages.
/// The event handlers.
/// The event sender.
/// The event arguments (or null to pass ).
public static void SafelyRaisePlainEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender = null, EventArgs args = null)
{
if (handlers == null)
return;
foreach (EventHandler handler in handlers.Cast())
{
// handle SMAPI exiting
if (monitor.IsExiting)
{
monitor.Log($"SMAPI shutting down: aborting {name} event.", LogLevel.Warn);
return;
}
// raise event
try
{
handler.Invoke(sender, args ?? EventArgs.Empty);
}
catch (Exception ex)
{
monitor.Log($"A mod failed handling the {name} event:\n{ex.GetLogSummary()}", LogLevel.Error);
}
}
}
/// Safely raise an event, and intercept any exceptions thrown by its handlers.
/// The event argument object type.
/// Encapsulates monitoring and logging.
/// The event name for error messages.
/// The event handlers.
/// The event sender.
/// The event arguments.
public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, TEventArgs args)
{
if (handlers == null)
return;
foreach (EventHandler handler in handlers.Cast>())
{
try
{
handler.Invoke(sender, args);
}
catch (Exception ex)
{
monitor.Log($"A mod failed handling the {name} event:\n{ex.GetLogSummary()}", LogLevel.Error);
}
}
}
/****
** Exceptions
****/
/// Get a string representation of an exception suitable for writing to the error log.
/// The error to summarise.
public static string GetLogSummary(this Exception exception)
{
switch (exception)
{
case TypeLoadException ex:
return $"Failed loading type '{ex.TypeName}': {exception}";
case ReflectionTypeLoadException ex:
string summary = exception.ToString();
foreach (Exception childEx in ex.LoaderExceptions)
summary += $"\n\n{childEx.GetLogSummary()}";
return summary;
default:
return exception.ToString();
}
}
/****
** Deprecation
****/
/// Log a deprecation warning for mods using an event.
/// The deprecation manager to extend.
/// The event handlers.
/// A noun phrase describing what is deprecated.
/// The SMAPI version which deprecated it.
/// How deprecated the code is.
public static void WarnForEvent(this DeprecationManager deprecationManager, Delegate[] handlers, string nounPhrase, string version, DeprecationLevel severity)
{
if (handlers == null || !handlers.Any())
return;
foreach (Delegate handler in handlers)
{
string modName = InternalExtensions.ModRegistry.GetModFrom(handler) ?? "an unknown mod"; // suppress stack trace for unknown mods, not helpful here
deprecationManager.Warn(modName, nounPhrase, version, severity);
}
}
}
}