Skip to content

Commit

Permalink
Merge pull request #15 from All-Of-Us-Mods/events-system
Browse files Browse the repository at this point in the history
initial event draft
  • Loading branch information
XtraCube authored Dec 21, 2024
2 parents bc756d5 + a23de7b commit d3a815f
Show file tree
Hide file tree
Showing 31 changed files with 820 additions and 33 deletions.
42 changes: 42 additions & 0 deletions MiraAPI.Example/ExampleEventHandlers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Events.Vanilla;
using MiraAPI.Example.Buttons.Freezer;
using Reactor.Utilities;

namespace MiraAPI.Example;

public static class ExampleEventHandlers
{
public static void Initialize()
{
// Register event handlers here
MiraEventManager.RegisterEventHandler<MiraButtonClickEvent<FreezeButton>>(FreezeButtonClickHandler, 1);
MiraEventManager.RegisterEventHandler<MiraButtonCancelledEvent<FreezeButton>>(FreezeButtonCancelledHandler);
MiraEventManager.RegisterEventHandler<UpdateSystemEvent>(UpdateSystemEventHandler);
}

public static void UpdateSystemEventHandler(UpdateSystemEvent @event)
{
Logger<ExamplePlugin>.Error(@event.SystemType.ToString());
}

// Example event handler
public static void FreezeButtonClickHandler(MiraButtonClickEvent<FreezeButton> @event)
{
Logger<ExamplePlugin>.Warning("Freeze button clicked!");

if (PlayerControl.LocalPlayer.Data.PlayerName == "stupid")
{
@event.Cancel();
@event.Button.SetTimer(15f);
}
}

// Example event handler
public static void FreezeButtonCancelledHandler(MiraButtonCancelledEvent<FreezeButton> @event)
{
Logger<ExamplePlugin>.Warning("Freeze button cancelled!");
@event.Button.OverrideName("Freeze Canceled");
}
}
5 changes: 5 additions & 0 deletions MiraAPI.Example/ExamplePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Example.Buttons.Freezer;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using Reactor.Utilities;

namespace MiraAPI.Example;

Expand All @@ -21,6 +25,7 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin
public ConfigFile GetConfigFile() => Config;
public override void Load()
{
ExampleEventHandlers.Initialize();
Harmony.PatchAll();
}
}
44 changes: 44 additions & 0 deletions MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using MiraAPI.Hud;

namespace MiraAPI.Events.Mira;

/// <summary>
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonCancelledEvent<T> : MiraEvent where T : CustomActionButton
{
/// <summary>
/// Gets Mira Button whose click was cancelled.
/// </summary>
public T Button { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(T button)
{
Button = button;
}
}

/// <summary>
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
public sealed class MiraButtonCancelledEvent : MiraEvent
{
/// <summary>
/// Gets Mira Button whose click was cancelled.
/// </summary>
public CustomActionButton Button { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent"/> class.
/// </summary>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(CustomActionButton button)
{
Button = button;
}
}
51 changes: 51 additions & 0 deletions MiraAPI/Events/Mira/MiraButtonClickEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using MiraAPI.Hud;

namespace MiraAPI.Events.Mira;

/// <summary>
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonClickEvent<T> : MiraCancelableEvent where T : CustomActionButton
{
/// <summary>
/// Gets Mira Button that was clicked.
/// </summary>
public T Button { get; }

/// <summary>
/// Gets the generic click event that is invoked before this button-specific event.
/// </summary>
public MiraButtonClickEvent GenericClickEvent { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button that was clicked.</param>
/// <param name="genericClickEvent">The generic click event invoked before button-specific events.</param>
public MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent)
{
GenericClickEvent = genericClickEvent;
Button = button;
}
}

/// <summary>
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
public sealed class MiraButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets Mira Button that was clicked.
/// </summary>
public CustomActionButton Button { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The Mira Button that was clicked.</param>
public MiraButtonClickEvent(CustomActionButton button)
{
Button = button;
}
}
28 changes: 28 additions & 0 deletions MiraAPI/Events/MiraCancelableEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace MiraAPI.Events;

/// <summary>
/// Abstract class for Mira Events that can be cancelled.
/// </summary>
public abstract class MiraCancelableEvent : MiraEvent
{
/// <summary>
/// Gets a value indicating whether the event is cancelled.
/// </summary>
public bool IsCancelled { get; private set; }

/// <summary>
/// Cancels the event.
/// </summary>
public void Cancel()
{
IsCancelled = true;
}

/// <summary>
/// Uncancels the event.
/// </summary>
public void UnCancel()
{
IsCancelled = false;
}
}
6 changes: 6 additions & 0 deletions MiraAPI/Events/MiraEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MiraAPI.Events;

/// <summary>
/// Abstract class for Mira Events.
/// </summary>
public abstract class MiraEvent;
87 changes: 87 additions & 0 deletions MiraAPI/Events/MiraEventManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using BepInEx.Unity.IL2CPP;
using Reactor.Utilities;

namespace MiraAPI.Events;

/// <summary>
/// Mira Event manager.
/// </summary>
public static class MiraEventManager
{
private static readonly Dictionary<Type, List<MiraEventWrapper>> EventWrappers = [];

/// <summary>
/// Invoke an event.
/// </summary>
/// <param name="eventInstance">The event instance.</param>
/// <typeparam name="T">Type of Event.</typeparam>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent<T>(T eventInstance) where T : MiraEvent
{
EventWrappers.TryGetValue(typeof(T), out var handlers);
if (handlers == null)
{
Logger<MiraApiPlugin>.Warning("No handlers for event " + typeof(T).Name);
return false;
}

foreach (var handler in handlers)
{
((Action<T>)handler.EventHandler).Invoke(eventInstance);
}

return true;
}

/// <summary>
/// Invoke an event and use a specific type to find the handlers.
/// </summary>
/// <param name="eventInstance">The event instance.</param>
/// <param name="type">The type to use for handler lookup.</param>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent(MiraEvent eventInstance, Type type)
{
EventWrappers.TryGetValue(type, out var handlers);
if (handlers == null)
{
Logger<MiraApiPlugin>.Warning("No handlers for event " + type.Name);
return false;
}

foreach (var handler in handlers)
{
handler.EventHandler.DynamicInvoke(eventInstance);
}

return true;
}

/// <summary>
/// Register an event.
/// </summary>
/// <param name="handler">The callback method/handler for the event.</param>
/// <param name="priority">The priority of the event handler. Higher values are called first.</param>
/// <typeparam name="T">Type of event.</typeparam>
public static void RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
{
if (!EventWrappers.ContainsKey(typeof(T)))
{
EventWrappers.Add(typeof(T), []);
}

var handlers = EventWrappers[typeof(T)];
handlers.Add(new MiraEventWrapper(handler, priority));

Logger<MiraApiPlugin>.Info("Registered event handler for " + typeof(T).Name);
}

internal static void SortAllHandlers()
{
foreach (var handlers in EventWrappers.Values)
{
handlers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
}
}
}
30 changes: 30 additions & 0 deletions MiraAPI/Events/MiraEventWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;

namespace MiraAPI.Events;

/// <summary>
/// Wrapper for event handlers.
/// </summary>
public class MiraEventWrapper
{
/// <summary>
/// Gets the event handler delegate.
/// </summary>
public Delegate EventHandler { get; }

/// <summary>
/// Gets the priority of the handler. Higher values are called first.
/// </summary>
public int Priority { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MiraEventWrapper"/> class.
/// </summary>
/// <param name="eventHandler">The action for the event handler.</param>
/// <param name="priority">The priority of the handler.</param>
public MiraEventWrapper(Delegate eventHandler, int priority)
{
EventHandler = eventHandler;
Priority = priority;
}
}
21 changes: 21 additions & 0 deletions MiraAPI/Events/Vanilla/AdminButtonClickEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;

/// <summary>
/// Button click event for <see cref="AdminButton"/> from Vanilla Among Us.
/// </summary>
public class AdminButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of <see cref="AdminButton"/> that was clicked.
/// </summary>
public AdminButton Button { get; }

/// <summary>
/// Initializes a new instance of the <see cref="AdminButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The AdminButton.</param>
public AdminButtonClickEvent(AdminButton button)
{
Button = button;
}
}
28 changes: 28 additions & 0 deletions MiraAPI/Events/Vanilla/AfterMurderEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;

/// <summary>
/// Event that is invoked after a player is murdered. Only called after a successful murder. This event is not cancelable.
/// </summary>
public class AfterMurderEvent : MiraEvent
{
/// <summary>
/// Gets the player that killed the target.
/// </summary>
public PlayerControl Source { get; }

/// <summary>
/// Gets the player that was killed.
/// </summary>
public PlayerControl Target { get; }

/// <summary>
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
/// </summary>
/// <param name="source">The killer.</param>
/// <param name="target">The killed player.</param>
public AfterMurderEvent(PlayerControl source, PlayerControl target)
{
Source = source;
Target = target;
}
}
28 changes: 28 additions & 0 deletions MiraAPI/Events/Vanilla/BeforeMurderEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;

/// <summary>
/// Event that is invoked before a player is murdered. This event is cancelable.
/// </summary>
public sealed class BeforeMurderEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the player that is killing the target.
/// </summary>
public PlayerControl Source { get; }

/// <summary>
/// Gets the player that is being killed.
/// </summary>
public PlayerControl Target { get; }

/// <summary>
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
/// </summary>
/// <param name="source">The player that is killing the target.</param>
/// <param name="target">The player that is being killed.</param>
public BeforeMurderEvent(PlayerControl source, PlayerControl target)
{
Source = source;
Target = target;
}
}
Loading

0 comments on commit d3a815f

Please sign in to comment.