From 8b475887e5d523c6211463d27e95b4dd5fefbb36 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 18:29:23 -0400
Subject: [PATCH 01/11] Allow JavaDocs to generate with the "note" tags
---
build.gradle | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/build.gradle b/build.gradle
index 10b07fd..e3082c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,13 @@ changelog {
from '1.0.0'
}
+tasks.withType(Javadoc).configureEach {
+ options { StandardJavadocDocletOptions options ->
+ options.windowTitle = 'EventBus ' + project.version
+ options.tags 'apiNote:a:API Note:', 'implNote:a:Implementation Note:', 'implSpec:a:Implementation Specification:'
+ }
+}
+
tasks.withType(JavaCompile).configureEach {
// Set up compile-time enforcement of the jSpecify spec via ErrorProne and NullAway
options.errorprone { ErrorProneOptions errorProne ->
From 207ed4c85f9793ecdc7b63f09b3b36332cad63c5 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 18:39:43 -0400
Subject: [PATCH 02/11] Prevent subprojects from overriding the root's JavaDocs
---
build.gradle | 1 +
eventbus-jmh/build.gradle | 4 ++++
eventbus-test-jar/build.gradle | 4 ++++
eventbus-test/build.gradle | 4 ++++
4 files changed, 13 insertions(+)
diff --git a/build.gradle b/build.gradle
index e3082c5..4c0b170 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,6 +22,7 @@ java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
modularity.inferModulePath = true
withSourcesJar()
+ withJavadocJar()
}
repositories {
diff --git a/eventbus-jmh/build.gradle b/eventbus-jmh/build.gradle
index 09b5b44..4e8da6e 100644
--- a/eventbus-jmh/build.gradle
+++ b/eventbus-jmh/build.gradle
@@ -36,6 +36,10 @@ extraJavaModuleInfo {
automaticModule('net.sf.jopt-simple:jopt-simple', 'jopt.simple')
}
+tasks.named('javadoc', Javadoc) {
+ enabled = false
+}
+
tasks.register('aggregateJmh', AggregateJmh) {
if (rootProject.file('jmh_data_input.json').exists())
inputData = rootProject.file('jmh_data_input.json')
diff --git a/eventbus-test-jar/build.gradle b/eventbus-test-jar/build.gradle
index 85b027b..6f0f3be 100644
--- a/eventbus-test-jar/build.gradle
+++ b/eventbus-test-jar/build.gradle
@@ -33,6 +33,10 @@ license {
newLine = false
}
+tasks.named('javadoc', Javadoc) {
+ enabled = false
+}
+
// Hack eclipse into knowing that the gradle deps are modules
eclipse.classpath {
containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
diff --git a/eventbus-test/build.gradle b/eventbus-test/build.gradle
index 1fef6cf..9566947 100644
--- a/eventbus-test/build.gradle
+++ b/eventbus-test/build.gradle
@@ -33,6 +33,10 @@ extraJavaModuleInfo {
failOnMissingModuleInfo = false
}
+tasks.named('javadoc', Javadoc) {
+ enabled = false
+}
+
tasks.named('test', Test) {
useJUnitPlatform()
}
From 608c66c92f8125774b98709fb28743d7bf0a0183 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 18:40:00 -0400
Subject: [PATCH 03/11] Fix JavaDocs not using links to external libraries
---
build.gradle | 1 +
settings.gradle | 2 ++
2 files changed, 3 insertions(+)
diff --git a/build.gradle b/build.gradle
index 4c0b170..790c840 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,6 +8,7 @@ plugins {
id 'org.gradlex.extra-java-module-info' version '1.11'
id 'net.minecraftforge.gradleutils' version '2.4.13'
id 'net.minecraftforge.licenser' version '1.1.1'
+ alias libs.plugins.javadoc.links
// Enforce jSpecify annotations at compile-time
id 'net.ltgt.errorprone' version '4.1.0'
diff --git a/settings.gradle b/settings.gradle
index 8aa86bb..b93b65a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -12,6 +12,8 @@ plugins {
dependencyResolutionManagement {
versionCatalogs {
libs {
+ plugin 'javadoc-links', 'io.freefair.javadoc-links' version '8.13.1'
+
// https://mvnrepository.com/artifact/org.jspecify/jspecify
library('jspecify-annotations', 'org.jspecify', 'jspecify') version '1.0.0'
From 3931a63591a88865acf0b06b3a58fe4895a50686 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 18:28:18 -0400
Subject: [PATCH 04/11] JavaDocs - EventBus module
The JavaDocs for the module file are consumed by the JavaDocs tool and
is effectively displayed as the index for the EventBus JavaDocs.
---
README.md | 17 ++++++++------
src/main/java/module-info.java | 42 ++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 1ef7678..6a60c68 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+
+
# EventBus
A flexible, high-performance, thread-safe subscriber-publisher framework designed with modern Java in mind.
@@ -14,13 +17,13 @@ First, add the Forge Maven repository and the EventBus dependency to your projec
```gradle
repositories {
maven {
- name = "Forge"
- url = "https://maven.minecraftforge.net"
+ name = 'Forge'
+ url = 'https://maven.minecraftforge.net'
}
}
dependencies {
- implementation "net.minecraftforge:eventbus:"
+ implementation 'net.minecraftforge:eventbus:'
}
```
@@ -47,11 +50,11 @@ Browse the `net.minecraftforge.eventbus.api` package and read the Javadocs for m
examples, check out Forge's extensive use of EventBus [here][Forge usages].
## Nullability
-The entirety of EventBus' API is `@NullMarked` and compliant with the [jSpecify specification](https://jspecify.dev/) -
-this means that everything is non-null by default unless otherwise specified.
+The entirety of EventBus' API is `@NullMarked` and compliant with the [jSpecify specification](https://jspecify.dev/).
+This means that everything is non-null by default unless otherwise specified.
-Attempting to pass a `null` value to a method param that isn't explicitly marked as `@Nullable` is an unsupported
-operation and won't be considered a breaking change if a future version throws an exception in such cases when it didn't
+Attempting to pass a `null` value to a method param that isn't explicitly marked as `@Nullable` is an *unsupported
+operation* and won't be considered a breaking change if a future version throws an exception in such cases when it didn't
before.
## Contributing
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index a4fba2c..88da016 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -4,6 +4,48 @@
*/
import org.jspecify.annotations.NullMarked;
+/**
+ * EventBus is a flexible, high-performance, thread-safe subscriber-publisher framework designed with modern Java in
+ * mind.
+ *
+ * Overview
+ * The core functionality of EventBus is to provide a simple and efficient way to handle
+ * {@linkplain net.minecraftforge.eventbus.api.event events} in a decoupled manner.
+ * Each event may have one or more {@linkplain net.minecraftforge.eventbus.api.bus.EventBus buses} associated with
+ * it, which are responsible for managing {@linkplain net.minecraftforge.eventbus.api.listener.EventListener listeners}
+ * and dispatching instances of the event object to them. To maximise performance, the underlying implementation is
+ * tailored on the fly based on the event's type,
+ * {@linkplain net.minecraftforge.eventbus.api.event.characteristic characteristics}, inheritance chain and the number
+ * and type of listeners registered to the bus.
+ *
+ * Example
+ * Here is a basic usage example of EventBus in action:
+ * {@snippet :
+ * import net.minecraftforge.eventbus.api.event.RecordEvent;
+ * import net.minecraftforge.eventbus.api.bus.EventBus;
+ *
+ * // Define an event and a bus for it
+ * record PlayerLoggedInEvent(String username) implements RecordEvent {
+ * public static final EventBus BUS = EventBus.create(PlayerLoggedInEvent.class);
+ * }
+ *
+ * // Register an event listener
+ * PlayerLoggedInEvent.BUS.addListener(event -> System.out.println("Player logged in: " + event.username()));
+ *
+ * // Post an event to the registered listeners
+ * PlayerLoggedInEvent.BUS.post(new PlayerLoggedInEvent("Paint_Ninja"));
+ *}
+ * There are several more example usages within the JavaDocs of the different packages and classes in this API
+ * module. These examples are non-exhaustive, but provide a good basis on which to build your usage of EventBus.
+ *
+ * Nullability
+ * The entirety of EventBus' API is {@link org.jspecify.annotations.NullMarked @NullMarked} and compliant with the
+ * jSpecify specification. This means that everything is
+ * {@linkplain org.jspecify.annotations.NonNull non-null} by default unless otherwise specified.
+ * Attempting to pass a {@code null} value to a method param that isn't explicitly marked as
+ * {@link org.jspecify.annotations.Nullable @Nullable} is an unsupported operation and won't be considered a
+ * breaking change if a future version throws an exception in such cases when it didn't before.
+ */
@NullMarked
module net.minecraftforge.eventbus {
requires java.logging;
From e49096bbb1d1342cdbac0d729ee5909427a4b81d Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 18:29:07 -0400
Subject: [PATCH 05/11] JavaDocs - BusGroup
Expanded on the documentation for BusGroup, including a non-exhaustive
simple example for the class JavaDoc and expanded explanations for the
methods.
---
.../eventbus/api/bus/BusGroup.java | 142 +++++++++++++++---
1 file changed, 122 insertions(+), 20 deletions(-)
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java b/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
index 3bb9559..71388e3 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
@@ -4,89 +4,191 @@
*/
package net.minecraftforge.eventbus.api.bus;
-import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.api.listener.EventListener;
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
import net.minecraftforge.eventbus.internal.BusGroupImpl;
+import net.minecraftforge.eventbus.internal.Event;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
/**
- * A collection of {@link EventBus} instances that are grouped together for easier management.
+ * A bus group is a collection of {@link EventBus} instances that are grouped together for easier management.
+ * Using a bus group allows consumers to manage all of their related event buses without needing to manually manage
+ * each one.
+ *
+ * Example
+ * Here is a small example showing the creation and disposal of a bus group.
+ * {@snippet :
+ * import net.minecraftforge.eventbus.api.bus.BusGroup;
+ * import net.minecraftforge.eventbus.api.bus.EventBus;
+ * import net.minecraftforge.eventbus.api.event.RecordEvent;
+ * import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
+ *
+ * import java.lang.invoke.MethodHandles;
+ *
+ * public class MyClass {
+ * public static final BusGroup BUS_GROUP = BusGroup.create("MyProject", RecordEvent.class);
+ *
+ * public record MyEvent(String message) implements RecordEvent {
+ * public static final EventBus BUS = EventBus.create(BUS_GROUP, MyEvent.class);
+ * }
+ *
+ * @SubscribeEvent
+ * private static void onMyEvent(MyEvent event) {
+ * System.out.println("Received event: " + event.message());
+ * }
+ *
+ * // if we only have one listener in our class, EventBus will throw an exception saying you should use BusGroup#addListener instead
+ * @SubscribeEvent
+ * private static void alsoOnMyEvent(MyEvent event) {
+ * System.out.println("Double checking, received event: " + event.message());
+ * }
+ *
+ * // begin program!
+ * public static void run() {
+ * // the bus group is already started! no need to call startup() on it.
+ *
+ * // MethodHandles.lookup() gives EventBus the ability to get method references
+ * // for all the @SubscribeEvent methods in this class.
+ * BUS_GROUP.register(MethodHandles.lookup(), MyClass.class);
+ * }
+ *
+ * // close program!
+ * public static void shutdown() {
+ * // dispose will shutdown and then dispose this bus group
+ * // consider it "freed memory" that should not be reused
+ * BUS_GROUP.dispose();
+ * }
+ * }
+ *}
*/
public sealed interface BusGroup permits BusGroupImpl {
+ /**
+ * The default bus group, which is used when an {@linkplain EventBus event bus} is created without specifying a
+ * group.
+ *
+ * @apiNote If you require tight controls over your event buses, you should create your own bus group instead. This
+ * bus group can be used and mutated by other consumers within the same environment.
+ * @see EventBus#create(Class)
+ */
BusGroup DEFAULT = create("default");
+ /**
+ * Creates a new bus group with the given name.
+ * The name for this bus group must be unique. An attempt to create a bus group with a name that is
+ * already in use will result in an {@link IllegalArgumentException}. If you must create a new bus group with a name
+ * that is in use, the relevant bus group must be {@linkplain #dispose() disposed}.
+ *
+ * @param name The name
+ * @return The new bus group
+ * @throws IllegalArgumentException If the name is already in use by another bus group
+ * @apiNote To enforce a base type with your bus group, use {@linkplain #create(String, Class)}.
+ */
static BusGroup create(String name) {
return new BusGroupImpl(name, Event.class);
}
+ /**
+ * Creates a new bus group with the given name.
+ * The given base type will enforce that all {@linkplain EventBus event buses} created within this group inherit
+ * it.
+ * The name for this bus group must be unique. An attempt to create a bus group with a name that is
+ * already in use will result in an {@link IllegalArgumentException}. If you must create a new bus group with a name
+ * that is in use, the relevant bus group must be {@linkplain #dispose() disposed}.
+ *
+ * @param name The name
+ * @return The new bus group
+ * @throws IllegalArgumentException If the name is already in use by another bus group
+ */
static BusGroup create(String name, Class> baseType) {
return new BusGroupImpl(name, baseType);
}
/**
* The unique name of this BusGroup.
+ * The uniqueness of this name is enforced when the bus group is {@linkplain #create(String) created}.
*/
String name();
/**
- * Starts up all EventBus instances associated with this BusGroup, allowing events to be posted again after a
+ * Starts up all EventBus instances associated with this bus group, allowing events to be posted again after a
* previous call to {@link #shutdown()}.
+ * Calling this method without having previously called {@link #shutdown()} will have no effect.
*/
void startup();
/**
- * Shuts down all EventBus instances associated with this BusGroup, preventing any further events from being posted
+ * Shuts down all EventBus instances associated with this bus group, preventing any further events from being posted
* until {@link #startup()} is called.
+ * Calling this method without having previously called {@link #startup()} will have no effect.
+ *
+ * @apiNote If you need to destroy this bus group and free up the resources it uses, use {@link #dispose()}.
*/
void shutdown();
/**
- * Shuts down all EventBus instances associated with this BusGroup, unregisters all listeners and frees resources
- * no longer needed.
- * Warning: This is a destructive operation - this BusGroup should not be used again after calling this method.
+ * {@linkplain #shutdown() Shuts down} all EventBus instances associated with this bus group,
+ * {@linkplain #unregister(Collection) unregisters} all listeners and frees resources no longer needed.
+ * This will effectively destroy this bus group. It should not be used again after calling this
+ * method.
+ *
+ * @apiNote If you plan on using this bus group again, use {@link #shutdown()} instead.
*/
void dispose();
/**
- * Experimental feature - may be removed, renamed or otherwise changed without notice.
- * Trims the backing lists of all EventBus instances associated with this BusGroup to free up resources.
- * Warning: This is only intended to be called once after all listeners are registered - calling this
+ * Trims the backing lists of all EventBus instances associated with this BusGroup to free up resources.
+ *
This is only intended to be called once after all listeners are registered. Calling this
* repeatedly may hurt performance.
+ *
+ * @apiNote This is an experimental feature! It may be removed, renamed or otherwise changed
+ * without notice.
*/
void trim();
/**
- * Registers all static methods annotated with {@link SubscribeEvent} in the given class.
+ * Registers all static methods annotated with {@link SubscribeEvent} in the given class.
+ * This is done by getting method references for those methods using the given
+ * {@linkplain MethodHandles.Lookup method handles lookup}. This lookup must be acquiored from
+ * {@link MethodHandles#lookup()}. Using {@link MethodHandles#publicLookup()} is unsupported because it
+ * doesn't work with {@link java.lang.invoke.LambdaMetafactory} as it could allow for access to private fields
+ * through inner class generation.
*
- * @param callerLookup {@code MethodHandles.lookup()} from the class containing listeners
+ * @param callerLookup {@link MethodHandles#lookup()} from the class containing listeners
* @param utilityClassWithStaticListeners the class containing the static listeners
* @return A collection of the registered listeners, which can be used to optionally unregister them later
- *
* @apiNote This method only registers static listeners.
- * If you want to register both instance and static methods, use
- * {@link BusGroup#register(MethodHandles.Lookup, Object)} instead.
+ * If you want to register both instance and static methods, use
+ * {@link BusGroup#register(MethodHandles.Lookup, Object)} instead.
*/
Collection register(MethodHandles.Lookup callerLookup, Class> utilityClassWithStaticListeners);
/**
* Registers all methods annotated with {@link SubscribeEvent} in the given object.
+ * Both the static and instance methods for the given object are registered. Keep in mind that, unlike
+ * with {@link #register(MethodHandles.Lookup, Class)}, you will need to register each object instance of the class
+ * using this method.
+ * This is done by getting method references for those methods using the given
+ * {@linkplain MethodHandles.Lookup method handles lookup}. This lookup must be acquiored from
+ * {@link MethodHandles#lookup()}. Using {@link MethodHandles#publicLookup()} is unsupported because it
+ * doesn't work with {@link java.lang.invoke.LambdaMetafactory} as it could allow for access to private fields
+ * through inner class generation.
*
* @param callerLookup {@code MethodHandles.lookup()} from the class containing the listeners
- * @param listener the object containing the static and/or instance listeners
+ * @param listener the object containing the static and/or instance listeners
* @return A collection of the registered listeners, which can be used to optionally unregister them later
- *
* @apiNote If you know all the listeners are static methods, use
- * {@link BusGroup#register(MethodHandles.Lookup, Class)} instead for better registration performance.
+ * {@link BusGroup#register(MethodHandles.Lookup, Class)} instead for better registration performance.
*/
Collection register(MethodHandles.Lookup callerLookup, Object listener);
/**
- * Unregisters the given listeners from this BusGroup.
+ * Unregisters the given listeners from this bus group.
+ *
* @param listeners A collection of listeners to unregister, obtained from
- * {@link #register(MethodHandles.Lookup, Class)} or {@link #register(MethodHandles.Lookup, Object)}
+ * {@link #register(MethodHandles.Lookup, Class)} or
+ * {@link #register(MethodHandles.Lookup, Object)}
*/
void unregister(Collection listeners);
}
From 58bebab95dfb67c7f34a4986ca73af3831179523 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 19:50:08 -0400
Subject: [PATCH 06/11] JavaDocs - EventBus
---
.../eventbus/api/bus/EventBus.java | 128 ++++++++++++++----
1 file changed, 100 insertions(+), 28 deletions(-)
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
index 8daaf0b..41e9bde 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
@@ -4,75 +4,143 @@
*/
package net.minecraftforge.eventbus.api.bus;
-import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
import net.minecraftforge.eventbus.api.listener.EventListener;
import net.minecraftforge.eventbus.api.listener.Priority;
import net.minecraftforge.eventbus.internal.AbstractEventBusImpl;
import net.minecraftforge.eventbus.internal.BusGroupImpl;
+import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.internal.EventBusImpl;
import java.util.function.Consumer;
+/**
+ * An event bus is a host of listeners for a specific event.
+ * It can be thought of much like an actual bus. A bus has passengers that are all headed for the same destination,
+ * or at least are on the same route.
+ *
+ * Usage
+ * The key idea to understand about an event bus is that is designed to be used with a specific event tied to a
+ * specific {@linkplain BusGroup bus group}. While not all listeners on the event bus may behave the same, they are all
+ * listening for the same event.
+ * Listeners can have different characteristics based on how it is registered (this may be subject to the event's
+ * {@linkplain net.minecraftforge.eventbus.api.event.characteristic characteristics}). They are registered through
+ * {@link #addListener(Consumer)} or one of its sister methods. Each registering method contains additional details on
+ * how the registered listener behaves.
+ *
+ * Example
+ * Here is a small example showing the simple registration of two event listeners with different priorities.
+ * {@snippet :
+ * import net.minecraftforge.eventbus.api.bus.EventBus;
+ * import net.minecraftforge.eventbus.api.event.MutableEvent;
+ * import net.minecraftforge.eventbus.api.listener.Priority;
+ *
+ * public class MyCustomEvent extends MutableEvent {
+ * protected static final String HELLO = "Hello, world!";
+ *
+ * public static final EventBus BUS = EventBus.create(MyCustomEvent.class);
+ *
+ * private static void onMyCustomEvent(MyCustomEvent event) {
+ * System.out.println("Received custom event: " + event);
+ * }
+ *
+ * private static void alsoOnMyCustomEvent(MyCustomEvent event) {
+ * System.out.println("Received custom event (but later!): " + event);
+ * }
+ *
+ * public static void run() {
+ * BUS.addListener(MyCustomEvent::onMyCustomEvent);
+ * BUS.addListener(Priority.LOW, MyCustomEvent::alsoOnMyCustomEvent);
+ * }
+ * }
+ *}
+ *
+ * Cancellability
+ * Events have the ability to be {@linkplain Cancellable cancellable}. If you need to use a cancellable event, use
+ * {@link CancellableEventBus} instead, which include special handling for posting cancellable events and recieving the
+ * cancellation state. As discussed in the documentation for the cancellable characteristic, the cancellation state is
+ * not attached to the event instance.
+ *
+ * @param The event type this bus is for
+ */
public sealed interface EventBus permits CancellableEventBus, AbstractEventBusImpl, EventBusImpl {
/**
* Adds a listener to this EventBus with the default priority of {@link Priority#NORMAL}.
+ *
* @param listener The listener to add
* @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
*/
EventListener addListener(Consumer listener);
/**
- * Adds a listener to this EventBus with the given priority.
- * @param priority The priority of this listener. Higher numbers are called first.
+ * Adds a listener to this EventBus with the given {@linkplain Priority priority}.
+ *
+ * @param priority The priority of this listener
* @param listener The listener to add
* @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
- * @see Priority For common priority values
+ * @see Priority
*/
EventListener addListener(byte priority, Consumer listener);
/**
* Re-adds a listener to this EventBus that was previously removed with {@link #removeListener(EventListener)}.
- * @param listener The exact same reference returned by an {@code addListener} method
- * @return The same reference that was passed in
+ *
+ * @param listener The event listener returned immediately after it was initially added
+ * @return The listener that was re-added
+ * @apiNote Using this over re-adding the listener with {@link #addListener(Consumer)} is recommended for
+ * performance, as it removes the need to create another {@link EventListener} state.
*/
EventListener addListener(EventListener listener);
/**
- * Removes a listener from this EventBus that was previously added with one of the {@code addListener} methods.
- * @param listener The exact same reference returned by an {@code addListener} method
+ * Removes a listener from this EventBus that was previously added with {@link #addListener(Consumer)} or one of its
+ * sisters.
+ *
+ * @param listener The event listener returned immediately after it was initially added
*/
void removeListener(EventListener listener);
/**
+ * Posts the given event to all listeners registered to this bus.
+ *
* @param event The instance of this event to post to listeners
- * @return {@code true} if the event implements {@link Cancellable} and the event was cancelled
- * by a listener
+ * @return {@code false}
+ * @apiNote This bus will always return {@code false} unless it is a
+ * {@linkplain CancellableEventBus cancellable event bus}.
+ * @see CancellableEventBus#post(Event)
*/
boolean post(T event);
/**
+ * Fires the given event to all listeners registered to this bus.
+ * After posting, the event itself is returned from this method. It may be mutated.
+ *
* @param event The instance of this event to fire to listeners
- * @return The possibly mutated event instance after all applicable listeners have been called
+ * @return The event after being posted
*/
T fire(T event);
/**
- * If making a new event instance is expensive, you can check against this method to avoid creating a new instance
- * unnecessarily.
- * @apiNote You only need to check this if event creation is expensive. If it's cheap, just call {@link #post(Event)}
- * or {@link #fire(Event)} directly and let the JIT handle it.
- * @return {@code true} if there are any listeners registered to this EventBus.
+ * Checks if this event bus has any listeners registered to it.
+ * If making a new event instance is expensive, you can check against this method to avoid creating a new
+ * instance unnecessarily.
+ *
+ * @return {@code true} if there are any listeners registered
+ * @apiNote If event creation is cheap, you should instead call {@link #post(Event)} or {@link #fire(Event)}
+ * directly and let the JIT handle the side effects.
*/
boolean hasListeners();
/**
- * Creates a new EventBus for the given event type on the default {@link BusGroup}.
- *
- * Important: The returned EventBus MUST be stored in a {@code static final} field - failing to do so
- * will severely hurt performance
- *
- * @apiNote There can only be one EventBus instance per event type per BusGroup.
+ * Creates a new EventBus for the given event type on the {@linkplain BusGroup#DEFAULT default bus group}.
+ * The returned EventBus must be stored in a {@code static final} field! Failing to do so will
+ * severely hinder performance.
+ * Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
+ * exists for the given type, it will be returned instead.
+ *
+ * @param eventType The event type for the bus
+ * @param The type of event this bus is for
+ * @return The newly-created event bus
*/
@SuppressWarnings("ClassEscapesDefinedScope") // E can be a subtype of Event which is publicly accessible
static EventBus create(Class eventType) {
@@ -80,12 +148,16 @@ static EventBus create(Class eventType) {
}
/**
- * Creates a new EventBus for the given event type on the given {@link BusGroup}.
- *
- * Important: The returned EventBus MUST be stored in a {@code static final} field - failing to do so
- * will severely hurt performance
- *
- * @apiNote There can only be one EventBus instance per event type per BusGroup.
+ * Creates a new event bus for the given event type on the given {@linkplain BusGroup bus group}.
+ * The returned event bus must be stored in a {@code static final} field! Failing to do so will
+ * severely hinder performance.
+ * Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
+ * exists for the given type, it will be returned instead.
+ *
+ * @param busGroup The bus group to create the event bus on
+ * @param eventType The event type for the bus
+ * @param The type of event this bus is for
+ * @return The newly-created event bus
*/
@SuppressWarnings("ClassEscapesDefinedScope") // E can be a subtype of Event which is publicly accessible
static EventBus create(BusGroup busGroup, Class eventType) {
From 674d6c75000b51e89a01ce68782ef471d2f47929 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 20:01:28 -0400
Subject: [PATCH 07/11] Add JetBrains annotations (not for nulls)
JetBrains annotations include a handful of useful documentation
annotations that can improve readability in the source files (ApiStatus)
and IDE support (Contract).
---
build.gradle | 1 +
settings.gradle | 1 +
src/main/java/module-info.java | 5 +++--
.../minecraftforge/eventbus/api/bus/BusGroup.java | 4 ++++
.../eventbus/api/bus/CancellableEventBus.java | 5 +++++
.../minecraftforge/eventbus/api/bus/EventBus.java | 4 ++++
.../api/event/characteristic/MonitorAware.java | 13 ++++++++++---
.../api/event/characteristic/SelfPosting.java | 15 +++++++++++----
.../eventbus/api/listener/EventListener.java | 4 ++++
.../eventbus/internal/MutableEventInternals.java | 4 +---
10 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/build.gradle b/build.gradle
index 790c840..99c29ce 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,6 +33,7 @@ repositories {
dependencies {
api libs.jspecify.annotations
+ compileOnly libs.jetbrains.annotations
errorprone libs.errorprone.core
errorprone libs.nullaway
}
diff --git a/settings.gradle b/settings.gradle
index b93b65a..3da23fa 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -16,6 +16,7 @@ dependencyResolutionManagement {
// https://mvnrepository.com/artifact/org.jspecify/jspecify
library('jspecify-annotations', 'org.jspecify', 'jspecify') version '1.0.0'
+ library 'jetbrains-annotations', 'org.jetbrains', 'annotations' version '26.0.2'
// https://mvnrepository.com/artifact/com.google.errorprone/error_prone_core
library('errorprone-core', 'com.google.errorprone', 'error_prone_core') version '2.36.0'
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 88da016..e9c79ad 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -48,8 +48,9 @@
*/
@NullMarked
module net.minecraftforge.eventbus {
- requires java.logging;
- requires org.jspecify;
+ requires java.logging; // Logging
+ requires org.jspecify; // Nullability
+ requires static org.jetbrains.annotations; // Other Static Analysis
exports net.minecraftforge.eventbus.api.bus;
exports net.minecraftforge.eventbus.api.event;
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java b/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
index 71388e3..0078baf 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/BusGroup.java
@@ -8,6 +8,8 @@
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
import net.minecraftforge.eventbus.internal.BusGroupImpl;
import net.minecraftforge.eventbus.internal.Event;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
@@ -109,6 +111,7 @@ static BusGroup create(String name, Class> baseType) {
* The unique name of this BusGroup.
* The uniqueness of this name is enforced when the bus group is {@linkplain #create(String) created}.
*/
+ @Contract(pure = true)
String name();
/**
@@ -145,6 +148,7 @@ static BusGroup create(String name, Class> baseType) {
* @apiNote This is an experimental feature! It may be removed, renamed or otherwise changed
* without notice.
*/
+ @ApiStatus.Experimental
void trim();
/**
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
index c788250..ad2e768 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
@@ -11,6 +11,7 @@
import net.minecraftforge.eventbus.api.listener.Priority;
import net.minecraftforge.eventbus.internal.BusGroupImpl;
import net.minecraftforge.eventbus.internal.CancellableEventBusImpl;
+import org.jetbrains.annotations.Contract;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -67,6 +68,10 @@ default EventListener addListener(boolean alwaysCancelling, Consumer listener
*/
EventListener addListener(ObjBooleanBiConsumer listener);
+ @Override
+ @Contract("_ -> _")
+ boolean post(T event);
+
/**
* Creates a new CancellableEventBus for the given event type on the default {@link BusGroup}.
*
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
index 41e9bde..96ef5ef 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
@@ -11,6 +11,7 @@
import net.minecraftforge.eventbus.internal.BusGroupImpl;
import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.internal.EventBusImpl;
+import org.jetbrains.annotations.Contract;
import java.util.function.Consumer;
@@ -90,6 +91,7 @@ public sealed interface EventBus permits CancellableEventBus, A
* @apiNote Using this over re-adding the listener with {@link #addListener(Consumer)} is recommended for
* performance, as it removes the need to create another {@link EventListener} state.
*/
+ @Contract("_ -> param1")
EventListener addListener(EventListener listener);
/**
@@ -109,6 +111,7 @@ public sealed interface EventBus permits CancellableEventBus, A
* {@linkplain CancellableEventBus cancellable event bus}.
* @see CancellableEventBus#post(Event)
*/
+ @Contract(value = "_ -> false")
boolean post(T event);
/**
@@ -118,6 +121,7 @@ public sealed interface EventBus permits CancellableEventBus, A
* @param event The instance of this event to fire to listeners
* @return The event after being posted
*/
+ @Contract(value = "_ -> param1")
T fire(T event);
/**
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
index 4364ab2..83db404 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
@@ -6,14 +6,21 @@
import net.minecraftforge.eventbus.api.event.MutableEvent;
import net.minecraftforge.eventbus.internal.MutableEventInternals;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
/**
- * Experimental feature - may be removed, renamed or otherwise changed without notice.
- * Events that are {@link MonitorAware} can provide stronger immutability guarantees to monitor listeners by
- * returning unmodifiable views or throwing exceptions on mutation attempts when monitoring.
+ * Events that are {@link MonitorAware} can provide stronger immutability guarantees to monitor listeners by returning
+ * unmodifiable views or throwing exceptions on mutation attempts when monitoring.
* Only supported for {@link MutableEvent} at this time.
+ *
+ * @apiNote This is an experimental feature! It may be removed, renamed or otherwise changed without
+ * notice.
*/
+@ApiStatus.Experimental
public non-sealed interface MonitorAware extends EventCharacteristic {
+ @Contract(pure = true)
+ @ApiStatus.NonExtendable
default boolean isMonitoring() {
assert this instanceof MutableEvent; // note: MutableEvent extends MutableEventInternals
return ((MutableEventInternals) this).isMonitoring;
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
index 8774fed..03140d3 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
@@ -6,12 +6,14 @@
import net.minecraftforge.eventbus.api.bus.EventBus;
import net.minecraftforge.eventbus.internal.Event;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
/**
- * Experimental feature - may be removed, renamed or otherwise changed without notice.
- * {@link SelfPosting} events are associated with a default {@link EventBus} in order to offer some convenience
- * instance methods.
- * Example
+ * Self-posting events are associated with a default {@link EventBus} in order to offer some convenience instance
+ * methods.
+ *
+ * Example
* {@snippet :
* import net.minecraftforge.eventbus.api.event.RecordEvent;
*
@@ -31,7 +33,11 @@
* // instead of this
* ExampleEvent.BUS.post(new ExampleEvent());
*}
+ *
+ * @apiNote This is an experimental feature! It may be removed, renamed or otherwise changed without
+ * notice.
*/
+@ApiStatus.Experimental
public non-sealed interface SelfPosting extends EventCharacteristic {
/**
* @implSpec This should directly return a {@code static final} field without additional logic or processing.
@@ -49,6 +55,7 @@ default boolean post() {
/**
* @see EventBus#fire(Event)
*/
+ @Contract(value = "-> this")
@SuppressWarnings("unchecked")
default T fire() {
return getDefaultBus().fire((T) this);
diff --git a/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java b/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
index 018f47c..e7039a3 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
@@ -8,6 +8,7 @@
import net.minecraftforge.eventbus.api.bus.EventBus;
import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.internal.EventListenerImpl;
+import org.jetbrains.annotations.Contract;
import java.util.function.Consumer;
@@ -19,17 +20,20 @@
* various conversion operations to different lambda types.
*/
public sealed interface EventListener permits EventListenerImpl {
+ @Contract(pure = true)
@SuppressWarnings("ClassEscapesDefinedScope") // ? can be a subtype of Event which is publicly accessible
Class extends Event> eventType();
/**
* @see Priority
*/
+ @Contract(pure = true)
byte priority();
/**
* @see CancellableEventBus#addListener(boolean, Consumer)
*/
+ @Contract(pure = true)
default boolean alwaysCancelling() {
return false;
}
diff --git a/src/main/java/net/minecraftforge/eventbus/internal/MutableEventInternals.java b/src/main/java/net/minecraftforge/eventbus/internal/MutableEventInternals.java
index 2b2e0fc..88c0cd1 100644
--- a/src/main/java/net/minecraftforge/eventbus/internal/MutableEventInternals.java
+++ b/src/main/java/net/minecraftforge/eventbus/internal/MutableEventInternals.java
@@ -8,8 +8,6 @@
import net.minecraftforge.eventbus.api.event.characteristic.MonitorAware;
public sealed abstract class MutableEventInternals permits MutableEvent {
- /**
- * @see MonitorAware
- */
+ /** @see MonitorAware#isMonitoring() */
public transient boolean isMonitoring;
}
From abbb0a040d899a15a685cc40e2c369c8318b3e31 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 22:13:04 -0400
Subject: [PATCH 08/11] JavaDocs - CancellableEventBus
---
README.md | 2 +-
.../eventbus/api/bus/CancellableEventBus.java | 193 ++++++++++++++----
.../eventbus/api/bus/EventBus.java | 14 +-
3 files changed, 157 insertions(+), 52 deletions(-)
diff --git a/README.md b/README.md
index 6a60c68..53c0295 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
+ `src/main/java/module-info.java` -->
# EventBus
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
index ad2e768..1b8006d 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
@@ -4,97 +4,200 @@
*/
package net.minecraftforge.eventbus.api.bus;
-import net.minecraftforge.eventbus.internal.Event;
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
import net.minecraftforge.eventbus.api.listener.EventListener;
import net.minecraftforge.eventbus.api.listener.ObjBooleanBiConsumer;
import net.minecraftforge.eventbus.api.listener.Priority;
import net.minecraftforge.eventbus.internal.BusGroupImpl;
import net.minecraftforge.eventbus.internal.CancellableEventBusImpl;
-import org.jetbrains.annotations.Contract;
+import net.minecraftforge.eventbus.internal.Event;
import java.util.function.Consumer;
import java.util.function.Predicate;
+/**
+ * Events can have characteristics, and one such is the ability to be {@linkplain Cancellable cancelled}. The state of
+ * an event's cancellation, however, is not attached to the event itself, but rather returned as a result of
+ * {@linkplain #post(Event) posting} the event. The cancellable event bus is a specialized type of event bus that is
+ * designed to handle this cancellable nature of events without explicitly requiring events themselves to store their
+ * cancellation state.
+ * For more details on the event bus in general, see {@link EventBus}.
+ *
+ * Usage
+ * Event listeners for cancellable events can have a few additional properties that set them apart from normal
+ * listeners. These are the ability to cancel the event or to
+ * {@linkplain EventListener#alwaysCancelling() always cancel} the event. The specialized
+ * {@link #addListener(Predicate)} method, along with its sisters in this class, are designed to give event listeners
+ * that characteristic.
+ *
+ * Example
+ * Here is a small example that showcases the different listeners this type of event bus can have.
+ * {@snippet :
+ * import net.minecraftforge.eventbus.api.bus.CancellableEventBus;
+ * import net.minecraftforge.eventbus.api.event.RecordEvent;
+ * import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
+ * import net.minecraftforge.eventbus.api.listener.Priority;
+ *
+ * import java.util.Random;
+ *
+ * public record MyCustomCancellableEvent() implements RecordEvent, Cancellable {
+ * // you MUST use the #create method in CancellableEventBus, or you won't be able to see the cancellation state from #post!
+ * public static final CancellableEventBus BUS = CancellableEventBus.create(MyCustomCancellableEvent.class);
+ *
+ * // a listener that does not cancel, only listens quietly
+ * private static void onMyCustomEvent(MyCustomCancellableEvent event) {
+ * System.out.println("Received custom event: " + event);
+ * }
+ *
+ * // a listener that might cancel the event
+ * private static boolean alsoOnMyCustomEvent(MyCustomCancellableEvent event) {
+ * System.out.println("Received custom event (but later!): " + event);
+ * return new Random().nextBoolean();
+ * }
+ *
+ * // a listener that always cancels (registered with CancellableEventBus#addListener(true, this::method))
+ * private static void alwaysCancelMyEvent(MyCustomCancellableEvent event) {
+ * System.err.println("We are cancelling the event!!!");
+ * }
+ *
+ * private static void cannotRecieveEvent(MyCustomCancellableEvent event) {
+ * throw new IllegalStateException("Event wasn't cancelled??? " + event);
+ * }
+ *
+ * // a monitoring listener that will always receive the event, even if it is cancelled
+ * private static void monitoringCancelledEvent(MyCustomCancellableEvent event, boolean cancelled) {
+ * System.out.println("Monitoring custom event: " + event + ", cancelled: " + cancelled);
+ * }
+ *
+ * public static void run() {
+ * BUS.addListener(MyCustomCancellableEvent::onMyCustomEvent);
+ * BUS.addListener(Priority.LOW, MyCustomCancellableEvent::alsoOnMyCustomEvent); // might cancel
+ * BUS.addListener(Priority.LOWEST + 1, true, MyCustomCancellableEvent::alwaysCancelMyEvent); // will always cancel
+ * BUS.addListener(MyCustomCancellableEvent::cannotRecieveEvent); // will never be called
+ * BUS.addListener(MyCustomCancellableEvent::monitoringCancelledEvent); // monitors events, even cancelled ones
+ * }
+ * }
+ *}
+ *
+ * @param The type of cancellable event for this bus
+ */
public sealed interface CancellableEventBus
- extends EventBus permits CancellableEventBusImpl {
+ extends EventBus permits CancellableEventBusImpl {
/**
- * Adds an always cancelling listener to this EventBus with the default priority of {@link Priority#NORMAL}.
- * @param alwaysCancelling If true, always cancel the event after calling the listener. This acts as if you
- * added a Predicate listener that always returns true, but with additional optimisations.
- * If false, you should use {@link #addListener(Consumer)} instead to avoid unnecessary
- * breaking changes if the event is no longer cancellable in the future.
- * @param listener The listener to add.
- * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}.
- * @see #addListener(Predicate) For adding a listener that can cancel the event conditionally
- * @see #addListener(Consumer) For adding a listener that never cancels the event
+ * Adds a listener to this EventBus with the default priority of {@link Priority#NORMAL}.
+ * This listener, based on the given boolean, may always cancel the event when it is invoked. If it is
+ * {@code true}, this method acts as if you {@linkplain #addListener(Predicate) added a predicate listener} that
+ * always returns {@code true}, but with additional optimisations.
+ * If you plan on passing in {@code false} instead, you should instead consider using
+ * {@link #addListener(Consumer)}.
+ *
+ * @param alwaysCancelling Whether to always cancel the event after calling the listener
+ * @param listener The listener to add
+ * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
*/
default EventListener addListener(boolean alwaysCancelling, Consumer listener) {
return addListener(Priority.NORMAL, alwaysCancelling, listener);
}
/**
- * Adds an always cancelling listener to this EventBus with the specified priority.
- * @param alwaysCancelling If true, always cancel the event after calling the listener. This acts as if you
- * added a Predicate listener that always returns true, but with additional optimisations.
- * If false, you should use {@link #addListener(byte, Consumer)} instead to avoid
- * unnecessary breaking changes if the event is no longer cancellable in the future
- * @param listener The listener to add.
- * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}.
- * @see Priority For common priority values
+ * Adds a listener to this EventBus with the given {@linkplain Priority priority}.
+ * This listener, based on the given boolean, may always cancel the event when it is invoked. If it is
+ * {@code true}, this method acts as if you {@linkplain #addListener(Predicate) added a predicate listener} that
+ * always returns {@code true}, but with additional optimisations.
+ * If you plan on passing in {@code false} instead, you should instead consider using
+ * {@link #addListener(Consumer)}.
+ *
+ * @param priority The priority for the listener
+ * @param alwaysCancelling Whether to always cancel the event after calling the listener
+ * @param listener The listener to add
+ * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
*/
EventListener addListener(byte priority, boolean alwaysCancelling, Consumer listener);
/**
- * Adds a possibly cancelling listener to this EventBus with the default priority of {@link Priority#NORMAL}.
- * @param listener The listener to add.
- * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}.
+ * Adds a listener to this EventBus with the default priority of {@link Priority#NORMAL}.
+ * The predicate listener can return {@code true} to cancel the event.
+ *
+ * @param listener The listener to add
+ * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
*/
EventListener addListener(Predicate listener);
/**
- * Adds a possibly cancelling listener to this EventBus with the specified priority.
- * @param priority The priority of this listener. Higher numbers are called first.
- * @param listener The listener to add.
- * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}.
+ * Adds a listener to this EventBus with the given {@linkplain Priority priority}.
+ * The predicate listener can return {@code true} to cancel the event.
+ *
+ * @param priority The priority for the listener
+ * @param listener The listener to add
+ * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
* @see Priority For common priority values
*/
EventListener addListener(byte priority, Predicate listener);
/**
* Adds a cancellation-aware monitoring listener to this EventBus.
- * @param listener The listener to add.
- * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}.
+ * This listener will always run at a priority of {@link Priority#MONITOR} and will always receive cancelled
+ * events.
+ *
+ * @param listener The listener to add
+ * @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
+ * @see Priority#MONITOR
*/
EventListener addListener(ObjBooleanBiConsumer listener);
+ /**
+ * Posts the given event to all listeners registered to this bus.
+ * Unlike {@link EventBus#post(Event)}, the cancellation state of the posted event is returned by this
+ * method.
+ *
+ * @param event The instance of this event to post to listeners
+ * @return {@code true} if the event was cancelled
+ */
@Override
- @Contract("_ -> _")
boolean post(T event);
/**
- * Creates a new CancellableEventBus for the given event type on the default {@link BusGroup}.
- *
- * Important: The returned EventBus MUST be stored in a {@code static final} field - failing to do so
- * will severely hurt performance
- *
- * @apiNote There can only be one EventBus instance per event type per BusGroup.
+ * {@inheritDoc}
+ *
+ * @deprecated Using this method with a cancellable event bus is not recommended, as it does not capture the event's
+ * cancellation state. Use {@link #post(Event)} instead.
+ */
+ @Deprecated
+ @Override
+ T fire(T event);
+
+ /**
+ * Creates a new cancellable event bus for the given {@linkplain net.minecraftforge.eventbus.api.event event} type
+ * on the {@linkplain BusGroup#DEFAULT default bus group}.
+ * The returned EventBus must be stored in a {@code static final} field! Failing to do so will
+ * severely hinder performance.
+ * Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
+ * exists for the given type, it will be returned instead.
+ *
+ * @param eventType The cancellable event type for the bus
+ * @param The type of cancellable event this bus is for
+ * @return The newly-created event bus
*/
@SuppressWarnings("ClassEscapesDefinedScope") // E can be a subtype of Event which is publicly accessible
- static CancellableEventBus create(Class eventType) {
+ static CancellableEventBus create(Class eventType) {
return create(BusGroup.DEFAULT, eventType);
}
/**
- * Creates a new CancellableEventBus for the given event type on the given {@link BusGroup}.
- *
- * Important: The returned EventBus MUST be stored in a {@code static final} field - failing to do so
- * will severely hurt performance
- *
- * @apiNote There can only be one EventBus instance per event type per BusGroup.
+ * Creates a new cancellable event bus for the given {@linkplain Cancellable cancellable}
+ * {@linkplain net.minecraftforge.eventbus.api.event event} type on the given {@linkplain BusGroup bus group}.
+ * The returned EventBus must be stored in a {@code static final} field! Failing to do so will
+ * severely hinder performance.
+ * Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
+ * exists for the given type, it will be returned instead.
+ *
+ * @param busGroup The bus group to create the event bus on
+ * @param eventType The cancellable event type for the bus
+ * @param The type of cancellable event this bus is for
+ * @return The newly-created event bus
*/
@SuppressWarnings("ClassEscapesDefinedScope") // E can be a subtype of Event which is publicly accessible
- static CancellableEventBus create(BusGroup busGroup, Class eventType) {
- return (CancellableEventBus) ((BusGroupImpl) busGroup).getOrCreateEventBus(eventType);
+ static CancellableEventBus create(BusGroup busGroup, Class eventType) {
+ return (CancellableEventBus) ((BusGroupImpl) busGroup).getOrCreateEventBus(eventType);
}
}
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
index 96ef5ef..9ef2c3f 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/EventBus.java
@@ -62,7 +62,7 @@
* cancellation state. As discussed in the documentation for the cancellable characteristic, the cancellation state is
* not attached to the event instance.
*
- * @param The event type this bus is for
+ * @param The type of event for this bus
*/
public sealed interface EventBus permits CancellableEventBus, AbstractEventBusImpl, EventBusImpl {
/**
@@ -76,7 +76,7 @@ public sealed interface EventBus permits CancellableEventBus, A
/**
* Adds a listener to this EventBus with the given {@linkplain Priority priority}.
*
- * @param priority The priority of this listener
+ * @param priority The priority for the listener
* @param listener The listener to add
* @return A reference that can be used to remove this listener later with {@link #removeListener(EventListener)}
* @see Priority
@@ -106,12 +106,12 @@ public sealed interface EventBus permits CancellableEventBus, A
* Posts the given event to all listeners registered to this bus.
*
* @param event The instance of this event to post to listeners
- * @return {@code false}
+ * @return {@code true} if the event was cancelled and this event bus is a
+ * {@linkplain CancellableEventBus cancellable event bus}
* @apiNote This bus will always return {@code false} unless it is a
* {@linkplain CancellableEventBus cancellable event bus}.
* @see CancellableEventBus#post(Event)
*/
- @Contract(value = "_ -> false")
boolean post(T event);
/**
@@ -136,7 +136,8 @@ public sealed interface EventBus permits CancellableEventBus, A
boolean hasListeners();
/**
- * Creates a new EventBus for the given event type on the {@linkplain BusGroup#DEFAULT default bus group}.
+ * Creates a new event bus for the given {@linkplain net.minecraftforge.eventbus.api.event event} type on the
+ * {@linkplain BusGroup#DEFAULT default bus group}.
* The returned EventBus must be stored in a {@code static final} field! Failing to do so will
* severely hinder performance.
* Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
@@ -152,7 +153,8 @@ static EventBus create(Class eventType) {
}
/**
- * Creates a new event bus for the given event type on the given {@linkplain BusGroup bus group}.
+ * Creates a new event bus for the given {@linkplain net.minecraftforge.eventbus.api.event event} type on the given
+ * {@linkplain BusGroup bus group}.
* The returned event bus must be stored in a {@code static final} field! Failing to do so will
* severely hinder performance.
* Additionally, there can only be one event bus instance per event type per bus group. If an event bus already
From 09ca1c986e978903ecb893135f9ce9dde968b8f7 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 22:47:09 -0400
Subject: [PATCH 09/11] JavaDocs - EventCharacteristic package
---
.../eventbus/api/bus/CancellableEventBus.java | 6 ++++++
.../eventbus/api/bus/package-info.java | 5 +++++
.../api/event/characteristic/Cancellable.java | 11 ++++++++--
.../event/characteristic/MonitorAware.java | 20 +++++++++++++++---
.../event/characteristic/SelfDestructing.java | 9 +++++---
.../api/event/characteristic/SelfPosting.java | 21 ++++++++++++++++---
.../event/characteristic/package-info.java | 5 +++++
7 files changed, 66 insertions(+), 11 deletions(-)
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
index 1b8006d..a55cd39 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/CancellableEventBus.java
@@ -29,6 +29,12 @@
* {@linkplain EventListener#alwaysCancelling() always cancel} the event. The specialized
* {@link #addListener(Predicate)} method, along with its sisters in this class, are designed to give event listeners
* that characteristic.
+ * When a listener is registered, it is not invoked if the event has been cancelled. The only exception to this is
+ * for {@linkplain Priority#MONITOR monitoring} listeners, which are always invoked at the end of the event posting even
+ * if the event was cancelled. Monitoring listeners can be added using either the {@link Priority#MONITOR} priority or
+ * by registering an {@link ObjBooleanBiConsumer} in {@link #addListener(ObjBooleanBiConsumer)}. Monitoring
+ * listeners are forbidden from mutating events. This contract can be strengthened by the event author using
+ * the {@link net.minecraftforge.eventbus.api.event.characteristic.MonitorAware} characteristic.
*
* Example
* Here is a small example that showcases the different listeners this type of event bus can have.
diff --git a/src/main/java/net/minecraftforge/eventbus/api/bus/package-info.java b/src/main/java/net/minecraftforge/eventbus/api/bus/package-info.java
index 1eb8184..ad6166b 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/bus/package-info.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/bus/package-info.java
@@ -2,6 +2,11 @@
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
+/**
+ * EventBus is built around the idea of {@linkplain net.minecraftforge.eventbus.api.bus.EventBus event buses} that are
+ * grouped together by {@linkplain net.minecraftforge.eventbus.api.bus.BusGroup bus groups}. This package contains this
+ * core part of the API.
+ */
@NullMarked
package net.minecraftforge.eventbus.api.bus;
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/Cancellable.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/Cancellable.java
index 4c97feb..d64f97d 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/Cancellable.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/Cancellable.java
@@ -8,7 +8,14 @@
import net.minecraftforge.eventbus.internal.Event;
/**
- * A cancellable event returns {@code true} from {@link CancellableEventBus#post(Event)} if it was cancelled.
- * When an event is cancelled, it will not be passed to any further non-monitor listeners.
+ * The ability to cancel an event is one of the core functionalities of EventBus. This functionality is carried in to
+ * this iteration of the EventBus API with the introduction of the cancellable
+ * {@linkplain net.minecraftforge.eventbus.api.event.characteristic characteristic}.
+ * A cancellable event returns {@code true} from {@link CancellableEventBus#post(Event)} if it was cancelled. When
+ * an event is cancelled, it will not be passed to any further
+ * non-{@linkplain net.minecraftforge.eventbus.api.listener.Priority#MONITOR monitor} listeners. For further details on
+ * a cancellable event's interactions with an event bus, see {@link CancellableEventBus}.
+ *
+ * @see CancellableEventBus
*/
public non-sealed interface Cancellable extends EventCharacteristic {}
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
index 83db404..b43eecb 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/MonitorAware.java
@@ -10,15 +10,29 @@
import org.jetbrains.annotations.Contract;
/**
- * Events that are {@link MonitorAware} can provide stronger immutability guarantees to monitor listeners by returning
- * unmodifiable views or throwing exceptions on mutation attempts when monitoring.
- * Only supported for {@link MutableEvent} at this time.
+ * Events that are {@linkplain net.minecraftforge.eventbus.api.listener.Priority#MONITOR monitor}-aware are able to
+ * provide stronger immutability guarantees to monitor listeners by returning unmodifiable views and ignoring (or
+ * throwing exceptions) on mutation attempts when monitoring.
*
+ * @implNote This characteristic is only supported for {@link MutableEvent} at this time. If used on any other type that
+ * does not extend it, an {@link IllegalArgumentException} will be thrown when the
+ * {@linkplain net.minecraftforge.eventbus.api.bus.EventBus event bus} is created for it.
* @apiNote This is an experimental feature! It may be removed, renamed or otherwise changed without
* notice.
*/
@ApiStatus.Experimental
public non-sealed interface MonitorAware extends EventCharacteristic {
+ /**
+ * Checks if this event is currently being
+ * {@linkplain net.minecraftforge.eventbus.api.listener.Priority#MONITOR monitored}. This can be used to provide
+ * stronger immutability guarantees as described by the documentation of {@link MonitorAware}.
+ *
+ * @return
+ * @implSpec This method
+ * {@linkplain org.jetbrains.annotations.ApiStatus.NonExtendable must not be overridden}! It uses
+ * internals of {@link MutableEvent} in order to determine the monitoring state of the event. If overridden, your
+ * event will be unable to determine if it is actually monitoring!
+ */
@Contract(pure = true)
@ApiStatus.NonExtendable
default boolean isMonitoring() {
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfDestructing.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfDestructing.java
index cfc141c..7ff2544 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfDestructing.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfDestructing.java
@@ -5,11 +5,14 @@
package net.minecraftforge.eventbus.api.event.characteristic;
import net.minecraftforge.eventbus.api.bus.EventBus;
-import net.minecraftforge.eventbus.internal.AbstractEventBusImpl;
/**
- * A self-destructing event will {@link AbstractEventBusImpl#dispose() dispose} of its associated {@link EventBus}
- * after it has been posted to free up resources, after which it cannot be posted to again.
+ * A self-destructing event will {@linkplain net.minecraftforge.eventbus.api.bus.BusGroup#dispose() dispose} of its
+ * associated {@link EventBus} after it has been posted to free up resources.
* This is useful for single-use lifecycle events.
+ *
+ * @apiNote Similar to {@link net.minecraftforge.eventbus.api.bus.BusGroup#dispose()}, the posting of this event is a
+ * destructive action that will cause its resources to be freed. It must not be used after it is
+ * posted!
*/
public non-sealed interface SelfDestructing extends EventCharacteristic {}
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
index 03140d3..144f087 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/SelfPosting.java
@@ -10,8 +10,8 @@
import org.jetbrains.annotations.Contract;
/**
- * Self-posting events are associated with a default {@link EventBus} in order to offer some convenience instance
- * methods.
+ * Self-posting events are associated with a default {@linkplain EventBus event bus} in order to offer some convenient
+ * instance methods.
*
* Example
* {@snippet :
@@ -40,21 +40,36 @@
@ApiStatus.Experimental
public non-sealed interface SelfPosting extends EventCharacteristic {
/**
- * @implSpec This should directly return a {@code static final} field without additional logic or processing.
+ * The default event bus for this event. It will be used by the {@link #post()} and {@link #fire()} methods.
+ *
+ * @return The default event bus for this event
+ * @implSpec This method must directly return a {@code static final} field without additional logic or processing.
+ * Failure to do so may result in performance hindrances.
*/
+ @Contract(pure = true)
EventBus getDefaultBus();
/**
+ * Posts this event to all listeners registered to its {@linkplain #getDefaultBus() default event bus}.
+ *
+ * @return {@code true} if the event was cancelled and this event bus is a
+ * {@linkplain net.minecraftforge.eventbus.api.bus.CancellableEventBus cancellable event bus}
* @see EventBus#post(Event)
*/
+ @ApiStatus.NonExtendable
@SuppressWarnings("unchecked")
default boolean post() {
return getDefaultBus().post((T) this);
}
/**
+ * Fires this event to all listeners registered to its {@linkplain #getDefaultBus() default event bus}.
+ * After posting, this event is returned from this method. It may be mutated.
+ *
+ * @return This event after being posted
* @see EventBus#fire(Event)
*/
+ @ApiStatus.NonExtendable
@Contract(value = "-> this")
@SuppressWarnings("unchecked")
default T fire() {
diff --git a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/package-info.java b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/package-info.java
index 37c81f0..5b279ba 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/package-info.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/event/characteristic/package-info.java
@@ -2,6 +2,11 @@
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
+/**
+ * Events can have characteristics that define how they behave. These characterists are computed when the
+ * {@linkplain net.minecraftforge.eventbus.api.bus.EventBus event bus} for the specific even is created, and those
+ * characteristics are defined as interfaces which the event type can implement.
+ */
@NullMarked
package net.minecraftforge.eventbus.api.event.characteristic;
From 63253e03a4924330e9e6cd386e46c379772de0b5 Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 23:01:53 -0400
Subject: [PATCH 10/11] JavaDocs - EventListner (potentially incomplete)
---
.../eventbus/api/listener/EventListener.java | 27 +++++++++++++++----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java b/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
index e7039a3..e491030 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/listener/EventListener.java
@@ -13,24 +13,41 @@
import java.util.function.Consumer;
/**
- * Users can retain instances of this interface to remove listeners that were previously added to the same
- * {@link EventBus}.You can obtain instances of this interface by calling any of the {@code addListener} methods
- * on an EventBus, such as {@link EventBus#addListener(Consumer)}.
- * Internally, this acts as a wrapper over lambdas to give them identity, enrich debug info and to allow
- * various conversion operations to different lambda types.
+ * An event listener holds info about a lister that was registered in an {@linkplain EventBus event bus} or
+ * {@linkplain net.minecraftforge.eventbus.api.bus.BusGroup bus group}. Consumers should retain instances of this
+ * interface in order to remove listeners that were previously added to the same event bus.
+ *
+ * @implNote Internally, this acts as a wrapper over lambdas to give them identity, enrich debug info and to allow
+ * various conversion operations to different lambda types.
*/
public sealed interface EventListener permits EventListenerImpl {
+ /**
+ * The event type that this listener is listening for.
+ *
+ * @return The event type
+ */
@Contract(pure = true)
@SuppressWarnings("ClassEscapesDefinedScope") // ? can be a subtype of Event which is publicly accessible
Class extends Event> eventType();
/**
+ * The priority of this listener.
+ * This is used by the {@linkplain EventBus event bus} to determine the order in which to invoke listeners.
+ *
+ * @return The priority of this listener
* @see Priority
*/
@Contract(pure = true)
byte priority();
/**
+ * Whether this listener will always cancel a
+ * {@linkplain net.minecraftforge.eventbus.api.event.characteristic.Cancellable cancellable} event.
+ * This is almost always {@code false} unless the listener is created using
+ * {@link CancellableEventBus#addListener(boolean, Consumer)} or
+ * {@link CancellableEventBus#addListener(byte, boolean, Consumer)}.
+ *
+ * @return {@code true} if this listener is always cancelling
* @see CancellableEventBus#addListener(boolean, Consumer)
*/
@Contract(pure = true)
From f567132154b560d3ca14abe4fcbd24a45fb4a7dc Mon Sep 17 00:00:00 2001
From: Jonathing
Date: Wed, 23 Apr 2025 23:02:03 -0400
Subject: [PATCH 11/11] JavaDocs - ObjBooleanBiConsumer
---
.../eventbus/api/listener/ObjBooleanBiConsumer.java | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/main/java/net/minecraftforge/eventbus/api/listener/ObjBooleanBiConsumer.java b/src/main/java/net/minecraftforge/eventbus/api/listener/ObjBooleanBiConsumer.java
index 5fba37c..0e9d060 100644
--- a/src/main/java/net/minecraftforge/eventbus/api/listener/ObjBooleanBiConsumer.java
+++ b/src/main/java/net/minecraftforge/eventbus/api/listener/ObjBooleanBiConsumer.java
@@ -7,9 +7,20 @@
import java.util.function.BiConsumer;
/**
- * A {@link BiConsumer} that takes an object and a primitive boolean, to avoid boxing.
+ * A {@linkplain BiConsumer bi-consumer} that accepts an object and a primitive boolean.
+ * This is used over {@link BiConsumer}{@code <}{@link Object}, {@link Boolean}{@code >} to avoid
+ * boxing.
+ *
+ * @see BiConsumer
*/
@FunctionalInterface
public interface ObjBooleanBiConsumer {
+ /**
+ * Performs this operation on the given arguments.
+ *
+ * @param obj The object
+ * @param bool The primitive boolean
+ * @see BiConsumer#accept(Object, Object)
+ */
void accept(T obj, boolean bool);
}