diff --git a/src/SampSharp.OpenMp.Core/Extensions/Extension.cs b/src/SampSharp.OpenMp.Core/Extensions/Extension.cs
index 82dc60e9..058b8c14 100644
--- a/src/SampSharp.OpenMp.Core/Extensions/Extension.cs
+++ b/src/SampSharp.OpenMp.Core/Extensions/Extension.cs
@@ -43,10 +43,26 @@ protected Extension()
///
public void Dispose()
{
+ if (IsDisposed)
+ {
+ return;
+ }
+
Detach();
FreeUnmanagedResources();
GC.SuppressFinalize(this);
+
+ Dispose(true);
+ }
+
+ ///
+ /// When overridden in a derived class, performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. This method is called by the public method and the finalizer.
+ ///
+ /// if called from ; if called from the finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+
}
///
@@ -57,6 +73,8 @@ public void Dispose()
// Theoretically, this should never be called, as one of the unmanaged resources is a GC handle pointing to this
// object. But just to be sure, we'll free the resources here as well.
FreeUnmanagedResources();
+
+ Dispose(false);
}
///
diff --git a/src/SampSharp.OpenMp.Core/IStartupContext.cs b/src/SampSharp.OpenMp.Core/IStartupContext.cs
index a5f4e59c..ac53394c 100644
--- a/src/SampSharp.OpenMp.Core/IStartupContext.cs
+++ b/src/SampSharp.OpenMp.Core/IStartupContext.cs
@@ -42,8 +42,18 @@ public interface IStartupContext
///
event EventHandler? Initialized;
+ ///
+ /// Occurs when the server is ready to run the gamemode.
+ ///
+ event EventHandler? Ready;
+
///
/// Occurs when an open.mp component is being freed. The component which is being freed is passed as an argument.
///
event EventHandler? ComponentFreed;
+
+ ///
+ /// Resets the unhandled exception handler to the default handler provided by SampSharp.
+ ///
+ void ResetExceptionHandler();
}
\ No newline at end of file
diff --git a/src/SampSharp.OpenMp.Core/SampSharpInitParams.cs b/src/SampSharp.OpenMp.Core/SampSharpInitParams.cs
index 4cbd988d..cd1ee998 100644
--- a/src/SampSharp.OpenMp.Core/SampSharpInitParams.cs
+++ b/src/SampSharp.OpenMp.Core/SampSharpInitParams.cs
@@ -41,4 +41,9 @@ public readonly ref struct SampSharpInitParams
/// Gets a pointer to an unmanaged function that configures a callback to be invoked when a component is being freed.
///
public readonly unsafe delegate* unmanaged[Cdecl] SetOnFreeComponent;
+
+ ///
+ /// Gets a pointer to an unmanaged function that configures a ready callback to be invoked.
+ ///
+ public readonly unsafe delegate* unmanaged[Cdecl] SetOnReady;
}
diff --git a/src/SampSharp.OpenMp.Core/StartupContext.cs b/src/SampSharp.OpenMp.Core/StartupContext.cs
index a3f134f8..2b9b05bb 100644
--- a/src/SampSharp.OpenMp.Core/StartupContext.cs
+++ b/src/SampSharp.OpenMp.Core/StartupContext.cs
@@ -11,6 +11,7 @@ public sealed class StartupContext : IStartupContext
// Delegates must be kept in memory to prevent GC
// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable
private readonly Action _cleanup;
+ private readonly Action _ready;
private readonly OnFreeComponentDelegate _freeComponent;
// ReSharper restore PrivateFieldCanBeConvertedToLocalVariable
@@ -21,7 +22,6 @@ public sealed class StartupContext : IStartupContext
private const int SupportedApiVersion = 1;
private IStartup? _configurator;
- private ExceptionHandler _unhandledExceptionHandler;
///
/// Initializes a new instance of the class.
@@ -33,23 +33,21 @@ public StartupContext(SampSharpInitParams init)
Core = init.Core;
ComponentList = init.ComponentList;
Info = init.Info;
- _unhandledExceptionHandler = (context, ex) =>
- {
- Core.LogLine(LogLevel.Error, $"Unhandled exception during {context}:");
- Core.LogLine(LogLevel.Error, ex.ToString());
- };
- SampSharpExceptionHandler.SetExceptionHandler(_unhandledExceptionHandler);
+ UnhandledExceptionHandler = FallbackExceptionHandler;
// Keep delegates in memory to prevent GC
_cleanup = OnCleanup;
+ _ready = OnReady;
_freeComponent = OnFreeComponent;
var cleanupPtr = Marshal.GetFunctionPointerForDelegate(_cleanup);
+ var readyPtr = Marshal.GetFunctionPointerForDelegate(_ready);
var freeComponentPtr = Marshal.GetFunctionPointerForDelegate(_freeComponent);
unsafe
{
init.SetOnCleanup(cleanupPtr);
+ init.SetOnReady(readyPtr);
init.SetOnFreeComponent(freeComponentPtr);
}
}
@@ -69,17 +67,26 @@ public StartupContext(SampSharpInitParams init)
///
public ExceptionHandler UnhandledExceptionHandler
{
- get => _unhandledExceptionHandler;
+ get;
set
{
- _unhandledExceptionHandler = value;
+ field = value;
SampSharpExceptionHandler.SetExceptionHandler(value);
}
}
+ ///
+ public void ResetExceptionHandler()
+ {
+ UnhandledExceptionHandler = FallbackExceptionHandler;
+ }
+
///
public event EventHandler? Cleanup;
+ ///
+ public event EventHandler? Ready;
+
///
public event EventHandler? Initialized;
@@ -131,11 +138,22 @@ private static void VersionCheck(SampSharpInitParams init)
}
}
+ private void FallbackExceptionHandler(string context, Exception ex)
+ {
+ Core.LogLine(LogLevel.Error, $"Unhandled exception during {context}:");
+ Core.LogLine(LogLevel.Error, ex.ToString());
+ }
+
private void OnCleanup()
{
Cleanup?.Invoke(this, EventArgs.Empty);
}
+ private void OnReady()
+ {
+ Ready?.Invoke(this, EventArgs.Empty);
+ }
+
private void OnFreeComponent(IComponent component)
{
ComponentFreed?.Invoke(this, component);
diff --git a/src/SampSharp.OpenMp.Entities/Events/EventDispatcher.cs b/src/SampSharp.OpenMp.Entities/Events/EventDispatcher.cs
index f1c53a45..7f091bde 100644
--- a/src/SampSharp.OpenMp.Entities/Events/EventDispatcher.cs
+++ b/src/SampSharp.OpenMp.Entities/Events/EventDispatcher.cs
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
@@ -26,7 +27,8 @@ internal sealed partial class EventDispatcher : IEventDispatcher, IEventService
///
/// Initializes a new instance of the class.
///
- public EventDispatcher(IServiceProvider serviceProvider, IEntityManager entityManager, ILogger logger, ISystemRegistry systemRegistry, IUnhandledExceptionHandler unhandledExceptionHandler)
+ public EventDispatcher(IServiceProvider serviceProvider, IEntityManager entityManager, ILogger logger, ISystemRegistry systemRegistry,
+ IUnhandledExceptionHandler unhandledExceptionHandler)
{
_serviceProvider = serviceProvider;
_entityManager = entityManager;
@@ -54,7 +56,7 @@ public void Invoke(string name, params ReadOnlySpan