From 4eb60abe0711149c75d85c83a5f5460d6303a8ac Mon Sep 17 00:00:00 2001 From: Tim203 Date: Tue, 12 Jul 2022 14:02:36 +0200 Subject: [PATCH] Added auto-binding --- ap/build.gradle.kts | 0 .../floodgate/ap/AutoBindProcessor.java | 38 +++ .../geysermc/floodgate/ap/ClassProcessor.java | 221 ++++++++++++++++++ .../javax.annotation.processing.Processor | 1 + core/build.gradle.kts | 3 + .../geysermc/floodgate/FloodgatePlatform.java | 19 +- .../floodgate/module/AutoBindModule.java | 39 ++++ .../floodgate/module/CommonModule.java | 11 +- .../geysermc/floodgate/news/NewsChecker.java | 3 + .../org/geysermc/floodgate/util/AutoBind.java | 38 +++ .../org/geysermc/floodgate/util/Metrics.java | 1 + .../floodgate/util/PostEnableMessages.java | 1 + .../org/geysermc/floodgate/util/Utils.java | 43 ++++ settings.gradle.kts | 1 + 14 files changed, 403 insertions(+), 16 deletions(-) create mode 100644 ap/build.gradle.kts create mode 100644 ap/src/main/java/org/geysermc/floodgate/ap/AutoBindProcessor.java create mode 100644 ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java create mode 100644 ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 core/src/main/java/org/geysermc/floodgate/module/AutoBindModule.java create mode 100644 core/src/main/java/org/geysermc/floodgate/util/AutoBind.java diff --git a/ap/build.gradle.kts b/ap/build.gradle.kts new file mode 100644 index 00000000..e69de29b diff --git a/ap/src/main/java/org/geysermc/floodgate/ap/AutoBindProcessor.java b/ap/src/main/java/org/geysermc/floodgate/ap/AutoBindProcessor.java new file mode 100644 index 00000000..52bd93d2 --- /dev/null +++ b/ap/src/main/java/org/geysermc/floodgate/ap/AutoBindProcessor.java @@ -0,0 +1,38 @@ +/* + * 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/Floodgate + */ + +package org.geysermc.floodgate.ap; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("*") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class AutoBindProcessor extends ClassProcessor { + public AutoBindProcessor() { + super("org.geysermc.floodgate.util.AutoBind"); + } +} diff --git a/ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java b/ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java new file mode 100644 index 00000000..3758711a --- /dev/null +++ b/ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java @@ -0,0 +1,221 @@ +/* + * 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/Floodgate + */ + +package org.geysermc.floodgate.ap; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/* + * Copied from Geyser + */ +public class ClassProcessor extends AbstractProcessor { + private final String annotationClassName; + + private Path outputPath; + + private final Set locations = new HashSet<>(); + + public ClassProcessor(String annotationClassName) { + this.annotationClassName = annotationClassName; + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Initializing processor " + annotationClassName + ); + + String outputFile = processingEnv.getOptions().get("metadataOutputFile"); + if (outputFile != null && !outputFile.isEmpty()) { + outputPath = Paths.get(outputFile); + } + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + if (!roundEnv.errorRaised()) { + complete(); + } + + return false; + } + + if (!contains(annotations, annotationClassName)) { + return false; + } + + for (Element element : roundEnv.getRootElements()) { + if (element.getKind() != ElementKind.CLASS) { + continue; + } + + if (!contains(element.getAnnotationMirrors(), annotationClassName)) { + continue; + } + + TypeElement typeElement = (TypeElement) element; + locations.add(typeElement.getQualifiedName().toString()); + } + return false; + } + + public boolean contains(Collection elements, String className) { + if (elements.isEmpty()) { + return false; + } + + for (TypeElement element : elements) { + if (element.getQualifiedName().contentEquals(className)) { + return true; + } + } + + return false; + } + + public boolean contains(List elements, String className) { + if (elements.isEmpty()) { + return false; + } + + for (AnnotationMirror element : elements) { + if (element.getAnnotationType().toString().equals(className)) { + return true; + } + } + + return false; + } + + public void complete() { + // Read existing annotation list and verify each class still has this annotation + try (BufferedReader reader = createReader()) { + if (reader != null) { + reader.lines().forEach(canonicalName -> { + if (!locations.contains(canonicalName)) { + + TypeElement element = + processingEnv.getElementUtils().getTypeElement(canonicalName); + + if (element != null && element.getKind() == ElementKind.CLASS && + contains(element.getAnnotationMirrors(), annotationClassName)) { + locations.add(canonicalName); + } + } + }); + } + } catch (IOException e) { + e.printStackTrace(); + } + + if (!locations.isEmpty()) { + try (BufferedWriter writer = createWriter()) { + for (String location : locations) { + writer.write(location); + writer.newLine(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } else { + processingEnv.getMessager().printMessage(Kind.NOTE, + "Did not find any classes annotated with " + annotationClassName + ); + } + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Completed processing for " + annotationClassName + ); + } + + private BufferedReader createReader() throws IOException { + if (outputPath != null) { + processingEnv.getMessager().printMessage(Kind.NOTE, + "Reading existing " + annotationClassName + " list from " + outputPath + ); + + return Files.newBufferedReader(outputPath); + } + + FileObject obj = processingEnv.getFiler().getResource( + StandardLocation.CLASS_OUTPUT, "", annotationClassName + ); + + if (obj != null) { + processingEnv.getMessager().printMessage( + Kind.NOTE, + "Reading existing " + annotationClassName + " list from " + obj.toUri() + ); + + try { + return new BufferedReader(obj.openReader(false)); + } catch (NoSuchFileException ignored) {} + } + return null; + } + + private BufferedWriter createWriter() throws IOException { + if (outputPath != null) { + processingEnv.getMessager().printMessage( + Kind.NOTE, "Writing " + annotationClassName + " to " + outputPath + ); + + return Files.newBufferedWriter(outputPath); + } + + FileObject obj = processingEnv.getFiler().createResource( + StandardLocation.CLASS_OUTPUT, "", annotationClassName + ); + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Writing " + annotationClassName + " to " + obj.toUri() + ); + + return new BufferedWriter(obj.openWriter()); + } +} diff --git a/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..53b2e379 --- /dev/null +++ b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +org.geysermc.floodgate.ap.AutoBindProcessor \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 258d98c5..c3b49264 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -8,6 +8,9 @@ dependencies { api(projects.api) api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion) + compileOnly(projects.ap) + annotationProcessor(projects.ap) + api("com.google.inject", "guice", Versions.guiceVersion) api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion) api("com.nukkitx.fastutil", "fastutil-int-object-maps", Versions.fastutilVersion) diff --git a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java index 038bfe50..f38b99e3 100644 --- a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java +++ b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java @@ -39,27 +39,22 @@ import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.event.PostEnableEvent; import org.geysermc.floodgate.event.ShutdownEvent; -import org.geysermc.floodgate.link.PlayerLinkLoader; import org.geysermc.floodgate.module.PostInitializeModule; -import org.geysermc.floodgate.news.NewsChecker; -import org.geysermc.floodgate.util.Metrics; -import org.geysermc.floodgate.util.PostEnableMessages; public class FloodgatePlatform { private static final UUID KEY = UUID.randomUUID(); - @Inject private FloodgateApi api; @Inject private PlatformInjector injector; @Inject private FloodgateConfig config; @Inject private Injector guice; @Inject - public void init(PacketHandlers packetHandlers, HandshakeHandlers handshakeHandlers) { - PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load(); - + public void init( + FloodgateApi api, + PlayerLink link, + PacketHandlers packetHandlers, + HandshakeHandlers handshakeHandlers) { InstanceHolder.set(api, link, this.injector, packetHandlers, handshakeHandlers, KEY); - - guice.getInstance(NewsChecker.class).start(); } public void enable(Module... postInitializeModules) throws RuntimeException { @@ -75,10 +70,6 @@ public void enable(Module... postInitializeModules) throws RuntimeException { this.guice = guice.createChildInjector(new PostInitializeModule(postInitializeModules)); - //todo add some kind of auto-load, as this looks kinda weird - guice.getInstance(PostEnableMessages.class); - guice.getInstance(Metrics.class); - guice.getInstance(PubSubSupport.class).publish(new PostEnableEvent()); } diff --git a/core/src/main/java/org/geysermc/floodgate/module/AutoBindModule.java b/core/src/main/java/org/geysermc/floodgate/module/AutoBindModule.java new file mode 100644 index 00000000..1c2933de --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/module/AutoBindModule.java @@ -0,0 +1,39 @@ +/* + * 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/Floodgate + */ + +package org.geysermc.floodgate.module; + +import com.google.inject.AbstractModule; +import org.geysermc.floodgate.util.AutoBind; +import org.geysermc.floodgate.util.Utils; + +public class AutoBindModule extends AbstractModule { + @Override + protected void configure() { + for (Class clazz : Utils.getGeneratedClassesForAnnotation(AutoBind.class)) { + bind(clazz).asEagerSingleton(); + } + } +} diff --git a/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java index 3e15eb1a..475f39e6 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -44,6 +44,7 @@ import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.handshake.HandshakeHandlers; import org.geysermc.floodgate.api.inject.PlatformInjector; +import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.packet.PacketHandlers; import org.geysermc.floodgate.api.player.FloodgatePlayer; @@ -56,7 +57,7 @@ import org.geysermc.floodgate.crypto.KeyProducer; import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher; import org.geysermc.floodgate.inject.CommonPlatformInjector; -import org.geysermc.floodgate.news.NewsChecker; +import org.geysermc.floodgate.link.PlayerLinkLoader; import org.geysermc.floodgate.packet.PacketHandlersImpl; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; @@ -92,7 +93,7 @@ public void hear(TypeLiteral type, TypeEncounter encounter) { bind(PacketHandlers.class).to(PacketHandlersImpl.class); bind(PacketHandlersImpl.class).asEagerSingleton(); - bind(NewsChecker.class).in(Singleton.class); + install(new AutoBindModule()); } @Provides @@ -101,6 +102,12 @@ public FloodgateConfig floodgateConfig(ConfigLoader configLoader) { return configLoader.load(); } + @Provides + @Singleton + public PlayerLink playerLink(PlayerLinkLoader linkLoader) { + return linkLoader.load(); + } + @Provides @Singleton public KeyProducer keyProducer() { diff --git a/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java b/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java index 62be4469..4ed98702 100644 --- a/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java +++ b/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java @@ -46,10 +46,12 @@ import org.geysermc.floodgate.news.data.BuildSpecificData; import org.geysermc.floodgate.news.data.CheckAfterData; import org.geysermc.floodgate.platform.command.CommandUtil; +import org.geysermc.floodgate.util.AutoBind; import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.HttpClient.HttpResponse; +@AutoBind @Listener public class NewsChecker { private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); @@ -71,6 +73,7 @@ public class NewsChecker { private boolean firstCheck; + @Inject public void start() { executorService.scheduleWithFixedDelay(this::checkNews, 0, 30, TimeUnit.MINUTES); } diff --git a/core/src/main/java/org/geysermc/floodgate/util/AutoBind.java b/core/src/main/java/org/geysermc/floodgate/util/AutoBind.java new file mode 100644 index 00000000..6c1eec2e --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/util/AutoBind.java @@ -0,0 +1,38 @@ +/* + * 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/Floodgate + */ + +package org.geysermc.floodgate.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Automatically binds an instance of itself as an eager singleton during the post-initialise stage + * of the FloodgatePlatform. Add the annotation to a class, and it should automatically create an + * instance and inject it for you. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoBind { +} diff --git a/core/src/main/java/org/geysermc/floodgate/util/Metrics.java b/core/src/main/java/org/geysermc/floodgate/util/Metrics.java index 008d215e..474d6e7a 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/Metrics.java +++ b/core/src/main/java/org/geysermc/floodgate/util/Metrics.java @@ -44,6 +44,7 @@ import org.geysermc.floodgate.config.FloodgateConfig.MetricsConfig; import org.geysermc.floodgate.platform.util.PlatformUtils; +@AutoBind public final class Metrics { private final MetricsBase metricsBase; diff --git a/core/src/main/java/org/geysermc/floodgate/util/PostEnableMessages.java b/core/src/main/java/org/geysermc/floodgate/util/PostEnableMessages.java index b324f4bd..a8628a56 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/PostEnableMessages.java +++ b/core/src/main/java/org/geysermc/floodgate/util/PostEnableMessages.java @@ -34,6 +34,7 @@ import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.event.PostEnableEvent; +@AutoBind @Listener public final class PostEnableMessages { private final List messages = new ArrayList<>(); diff --git a/core/src/main/java/org/geysermc/floodgate/util/Utils.java b/core/src/main/java/org/geysermc/floodgate/util/Utils.java index 9d2d93c7..c1765cbf 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/Utils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/Utils.java @@ -28,15 +28,20 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelPipeline; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.annotation.Annotation; import java.util.Locale; import java.util.Properties; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class Utils { private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^\\w{0,16}$"); @@ -131,4 +136,42 @@ public static CompletableFuture failedFuture(Throwable ex) { future.completeExceptionally(ex); return future; } + + /** + * Returns a set of all the classes that are annotated by a given annotation. + * Keep in mind that these are from a set of generated annotations generated + * at compile time by the annotation processor, meaning that arbitrary annotations + * cannot be passed into this method and expected to get a set of classes back. + * + * @param annotationClass the annotation class + * @return a set of all the classes annotated by the given annotation + */ + public static Set> getGeneratedClassesForAnnotation(Class annotationClass) { + return getGeneratedClassesForAnnotation(annotationClass.getName()); + } + + /** + * Returns a set of all the classes that are annotated by a given annotation. + * Keep in mind that these are from a set of generated annotations generated + * at compile time by the annotation processor, meaning that arbitrary annotations + * cannot be passed into this method and expected to have a set of classes + * returned back. + * + * @param input the fully qualified name of the annotation + * @return a set of all the classes annotated by the given annotation + */ + public static Set> getGeneratedClassesForAnnotation(String input) { + try (InputStream annotatedClass = Utils.class.getClassLoader().getResourceAsStream(input); + BufferedReader reader = new BufferedReader(new InputStreamReader(annotatedClass))) { + return reader.lines().map(className -> { + try { + return Class.forName(className); + } catch (ClassNotFoundException ex) { + throw new RuntimeException("Failed to find class for annotation " + input, ex); + } + }).collect(Collectors.toSet()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7df9d09c..4a44a9d9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,6 +51,7 @@ pluginManagement { rootProject.name = "floodgate-parent" include(":api") +include(":ap") include(":core") include(":bungee") include(":spigot")