@@ -33,7 +33,8 @@ public abstract class Plugin : IDisposable
3333 /// <summary>
3434 /// The directory containing the plugin folders. Files can be contained in any subdirectory.
3535 /// </summary>
36- public static readonly string PluginsDirectory = Combine ( GetDirectoryName ( System . AppContext . BaseDirectory ) , "Plugins" ) ;
36+ public static readonly string PluginsDirectory =
37+ Combine ( GetDirectoryName ( System . AppContext . BaseDirectory ) , "Plugins" ) ;
3738
3839 private static readonly FileSystemWatcher configWatcher ;
3940
@@ -67,14 +68,27 @@ public abstract class Plugin : IDisposable
6768 /// </summary>
6869 public virtual Version Version => GetType ( ) . Assembly . GetName ( ) . Version ;
6970
71+ /// <summary>
72+ /// If the plugin should be stopped when an exception is thrown.
73+ /// Default is StopNode.
74+ /// </summary>
75+ protected internal virtual UnhandledExceptionPolicy ExceptionPolicy { get ; init ; } = UnhandledExceptionPolicy . StopNode ;
76+
77+ /// <summary>
78+ /// The plugin will be stopped if an exception is thrown.
79+ /// But it also depends on <see cref="UnhandledExceptionPolicy"/>.
80+ /// </summary>
81+ internal bool IsStopped { get ; set ; }
82+
7083 static Plugin ( )
7184 {
7285 if ( ! Directory . Exists ( PluginsDirectory ) ) return ;
7386 configWatcher = new FileSystemWatcher ( PluginsDirectory )
7487 {
7588 EnableRaisingEvents = true ,
7689 IncludeSubdirectories = true ,
77- NotifyFilter = NotifyFilters . FileName | NotifyFilters . DirectoryName | NotifyFilters . CreationTime | NotifyFilters . LastWrite | NotifyFilters . Size ,
90+ NotifyFilter = NotifyFilters . FileName | NotifyFilters . DirectoryName | NotifyFilters . CreationTime |
91+ NotifyFilters . LastWrite | NotifyFilters . Size ,
7892 } ;
7993 configWatcher . Changed += ConfigWatcher_Changed ;
8094 configWatcher . Created += ConfigWatcher_Changed ;
@@ -106,7 +120,8 @@ private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e)
106120 {
107121 case ".json" :
108122 case ".dll" :
109- Utility . Log ( nameof ( Plugin ) , LogLevel . Warning , $ "File { e . Name } is { e . ChangeType } , please restart node.") ;
123+ Utility . Log ( nameof ( Plugin ) , LogLevel . Warning ,
124+ $ "File { e . Name } is { e . ChangeType } , please restart node.") ;
110125 break ;
111126 }
112127 }
@@ -119,7 +134,8 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven
119134 AssemblyName an = new ( args . Name ) ;
120135
121136 Assembly assembly = AppDomain . CurrentDomain . GetAssemblies ( ) . FirstOrDefault ( a => a . FullName == args . Name ) ??
122- AppDomain . CurrentDomain . GetAssemblies ( ) . FirstOrDefault ( a => a . GetName ( ) . Name == an . Name ) ;
137+ AppDomain . CurrentDomain . GetAssemblies ( )
138+ . FirstOrDefault ( a => a . GetName ( ) . Name == an . Name ) ;
123139 if ( assembly != null ) return assembly ;
124140
125141 string filename = an . Name + ".dll" ;
@@ -150,7 +166,8 @@ public virtual void Dispose()
150166 /// <returns>The content of the configuration file read.</returns>
151167 protected IConfigurationSection GetConfiguration ( )
152168 {
153- return new ConfigurationBuilder ( ) . AddJsonFile ( ConfigFile , optional : true ) . Build ( ) . GetSection ( "PluginConfiguration" ) ;
169+ return new ConfigurationBuilder ( ) . AddJsonFile ( ConfigFile , optional : true ) . Build ( )
170+ . GetSection ( "PluginConfiguration" ) ;
154171 }
155172
156173 private static void LoadPlugin ( Assembly assembly )
@@ -187,6 +204,7 @@ internal static void LoadPlugins()
187204 catch { }
188205 }
189206 }
207+
190208 foreach ( Assembly assembly in assemblies )
191209 {
192210 LoadPlugin ( assembly ) ;
@@ -229,7 +247,46 @@ protected internal virtual void OnSystemLoaded(NeoSystem system)
229247 /// <returns><see langword="true"/> if the <paramref name="message"/> is handled by a plugin; otherwise, <see langword="false"/>.</returns>
230248 public static bool SendMessage ( object message )
231249 {
232- return Plugins . Any ( plugin => plugin . OnMessage ( message ) ) ;
250+ foreach ( var plugin in Plugins )
251+ {
252+ if ( plugin . IsStopped )
253+ {
254+ continue ;
255+ }
256+
257+ bool result ;
258+ try
259+ {
260+ result = plugin . OnMessage ( message ) ;
261+ }
262+ catch ( Exception ex )
263+ {
264+ Utility . Log ( nameof ( Plugin ) , LogLevel . Error , ex ) ;
265+
266+ switch ( plugin . ExceptionPolicy )
267+ {
268+ case UnhandledExceptionPolicy . StopNode :
269+ throw ;
270+ case UnhandledExceptionPolicy . StopPlugin :
271+ plugin . IsStopped = true ;
272+ break ;
273+ case UnhandledExceptionPolicy . Ignore :
274+ break ;
275+ default :
276+ throw new InvalidCastException ( $ "The exception policy { plugin . ExceptionPolicy } is not valid.") ;
277+ }
278+
279+ continue ; // Skip to the next plugin if an exception is handled
280+ }
281+
282+ if ( result )
283+ {
284+ return true ;
285+ }
286+ }
287+
288+ return false ;
233289 }
290+
234291 }
235292}
0 commit comments