diff --git a/Obsidian.API/Events/BaseMinecraftEventArgs.cs b/Obsidian.API/Events/BaseMinecraftEventArgs.cs
index 26fdac91b..98330e08b 100644
--- a/Obsidian.API/Events/BaseMinecraftEventArgs.cs
+++ b/Obsidian.API/Events/BaseMinecraftEventArgs.cs
@@ -3,13 +3,17 @@
///
/// Represents the base class for classes that contain minecraft event data.
///
-public class BaseMinecraftEventArgs : AsyncEventArgs
+public abstract class BaseMinecraftEventArgs : AsyncEventArgs, INamedEvent
{
+ public static string Name => string.Empty;
+
///
/// Server this event took place in.
///
public IServer Server { get; }
+ public static EventType EventType { get; }
+
///
/// Constructs a new instance of the class.
///
diff --git a/Obsidian.API/Events/BlockBreakEventArgs.cs b/Obsidian.API/Events/BlockBreakEventArgs.cs
index 0c11f3ff4..11a6b56bb 100644
--- a/Obsidian.API/Events/BlockBreakEventArgs.cs
+++ b/Obsidian.API/Events/BlockBreakEventArgs.cs
@@ -2,6 +2,8 @@
public class BlockBreakEventArgs : BlockEventArgs, ICancellable
{
+ public static new string Name => "BlockBreak";
+
///
/// Player that has broken the block.
///
diff --git a/Obsidian.API/Events/BlockEventArgs.cs b/Obsidian.API/Events/BlockEventArgs.cs
index bfdedb7ca..1a91b8bba 100644
--- a/Obsidian.API/Events/BlockEventArgs.cs
+++ b/Obsidian.API/Events/BlockEventArgs.cs
@@ -2,6 +2,8 @@
public abstract class BlockEventArgs : BaseMinecraftEventArgs
{
+ public static new string Name => "BlockEvent";
+
///
/// The impacted block.
///
diff --git a/Obsidian.API/Events/ContainerClickEventArgs.cs b/Obsidian.API/Events/ContainerClickEventArgs.cs
index 5f017463b..6f817473c 100644
--- a/Obsidian.API/Events/ContainerClickEventArgs.cs
+++ b/Obsidian.API/Events/ContainerClickEventArgs.cs
@@ -1,9 +1,12 @@
-using System.Diagnostics.CodeAnalysis;
+using Obsidian.API.BlockStates;
+using System.Diagnostics.CodeAnalysis;
namespace Obsidian.API.Events;
public sealed class ContainerClickEventArgs : ContainerEventArgs, ICancellable
{
+ public static new string Name => "ContainerClick";
+
///
/// Gets the current item that was clicked
///
diff --git a/Obsidian.API/Events/ContainerClosedEventArgs.cs b/Obsidian.API/Events/ContainerClosedEventArgs.cs
index 75924b67d..22b7d8b0c 100644
--- a/Obsidian.API/Events/ContainerClosedEventArgs.cs
+++ b/Obsidian.API/Events/ContainerClosedEventArgs.cs
@@ -1,6 +1,8 @@
namespace Obsidian.API.Events;
public sealed class ContainerClosedEventArgs : ContainerEventArgs, ICancellable
{
+ public static new string Name => "ContainerClosed";
+
public bool IsCancelled { get; private set; }
internal ContainerClosedEventArgs(IPlayer player, IServer server) : base(player, server)
diff --git a/Obsidian.API/Events/ContainerEventArgs.cs b/Obsidian.API/Events/ContainerEventArgs.cs
index 4babfc1ec..7c51ce8ff 100644
--- a/Obsidian.API/Events/ContainerEventArgs.cs
+++ b/Obsidian.API/Events/ContainerEventArgs.cs
@@ -2,6 +2,8 @@
public class ContainerEventArgs : PlayerEventArgs
{
+ public static new string Name => "ContainerEvent";
+
public required BaseContainer Container { get; init; }
protected ContainerEventArgs(IPlayer player, IServer server) : base(player, server)
diff --git a/Obsidian.API/Events/EntityEventArgs.cs b/Obsidian.API/Events/EntityEventArgs.cs
index e810b7a9d..6cd91a2c5 100644
--- a/Obsidian.API/Events/EntityEventArgs.cs
+++ b/Obsidian.API/Events/EntityEventArgs.cs
@@ -2,6 +2,8 @@
public class EntityEventArgs : BaseMinecraftEventArgs, ICancellable
{
+ public static new string Name => "EntityEvent";
+
///
/// The entity involved in this event.
///
diff --git a/Obsidian.API/Events/EntityInteractEventArgs.cs b/Obsidian.API/Events/EntityInteractEventArgs.cs
index 957d7299e..82370a1ec 100644
--- a/Obsidian.API/Events/EntityInteractEventArgs.cs
+++ b/Obsidian.API/Events/EntityInteractEventArgs.cs
@@ -2,6 +2,8 @@
public class EntityInteractEventArgs : EntityEventArgs
{
+ public static new string Name => "EntityInteract";
+
///
/// The player who interacted with the entity.
///
diff --git a/Obsidian.API/Events/IncomingChatMessageEventArgs.cs b/Obsidian.API/Events/IncomingChatMessageEventArgs.cs
index 662acd75d..166b3a4f4 100644
--- a/Obsidian.API/Events/IncomingChatMessageEventArgs.cs
+++ b/Obsidian.API/Events/IncomingChatMessageEventArgs.cs
@@ -2,6 +2,8 @@
public class IncomingChatMessageEventArgs : PlayerEventArgs, ICancellable
{
+ public static new string Name => "IncomingChatMessage";
+
///
/// The message that was sent.
///
diff --git a/Obsidian.API/Events/PacketReceivedEventArgs.cs b/Obsidian.API/Events/PacketReceivedEventArgs.cs
index a92f43d54..d262b6864 100644
--- a/Obsidian.API/Events/PacketReceivedEventArgs.cs
+++ b/Obsidian.API/Events/PacketReceivedEventArgs.cs
@@ -2,6 +2,8 @@
public sealed class PacketReceivedEventArgs : PlayerEventArgs, ICancellable
{
+ public static new string Name => "PacketReceived";
+
///
/// Id of the received packet.
///
diff --git a/Obsidian.API/Events/PermissionGrantedEventArgs.cs b/Obsidian.API/Events/PermissionGrantedEventArgs.cs
index 7c9f1bf5b..a6d6b0743 100644
--- a/Obsidian.API/Events/PermissionGrantedEventArgs.cs
+++ b/Obsidian.API/Events/PermissionGrantedEventArgs.cs
@@ -2,6 +2,8 @@
public class PermissionGrantedEventArgs : PlayerEventArgs
{
+ public static new string Name => "PermissionGranted";
+
public string Permission { get; }
public PermissionGrantedEventArgs(IPlayer player, IServer server, string permission) : base(player, server)
diff --git a/Obsidian.API/Events/PermissionRevokedEventArgs.cs b/Obsidian.API/Events/PermissionRevokedEventArgs.cs
index 65b00a48e..0f68d0a39 100644
--- a/Obsidian.API/Events/PermissionRevokedEventArgs.cs
+++ b/Obsidian.API/Events/PermissionRevokedEventArgs.cs
@@ -2,6 +2,8 @@
public class PermissionRevokedEventArgs : PlayerEventArgs
{
+ public static new string Name => "PermissionRevoked";
+
public string Permission { get; }
public PermissionRevokedEventArgs(IPlayer player, IServer server, string permission) : base(player, server)
diff --git a/Obsidian.API/Events/PlayerAttackEntityEventArgs.cs b/Obsidian.API/Events/PlayerAttackEntityEventArgs.cs
index ae2539de4..2d5eda423 100644
--- a/Obsidian.API/Events/PlayerAttackEntityEventArgs.cs
+++ b/Obsidian.API/Events/PlayerAttackEntityEventArgs.cs
@@ -3,6 +3,8 @@
//TODO check if player crits and calculate damage
public class PlayerAttackEntityEventArgs : EntityEventArgs
{
+ public static new string Name => "PlayerAttackEntity";
+
///
/// The player who interacted with the entity.
///
diff --git a/Obsidian.API/Events/PlayerEventArgs.cs b/Obsidian.API/Events/PlayerEventArgs.cs
index 9769748f6..aa51476c1 100644
--- a/Obsidian.API/Events/PlayerEventArgs.cs
+++ b/Obsidian.API/Events/PlayerEventArgs.cs
@@ -2,6 +2,8 @@
public class PlayerEventArgs : BaseMinecraftEventArgs
{
+ public static new string Name => "PlayerEvent";
+
///
/// The player involved in this event.
///
diff --git a/Obsidian.API/Events/PlayerInteractEventArgs.cs b/Obsidian.API/Events/PlayerInteractEventArgs.cs
index 9f30911dd..39b62a148 100644
--- a/Obsidian.API/Events/PlayerInteractEventArgs.cs
+++ b/Obsidian.API/Events/PlayerInteractEventArgs.cs
@@ -7,6 +7,8 @@
///
public sealed class PlayerInteractEventArgs : PlayerEventArgs, ICancellable
{
+ public static new string Name => "PlayerInteract";
+
///
/// The item that was being held when interacting.
///
diff --git a/Obsidian.API/Events/PlayerJoinEventArgs.cs b/Obsidian.API/Events/PlayerJoinEventArgs.cs
index 49a8b8c81..c672708f2 100644
--- a/Obsidian.API/Events/PlayerJoinEventArgs.cs
+++ b/Obsidian.API/Events/PlayerJoinEventArgs.cs
@@ -2,6 +2,8 @@
public class PlayerJoinEventArgs : PlayerEventArgs
{
+ public static new string Name => "PlayerJoin";
+
///
/// The date the player joined.
///
diff --git a/Obsidian.API/Events/PlayerLeaveEventArgs.cs b/Obsidian.API/Events/PlayerLeaveEventArgs.cs
index ddcf71c91..6da1a5e77 100644
--- a/Obsidian.API/Events/PlayerLeaveEventArgs.cs
+++ b/Obsidian.API/Events/PlayerLeaveEventArgs.cs
@@ -2,6 +2,8 @@
public class PlayerLeaveEventArgs : PlayerEventArgs
{
+ public static new string Name => "PlayerLeave";
+
///
/// The date the player left.
///
diff --git a/Obsidian.API/Events/PlayerTeleportEventArgs.cs b/Obsidian.API/Events/PlayerTeleportEventArgs.cs
index 2331e3113..0fbf8b752 100644
--- a/Obsidian.API/Events/PlayerTeleportEventArgs.cs
+++ b/Obsidian.API/Events/PlayerTeleportEventArgs.cs
@@ -2,6 +2,8 @@
public class PlayerTeleportEventArgs : PlayerEventArgs
{
+ public static new string Name => "PlayerTeleport";
+
public VectorF OldPosition { get; }
public VectorF NewPosition { get; }
diff --git a/Obsidian.API/Events/ServerStatusRequestEventArgs.cs b/Obsidian.API/Events/ServerStatusRequestEventArgs.cs
index ce363fea7..16caa22ce 100644
--- a/Obsidian.API/Events/ServerStatusRequestEventArgs.cs
+++ b/Obsidian.API/Events/ServerStatusRequestEventArgs.cs
@@ -2,6 +2,8 @@
public class ServerStatusRequestEventArgs : BaseMinecraftEventArgs
{
+ public static new string Name => "ServerStatusRequest";
+
public IServerStatus Status { get; }
internal ServerStatusRequestEventArgs(IServer server, IServerStatus status) : base(server)
diff --git a/Obsidian.API/_Enums/EventType.cs b/Obsidian.API/_Enums/EventType.cs
new file mode 100644
index 000000000..c329857e3
--- /dev/null
+++ b/Obsidian.API/_Enums/EventType.cs
@@ -0,0 +1,21 @@
+namespace Obsidian.API;
+
+public enum EventType
+{
+ PacketReceived,
+ QueuePacket,
+ PlayerJoin,
+ PlayerLeave,
+ PlayerTeleported,
+ PermissionGranted,
+ PermissionRevoked,
+ ContainerClick,
+ BlockBreak,
+ IncomingChatMessage,
+ ServerStatusRequest,
+ EntityInteract,
+ PlayerAttackEntity,
+ PlayerInteract,
+ ContainerClosed,
+ Custom
+}
diff --git a/Obsidian.API/_Interfaces/INamedEvent.cs b/Obsidian.API/_Interfaces/INamedEvent.cs
new file mode 100644
index 000000000..00e533b20
--- /dev/null
+++ b/Obsidian.API/_Interfaces/INamedEvent.cs
@@ -0,0 +1,5 @@
+namespace Obsidian.API;
+public interface INamedEvent
+{
+ public static abstract string Name { get; }
+}
diff --git a/Obsidian/Client.cs b/Obsidian/Client.cs
index 511b9d80d..7b15f6246 100644
--- a/Obsidian/Client.cs
+++ b/Obsidian/Client.cs
@@ -317,7 +317,7 @@ public async Task StartConnectionAsync()
var configurationPacketReceived = new PacketReceivedEventArgs(Player, this.server, id, data);
- await this.server.Events.PacketReceived.InvokeAsync(configurationPacketReceived);
+ await this.server.packetReceived.InvokeAsync(configurationPacketReceived);
if (!configurationPacketReceived.IsCancelled)
await this.handler.HandleConfigurationPackets(id, data, this);
@@ -328,7 +328,7 @@ public async Task StartConnectionAsync()
var playPacketReceived = new PacketReceivedEventArgs(Player, this.server, id, data);
- await this.server.Events.PacketReceived.InvokeAsync(playPacketReceived);
+ await this.server.packetReceived.InvokeAsync(playPacketReceived);
if (!playPacketReceived.IsCancelled)
await handler.HandlePlayPackets(id, data, this);
@@ -345,7 +345,7 @@ public async Task StartConnectionAsync()
if (State == ClientState.Play)
{
Debug.Assert(Player is not null);
- await this.server.Events.PlayerLeave.InvokeAsync(new PlayerLeaveEventArgs(Player, this.server, DateTimeOffset.Now));
+ await this.server.playerLeave.InvokeAsync(new PlayerLeaveEventArgs(Player, this.server, DateTimeOffset.Now));
}
Disconnected?.Invoke(this);
@@ -365,7 +365,7 @@ private async Task HandleServerStatusRequestAsync()
{
var status = new ServerStatus(this.server);
- _ = await this.server.Events.ServerStatusRequest.InvokeAsync(new ServerStatusRequestEventArgs(this.server, status));
+ _ = await this.server.serverStatusRequest.InvokeAsync(new ServerStatusRequestEventArgs(this.server, status));
SendPacket(new RequestResponse(status));
}
@@ -569,7 +569,7 @@ await QueuePacketAsync(new SynchronizePlayerPositionPacket
await Player.UpdateChunksAsync(distance: 7);
await SendInfoAsync();
- await this.server.Events.PlayerJoin.InvokeAsync(new PlayerJoinEventArgs(Player, this.server, DateTimeOffset.Now));
+ await this.server.playerJoin.InvokeAsync(new PlayerJoinEventArgs(Player, this.server, DateTimeOffset.Now));
}
#region Packet sending
@@ -741,10 +741,10 @@ internal void SendPacket(IClientboundPacket packet)
internal async Task QueuePacketAsync(IClientboundPacket packet)
{
- var args = await this.server.Events.QueuePacket.InvokeAsync(new QueuePacketEventArgs(this, packet));
+ var args = await this.server.queuePacket.InvokeAsync(new QueuePacketEventArgs(this.server, this, packet));
if (args.IsCancelled)
{
- Logger.LogDebug("Packet {PacketId} was sent to the queue, however an event handler registered in {Name} has cancelled it.", args.Packet.Id, nameof(Server.Events));
+ Logger.LogDebug("Packet {PacketId} was sent to the queue, however an event handler has cancelled it.", args.Packet.Id);
}
else
{
diff --git a/Obsidian/Entities/Player.cs b/Obsidian/Entities/Player.cs
index a02899f59..de8ce1fad 100644
--- a/Obsidian/Entities/Player.cs
+++ b/Obsidian/Entities/Player.cs
@@ -225,7 +225,7 @@ public async override Task TeleportAsync(VectorF pos)
var tid = Globals.Random.Next(0, 999);
- await client.server.Events.PlayerTeleported.InvokeAsync(
+ await client.server.playerTeleported.InvokeAsync(
new PlayerTeleportEventArgs
(
this,
@@ -786,7 +786,7 @@ public async Task GrantPermissionAsync(string permissionNode)
await SavePermsAsync();
if (result)
- await this.client.server.Events.PermissionGranted.InvokeAsync(new PermissionGrantedEventArgs(this, this.client.server, permissionNode));
+ await this.client.server.permissionGranted.InvokeAsync(new PermissionGrantedEventArgs(this, this.client.server, permissionNode));
return result;
}
@@ -808,7 +808,7 @@ public async Task RevokePermissionAsync(string permissionNode)
parent.Children.Remove(childToRemove);
await this.SavePermsAsync();
- await this.client.server.Events.PermissionRevoked.InvokeAsync(new PermissionRevokedEventArgs(this, this.client.server, permissionNode));
+ await this.client.server.permissionRevoked.InvokeAsync(new PermissionRevokedEventArgs(this, this.client.server, permissionNode));
return true;
}
diff --git a/Obsidian/Events/AsyncEvent.T.cs b/Obsidian/Events/AsyncEvent.T.cs
index 28e153c80..a1ccea7ef 100644
--- a/Obsidian/Events/AsyncEvent.T.cs
+++ b/Obsidian/Events/AsyncEvent.T.cs
@@ -1,12 +1,13 @@
-using System.Reflection;
+using Obsidian.API.Events;
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Obsidian.Events;
-public sealed class AsyncEvent : IEventRegistry
+public sealed class AsyncEvent where T : BaseMinecraftEventArgs, INamedEvent
{
- public string? Name { get; } // Name must be set in order to be visible to plugins
+ public string Name { get; } // Name must be set in order to be visible to plugins
private readonly SemaphoreSlim semaphore = new(1);
private readonly List> hooks = [];
@@ -14,9 +15,16 @@ public sealed class AsyncEvent : IEventRegistry
public AsyncEvent()
{
+ this.Name = T.Name;
}
- public AsyncEvent(string? name, Action, Exception>? exceptionHandler)
+ public AsyncEvent(Action, Exception>? exceptionHandler)
+ {
+ Name = T.Name;
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ public AsyncEvent(string name, Action, Exception>? exceptionHandler)
{
Name = name;
this.exceptionHandler = exceptionHandler;
diff --git a/Obsidian/Events/EventArgs/BasePacketEventArgs.cs b/Obsidian/Events/EventArgs/BasePacketEventArgs.cs
index dc16db6f2..dcd5699bc 100644
--- a/Obsidian/Events/EventArgs/BasePacketEventArgs.cs
+++ b/Obsidian/Events/EventArgs/BasePacketEventArgs.cs
@@ -3,7 +3,7 @@
namespace Obsidian.Events.EventArgs;
-public class BasePacketEventArgs : AsyncEventArgs
+public class BasePacketEventArgs : BaseMinecraftEventArgs
{
///
/// The client that invoked the event.
@@ -15,7 +15,7 @@ public class BasePacketEventArgs : AsyncEventArgs
///
public IPacket Packet { get; private set; }
- internal BasePacketEventArgs(Client client, IPacket packet)
+ internal BasePacketEventArgs(Server server, Client client, IPacket packet) : base(server)
{
Client = client;
Packet = packet;
diff --git a/Obsidian/Events/EventArgs/QueuePacketEventArgs.cs b/Obsidian/Events/EventArgs/QueuePacketEventArgs.cs
index 8a3f6d910..713193e9d 100644
--- a/Obsidian/Events/EventArgs/QueuePacketEventArgs.cs
+++ b/Obsidian/Events/EventArgs/QueuePacketEventArgs.cs
@@ -5,10 +5,12 @@ namespace Obsidian.Events.EventArgs;
public class QueuePacketEventArgs : BasePacketEventArgs, ICancellable
{
+ public static new string Name => "QueuePacket";
+
///
public bool IsCancelled { get; private set; }
- internal QueuePacketEventArgs(Client client, IPacket packet) : base(client, packet)
+ internal QueuePacketEventArgs(Server server, Client client, IPacket packet) : base(server, client, packet)
{
}
diff --git a/Obsidian/Events/IEventRegistry.cs b/Obsidian/Events/IEventRegistry.cs
index 929ce42dc..50e41b6b2 100644
--- a/Obsidian/Events/IEventRegistry.cs
+++ b/Obsidian/Events/IEventRegistry.cs
@@ -4,7 +4,7 @@ namespace Obsidian.Events;
public interface IEventRegistry
{
- public string? Name { get; }
+ public string Name { get; }
public bool TryRegisterEvent(MethodInfo method, object? instance, out Delegate? @delegate);
public bool UnregisterEvent(Delegate @delegate);
diff --git a/Obsidian/Events/MinecraftEvent.cs b/Obsidian/Events/MinecraftEvent.cs
index fd85dc9d1..c4a7fab5a 100644
--- a/Obsidian/Events/MinecraftEvent.cs
+++ b/Obsidian/Events/MinecraftEvent.cs
@@ -7,13 +7,15 @@ internal readonly struct MinecraftEvent
{
public required Type EventType { get; init; }
- public required Type ModuleType { get; init; }
+ public Type? ModuleType { get; init; }
public required PluginContainer PluginContainer { get; init; }
public required Priority Priority { get; init; }
- public required ObjectFactory ModuleFactory { get; init; }
+ public ObjectFactory? ModuleFactory { get; init; }
- public required ObjectMethodExecutor MethodExecutor { get; init; }
+ public ObjectMethodExecutor? MethodExecutor { get; init; }
+
+ public Delegate? MethodDelegate { get; init; }
}
diff --git a/Obsidian/Net/Packets/Play/CloseContainerPacket.cs b/Obsidian/Net/Packets/Play/CloseContainerPacket.cs
index fe8447e02..dec9b2837 100644
--- a/Obsidian/Net/Packets/Play/CloseContainerPacket.cs
+++ b/Obsidian/Net/Packets/Play/CloseContainerPacket.cs
@@ -16,6 +16,6 @@ public async ValueTask HandleAsync(Server server, Player player)
if (WindowId == 0)
return;
- await server.Events.ContainerClosed.InvokeAsync(new ContainerClosedEventArgs(player, server) { Container = player.OpenedContainer! });
+ await server.containerClosed.InvokeAsync(new ContainerClosedEventArgs(player, server) { Container = player.OpenedContainer! });
}
}
diff --git a/Obsidian/Net/Packets/Play/Serverbound/ClickContainerPacket.cs b/Obsidian/Net/Packets/Play/Serverbound/ClickContainerPacket.cs
index 6a3e7cdcb..02c79a10d 100644
--- a/Obsidian/Net/Packets/Play/Serverbound/ClickContainerPacket.cs
+++ b/Obsidian/Net/Packets/Play/Serverbound/ClickContainerPacket.cs
@@ -275,7 +275,7 @@ private async Task HandleMouseClick(BaseContainer container, Server server, Play
{
if (!CarriedItem.IsAir)
{
- var @event = await server.Events.ContainerClick.InvokeAsync(new ContainerClickEventArgs(player, server, container, CarriedItem)
+ var @event = await server.containerClick.InvokeAsync(new ContainerClickEventArgs(player, server, container, CarriedItem)
{
Slot = slot
});
diff --git a/Obsidian/Net/Packets/Play/Serverbound/InteractPacket.cs b/Obsidian/Net/Packets/Play/Serverbound/InteractPacket.cs
index 5124be933..1c83d7931 100644
--- a/Obsidian/Net/Packets/Play/Serverbound/InteractPacket.cs
+++ b/Obsidian/Net/Packets/Play/Serverbound/InteractPacket.cs
@@ -30,15 +30,15 @@ public async ValueTask HandleAsync(Server server, Player player)
switch (Type)
{
case InteractionType.Interact:
- await server.Events.EntityInteract.InvokeAsync(new EntityInteractEventArgs(player, entity, server, Sneaking));
+ await server.entityInteract.InvokeAsync(new EntityInteractEventArgs(player, entity, server, Sneaking));
break;
case InteractionType.Attack:
- await server.Events.PlayerAttackEntity.InvokeAsync(new PlayerAttackEntityEventArgs(player, entity, server, Sneaking));
+ await server.playerAttackEntity.InvokeAsync(new PlayerAttackEntityEventArgs(player, entity, server, Sneaking));
break;
case InteractionType.InteractAt:
- await server.Events.EntityInteract.InvokeAsync(new EntityInteractEventArgs(player, entity, server, Hand, Target, Sneaking));
+ await server.entityInteract.InvokeAsync(new EntityInteractEventArgs(player, entity, server, Hand, Target, Sneaking));
break;
}
}
diff --git a/Obsidian/Net/Packets/Play/Serverbound/PlayerActionPacket.cs b/Obsidian/Net/Packets/Play/Serverbound/PlayerActionPacket.cs
index dffc9be25..5c8e9d3da 100644
--- a/Obsidian/Net/Packets/Play/Serverbound/PlayerActionPacket.cs
+++ b/Obsidian/Net/Packets/Play/Serverbound/PlayerActionPacket.cs
@@ -35,7 +35,7 @@ public async ValueTask HandleAsync(Server server, Player player)
SequenceID = Sequence
});
- var blockBreakEvent = await server.Events.BlockBreak.InvokeAsync(new BlockBreakEventArgs(server, player, block, Position));
+ var blockBreakEvent = await server.blockBreak.InvokeAsync(new BlockBreakEventArgs(server, player, block, Position));
if (blockBreakEvent.Handled)
return;
}
diff --git a/Obsidian/Net/Packets/Play/Serverbound/UseItemOnPacket.cs b/Obsidian/Net/Packets/Play/Serverbound/UseItemOnPacket.cs
index f9eed2e8d..b4721e3bd 100644
--- a/Obsidian/Net/Packets/Play/Serverbound/UseItemOnPacket.cs
+++ b/Obsidian/Net/Packets/Play/Serverbound/UseItemOnPacket.cs
@@ -41,7 +41,7 @@ public async ValueTask HandleAsync(Server server, Player player)
if (TagsRegistry.Blocks.PlayersCanInteract.Entries.Contains(b.RegistryId) && !player.Sneaking)
{
- await server.Events.PlayerInteract.InvokeAsync(new PlayerInteractEventArgs(player, server)
+ await server.playerInteract.InvokeAsync(new PlayerInteractEventArgs(player, server)
{
Item = currentItem,
Block = b,
diff --git a/Obsidian/Net/Packets/Play/Serverbound/UseItemPacket.cs b/Obsidian/Net/Packets/Play/Serverbound/UseItemPacket.cs
index 4e0256c1a..4d5362179 100644
--- a/Obsidian/Net/Packets/Play/Serverbound/UseItemPacket.cs
+++ b/Obsidian/Net/Packets/Play/Serverbound/UseItemPacket.cs
@@ -16,7 +16,7 @@ public partial class UseItemPacket : IServerboundPacket
public async ValueTask HandleAsync(Server server, Player player)
{
- await server.Events.PlayerInteract.InvokeAsync(new PlayerInteractEventArgs(player, server)
+ await server.playerInteract.InvokeAsync(new PlayerInteractEventArgs(player, server)
{
Item = this.Hand == Hand.MainHand ? player.GetHeldItem() : player.GetOffHandItem(),
Hand = this.Hand,
diff --git a/Obsidian/Plugins/PluginManager.cs b/Obsidian/Plugins/PluginManager.cs
index a1dd29cc4..4f183187b 100644
--- a/Obsidian/Plugins/PluginManager.cs
+++ b/Obsidian/Plugins/PluginManager.cs
@@ -7,6 +7,8 @@
using Obsidian.Plugins.PluginProviders;
using Obsidian.Plugins.ServiceProviders;
using Obsidian.Registries;
+using Obsidian.Services;
+using System.Collections.Frozen;
namespace Obsidian.Plugins;
@@ -43,14 +45,15 @@ public sealed class PluginManager
public IServiceProvider PluginServiceProvider { get; private set; } = default!;
- public PluginManager(IServiceProvider serverProvider, IServer server, ILogger logger)
+ public PluginManager(IServiceProvider serverProvider, IServer server,
+ EventDispatcher eventDispatcher, CommandHandler commandHandler, ILogger logger)
{
var env = serverProvider.GetRequiredService();
this.server = server;
this.logger = logger;
this.serverProvider = serverProvider;
- this.pluginRegistry = new PluginRegistry(server);
+ this.pluginRegistry = new PluginRegistry(this, eventDispatcher, commandHandler, logger);
PluginProviderSelector.RemotePluginProvider = new RemotePluginProvider(logger);
PluginProviderSelector.UncompiledPluginProvider = new UncompiledPluginProvider(logger);
@@ -113,7 +116,6 @@ private void ConfigureInitialServices(IServerEnvironment env)
PluginServiceHandler.InjectServices(this.serverProvider, pluginContainer, this.logger);
pluginContainer.Plugin.unload = async () => await UnloadPluginAsync(pluginContainer);
-
if (pluginContainer.IsReady)
{
lock (plugins)
diff --git a/Obsidian/Plugins/PluginRegistry.cs b/Obsidian/Plugins/PluginRegistry.cs
index 07afc5fa3..56056762a 100644
--- a/Obsidian/Plugins/PluginRegistry.cs
+++ b/Obsidian/Plugins/PluginRegistry.cs
@@ -1,14 +1,17 @@
-using Obsidian.API.Events;
+using Microsoft.Extensions.Logging;
+using Obsidian.API.Events;
using Obsidian.API.Plugins;
using Obsidian.Commands.Framework;
+using Obsidian.Services;
using System.Reflection;
namespace Obsidian.Plugins;
-public sealed class PluginRegistry(IServer server) : IPluginRegistry
+public sealed class PluginRegistry(PluginManager pluginManager, EventDispatcher eventDispatcher, CommandHandler commandHandler, ILogger logger) : IPluginRegistry
{
- private readonly Server server = (Server)server;
-
- private CommandHandler CommandHandler => this.server.CommandsHandler;
+ private readonly PluginManager pluginManager = pluginManager;
+ private readonly EventDispatcher eventDispatcher = eventDispatcher;
+ private readonly CommandHandler commandHandler = commandHandler;
+ private readonly ILogger logger = logger;
public IPluginRegistry MapCommand(ContextDelegate contextDelegate)
{
@@ -18,7 +21,7 @@ public IPluginRegistry MapCommand(ContextDelegate contextDelegat
public IPluginRegistry MapCommands()
{
- var asm = Assembly.GetExecutingAssembly();
+ var executingAssembly = Assembly.GetExecutingAssembly();
return this;
@@ -26,19 +29,28 @@ public IPluginRegistry MapCommands()
public IPluginRegistry MapEvent(ContextDelegate contextDelegate, Priority priority = Priority.Low) where TEventArgs : BaseMinecraftEventArgs
{
+ var executingAssembly = Assembly.GetExecutingAssembly();
+
+ var pluginContainer = this.pluginManager.Plugins.First(x => x.PluginAssembly == executingAssembly);
+ this.eventDispatcher.RegisterEvent(pluginContainer, contextDelegate, priority);
return this;
}
public IPluginRegistry MapEvents()
{
+ var executingAssembly = Assembly.GetExecutingAssembly();
+
+ var pluginContainer = this.pluginManager.Plugins.First(x => x.PluginAssembly == executingAssembly);
+
+ this.eventDispatcher.RegisterEvents(pluginContainer);
return this;
}
public IPluginRegistry RegisterArgumentHandler(T parser) where T : BaseArgumentParser
{
- this.CommandHandler.AddArgumentParser(parser);
+ this.commandHandler.AddArgumentParser(parser);
return this;
}
diff --git a/Obsidian/Server.Events.cs b/Obsidian/Server.Events.cs
index 2e1565386..abbe1352b 100644
--- a/Obsidian/Server.Events.cs
+++ b/Obsidian/Server.Events.cs
@@ -13,39 +13,39 @@ namespace Obsidian;
public partial class Server
{
- private AsyncEvent packetReceived = default!;
- private AsyncEvent queuePacket = default!;
- private AsyncEvent playerJoin = default!;
- private AsyncEvent playerLeave = default!;
- private AsyncEvent playerTeleported = default!;
- private AsyncEvent permissionGranted = default!;
- private AsyncEvent permissionRevoked = default!;
- private AsyncEvent containerClick = default!;
- private AsyncEvent blockBreak = default!;
- private AsyncEvent incomingChatMessage = default!;
- private AsyncEvent serverStatusRequest = default!;
- private AsyncEvent entityInteract = default!;
- private AsyncEvent playerAttackEntity = default!;
- private AsyncEvent playerInteract = default!;
- private AsyncEvent containerClosed = default!;
+ internal AsyncEvent packetReceived = default!;
+ internal AsyncEvent queuePacket = default!;
+ internal AsyncEvent playerJoin = default!;
+ internal AsyncEvent playerLeave = default!;
+ internal AsyncEvent playerTeleported = default!;
+ internal AsyncEvent permissionGranted = default!;
+ internal AsyncEvent permissionRevoked = default!;
+ internal AsyncEvent containerClick = default!;
+ internal AsyncEvent blockBreak = default!;
+ internal AsyncEvent incomingChatMessage = default!;
+ internal AsyncEvent serverStatusRequest = default!;
+ internal AsyncEvent entityInteract = default!;
+ internal AsyncEvent playerAttackEntity = default!;
+ internal AsyncEvent playerInteract = default!;
+ internal AsyncEvent containerClosed = default!;
private void InitializeEvents()
{
- this.packetReceived = new("PacketReceived", this.HandleException);
- this.queuePacket = new("QueuePacket", this.HandleException);
- this.playerJoin = new("PlayerJoin", this.HandleException);
- this.playerLeave = new("PlayerLeave", this.HandleException);
- this.playerTeleported = new("PlayerTeleported", this.HandleException);
- this.permissionGranted = new("PermissionGranted", this.HandleException);
- this.permissionRevoked = new("PermissionRevoked", this.HandleException);
- this.containerClick = new("ContainerClick", this.HandleException);
- this.blockBreak = new("BlockBreak", this.HandleException);
- this.incomingChatMessage = new("IncomingChatMessage", this.HandleException);
- this.serverStatusRequest = new("ServerStatusRequest", this.HandleException);
- this.entityInteract = new("EntityInteract", this.HandleException);
- this.playerAttackEntity = new("PlayerAttackEntity", this.HandleException);
- this.playerInteract = new("PlayerInteract", this.HandleException);
- this.containerClosed = new("ContainerClosed", this.HandleException);
+ this.packetReceived = new(this.HandleException);
+ this.queuePacket = new(this.HandleException);
+ this.playerJoin = new(this.HandleException);
+ this.playerLeave = new(this.HandleException);
+ this.playerTeleported = new(this.HandleException);
+ this.permissionGranted = new(this.HandleException);
+ this.permissionRevoked = new(this.HandleException);
+ this.containerClick = new(this.HandleException);
+ this.blockBreak = new(this.HandleException);
+ this.incomingChatMessage = new(this.HandleException);
+ this.serverStatusRequest = new(this.HandleException);
+ this.entityInteract = new(this.HandleException);
+ this.playerAttackEntity = new(this.HandleException);
+ this.playerInteract = new(this.HandleException);
+ this.containerClosed = new(this.HandleException);
this.playerLeave += OnPlayerLeave;
this.playerJoin += OnPlayerJoin;
@@ -380,8 +380,8 @@ private async Task OnPlayerJoin(PlayerJoinEventArgs e)
}
}
- private void HandleException(AsyncEvent e, Exception exception)
+ private void HandleException(AsyncEvent e, Exception exception) where T : BaseMinecraftEventArgs, INamedEvent
{
- this._logger.LogCritical(exception, "Failed to execute event.");
+ this._logger.LogCritical(exception, "Failed to execute event {eventName}.", T.Name);
}
}
diff --git a/Obsidian/Server.cs b/Obsidian/Server.cs
index b7012be3a..9cd5ae615 100644
--- a/Obsidian/Server.cs
+++ b/Obsidian/Server.cs
@@ -62,7 +62,6 @@ public static string VERSION
private readonly ILoggerFactory loggerFactory;
private readonly RconServer _rconServer;
private readonly IUserCache userCache;
- private readonly EventDispatcher eventDispatcher;
private readonly ILogger _logger;
private readonly IServiceProvider serviceProvider;
@@ -74,6 +73,7 @@ public static string VERSION
public DateTimeOffset StartTime { get; private set; }
public PluginManager PluginManager { get; }
+ public EventDispatcher EventDispatcher { get; }
public IOperatorList Operators { get; }
public IScoreboardManager ScoreboardManager { get; private set; }
@@ -120,9 +120,10 @@ public Server(
_logger.LogDebug(message: "Initializing command handler...");
- PluginManager = new PluginManager(this.serviceProvider, this, _logger);
CommandsHandler = new CommandHandler(loggerFactory.CreateLogger());
+ PluginManager = new PluginManager(this.serviceProvider, this, eventDispatcher, CommandsHandler, _logger);
+
_logger.LogDebug("Registering commands...");
CommandsHandler.RegisterCommandClass(null);
@@ -131,7 +132,7 @@ public Server(
_logger.LogDebug("Done registering commands.");
this.userCache = playerCache;
- this.eventDispatcher = eventDispatcher;
+ this.EventDispatcher = eventDispatcher;
this.loggerFactory = loggerFactory;
this.WorldManager = worldManager;
diff --git a/Obsidian/Services/EventDispatcher.cs b/Obsidian/Services/EventDispatcher.cs
index 49c65eaf4..1a3c27238 100644
--- a/Obsidian/Services/EventDispatcher.cs
+++ b/Obsidian/Services/EventDispatcher.cs
@@ -2,20 +2,42 @@
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
using Obsidian.API.Events;
+using Obsidian.API.Plugins;
using Obsidian.Events;
+using Obsidian.Events.EventArgs;
using Obsidian.Plugins;
+using System.Collections.Frozen;
+using System.Diagnostics;
using System.Reflection;
namespace Obsidian.Services;
//TODO BETTER NAME MAYBE??
-public sealed class EventDispatcher(ILogger logger) : IDisposable
+public sealed class EventDispatcher : IDisposable
{
- private static Type eventPriorityAttributeType = typeof(EventPriorityAttribute);
- private static Type baseMinecraftEventArgsType = typeof(BaseMinecraftEventArgs);
+ private static readonly Type eventPriorityAttributeType = typeof(EventPriorityAttribute);
+ private static readonly Type baseMinecraftEventArgsType = typeof(BaseMinecraftEventArgs);
+ private static readonly Type minecraftEventHandlerType = typeof(MinecraftEventHandler);
- private readonly List registeredEvents = [];
- private readonly ILogger logger = logger;
+ private readonly ILogger logger;
+
+ private readonly FrozenDictionary> registeredEvents;
+
+ public EventDispatcher(ILogger logger)
+ {
+ this.logger = logger;
+
+ var events = typeof(INamedEvent).Assembly.GetTypes().Where(x => x.IsAssignableFrom(baseMinecraftEventArgsType));
+ var dict = new Dictionary>();
+ foreach (var eventType in events)
+ {
+ var eventName = eventType.GetProperty("Name")?.GetValue(null)?.ToString() ?? throw new NullReferenceException();
+
+ dict.Add(eventName, []);
+ }
+
+ this.registeredEvents = dict.ToFrozenDictionary();
+ }
public void RegisterEvents(PluginContainer pluginContainer) where TEventModule : MinecraftEventHandler
{
@@ -27,11 +49,15 @@ public void RegisterEvents(PluginContainer pluginContainer) where
{
var eventPriorityAttribute = method.GetCustomAttribute()!;
var eventType = method.GetParameters().FirstOrDefault()?.ParameterType ?? throw new InvalidOperationException("Method must contain a BaseMinecraftEventArgs type as the first parameter.");
+ var eventName = eventType.GetProperty("Name")?.GetValue(null)?.ToString() ?? throw new NullReferenceException();
if (!eventType.IsAssignableFrom(baseMinecraftEventArgsType))
throw new InvalidOperationException("Method must contain a BaseMinecraftEventArgs type as the first parameter.");
- this.registeredEvents.Add(new()
+ if (!this.registeredEvents.TryGetValue(eventName, out var values))
+ continue;
+
+ values.Add(new()
{
EventType = eventType,
ModuleFactory = ActivatorUtilities.CreateFactory(eventModule, []),
@@ -43,39 +69,116 @@ public void RegisterEvents(PluginContainer pluginContainer) where
}
}
- public async ValueTask ExecuteEventAsync(TEventArgs eventArgs) where TEventArgs : BaseMinecraftEventArgs
+ public void RegisterEvent(PluginContainer pluginContainer, ContextDelegate contextDelegate, Priority priority = Priority.Low) where TEventArgs : BaseMinecraftEventArgs, INamedEvent
+ {
+ if (!this.registeredEvents.TryGetValue(TEventArgs.Name, out var values))
+ return;
+
+ values.Add(new()
+ {
+ EventType = typeof(TEventArgs),
+ PluginContainer = pluginContainer,
+ Priority = priority,
+ MethodDelegate = contextDelegate
+ });
+
+ }
+
+ public void RegisterEvents(PluginContainer pluginContainer)
{
- var events = this.registeredEvents.Where(x => x.EventType == eventArgs.GetType())
- .OrderBy(x => x.Priority);//Plugins with the lowest priority must be called first
+ var modules = pluginContainer.PluginAssembly.GetTypes().Where(x => x.IsAssignableFrom(minecraftEventHandlerType));
+
+ foreach (var eventModule in modules)
+ {
+ var methods = eventModule.GetMethods().Where(x => x.CustomAttributes.Any(x => x.AttributeType == eventPriorityAttributeType));
+
+ foreach (var method in methods)
+ {
+ var eventPriorityAttribute = method.GetCustomAttribute()!;
+ var eventType = method.GetParameters().FirstOrDefault()?.ParameterType ?? throw new InvalidOperationException("Method must contain a BaseMinecraftEventArgs type as the first parameter.");
+ var eventName = eventType.GetProperty("Name")?.GetValue(null)?.ToString() ?? throw new NullReferenceException();
+
+ if (!eventType.IsAssignableFrom(baseMinecraftEventArgsType))
+ throw new InvalidOperationException("Method must contain a BaseMinecraftEventArgs type as the first parameter.");
+
+ if (!this.registeredEvents.TryGetValue(eventName, out var values))
+ continue;
+
+ values.Add(new()
+ {
+ EventType = eventType,
+ ModuleFactory = ActivatorUtilities.CreateFactory(eventModule, []),
+ PluginContainer = pluginContainer,
+ Priority = eventPriorityAttribute.Priority,
+ MethodExecutor = ObjectMethodExecutor.Create(method, eventModule.GetTypeInfo()),
+ ModuleType = eventModule
+ });
+ }
+ }
+ }
+
+ public async ValueTask ExecuteEventAsync(TEventArgs eventArgs) where TEventArgs : BaseMinecraftEventArgs, INamedEvent
+ {
+ var eventType = eventArgs.GetType();
+
+ if (!this.registeredEvents.TryGetValue(TEventArgs.Name, out var events))
+ return EventResult.Completed;
+
+ var foundEvents = events.OrderBy(x => x.Priority);//Plugins with the lowest priority must be called first
var eventResult = EventResult.Completed;
- foreach (var @event in events)
+ foreach (var @event in foundEvents)
{
- var module = @event.ModuleFactory.Invoke(@event.PluginContainer.ServiceScope.ServiceProvider, null)//Will inject services through constructor
+ if (@event.ModuleType is not null)
+ {
+ var module = @event.ModuleFactory!.Invoke(@event.PluginContainer.ServiceScope.ServiceProvider, null)//Will inject services through constructor
?? throw new InvalidOperationException("Failed to initialize module from factory.");
- //inject through attribute
- @event.PluginContainer.InjectServices(this.logger, module);
+ //inject through attribute
+ @event.PluginContainer.InjectServices(this.logger, module);
- try
- {
- var returnResult = @event.MethodExecutor.Execute(module, new[] { eventArgs });//Maybe have method param service injection??
+ try
+ {
+ var returnResult = @event.MethodExecutor!.Execute(module, new[] { eventArgs });//Maybe have method param service injection??
- if (returnResult is ValueTask valueTask)
- await valueTask;
- else if (returnResult is Task task)
- await task.ConfigureAwait(false);
+ if (returnResult is ValueTask valueTask)
+ await valueTask;
+ else if (returnResult is Task task)
+ await task.ConfigureAwait(false);
- if (eventArgs is ICancellable cancellable && cancellable.IsCancelled)
- eventResult = EventResult.Cancelled;
+ if (eventArgs is ICancellable cancellable && cancellable.IsCancelled)
+ eventResult = EventResult.Cancelled;
+ }
+ catch (OperationCanceledException) { }//IGNORE this exception
+ catch (Exception ex)
+ {
+ this.logger.LogCritical(ex, "failed to execute event.");
+
+ return EventResult.Failed;
+ }
}
- catch (OperationCanceledException) { }//IGNORE this exception
- catch (Exception ex)
+ else
{
- this.logger.LogCritical(ex, "failed to execute event.");
-
- return EventResult.Failed;
+ try
+ {
+ var returnResult = @event.MethodDelegate!.DynamicInvoke(eventArgs);//Maybe have method param service injection??
+
+ if (returnResult is ValueTask valueTask)
+ await valueTask;
+ else if (returnResult is Task task)
+ await task.ConfigureAwait(false);
+
+ if (eventArgs is ICancellable cancellable && cancellable.IsCancelled)
+ eventResult = EventResult.Cancelled;
+ }
+ catch (OperationCanceledException) { }//IGNORE this exception
+ catch (Exception ex)
+ {
+ this.logger.LogCritical(ex, "failed to execute event.");
+
+ return EventResult.Failed;
+ }
}
}
@@ -84,7 +187,10 @@ public async ValueTask ExecuteEventAsync(TEventArgs eve
public void Dispose()
{
- this.registeredEvents.Clear();
+ foreach (var (_, values) in this.registeredEvents)
+ {
+ values.Clear();
+ }
}
}