1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace StardewModdingAPI.Framework
{
/// <summary>Provides extension methods for SMAPI's internal use.</summary>
internal static class InternalExtensions
{
/*********
** Properties
*********/
/// <summary>Tracks the installed mods.</summary>
private static ModRegistry ModRegistry;
/*********
** Public methods
*********/
/// <summary>Injects types required for backwards compatibility.</summary>
/// <param name="modRegistry">Tracks the installed mods.</param>
internal static void Shim(ModRegistry modRegistry)
{
InternalExtensions.ModRegistry = modRegistry;
}
/****
** IMonitor
****/
/// <summary>Safely raise an <see cref="EventHandler"/> event, and intercept any exceptions thrown by its handlers.</summary>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="name">The event name for error messages.</param>
/// <param name="handlers">The event handlers.</param>
/// <param name="sender">The event sender.</param>
/// <param name="args">The event arguments (or <c>null</c> to pass <see cref="EventArgs.Empty"/>).</param>
public static void SafelyRaisePlainEvent(this IMonitor monitor, string name, IEnumerable<Delegate> handlers, object sender = null, EventArgs args = null)
{
if (handlers == null)
return;
foreach (EventHandler handler in handlers.Cast<EventHandler>())
{
// 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);
}
}
}
/// <summary>Safely raise an <see cref="EventHandler{TEventArgs}"/> event, and intercept any exceptions thrown by its handlers.</summary>
/// <typeparam name="TEventArgs">The event argument object type.</typeparam>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="name">The event name for error messages.</param>
/// <param name="handlers">The event handlers.</param>
/// <param name="sender">The event sender.</param>
/// <param name="args">The event arguments.</param>
public static void SafelyRaiseGenericEvent<TEventArgs>(this IMonitor monitor, string name, IEnumerable<Delegate> handlers, object sender, TEventArgs args)
{
if (handlers == null)
return;
foreach (EventHandler<TEventArgs> handler in handlers.Cast<EventHandler<TEventArgs>>())
{
try
{
handler.Invoke(sender, args);
}
catch (Exception ex)
{
monitor.Log($"A mod failed handling the {name} event:\n{ex.GetLogSummary()}", LogLevel.Error);
}
}
}
/****
** Exceptions
****/
/// <summary>Get a string representation of an exception suitable for writing to the error log.</summary>
/// <param name="exception">The error to summarise.</param>
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
****/
/// <summary>Log a deprecation warning for mods using an event.</summary>
/// <param name="deprecationManager">The deprecation manager to extend.</param>
/// <param name="handlers">The event handlers.</param>
/// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
/// <param name="version">The SMAPI version which deprecated it.</param>
/// <param name="severity">How deprecated the code is.</param>
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);
}
}
}
}
|