1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Added auto-binding

This commit is contained in:
Tim203
2022-07-12 14:02:36 +02:00
parent 450eeed2a3
commit 4eb60abe07
14 changed files with 403 additions and 16 deletions

0
ap/build.gradle.kts Normal file
View File

View File

@@ -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");
}
}

View File

@@ -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<String> 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<? extends TypeElement> 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<? extends TypeElement> 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<? extends AnnotationMirror> 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());
}
}

View File

@@ -0,0 +1 @@
org.geysermc.floodgate.ap.AutoBindProcessor

View File

@@ -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)

View File

@@ -39,27 +39,22 @@ import org.geysermc.floodgate.api.packet.PacketHandlers;
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 class FloodgatePlatform {
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());
}

View File

@@ -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();
}
}
}

View File

@@ -44,6 +44,7 @@ import org.geysermc.floodgate.api.FloodgateApi;
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.FloodgateCipher;
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 class CommonModule extends AbstractModule {
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 class CommonModule extends AbstractModule {
return configLoader.load();
}
@Provides
@Singleton
public PlayerLink playerLink(PlayerLinkLoader linkLoader) {
return linkLoader.load();
}
@Provides
@Singleton
public KeyProducer keyProducer() {

View File

@@ -46,10 +46,12 @@ import org.geysermc.floodgate.news.data.AnnouncementData;
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);
}

View File

@@ -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 {
}

View File

@@ -44,6 +44,7 @@ import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.config.FloodgateConfig.MetricsConfig;
import org.geysermc.floodgate.platform.util.PlatformUtils;
@AutoBind
public final class Metrics {
private final MetricsBase metricsBase;

View File

@@ -34,6 +34,7 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.event.PostEnableEvent;
@AutoBind
@Listener
public final class PostEnableMessages {
private final List<String> messages = new ArrayList<>();

View File

@@ -28,15 +28,20 @@ package org.geysermc.floodgate.util;
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 class Utils {
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<Class<?>> getGeneratedClassesForAnnotation(Class<? extends Annotation> 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<Class<?>> 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);
}
}
}

View File

@@ -51,6 +51,7 @@ pluginManagement {
rootProject.name = "floodgate-parent"
include(":api")
include(":ap")
include(":core")
include(":bungee")
include(":spigot")