From b82c66168883045896bae92866a5ecb580b10ca8 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 15 Jan 2022 22:54:08 -0600 Subject: [PATCH] Implement simple event system --- api/geyser/pom.xml | 6 + .../org/geysermc/geyser/api/GeyserApi.java | 9 ++ .../api/connection/GeyserConnection.java | 2 +- .../geyser/api/event/Cancellable.java | 46 ++++++++ .../org/geysermc/geyser/api/event/Event.java | 41 +++++++ .../geysermc/geyser/api/event/EventBus.java | 100 ++++++++++++++++ .../geyser/api/event/EventSubscription.java | 93 +++++++++++++++ .../geysermc/geyser/api/event/Subscribe.java | 99 ++++++++++++++++ .../lifecycle/GeyserPostInitializeEvent.java | 34 ++++++ .../event/lifecycle/GeyserShutdownEvent.java | 34 ++++++ core/pom.xml | 7 ++ .../java/org/geysermc/geyser/GeyserImpl.java | 15 +++ .../geysermc/geyser/event/GeyserEventBus.java | 110 ++++++++++++++++++ .../geyser/event/GeyserEventSubscription.java | 82 +++++++++++++ 14 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java 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 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 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 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 consumer) { + return this.subscribe(eventClass, consumer, null, Subscribe.Priority.NORMAL); + } + + @NonNull + @Override + public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer 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) 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 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 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(); + } +}