diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml
index 5c598f65b..88f1cafdc 100644
--- a/api/geyser/pom.xml
+++ b/api/geyser/pom.xml
@@ -32,6 +32,12 @@
${adventure.version}
compile
+
+ net.kyori
+ event-api
+ 3.0.0
+ provided
+
org.geysermc
base-api
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java
index d5fd09381..dbdee8b4e 100644
--- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java
@@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.Geyser;
import org.geysermc.api.GeyserApiBase;
import org.geysermc.geyser.api.connection.GeyserConnection;
+import org.geysermc.geyser.api.event.EventBus;
import org.geysermc.geyser.api.extension.ExtensionManager;
import java.util.List;
@@ -88,6 +89,14 @@ public interface GeyserApi extends GeyserApiBase {
*/
ExtensionManager extensionManager();
+ /**
+ * Gets the {@link EventBus} for handling
+ * Geyser events.
+ *
+ * @return the event bus
+ */
+ EventBus eventBus();
+
/**
* Gets the current {@link GeyserApiBase} instance.
*
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
index b673b60f6..036c7c591 100644
--- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
@@ -28,7 +28,7 @@ package org.geysermc.geyser.api.connection;
import org.geysermc.api.connection.Connection;
/**
- * Represents a player session used in Geyser.
+ * Represents a player connection used in Geyser.
*/
public interface GeyserConnection extends Connection {
}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java
new file mode 100644
index 000000000..94d0b832d
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event;
+
+/**
+ * Represents a cancellable event.
+ */
+public interface Cancellable {
+
+ /**
+ * Gets if the event is cancelled.
+ *
+ * @return if the event is cancelled
+ */
+ boolean isCancelled();
+
+ /**
+ * Cancels the event.
+ *
+ * @param cancelled if the event is cancelled
+ */
+ void setCancelled(boolean cancelled);
+}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java
new file mode 100644
index 000000000..c32e1701e
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event;
+
+/**
+ * Represents an event.
+ */
+public interface Event {
+
+ /**
+ * Gets if the event is async.
+ *
+ * @return if the event is async
+ */
+ default boolean isAsync() {
+ return false;
+ }
+}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java
new file mode 100644
index 000000000..a26e8c653
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.geysermc.geyser.api.extension.Extension;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Represents a bus capable of subscribing
+ * or "listening" to events and firing them.
+ */
+public interface EventBus {
+
+ /**
+ * Subscribes to the given event see {@link EventSubscription}.
+ *
+ * @param eventClass the class of the event
+ * @param consumer the consumer for handling the event
+ * @param the event class
+ * @return the event subscription
+ */
+ @NonNull
+ EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer super T> consumer);
+
+ /**
+ * Subscribes to the given event see {@link EventSubscription}.
+ *
+ * The difference between this method and {@link #subscribe(Class, Consumer)}
+ * is that this method takes in an extension parameter which allows for
+ * the event to be unsubscribed upon extension disable and reloads.
+ *
+ * @param extension the extension to subscribe the event to
+ * @param eventClass the class of the event
+ * @param consumer the consumer for handling the event
+ * @param the event class
+ * @return the event subscription
+ */
+ @NonNull
+ EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer super T> consumer);
+
+ /**
+ * Unsubscribes the given {@link EventSubscription}.
+ *
+ * @param subscription the event subscription
+ */
+ void unsubscribe(@NonNull EventSubscription subscription);
+
+ /**
+ * Registers events for the given listener.
+ *
+ * @param extension the extension registering the event
+ * @param eventHolder the listener
+ */
+ void register(@NonNull Extension extension, @NonNull Object eventHolder);
+
+ /**
+ * Fires the given {@link Event}.
+ *
+ * @param event the event to fire
+ *
+ * @return true if the event successfully fired
+ */
+ boolean fire(@NonNull Event event);
+
+ /**
+ * Gets the subscriptions for the given event class.
+ *
+ * @param eventClass the event class
+ * @param the value
+ * @return the subscriptions for the event class
+ */
+ @NonNull
+ Set> subscriptions(@NonNull Class eventClass);
+}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java
new file mode 100644
index 000000000..65939a7b0
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.geysermc.geyser.api.extension.Extension;
+
+import java.util.function.Consumer;
+
+/**
+ * Represents a subscribed listener to a {@link Event}. Wraps around
+ * the event and is capable of unsubscribing from the event or give
+ * information about it.
+ *
+ * @param the class of the event
+ */
+public interface EventSubscription {
+
+ /**
+ * Gets the event class.
+ *
+ * @return the event class
+ */
+ @NonNull
+ Class eventClass();
+
+ /**
+ * Gets the consumer responsible for handling
+ * this event.
+ *
+ * @return the consumer responsible for this event
+ */
+ @NonNull
+ Consumer super T> eventConsumer();
+
+ /**
+ * Gets the {@link Extension} that owns this
+ * event subscription.
+ *
+ * @return the extension that owns this subscription
+ */
+ @NonNull
+ Extension owner();
+
+ /**
+ * Gets the priority of this event subscription.
+ *
+ * @return the priority of this event subscription
+ */
+ Subscribe.Priority priority();
+
+ /**
+ * Gets if this event subscription is active.
+ *
+ * @return if this event subscription is active
+ */
+ boolean isActive();
+
+ /**
+ * Unsubscribes from this event listener
+ */
+ void unsubscribe();
+
+ /**
+ * Invokes the given event
+ *
+ * @param event the event
+ */
+ void invoke(@NonNull T event) throws Throwable;
+}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java
new file mode 100644
index 000000000..581aa6fa0
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.experimental.Accessors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to signify the given method is
+ * an {@link Event}. Only should be applied to methods
+ * where the class containing them is designated for
+ * events specifically.
+ *
+ * When using {@link EventBus#subscribe}, this annotation should
+ * not be applied whatsoever as it will have no use and potentially
+ * throw errors due to it being used wrongly.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Subscribe {
+
+ /**
+ * The {@link Priority} of the event
+ *
+ * @return the priority of the event
+ */
+ @NonNull
+ Priority priority() default Priority.NORMAL;
+
+ /**
+ * Represents the priority of an event.
+ */
+ @Accessors(fluent = true)
+ @Getter
+ @AllArgsConstructor
+ enum Priority {
+
+ /**
+ * The lowest priority. Called first to
+ * allow for other events to customize
+ * the outcome
+ */
+ LOWEST(net.kyori.event.PostOrders.FIRST),
+
+ /**
+ * The second lowest priority.
+ */
+ LOW(net.kyori.event.PostOrders.EARLY),
+
+ /**
+ * Normal priority. Event is neither
+ * important nor unimportant
+ */
+ NORMAL(net.kyori.event.PostOrders.NORMAL),
+
+ /**
+ * The second highest priority
+ */
+ HIGH(net.kyori.event.PostOrders.LATE),
+
+ /**
+ * The highest of importance! Event is called
+ * last and has the final say in the outcome
+ */
+ HIGHEST(net.kyori.event.PostOrders.LAST);
+
+ private final int postOrder;
+ }
+}
\ No newline at end of file
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java
new file mode 100644
index 000000000..94d3d8cb8
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event.lifecycle;
+
+import org.geysermc.geyser.api.event.Event;
+
+/**
+ * Called when Geyser has completed initializing.
+ */
+public class GeyserPostInitializeEvent implements Event {
+}
diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java
new file mode 100644
index 000000000..baf8b3445
--- /dev/null
+++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.api.event.lifecycle;
+
+import org.geysermc.geyser.api.event.Event;
+
+/**
+ * Called when Geyser is shutting down.
+ */
+public class GeyserShutdownEvent implements Event {
+}
diff --git a/core/pom.xml b/core/pom.xml
index 4837660f9..6f4e0fa3c 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -244,6 +244,13 @@
${adventure.version}
compile
+
+
+ net.kyori
+ event-api
+ 3.0.0
+ compile
+
junit
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
index 6e740cb44..573934b91 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
@@ -49,9 +49,13 @@ import org.geysermc.floodgate.crypto.Base64Topping;
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.news.NewsItemAction;
import org.geysermc.geyser.api.GeyserApi;
+import org.geysermc.geyser.api.event.EventBus;
+import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
+import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent;
import org.geysermc.geyser.command.CommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions;
+import org.geysermc.geyser.event.GeyserEventBus;
import org.geysermc.geyser.extension.GeyserExtensionManager;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.network.ConnectorServerEventHandler;
@@ -123,6 +127,7 @@ public class GeyserImpl implements GeyserApi {
private final PlatformType platformType;
private final GeyserBootstrap bootstrap;
+ private final EventBus eventBus;
private final GeyserExtensionManager extensionManager;
private Metrics metrics;
@@ -158,6 +163,7 @@ public class GeyserImpl implements GeyserApi {
MinecraftLocale.init();
/* Load Extensions */
+ this.eventBus = new GeyserEventBus();
this.extensionManager = new GeyserExtensionManager();
this.extensionManager.init();
@@ -411,6 +417,8 @@ public class GeyserImpl implements GeyserApi {
}
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
+
+ this.eventBus.fire(new GeyserPostInitializeEvent());
}
@Override
@@ -466,6 +474,8 @@ public class GeyserImpl implements GeyserApi {
ResourcePack.PACKS.clear();
+ this.eventBus.fire(new GeyserShutdownEvent());
+
this.extensionManager.disableExtensions();
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
@@ -494,6 +504,11 @@ public class GeyserImpl implements GeyserApi {
return this.extensionManager;
}
+ @Override
+ public EventBus eventBus() {
+ return this.eventBus;
+ }
+
public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) {
if (instance == null) {
return new GeyserImpl(platformType, bootstrap);
diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java
new file mode 100644
index 000000000..93a042826
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.event;
+
+import net.kyori.event.SimpleEventBus;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.geysermc.geyser.api.event.Event;
+import org.geysermc.geyser.api.event.EventBus;
+import org.geysermc.geyser.api.event.EventSubscription;
+import org.geysermc.geyser.api.event.Subscribe;
+import org.geysermc.geyser.api.extension.Extension;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class GeyserEventBus implements EventBus {
+ private final SimpleEventBus bus = new SimpleEventBus<>(Event.class);
+
+ @NonNull
+ @Override
+ public EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer super T> consumer) {
+ return this.subscribe(eventClass, consumer, null, Subscribe.Priority.NORMAL);
+ }
+
+ @NonNull
+ @Override
+ public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer super T> consumer) {
+ return this.subscribe(eventClass, consumer, extension, Subscribe.Priority.NORMAL);
+ }
+
+ @Override
+ public void unsubscribe(@NonNull EventSubscription subscription) {
+ this.bus.unregister((GeyserEventSubscription) subscription);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void register(@NonNull Extension extension, @NonNull Object eventHolder) {
+ for (Method method : eventHolder.getClass().getMethods()) {
+ if (!method.isAnnotationPresent(Subscribe.class)) {
+ continue;
+ }
+
+ if (method.getParameterCount() > 1) {
+ continue;
+ }
+
+ if (!Event.class.isAssignableFrom(method.getParameters()[0].getType())) {
+ continue;
+ }
+
+ Subscribe subscribe = method.getAnnotation(Subscribe.class);
+ this.subscribe((Class extends Event>) method.getParameters()[0].getType(), (event) -> {
+ try {
+ method.invoke(eventHolder, event);
+ } catch (IllegalAccessException | InvocationTargetException ex) {
+ ex.printStackTrace();
+ }
+ }, extension, subscribe.priority());
+ }
+ }
+
+ @Override
+ public boolean fire(@NonNull Event event) {
+ return this.bus.post(event).wasSuccessful();
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
+ @Override
+ public Set> subscriptions(@NonNull Class eventClass) {
+ return bus.subscribers().values()
+ .stream()
+ .filter(sub -> sub instanceof EventSubscription && ((EventSubscription>) sub).eventClass().isAssignableFrom(eventClass))
+ .map(sub -> ((EventSubscription) sub))
+ .collect(Collectors.toSet());
+ }
+
+ private EventSubscription subscribe(Class eventClass, Consumer super T> handler, Extension extension, Subscribe.Priority priority) {
+ GeyserEventSubscription eventSubscription = new GeyserEventSubscription<>(this, eventClass, handler, extension, priority);
+ this.bus.register(eventClass, eventSubscription);
+ return eventSubscription;
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java
new file mode 100644
index 000000000..e160291c9
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.event;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.Accessors;
+import net.kyori.event.EventSubscriber;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.geysermc.geyser.api.event.Event;
+import org.geysermc.geyser.api.event.EventBus;
+import org.geysermc.geyser.api.event.EventSubscription;
+import org.geysermc.geyser.api.event.Subscribe;
+import org.geysermc.geyser.api.extension.Extension;
+
+import java.util.function.Consumer;
+
+@Getter
+@Accessors(fluent = true)
+@RequiredArgsConstructor
+public class GeyserEventSubscription implements EventSubscription, EventSubscriber {
+ private final EventBus eventBus;
+ private final Class eventClass;
+ private final Consumer super T> eventConsumer;
+ private final Extension owner;
+ private final Subscribe.Priority priority;
+ @Getter(AccessLevel.NONE) private boolean active;
+
+ @Override
+ public boolean isActive() {
+ return this.active;
+ }
+
+ @Override
+ public void unsubscribe() {
+ if (!this.active) {
+ return;
+ }
+
+ this.active = false;
+ this.eventBus.unsubscribe(this);
+ }
+
+ @Override
+ public void invoke(@NonNull T event) throws Throwable {
+ try {
+ this.eventConsumer.accept(event);
+ } catch (Throwable ex) {
+ this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName());
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public int postOrder() {
+ return this.priority.postOrder();
+ }
+}