From ff280ef19216a5b6fdaebcc5b375b82fe418afb2 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 17 Jul 2021 13:36:04 -0500 Subject: [PATCH 01/11] Replace Reflections usage with an annotation processor Reduces jar size by about 1.5-2mb --- ap/pom.xml | 14 ++ .../processor/BlockEntityProcessor.java | 38 +++++ .../geysermc/processor/ClassProcessor.java | 151 ++++++++++++++++++ .../processor/CollisionRemapperProcessor.java | 38 +++++ .../processor/ItemRemapperProcessor.java | 38 +++++ .../processor/PacketTranslatorProcessor.java | 38 +++++ .../processor/SoundHandlerProcessor.java | 38 +++++ bootstrap/bungeecord/pom.xml | 8 - bootstrap/pom.xml | 10 ++ bootstrap/spigot/pom.xml | 8 - bootstrap/sponge/pom.xml | 8 - bootstrap/velocity/pom.xml | 8 - connector/pom.xml | 66 +------- .../geysermc/connector/GeyserConnector.java | 12 -- .../translators/PacketTranslatorRegistry.java | 5 +- .../translators/item/ItemTranslator.java | 5 +- .../connector/registry/Registries.java | 6 +- .../loader/AnnotatedRegistryLoader.java | 5 +- .../loader/CollisionRegistryLoader.java | 4 +- .../geysermc/connector/utils/FileUtils.java | 62 ++++--- .../javax.annotation.processing.Processor | 5 + pom.xml | 1 + 22 files changed, 424 insertions(+), 144 deletions(-) create mode 100644 ap/pom.xml create mode 100644 ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java create mode 100644 ap/src/main/java/org/geysermc/processor/ClassProcessor.java create mode 100644 ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java create mode 100644 ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java create mode 100644 ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java create mode 100644 ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java create mode 100644 connector/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/ap/pom.xml b/ap/pom.xml new file mode 100644 index 000000000..7e263c0d1 --- /dev/null +++ b/ap/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + org.geysermc + geyser-parent + 1.4.1-SNAPSHOT + + + ap + 1.4.1-SNAPSHOT + \ No newline at end of file diff --git a/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java b/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java new file mode 100644 index 000000000..52a6e3aae --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("org.geysermc.connector.network.translators.world.block.entity.BlockEntity") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class BlockEntityProcessor extends ClassProcessor { + public BlockEntityProcessor() { + super("org.geysermc.connector.network.translators.world.block.entity.BlockEntity"); + } +} diff --git a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java b/ap/src/main/java/org/geysermc/processor/ClassProcessor.java new file mode 100644 index 000000000..3531d8794 --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/ClassProcessor.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +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; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class ClassProcessor extends AbstractProcessor { + private final String annotationClassName; + + private Path outputPath; + + private final List locations = new ArrayList<>(); + + public ClassProcessor(String annotationClassName) { + this.annotationClassName = annotationClassName; + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Initializing processor " + this.annotationClassName); + + String outputFile = processingEnv.getOptions().get("metadataOutputFile"); + if (outputFile != null && !outputFile.isEmpty()) { + this.outputPath = Paths.get(outputFile); + } + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + if (!roundEnv.errorRaised()) { + complete(); + } + + return false; + } + + if (!contains(annotations, this.annotationClassName)) { + return false; + } + + for (Element element : roundEnv.getRootElements()) { + if (element.getKind() != ElementKind.CLASS) { + continue; + } + + if (!contains(element.getAnnotationMirrors(), this.annotationClassName)) { + continue; + } + + TypeElement typeElement = (TypeElement) element; + this.locations.add(typeElement.getQualifiedName().toString()); + } + return true; + } + + 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() { + try (BufferedWriter writer = this.createWriter()) { + for (String location : this.locations) { + writer.write(location); + writer.newLine(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + + this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Completed processing for " + this.annotationClassName); + } + + private BufferedWriter createWriter() throws IOException { + if (this.outputPath != null) { + this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Writing " + this.annotationClassName + " to " + this.outputPath); + return Files.newBufferedWriter(this.outputPath); + } + + FileObject obj = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", this.annotationClassName); + this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Writing " + this.annotationClassName + " to " + obj.toUri()); + return new BufferedWriter(obj.openWriter()); + } +} diff --git a/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java b/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java new file mode 100644 index 000000000..3f9111155 --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("org.geysermc.connector.network.translators.collision.CollisionRemapper") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class CollisionRemapperProcessor extends ClassProcessor { + public CollisionRemapperProcessor() { + super("org.geysermc.connector.network.translators.collision.CollisionRemapper"); + } +} diff --git a/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java b/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java new file mode 100644 index 000000000..cbfd939c4 --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("org.geysermc.connector.network.translators.ItemRemapper") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class ItemRemapperProcessor extends ClassProcessor { + public ItemRemapperProcessor() { + super("org.geysermc.connector.network.translators.ItemRemapper"); + } +} diff --git a/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java b/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java new file mode 100644 index 000000000..cdfbcbe4a --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("org.geysermc.connector.network.translators.Translator") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class PacketTranslatorProcessor extends ClassProcessor { + public PacketTranslatorProcessor() { + super("org.geysermc.connector.network.translators.Translator"); + } +} diff --git a/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java b/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java new file mode 100644 index 000000000..ee4b2ef45 --- /dev/null +++ b/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2021 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.processor; + +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; + +@SupportedAnnotationTypes("org.geysermc.connector.network.translators.sound.SoundHandler") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class SoundHandlerProcessor extends ClassProcessor { + public SoundHandlerProcessor() { + super("org.geysermc.connector.network.translators.sound.SoundHandler"); + } +} diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 0f13390d5..839b8982c 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -71,10 +71,6 @@ io.netty.channel.kqueue org.geysermc.platform.bungeecord.shaded.io.netty.channel.kqueue - - org.reflections - org.geysermc.platform.bungeecord.shaded.reflections - com.google.common org.geysermc.platform.bungeecord.shaded.google.common @@ -83,10 +79,6 @@ com.google.guava org.geysermc.platform.bungeecord.shaded.google.guava - - org.dom4j - org.geysermc.platform.bungeecord.shaded.dom4j - net.kyori org.geysermc.platform.bungeecord.shaded.kyori diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 98a08ee31..6bbd1a0b6 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -29,6 +29,16 @@ https://repo.velocitypowered.com/snapshots/ + + + + org.geysermc + ap + 1.4.1-SNAPSHOT + provided + + + bungeecord spigot diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 133ea7779..4277dc235 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -83,10 +83,6 @@ com.fasterxml.jackson org.geysermc.platform.spigot.shaded.jackson - - org.reflections - org.geysermc.platform.spigot.shaded.reflections - com.google.common org.geysermc.platform.spigot.shaded.google.common @@ -95,10 +91,6 @@ com.google.guava org.geysermc.platform.spigot.shaded.google.guava - - org.dom4j - org.geysermc.platform.spigot.shaded.dom4j - net.kyori org.geysermc.platform.spigot.shaded.kyori diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index e0f019152..77f4fe78a 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -69,10 +69,6 @@ it.unimi.dsi.fastutil org.geysermc.platform.sponge.shaded.fastutil - - org.reflections - org.geysermc.platform.sponge.shaded.reflections - com.google.common org.geysermc.platform.sponge.shaded.google.common @@ -81,10 +77,6 @@ com.google.guava org.geysermc.platform.sponge.shaded.google.guava - - org.dom4j - org.geysermc.platform.sponge.shaded.dom4j - net.kyori org.geysermc.platform.sponge.shaded.kyori diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index c67f5a628..5c12b6a46 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -65,10 +65,6 @@ it.unimi.dsi.fastutil org.geysermc.platform.velocity.shaded.fastutil - - org.reflections - org.geysermc.platform.velocity.shaded.reflections - com.google.common org.geysermc.platform.velocity.shaded.google.common @@ -77,10 +73,6 @@ com.google.guava org.geysermc.platform.velocity.shaded.google.guava - - org.dom4j - org.geysermc.platform.velocity.shaded.dom4j - net.kyori.adventure.text.serializer.gson.legacyimpl org.geysermc.platform.velocity.shaded.kyori.legacyimpl diff --git a/connector/pom.xml b/connector/pom.xml index 6849066d9..e6cc1c302 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -18,6 +18,12 @@ + + org.geysermc + ap + 1.4.1-SNAPSHOT + provided + org.geysermc common @@ -207,18 +213,6 @@ osx-x86_64 - - org.reflections - reflections - 0.9.11 - compile - - - org.dom4j - dom4j - 2.1.3 - compile - net.kyori @@ -342,54 +336,6 @@ - - org.codehaus.gmavenplus - gmavenplus-plugin - 1.9.1 - - - process-classes - - execute - - - - - - - - - - - org.reflections - reflections - 0.9.11 - - - org.dom4j - dom4j - 2.1.3 - - - org.codehaus.groovy - groovy-all - 3.0.5 - runtime - pom - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 291f2d7e5..173e41573 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -524,18 +524,6 @@ public class GeyserConnector { return !"DEV".equals(GeyserConnector.VERSION); } - /** - * Whether to use XML reflections in the jar or manually find the reflections. - * Will return true if in production and the platform is not Fabric. - * On Fabric - it complains about being unable to create a default XMLReader. - * On other platforms this should only be true in compiled jars. - * - * @return whether to use XML reflections - */ - public boolean useXmlReflections() { - return !this.getPlatformType().equals(PlatformType.FABRIC) && isProductionEnvironment(); - } - public static GeyserConnector getInstance() { return instance; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index 2469f65da..20c40a785 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -35,7 +35,6 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; -import org.reflections.Reflections; import java.util.HashMap; import java.util.Map; @@ -49,9 +48,7 @@ public class PacketTranslatorRegistry { private static final ObjectArrayList> IGNORED_PACKETS = new ObjectArrayList<>(); static { - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators") : new Reflections("org.geysermc.connector.network.translators"); - - for (Class clazz : ref.getTypesAnnotatedWith(Translator.class)) { + for (Class clazz : FileUtils.getGeneratedClassesForAnnotation(Translator.class)) { Class packet = clazz.getAnnotation(Translator.class).packet(); GeyserConnector.getInstance().getLogger().debug("Found annotated translator: " + clazz.getCanonicalName() + " : " + packet.getSimpleName()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index c4d098f79..0fdfc3cc7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -43,7 +43,6 @@ import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LocaleUtils; -import org.reflections.Reflections; import java.util.*; import java.util.stream.Collectors; @@ -61,10 +60,8 @@ public abstract class ItemTranslator { static { /* Load item translators */ - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.item") : new Reflections("org.geysermc.connector.network.translators.item"); - Map loadedNbtItemTranslators = new HashMap<>(); - for (Class clazz : ref.getTypesAnnotatedWith(ItemRemapper.class)) { + for (Class clazz : FileUtils.getGeneratedClassesForAnnotation(ItemRemapper.class)) { int priority = clazz.getAnnotation(ItemRemapper.class).priority(); GeyserConnector.getInstance().getLogger().debug("Found annotated item translator: " + clazz.getCanonicalName()); diff --git a/connector/src/main/java/org/geysermc/connector/registry/Registries.java b/connector/src/main/java/org/geysermc/connector/registry/Registries.java index 6a0498a79..bd6520b38 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/Registries.java +++ b/connector/src/main/java/org/geysermc/connector/registry/Registries.java @@ -55,9 +55,9 @@ import java.util.Set; public class Registries { public static final SimpleRegistry BIOMES = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT); - public static final SimpleMappedRegistry BLOCK_ENTITIES = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.world.block.entity", BlockEntityRegistryLoader::new); + public static final SimpleMappedRegistry BLOCK_ENTITIES = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.world.block.entity.BlockEntity", BlockEntityRegistryLoader::new); - public static final SimpleMappedRegistry COLLISIONS = SimpleMappedRegistry.create(Pair.of("org.geysermc.connector.network.translators.collision.translators", "mappings/collision.json"), CollisionRegistryLoader::new); + public static final SimpleMappedRegistry COLLISIONS = SimpleMappedRegistry.create(Pair.of("org.geysermc.connector.network.translators.collision.translators.Translator", "mappings/collision.json"), CollisionRegistryLoader::new); public static final VersionedRegistry>> CRAFTING_DATA = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); @@ -77,7 +77,7 @@ public class Registries { public static final SimpleMappedRegistry SOUND_EFFECTS = SimpleMappedRegistry.create("mappings/effects.json", SoundEffectsRegistryLoader::new); - public static final SimpleMappedRegistry> SOUND_HANDLERS = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.sound", SoundHandlerRegistryLoader::new); + public static final SimpleMappedRegistry> SOUND_HANDLERS = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.sound.SoundHandler", SoundHandlerRegistryLoader::new); public static void init() { // no-op diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java index 4f6354af5..9f93b3873 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java @@ -26,9 +26,7 @@ package org.geysermc.connector.registry.loader; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; -import org.reflections.Reflections; import java.lang.annotation.Annotation; import java.util.Map; @@ -46,8 +44,7 @@ public class AnnotatedRegistryLoader implements Regi @Override public Map load(String input) { Map entries = new Object2ObjectOpenHashMap<>(); - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections(input) : new Reflections(input); - for (Class clazz : ref.getTypesAnnotatedWith(this.annotation)) { + for (Class clazz : FileUtils.getGeneratedClassesForAnnotation(input)) { try { entries.put(this.mapper.apply(clazz.getAnnotation(this.annotation)), (V) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException ex) { diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java index 70ba8b253..b20e1fe0e 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java @@ -40,7 +40,6 @@ import org.geysermc.connector.network.translators.collision.translators.OtherCol import org.geysermc.connector.network.translators.collision.translators.SolidCollision; import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.FileUtils; -import org.reflections.Reflections; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -55,8 +54,7 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader collisions = new Int2ObjectOpenHashMap<>(); Map, CollisionInfo> annotationMap = new HashMap<>(); - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections(input.key()) : new Reflections(input.key()); - for (Class clazz : ref.getTypesAnnotatedWith(CollisionRemapper.class)) { + for (Class clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) { GeyserConnector.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName()); CollisionRemapper collisionRemapper = clazz.getAnnotation(CollisionRemapper.class); diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 3fb5c2ebb..3369f16a5 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -30,16 +30,15 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.geysermc.connector.GeyserConnector; -import org.reflections.Reflections; -import org.reflections.serializers.XmlSerializer; -import org.reflections.util.ConfigurationBuilder; import java.io.*; -import java.net.URL; +import java.lang.annotation.Annotation; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.security.MessageDigest; +import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; public class FileUtils { @@ -193,24 +192,6 @@ public class FileUtils { return sha1; } - /** - * Get the stored reflection data for a given path - * - * @param path The path to get the reflection data for - * @return The created Reflections object - */ - public static Reflections getReflections(String path) { - Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners()); - XmlSerializer serializer = new XmlSerializer(); - URL resource = FileUtils.class.getClassLoader().getResource("META-INF/reflections/" + path + "-reflections.xml"); - try (InputStream inputStream = resource.openConnection().getInputStream()) { - reflections.merge(serializer.read(inputStream)); - } catch (IOException ignored) { - } - - return reflections; - } - /** * An android compatible version of {@link Files#readAllBytes} * @@ -242,4 +223,41 @@ public class FileUtils { throw new RuntimeException("Error while trying to read input stream!"); } } + + /** + * 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 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) { + InputStream annotatedClass = FileUtils.getResource(input); + BufferedReader reader = new BufferedReader(new InputStreamReader(annotatedClass)); + return reader.lines().map(className -> { + try { + return Class.forName(className); + } catch (ClassNotFoundException ex) { + GeyserConnector.getInstance().getLogger().error("Failed to find class " + className, ex); + throw new RuntimeException(ex); + } + }).collect(Collectors.toSet()); + } } diff --git a/connector/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/connector/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000..463d1efad --- /dev/null +++ b/connector/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,5 @@ +org.geysermc.processor.BlockEntityProcessor +org.geysermc.processor.CollisionRemapperProcessor +org.geysermc.processor.ItemRemapperProcessor +org.geysermc.processor.PacketTranslatorProcessor +org.geysermc.processor.SoundHandlerProcessor \ No newline at end of file diff --git a/pom.xml b/pom.xml index c03efb782..f8ab9165f 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ + ap bootstrap common connector From 697f35f6577f64b9e097d6d99ded58be04f4b0fe Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Jul 2021 18:09:55 -0400 Subject: [PATCH 02/11] Specify the first and last relevant block runtime IDs for items Improves startup time and processing --- .../populator/ItemRegistryPopulator.java | 74 +++++++++---------- .../registry/type/GeyserMappingItem.java | 5 +- connector/src/main/resources/mappings | 2 +- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java index 4715b6915..16aa56121 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java @@ -49,7 +49,6 @@ import org.geysermc.connector.network.translators.item.StoredItemMappings; import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.registry.Registries; import org.geysermc.connector.registry.type.*; -import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.FileUtils; import java.io.ByteArrayInputStream; @@ -268,11 +267,11 @@ public class ItemRegistryPopulator { if (bedrockIdentifier == null) { throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); } - int stackSize = mappingItem.getStackSize() == null ? 64 : mappingItem.getStackSize(); + int stackSize = mappingItem.getStackSize(); int bedrockBlockId = -1; - Integer blockRuntimeIdNode = entry.getValue().getBlockRuntimeId(); - if (blockRuntimeIdNode != null) { + Integer firstBlockRuntimeId = entry.getValue().getFirstBlockRuntimeId(); + if (firstBlockRuntimeId != null) { int blockIdOverride = bedrockBlockIdOverrides.getOrDefault(bedrockIdentifier, -1); if (blockIdOverride != -1) { // Straight from BDS is our best chance of getting an item that doesn't run into issues @@ -282,52 +281,51 @@ public class ItemRegistryPopulator { int aValidBedrockBlockId = blacklistedIdentifiers.getOrDefault(bedrockIdentifier, -1); if (aValidBedrockBlockId == -1) { // Fallback - bedrockBlockId = blockMappings.getBedrockBlockId(blockRuntimeIdNode); + bedrockBlockId = blockMappings.getBedrockBlockId(firstBlockRuntimeId); } else { // As of 1.16.220, every item requires a block runtime ID attached to it. // This is mostly for identifying different blocks with the same item ID - wool, slabs, some walls. // However, in order for some visuals and crafting to work, we need to send the first matching block state // as indexed by Bedrock's block palette // There are exceptions! But, ideally, the block ID override should take care of those. - String javaBlockIdentifier = BlockRegistries.JAVA_BLOCKS.get(blockRuntimeIdNode).getCleanJavaIdentifier(); NbtMapBuilder requiredBlockStatesBuilder = NbtMap.builder(); String correctBedrockIdentifier = blockMappings.getBedrockBlockStates().get(aValidBedrockBlockId).getString("name"); boolean firstPass = true; - for (Map.Entry blockEntry : BlockRegistries.JAVA_IDENTIFIERS.get().entrySet()) { - String aBlockIdentifier = BlockUtils.getCleanIdentifier(blockEntry.getKey()); - if (aBlockIdentifier.equals(javaBlockIdentifier)) { - int bedrockBlockRuntimeId = blockMappings.getBedrockBlockId(blockEntry.getValue()); - NbtMap blockTag = blockMappings.getBedrockBlockStates().get(bedrockBlockRuntimeId); - String bedrockName = blockTag.getString("name"); - if (!bedrockName.equals(correctBedrockIdentifier)) { - continue; - } - NbtMap states = blockTag.getCompound("states"); + // Block states are all grouped together. In the mappings, we store the first block runtime ID in order, + // and the last, if relevant. We then iterate over all those values and get their Bedrock equivalents + Integer lastBlockRuntimeId = entry.getValue().getLastBlockRuntimeId() == null ? firstBlockRuntimeId : entry.getValue().getLastBlockRuntimeId(); + for (int i = firstBlockRuntimeId; i <= lastBlockRuntimeId; i++) { + int bedrockBlockRuntimeId = blockMappings.getBedrockBlockId(i); + NbtMap blockTag = blockMappings.getBedrockBlockStates().get(bedrockBlockRuntimeId); + String bedrockName = blockTag.getString("name"); + if (!bedrockName.equals(correctBedrockIdentifier)) { + continue; + } + NbtMap states = blockTag.getCompound("states"); - if (firstPass) { - firstPass = false; - if (states.size() == 0) { - // No need to iterate and find all block states - this is the one, as there can't be any others - bedrockBlockId = bedrockBlockRuntimeId; - break; - } - requiredBlockStatesBuilder.putAll(states); - continue; - } - for (Map.Entry nbtEntry : states.entrySet()) { - Object value = requiredBlockStatesBuilder.get(nbtEntry.getKey()); - if (value != null && !nbtEntry.getValue().equals(value)) { // Null means this value has already been removed/deemed as unneeded - // This state can change between different block states, and therefore is not required - // to build a successful block state of this - requiredBlockStatesBuilder.remove(nbtEntry.getKey()); - } - } - if (requiredBlockStatesBuilder.size() == 0) { - // There are no required block states - // E.G. there was only a direction property that is no longer in play - // (States that are important include color for glass) + if (firstPass) { + firstPass = false; + if (states.size() == 0) { + // No need to iterate and find all block states - this is the one, as there can't be any others + bedrockBlockId = bedrockBlockRuntimeId; break; } + requiredBlockStatesBuilder.putAll(states); + continue; + } + for (Map.Entry nbtEntry : states.entrySet()) { + Object value = requiredBlockStatesBuilder.get(nbtEntry.getKey()); + if (value != null && !nbtEntry.getValue().equals(value)) { // Null means this value has already been removed/deemed as unneeded + // This state can change between different block states, and therefore is not required + // to build a successful block state of this + requiredBlockStatesBuilder.remove(nbtEntry.getKey()); + } + } + if (requiredBlockStatesBuilder.size() == 0) { + // There are no required block states + // E.G. there was only a direction property that is no longer in play + // (States that are important include color for glass) + break; } } diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java b/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java index 9219a8793..da91c412e 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java +++ b/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java @@ -35,8 +35,9 @@ import lombok.Data; public class GeyserMappingItem { @JsonProperty("bedrock_identifier") String bedrockIdentifier; @JsonProperty("bedrock_data") int bedrockData; - Integer blockRuntimeId; - @JsonProperty("stack_size") Integer stackSize; + Integer firstBlockRuntimeId; + Integer lastBlockRuntimeId; + @JsonProperty("stack_size") int stackSize = 64; @JsonProperty("tool_type") String toolType; @JsonProperty("tool_tier") String toolTier; } diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 7a67fa9ff..c921aa9b1 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 7a67fa9ff78496f4fc30b8f72d0eff451f1771e2 +Subproject commit c921aa9b12c2a1a471ef5f00aa040924426641dc From fde36a22342f768bc50e849c67226a9d10b292b6 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 12:47:02 -0500 Subject: [PATCH 03/11] Fix check for missing item mapping and update mappings submodule --- .../connector/registry/populator/ItemRegistryPopulator.java | 5 +++-- connector/src/main/resources/mappings | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java index 16aa56121..9c1f3aa2b 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java @@ -120,6 +120,7 @@ public class ItemRegistryPopulator { // Used to get the Bedrock namespaced ID (in instances where there are small differences) Object2IntMap bedrockIdentifierToId = new Object2IntOpenHashMap<>(); + bedrockIdentifierToId.defaultReturnValue(Short.MIN_VALUE); List itemNames = new ArrayList<>(); @@ -264,8 +265,8 @@ public class ItemRegistryPopulator { } String bedrockIdentifier = mappingItem.getBedrockIdentifier(); int bedrockId = bedrockIdentifierToId.getInt(bedrockIdentifier); - if (bedrockIdentifier == null) { - throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); + if (bedrockId == Short.MIN_VALUE) { + throw new RuntimeException("Missing Bedrock ID in mappings: " + bedrockIdentifier); } int stackSize = mappingItem.getStackSize(); diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index c921aa9b1..8351b0f5b 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit c921aa9b12c2a1a471ef5f00aa040924426641dc +Subproject commit 8351b0f5bb6e9a1d614f84e18c91e82288c34bf6 From b4921132e170d818cb549608016ea1e90273be1e Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 14:43:17 -0500 Subject: [PATCH 04/11] Replace BiMap with an Object2IntBiMap --- .../living/merchant/VillagerEntity.java | 2 +- ...BedrockInventoryTransactionTranslator.java | 2 +- .../player/BedrockActionTranslator.java | 2 +- .../holder/BlockInventoryHolder.java | 2 +- .../chest/DoubleChestInventoryTranslator.java | 2 +- .../java/world/JavaBlockChangeTranslator.java | 2 +- .../connector/registry/BlockRegistries.java | 5 +- .../loader/CollisionRegistryLoader.java | 11 +- .../connector/utils/Object2IntBiMap.java | 191 ++++++++++++++++++ 9 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java index 3c94f9de2..9aca12ba5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java @@ -114,7 +114,7 @@ public class VillagerEntity extends AbstractMerchantEntity { if (bedPosition != null) { bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); } - String bedRotationZ = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().get(bedId); + String bedRotationZ = BlockRegistries.JAVA_IDENTIFIERS.get().get(bedId); setRotation(rotation); setOnGround(isOnGround); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 6e02d4c6b..589fa49d0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -232,7 +232,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE) { // Otherwise insufficient permissions int blockState = session.getBlockMappings().getJavaBlockState(packet.getBlockRuntimeId()); - String blockName = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().getOrDefault(blockState, ""); + String blockName = BlockRegistries.JAVA_IDENTIFIERS.get().getOrDefault(blockState, ""); // In the future this can be used for structure blocks too, however not all elements // are available in each GUI if (blockName.contains("jigsaw")) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index c116045cb..2603f2a0f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -178,7 +178,7 @@ public class BedrockActionTranslator extends PacketTranslator 1 && (javaBlockString[0].equals("minecraft:chest") || javaBlockString[0].equals("minecraft:trapped_chest")) && !javaBlockString[1].contains("type=single")) { inventory.setHolderPosition(session.getLastInteractionBlockPosition()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java index 5abe088a9..d144e99b8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java @@ -101,7 +101,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); @@ -43,7 +42,7 @@ public class BlockRegistries { public static final SimpleMappedRegistry JAVA_BLOCKS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); - public static final MappedRegistry> JAVA_IDENTIFIERS = MappedRegistry.create(RegistryLoaders.empty(HashBiMap::create)); + public static final MappedRegistry> JAVA_IDENTIFIERS = MappedRegistry.create(RegistryLoaders.empty(Object2IntBiMap::new)); public static final SimpleMappedRegistry JAVA_CLEAN_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java index b20e1fe0e..fadd1e0d2 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java @@ -26,10 +26,10 @@ package org.geysermc.connector.registry.loader; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.common.collect.BiMap; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import lombok.AllArgsConstructor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.collision.BoundingBox; @@ -40,6 +40,7 @@ import org.geysermc.connector.network.translators.collision.translators.OtherCol import org.geysermc.connector.network.translators.collision.translators.SolidCollision; import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.Object2IntBiMap; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -71,16 +72,16 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader javaIdBlockMap = BlockRegistries.JAVA_IDENTIFIERS.get(); + Object2IntBiMap javaIdBlockMap = BlockRegistries.JAVA_IDENTIFIERS.get(); // Map of classes that don't change based on parameters that have already been created Map, BlockCollision> instantiatedCollision = new HashMap<>(); - for (Map.Entry entry : javaIdBlockMap.entrySet()) { - BlockCollision newCollision = instantiateCollision(entry.getKey(), entry.getValue(), annotationMap, instantiatedCollision, collisionList); + for (Object2IntMap.Entry entry : javaIdBlockMap.object2IntEntrySet()) { + BlockCollision newCollision = instantiateCollision(entry.getKey(), entry.getIntValue(), annotationMap, instantiatedCollision, collisionList); if (newCollision != null) { instantiatedCollision.put(newCollision.getClass(), newCollision); } - collisions.put(entry.getValue().intValue(), newCollision); + collisions.put(entry.getIntValue(), newCollision); } return collisions; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java b/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java new file mode 100644 index 000000000..915ce5ac3 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2019-2021 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.connector.utils; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectSets; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Objects; + +public class Object2IntBiMap implements Object2IntMap { + private final Object2IntMap forwards; + private final Int2ObjectMap backwards; + + public Object2IntBiMap() { + this(16); + } + + public Object2IntBiMap(int expected) { + this(expected, 0.75F); + } + + public Object2IntBiMap(T defaultForwardsValue) { + this(16, 0.75F, defaultForwardsValue, -1); + } + + public Object2IntBiMap(int expected, float loadFactor) { + this(expected, loadFactor, -1); + } + + public Object2IntBiMap(int expected, float loadFactor, int defaultBackwardsValue) { + this(expected, loadFactor, null, defaultBackwardsValue); + } + + public Object2IntBiMap(int expected, float loadFactor, T defaultForwardsValue, int defaultBackwardsValue) { + this.forwards = new Object2IntOpenHashMap<>(expected, loadFactor); + this.backwards = new Int2ObjectOpenHashMap<>(expected, loadFactor); + this.forwards.defaultReturnValue(defaultBackwardsValue); + this.backwards.defaultReturnValue(defaultForwardsValue); + } + + @Override + public int size() { + return this.forwards.size(); + } + + @Override + public boolean isEmpty() { + return this.forwards.isEmpty(); + } + + @Override + public int getInt(Object o) { + return this.forwards.getInt(o); + } + + public T get(int key) { + return this.backwards.get(key); + } + + @Override + public int getOrDefault(Object key, int defaultValue) { + return this.forwards.getOrDefault(key, defaultValue); + } + + public T getOrDefault(int key, T defaultValue) { + return this.backwards.getOrDefault(key, defaultValue); + } + + @Override + public void defaultReturnValue(int i) { + this.forwards.defaultReturnValue(i); + } + + public void defaultReturnValue(T v) { + this.backwards.defaultReturnValue(v); + } + + @Override + public int defaultReturnValue() { + return this.forwards.defaultReturnValue(); + } + + public T backwardsDefaultReturnValue() { + return this.backwards.defaultReturnValue(); + } + + @Override + public ObjectSet> object2IntEntrySet() { + return ObjectSets.unmodifiable(this.forwards.object2IntEntrySet()); + } + + public ObjectSet> int2ObjectEntrySet() { + return ObjectSets.unmodifiable(this.backwards.int2ObjectEntrySet()); + } + + @Override + public ObjectSet keySet() { + return this.forwards.keySet(); + } + + @Override + public IntCollection values() { + return this.forwards.values(); + } + + @Override + public boolean containsKey(Object o) { + return this.forwards.containsKey(o); + } + + @Override + public boolean containsValue(int i) { + return this.backwards.containsKey(i); + } + + @Override + public int put(T key, int value) { + this.backwards.put(value, key); + return this.forwards.put(key, value); + } + + @Override + public void putAll(@NotNull Map m) { + this.forwards.putAll(m); + for (Map.Entry entry : m.entrySet()) { + this.backwards.put((int) entry.getValue(), entry.getKey()); + } + } + + @Override + public int removeInt(Object key) { + if (!this.forwards.containsKey(key)) { + return this.defaultReturnValue(); + } + + int value = this.forwards.getInt(key); + if (!this.backwards.containsKey(value)) { + return this.defaultReturnValue(); + }; + this.backwards.remove(value); + return this.forwards.removeInt(key); + } + + @Override + public int hashCode() { + return this.forwards.hashCode(); + } + + @Override + public String toString() { + return this.forwards.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Object2IntBiMap that = (Object2IntBiMap) o; + return Objects.equals(this.forwards, that.forwards) && Objects.equals(this.backwards, that.backwards); + } +} From 1ad952b5814060957a6b677c3f2773b65cdf6498 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 15:20:40 -0500 Subject: [PATCH 05/11] Handle locale stuff downloading of main thread and add doc to Object2IntBiMap Technically LocaleUtils is not thread safe and people running Geyser for the first time on slow internet connections may see some untranslated messages. However, we were seeing startup times of about 1+ minutes on these slow connections, and it's better that players see untranslated messages for a short period of time rather than having to wait over a minute for the program to start up. Once the locale is installed, it doesn't need to be redownloaded again (unless there is a game update) and all translated messages will just work once this download is complete without clients needing to relog. --- .../geysermc/connector/utils/LocaleUtils.java | 237 +++++++++--------- .../connector/utils/Object2IntBiMap.java | 9 + 2 files changed, 129 insertions(+), 117 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index 7e32553b6..bd2bb9370 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -39,6 +39,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.zip.ZipFile; public class LocaleUtils { @@ -56,53 +57,55 @@ public class LocaleUtils { localesFolder.mkdir(); // Download the latest asset list and cache it - generateAssetCache(); - downloadAndLoadLocale(LanguageUtils.getDefaultLocale()); + generateAssetCache().whenComplete((aVoid, ex) -> downloadAndLoadLocale(LanguageUtils.getDefaultLocale())); } /** * Fetch the latest versions asset cache from Mojang so we can grab the locale files later */ - private static void generateAssetCache() { - try { - // Get the version manifest from Mojang - VersionManifest versionManifest = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); + private static CompletableFuture generateAssetCache() { + return CompletableFuture.supplyAsync(() -> { + try { + // Get the version manifest from Mojang + VersionManifest versionManifest = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); - // Get the url for the latest version of the games manifest - String latestInfoURL = ""; - for (Version version : versionManifest.getVersions()) { - if (version.getId().equals(MinecraftConstants.GAME_VERSION)) { - latestInfoURL = version.getUrl(); - break; + // Get the url for the latest version of the games manifest + String latestInfoURL = ""; + for (Version version : versionManifest.getVersions()) { + if (version.getId().equals(MinecraftConstants.GAME_VERSION)) { + latestInfoURL = version.getUrl(); + break; + } } + + // Make sure we definitely got a version + if (latestInfoURL.isEmpty()) { + throw new Exception(LanguageUtils.getLocaleStringLog("geyser.locale.fail.latest_version")); + } + + // Get the individual version manifest + VersionInfo versionInfo = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); + + // Get the client jar for use when downloading the en_us locale + GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); + clientJarInfo = versionInfo.getDownloads().get("client"); + GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(clientJarInfo)); + + // Get the assets list + JsonNode assets = GeyserConnector.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); + + // Put each asset into an array for use later + Iterator> assetIterator = assets.fields(); + while (assetIterator.hasNext()) { + Map.Entry entry = assetIterator.next(); + Asset asset = GeyserConnector.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); + ASSET_MAP.put(entry.getKey(), asset); + } + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.locale.fail.asset_cache", (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()))); } - - // Make sure we definitely got a version - if (latestInfoURL.isEmpty()) { - throw new Exception(LanguageUtils.getLocaleStringLog("geyser.locale.fail.latest_version")); - } - - // Get the individual version manifest - VersionInfo versionInfo = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); - - // Get the client jar for use when downloading the en_us locale - GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); - clientJarInfo = versionInfo.getDownloads().get("client"); - GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(clientJarInfo)); - - // Get the assets list - JsonNode assets = GeyserConnector.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); - - // Put each asset into an array for use later - Iterator> assetIterator = assets.fields(); - while (assetIterator.hasNext()) { - Map.Entry entry = assetIterator.next(); - Asset asset = GeyserConnector.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); - ASSET_MAP.put(entry.getKey(), asset); - } - } catch (Exception e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.locale.fail.asset_cache", (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()))); - } + return null; + }); } /** @@ -311,107 +314,107 @@ public class LocaleUtils { public static void init() { // no-op } -} -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class VersionManifest { - @JsonProperty("latest") - private LatestVersion latestVersion; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionManifest { + @JsonProperty("latest") + private LatestVersion latestVersion; - @JsonProperty("versions") - private List versions; -} + @JsonProperty("versions") + private List versions; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class LatestVersion { - @JsonProperty("release") - private String release; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class LatestVersion { + @JsonProperty("release") + private String release; - @JsonProperty("snapshot") - private String snapshot; -} + @JsonProperty("snapshot") + private String snapshot; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class Version { - @JsonProperty("id") - private String id; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class Version { + @JsonProperty("id") + private String id; - @JsonProperty("type") - private String type; + @JsonProperty("type") + private String type; - @JsonProperty("url") - private String url; + @JsonProperty("url") + private String url; - @JsonProperty("time") - private String time; + @JsonProperty("time") + private String time; - @JsonProperty("releaseTime") - private String releaseTime; -} + @JsonProperty("releaseTime") + private String releaseTime; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class VersionInfo { - @JsonProperty("id") - private String id; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionInfo { + @JsonProperty("id") + private String id; - @JsonProperty("type") - private String type; + @JsonProperty("type") + private String type; - @JsonProperty("time") - private String time; + @JsonProperty("time") + private String time; - @JsonProperty("releaseTime") - private String releaseTime; + @JsonProperty("releaseTime") + private String releaseTime; - @JsonProperty("assetIndex") - private AssetIndex assetIndex; + @JsonProperty("assetIndex") + private AssetIndex assetIndex; - @JsonProperty("downloads") - private Map downloads; -} + @JsonProperty("downloads") + private Map downloads; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class VersionDownload { - @JsonProperty("sha1") - private String sha1; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionDownload { + @JsonProperty("sha1") + private String sha1; - @JsonProperty("size") - private int size; + @JsonProperty("size") + private int size; - @JsonProperty("url") - private String url; -} + @JsonProperty("url") + private String url; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class AssetIndex { - @JsonProperty("id") - private String id; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class AssetIndex { + @JsonProperty("id") + private String id; - @JsonProperty("sha1") - private String sha1; + @JsonProperty("sha1") + private String sha1; - @JsonProperty("size") - private int size; + @JsonProperty("size") + private int size; - @JsonProperty("totalSize") - private int totalSize; + @JsonProperty("totalSize") + private int totalSize; - @JsonProperty("url") - private String url; -} + @JsonProperty("url") + private String url; + } -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -class Asset { - @JsonProperty("hash") - private String hash; + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class Asset { + @JsonProperty("hash") + private String hash; - @JsonProperty("size") - private int size; + @JsonProperty("size") + private int size; + } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java b/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java index 915ce5ac3..27277923b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Object2IntBiMap.java @@ -37,6 +37,15 @@ import org.jetbrains.annotations.NotNull; import java.util.Map; import java.util.Objects; +/** + * A primitive int BiMap implementation built around fastutil to + * reduce boxing and the memory footprint. Protocol has a + * {@link com.nukkitx.protocol.util.Int2ObjectBiMap} class, but it + * does not extend the Map interface making it difficult to utilize + * it in for loops and the registry system. + * + * @param the value + */ public class Object2IntBiMap implements Object2IntMap { private final Object2IntMap forwards; private final Int2ObjectMap backwards; From 95a1cbfa248f137dc54552ba64a8061df314c101 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 15:43:04 -0500 Subject: [PATCH 06/11] Some more micro optimizations --- .../network/translators/PacketTranslatorRegistry.java | 4 ++-- .../connector/registry/loader/CollisionRegistryLoader.java | 6 +++--- .../registry/populator/RecipeRegistryPopulator.java | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index 20c40a785..d8f726223 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -36,11 +36,11 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; -import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; public class PacketTranslatorRegistry { - private final Map, PacketTranslator> translators = new HashMap<>(); + private final Map, PacketTranslator> translators = new IdentityHashMap<>(); public static final PacketTranslatorRegistry JAVA_TRANSLATOR = new PacketTranslatorRegistry<>(); public static final PacketTranslatorRegistry BEDROCK_TRANSLATOR = new PacketTranslatorRegistry<>(); diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java index fadd1e0d2..e91ecb9df 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java @@ -44,7 +44,7 @@ import org.geysermc.connector.utils.Object2IntBiMap; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; import java.util.regex.Pattern; @@ -54,7 +54,7 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader load(Pair input) { Int2ObjectMap collisions = new Int2ObjectOpenHashMap<>(); - Map, CollisionInfo> annotationMap = new HashMap<>(); + Map, CollisionInfo> annotationMap = new IdentityHashMap<>(); for (Class clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) { GeyserConnector.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName()); @@ -75,7 +75,7 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader javaIdBlockMap = BlockRegistries.JAVA_IDENTIFIERS.get(); // Map of classes that don't change based on parameters that have already been created - Map, BlockCollision> instantiatedCollision = new HashMap<>(); + Map, BlockCollision> instantiatedCollision = new IdentityHashMap<>(); for (Object2IntMap.Entry entry : javaIdBlockMap.object2IntEntrySet()) { BlockCollision newCollision = instantiateCollision(entry.getKey(), entry.getIntValue(), annotationMap, instantiatedCollision, collisionList); if (newCollision != null) { diff --git a/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java b/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java index 97eeccece..a47457d14 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java @@ -67,7 +67,7 @@ public class RecipeRegistryPopulator { } int currentRecipeId = LAST_RECIPE_NET_ID; - for (Map.Entry version : Registries.ITEMS.get().entrySet()) { + for (Int2ObjectMap.Entry version : Registries.ITEMS.get().int2ObjectEntrySet()) { // Make a bit of an assumption here that the last recipe net ID will be equivalent between all versions LAST_RECIPE_NET_ID = currentRecipeId; Map> craftingData = new EnumMap<>(RecipeType.class); @@ -113,8 +113,8 @@ public class RecipeRegistryPopulator { c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); } - Registries.CRAFTING_DATA.register(version.getKey(), craftingData); - Registries.RECIPES.register(version.getKey(), recipes); + Registries.CRAFTING_DATA.register(version.getIntKey(), craftingData); + Registries.RECIPES.register(version.getIntKey(), recipes); } } From 15863bdaef6e95fbb3b23dd26e2a7327b080a978 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 16:24:55 -0500 Subject: [PATCH 07/11] Disable netty's resource leak detector if no level is set --- .../main/java/org/geysermc/connector/GeyserConnector.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 173e41573..98dfbc5ce 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -34,6 +34,7 @@ import com.nukkitx.network.util.EventLoops; import com.nukkitx.protocol.bedrock.BedrockServer; import io.netty.channel.epoll.Epoll; import io.netty.channel.kqueue.KQueue; +import io.netty.util.ResourceLeakDetector; import lombok.Getter; import lombok.Setter; import org.geysermc.common.PlatformType; @@ -125,6 +126,10 @@ public class GeyserConnector { private Metrics metrics; private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) { + if (System.getProperty("io.netty.leakDetection.level") == null) { + ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance + } + long startupTime = System.currentTimeMillis(); instance = this; From ae4e4397e3682d0eddb7f44fcf8ed3aa1918750a Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 17:04:16 -0500 Subject: [PATCH 08/11] Use a CompleteableFuture rather than constructing a new thread for logins --- .../network/session/GeyserSession.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 23f36b5f8..0daf15b60 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -560,8 +560,10 @@ public class GeyserSession implements CommandSender { } loggingIn = true; - // new thread so clients don't timeout - new Thread(() -> { + + // Use a future to prevent timeouts as all the authentication is handled sync + // This will be changed with the new protocol library. + CompletableFuture.supplyAsync(() -> { try { if (password != null && !password.isEmpty()) { AuthenticationService authenticationService; @@ -587,15 +589,14 @@ public class GeyserSession implements CommandSender { protocol = new MinecraftProtocol(validUsername); } - - connectDownstream(); } catch (InvalidCredentialsException | IllegalArgumentException e) { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.invalid", username)); disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.invalid.kick", getClientData().getLanguageCode())); } catch (RequestException ex) { ex.printStackTrace(); } - }).start(); + return null; + }).whenComplete((aVoid, ex) -> connectDownstream()); } /** @@ -608,8 +609,10 @@ public class GeyserSession implements CommandSender { } loggingIn = true; - // new thread so clients don't timeout - new Thread(() -> { + + // Use a future to prevent timeouts as all the authentication is handled sync + // This will be changed with the new protocol library. + CompletableFuture.supplyAsync(() -> { try { MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserConnector.OAUTH_CLIENT_ID); @@ -629,7 +632,8 @@ public class GeyserSession implements CommandSender { } catch (RequestException ex) { ex.printStackTrace(); } - }).start(); + return null; + }).whenComplete((aVoid, ex) -> connectDownstream()); } /** From 39324ce95dca8e16c2229f34d5c77a7c2c901242 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 17:41:47 -0500 Subject: [PATCH 09/11] Go back to normal thread for now using MSA auth, and only toggle leak detector on standalone --- .../java/org/geysermc/connector/GeyserConnector.java | 2 +- .../connector/network/session/GeyserSession.java | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 98dfbc5ce..107a48c4d 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -126,7 +126,7 @@ public class GeyserConnector { private Metrics metrics; private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) { - if (System.getProperty("io.netty.leakDetection.level") == null) { + if (platformType == PlatformType.STANDALONE && System.getProperty("io.netty.leakDetection.level") == null) { ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 0daf15b60..f9ae137bf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -609,10 +609,8 @@ public class GeyserSession implements CommandSender { } loggingIn = true; - - // Use a future to prevent timeouts as all the authentication is handled sync - // This will be changed with the new protocol library. - CompletableFuture.supplyAsync(() -> { + // new thread so clients don't timeout + new Thread(() -> { try { MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserConnector.OAUTH_CLIENT_ID); @@ -632,8 +630,7 @@ public class GeyserSession implements CommandSender { } catch (RequestException ex) { ex.printStackTrace(); } - return null; - }).whenComplete((aVoid, ex) -> connectDownstream()); + }).start(); } /** @@ -650,7 +647,7 @@ public class GeyserSession implements CommandSender { connectDownstream(); } catch (RequestException e) { if (!(e instanceof AuthPendingException)) { - e.printStackTrace(); + throw new RuntimeException("Failed to log in with Microsoft code!", e); } else { // Wait one second before trying again connector.getGeneralThreadPool().schedule(() -> attemptCodeAuthentication(msaAuthenticationService), 1, TimeUnit.SECONDS); From 1a0ac26398a50d49153de3afc16f29f2335d62ba Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 18 Jul 2021 17:44:08 -0500 Subject: [PATCH 10/11] Move leak detector to standalone bootstrap class --- .../platform/standalone/GeyserStandaloneBootstrap.java | 5 +++++ .../main/java/org/geysermc/connector/GeyserConnector.java | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index 551b0e584..1a25d2792 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.AnnotatedField; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import io.netty.util.ResourceLeakDetector; import lombok.Getter; import net.minecrell.terminalconsole.TerminalConsoleAppender; import org.apache.logging.log4j.Level; @@ -80,6 +81,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { private static final Map argsConfigKeys = new HashMap<>(); public static void main(String[] args) { + if (System.getProperty("io.netty.leakDetection.level") == null) { + ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance + } + GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); // Set defaults boolean useGuiOpts = bootstrap.useGui; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 107a48c4d..173e41573 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -34,7 +34,6 @@ import com.nukkitx.network.util.EventLoops; import com.nukkitx.protocol.bedrock.BedrockServer; import io.netty.channel.epoll.Epoll; import io.netty.channel.kqueue.KQueue; -import io.netty.util.ResourceLeakDetector; import lombok.Getter; import lombok.Setter; import org.geysermc.common.PlatformType; @@ -126,10 +125,6 @@ public class GeyserConnector { private Metrics metrics; private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) { - if (platformType == PlatformType.STANDALONE && System.getProperty("io.netty.leakDetection.level") == null) { - ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance - } - long startupTime = System.currentTimeMillis(); instance = this; From ef7c6732767151d958e2192dc2bd167a3e0d139e Mon Sep 17 00:00:00 2001 From: Redned Date: Wed, 21 Jul 2021 21:39:30 -0500 Subject: [PATCH 11/11] Properly use CompleteableFuture for MSA auth --- .../network/session/GeyserSession.java | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index f9ae137bf..df2e3767c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -609,28 +609,31 @@ public class GeyserSession implements CommandSender { } loggingIn = true; + + // This just looks cool + SetTimePacket packet = new SetTimePacket(); + packet.setTime(16000); + sendUpstreamPacket(packet); + // new thread so clients don't timeout - new Thread(() -> { + MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserConnector.OAUTH_CLIENT_ID); + + // Use a future to prevent timeouts as all the authentication is handled sync + // This will be changed with the new protocol library. + CompletableFuture.supplyAsync(() -> { try { - MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserConnector.OAUTH_CLIENT_ID); - - MsaAuthenticationService.MsCodeResponse response = msaAuthenticationService.getAuthCode(); - LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, response); - - // This just looks cool - SetTimePacket packet = new SetTimePacket(); - packet.setTime(16000); - sendUpstreamPacket(packet); - - // Wait for the code to validate - attemptCodeAuthentication(msaAuthenticationService); - } catch (InvalidCredentialsException | IllegalArgumentException e) { - connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.invalid", getAuthData().getName())); - disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.invalid.kick", getClientData().getLanguageCode())); - } catch (RequestException ex) { - ex.printStackTrace(); + return msaAuthenticationService.getAuthCode(); + } catch (RequestException e) { + throw new CompletionException(e); } - }).start(); + }).whenComplete((response, ex) -> { + if (ex != null) { + ex.printStackTrace(); + return; + } + LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, response); + attemptCodeAuthentication(msaAuthenticationService); + }); } /**