From 8627787ea991f66b368e93648c79abf051820fe9 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Mon, 10 Jan 2022 18:45:26 +0100 Subject: [PATCH 001/125] Added basic extension loading --- .../java/org/geysermc/geyser/GeyserImpl.java | 5 + .../geysermc/geyser/extension/Extension.java | 50 ++++ .../extension/ExtensionClassLoader.java | 97 +++++++ .../extension/ExtensionDescription.java | 94 +++++++ .../geyser/extension/ExtensionLoader.java | 173 +++++++++++++ .../geyser/extension/ExtensionManager.java | 243 ++++++++++++++++++ .../geyser/extension/GeyserExtension.java | 176 +++++++++++++ .../InvalidDescriptionException.java | 40 +++ .../exception/InvalidExtensionException.java | 40 +++ 9 files changed, 918 insertions(+) create mode 100644 core/src/main/java/org/geysermc/geyser/extension/Extension.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index eaadd15fa..abfc7bb83 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -52,6 +52,7 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.command.CommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.extension.ExtensionManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.ConnectorServerEventHandler; import org.geysermc.geyser.pack.ResourcePack; @@ -161,6 +162,8 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); + ExtensionManager.init(); + if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { @@ -444,6 +447,8 @@ public class GeyserImpl implements GeyserApi { newsHandler.shutdown(); this.getCommandManager().getCommands().clear(); + ExtensionManager.getExtensionManager().disableExtensions(); + bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } diff --git a/core/src/main/java/org/geysermc/geyser/extension/Extension.java b/core/src/main/java/org/geysermc/geyser/extension/Extension.java new file mode 100644 index 000000000..a911e56ad --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/Extension.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserImpl; +import java.io.File; +import java.io.InputStream; + +public interface Extension { + void onLoad(); + void onEnable(); + void onDisable(); + + boolean isEnabled(); + boolean isDisabled(); + + File getDataFolder(); + ExtensionDescription getDescription(); + String getName(); + + InputStream getResource(String filename); + void saveResource(String filename, boolean replace); + + GeyserImpl getGeyser(); + ClassLoader getClassLoader(); + ExtensionLoader getExtensionLoader(); +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java new file mode 100644 index 000000000..3261adc01 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.extension.exception.InvalidExtensionException; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class ExtensionClassLoader extends URLClassLoader { + private ExtensionLoader loader; + private Map classes = new HashMap<>(); + public GeyserExtension extension; + + public ExtensionClassLoader(ExtensionLoader loader, ClassLoader parent, ExtensionDescription description, File file) throws InvalidExtensionException, MalformedURLException { + super(new URL[] { file.toURI().toURL() }, parent); + this.loader = loader; + + try { + Class jarClass; + try { + jarClass = Class.forName(description.getMain(), true, this); + } catch (ClassNotFoundException ex) { + throw new InvalidExtensionException("Class " + description.getMain() + " not found, extension cannot be loaded", ex); + } + + Class extensionClass; + try { + extensionClass = jarClass.asSubclass(GeyserExtension.class); + } catch (ClassCastException ex) { + throw new InvalidExtensionException("Main class " + description.getMain() + " should extends GeyserExtension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); + } + + extension = extensionClass.newInstance(); + } catch (IllegalAccessException ex) { + throw new InvalidExtensionException("No public constructor", ex); + } catch (InstantiationException ex) { + throw new InvalidExtensionException("Abnormal extension type", ex); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + return this.findClass(name, true); + } + + protected Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { + if (name.startsWith("org.geysermc.geyser.") || name.startsWith("org.geysermc.connector.") || name.startsWith("net.minecraft.")) { + throw new ClassNotFoundException(name); + } + Class result = classes.get(name); + if(result == null) { + if(checkGlobal) { + result = loader.getClassByName(name); + } + if(result == null) { + result = super.findClass(name); + if (result != null) { + loader.setClass(name, result); + } + } + classes.put(name, result); + } + return result; + } + + Set getClasses() { + return classes.keySet(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java new file mode 100644 index 000000000..9f714dc11 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.extension.exception.InvalidDescriptionException; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.util.*; + +public class ExtensionDescription { + private String name; + private String main; + private List api; + private String version; + private final List authors = new ArrayList<>(); + + public ExtensionDescription(String yamlString) throws InvalidDescriptionException { + DumperOptions dumperOptions = new DumperOptions(); + dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(dumperOptions); + this.loadMap(yaml.loadAs(yamlString, LinkedHashMap.class)); + } + + private void loadMap(Map yamlMap) throws InvalidDescriptionException { + this.name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); + if (this.name.equals("")) { + throw new InvalidDescriptionException("Invalid extension name"); + } + this.name = this.name.replace(" ", "_"); + this.version = String.valueOf(yamlMap.get("version")); + this.main = (String) yamlMap.get("main"); + + Object api = yamlMap.get("api"); + if (api instanceof List) { + this.api = (List) api; + } else { + List list = new ArrayList<>(); + list.add((String) api); + this.api = list; + } + + if (yamlMap.containsKey("author")) { + this.authors.add((String) yamlMap.get("author")); + } + + if (yamlMap.containsKey("authors")) { + this.authors.addAll((Collection) yamlMap.get("authors")); + } + } + + public String getName() { + return this.name; + } + + public String getMain() { + return this.main; + } + + public List getAPIVersions() { + return api; + } + + public String getVersion() { + return this.version; + } + + public List getAuthors() { + return this.authors; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java new file mode 100644 index 000000000..15795c2c5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.extension.exception.InvalidExtensionException; +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; + +public class ExtensionLoader { + private final Map classes = new HashMap<>(); + private final Map classLoaders = new HashMap<>(); + + public GeyserExtension loadExtension(File file) throws InvalidExtensionException { + if (file == null) { + throw new InvalidExtensionException("File is null"); + } + + if (!file.exists()) { + throw new InvalidExtensionException(new FileNotFoundException(file.getPath()) + " does not exist"); + } + + final ExtensionDescription description; + try { + description = getExtensionDescription(file); + } catch (InvalidDescriptionException e) { + throw new InvalidExtensionException(e); + } + + final File parentFile = file.getParentFile(); + final File dataFolder = new File(parentFile, description.getName()); + if (dataFolder.exists() && !dataFolder.isDirectory()) { + throw new InvalidExtensionException("The folder " + dataFolder.getPath() + " is not a directory and is the data folder for the extension " + description.getName() + "!"); + } + + final ExtensionClassLoader loader; + try { + loader = new ExtensionClassLoader(this, getClass().getClassLoader(), description, file); + } catch (Throwable e) { + throw new InvalidExtensionException(e); + } + classLoaders.put(description.getName(), loader); + + setup(loader.extension, description, dataFolder, file); + return loader.extension; + } + + private void setup(GeyserExtension extension, ExtensionDescription description, File dataFolder, File file) { + extension.init(GeyserImpl.getInstance(), description, dataFolder, file, this); + extension.onLoad(); + } + + public ExtensionDescription getExtensionDescription(File file) throws InvalidDescriptionException { + JarFile jarFile = null; + InputStream stream = null; + + try { + jarFile = new JarFile(file); + + JarEntry descriptionEntry = jarFile.getJarEntry("extension.yml"); + if (descriptionEntry == null) { + throw new InvalidDescriptionException(new FileNotFoundException("extension.yml") + " does not exist in the jar file!"); + } + + stream = jarFile.getInputStream(descriptionEntry); + + InputStreamReader reader = new InputStreamReader(stream); + StringBuilder builder = new StringBuilder(); + String temp; + BufferedReader bufferedReader = new BufferedReader(reader); + temp = bufferedReader.readLine(); + while (temp != null) { + if (builder.length() != 0) { + builder.append("\n"); + } + builder.append(temp); + temp = bufferedReader.readLine(); + } + + return new ExtensionDescription(builder.toString()); + } catch (IOException e) { + throw new InvalidDescriptionException(e); + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException e) { + } + } + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + public Pattern[] getExtensionFilters() { + return new Pattern[] { Pattern.compile("^.+\\.jar$") }; + } + + public Class getClassByName(final String name) throws ClassNotFoundException{ + Class clazz = classes.get(name); + try { + for(ExtensionClassLoader loader : classLoaders.values()) { + try { + clazz = loader.findClass(name,false); + } catch(NullPointerException e) { + } + } + return clazz; + } catch(NullPointerException s) { + return null; + } + } + + public void setClass(String name, final Class clazz) { + if(!classes.containsKey(name)) { + classes.put(name,clazz); + } + } + + protected void removeClass(String name) { + Class clazz = classes.remove(name); + } + + public void enableExtension(Extension extension) { + if (extension instanceof GeyserExtension) { + if(!extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.getDescription().getName()); + ((GeyserExtension) extension).setEnabled(true); + } + } + } + + public void disableExtension(Extension extension) { + if (extension instanceof GeyserExtension) { + if(extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.getDescription().getName()); + ((GeyserExtension) extension).setEnabled(false); + } + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java new file mode 100644 index 000000000..ad757d499 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserImpl; + +import java.io.File; +import java.io.FilenameFilter; +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.regex.Pattern; + +public class ExtensionManager { + private static ExtensionManager extensionManager = null; + + protected Map extensions = new LinkedHashMap<>(); + protected Map fileAssociations = new HashMap<>(); + + public static void init() { + GeyserImpl.getInstance().getLogger().info("Loading extensions..."); + extensionManager = new ExtensionManager(); + extensionManager.registerInterface(ExtensionLoader.class); + extensionManager.loadExtensions(new File("extensions")); + GeyserImpl.getInstance().getLogger().info("Loaded " + extensionManager.extensions.size() + " extensions."); + + for (Extension extension : extensionManager.getExtensions().values()) { + if (!extension.isEnabled()) { + extensionManager.enableExtension(extension); + } + } + } + + public static ExtensionManager getExtensionManager() { + return extensionManager; + } + + public Extension getExtension(String name) { + if (this.extensions.containsKey(name)) { + return this.extensions.get(name); + } + return null; + } + + public Map getExtensions() { + return this.extensions; + } + + public void registerInterface(Class loader) { + ExtensionLoader instance; + + if (ExtensionLoader.class.isAssignableFrom(loader)) { + Constructor constructor; + + try { + constructor = loader.getConstructor(); + instance = constructor.newInstance(); + } catch (NoSuchMethodException ex) { // This should never happen + String className = loader.getName(); + + throw new IllegalArgumentException("Class " + className + " does not have a public constructor", ex); + } catch (Exception ex) { // This should never happen + throw new IllegalArgumentException("Unexpected exception " + ex.getClass().getName() + " while attempting to construct a new instance of " + loader.getName(), ex); + } + } else { + throw new IllegalArgumentException("Class " + loader.getName() + " does not implement interface ExtensionLoader"); + } + + Pattern[] patterns = instance.getExtensionFilters(); + + synchronized (this) { + for (Pattern pattern : patterns) { + fileAssociations.put(pattern, instance); + } + } + } + + public GeyserExtension loadExtension(File file, Map loaders) { + for (ExtensionLoader loader : (loaders == null ? this.fileAssociations : loaders).values()) { + for (Pattern pattern : loader.getExtensionFilters()) { + if (pattern.matcher(file.getName()).matches()) { + try { + ExtensionDescription description = loader.getExtensionDescription(file); + if (description != null) { + GeyserExtension extension = loader.loadExtension(file); + + if (extension != null) { + this.extensions.put(extension.getDescription().getName(), extension); + + return extension; + } + } + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error("Could not load extension", e); + return null; + } + } + } + } + + return null; + } + + public Map loadExtensions(File dictionary) { + if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { + GeyserImpl.getInstance().getLogger().error("Cannot load extensions in a development environment, aborting extension loading"); + return new HashMap<>(); + } + if (!GeyserImpl.VERSION.contains(".")) { + GeyserImpl.getInstance().getLogger().error("Something went wrong with the Geyser version number, aborting extension loading"); + return new HashMap<>(); + } + + if (!dictionary.exists()) { + dictionary.mkdir(); + } + if (!dictionary.isDirectory()) { + return new HashMap<>(); + } + + Map extensions = new LinkedHashMap<>(); + Map loadedExtensions = new LinkedHashMap<>(); + + for (final ExtensionLoader loader : this.fileAssociations.values()) { + for (File file : dictionary.listFiles((dir, name) -> { + for (Pattern pattern : loader.getExtensionFilters()) { + if (pattern.matcher(name).matches()) { + return true; + } + } + return false; + })) { + if (file.isDirectory()) { + continue; + } + + try { + ExtensionDescription description = loader.getExtensionDescription(file); + if (description != null) { + String name = description.getName(); + + if (extensions.containsKey(name) || this.getExtension(name) != null) { + GeyserImpl.getInstance().getLogger().warning("Found duplicate extension '" + name + "', ignoring '" + file.getName() + "'"); + continue; + } + + boolean compatible = false; + + for (String version : description.getAPIVersions()) { + try { + //Check the format: majorVersion.minorVersion.patch + if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", version)) { + throw new IllegalArgumentException(); + } + } catch (NullPointerException | IllegalArgumentException e) { + GeyserImpl.getInstance().getLogger().error("Could't load extension " + name + ": Wrong API format"); + continue; + } + + String[] versionArray = version.split("\\."); + String[] apiVersion = GeyserImpl.VERSION.split("\\."); + + //Completely different API version + if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + continue; + } + + //If the extension requires new API features, being backwards compatible + if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + continue; + } + + compatible = true; + break; + } + + if (!compatible) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name +": Incompatible API version"); + } + + extensions.put(name, file); + loadedExtensions.put(name, this.loadExtension(file, this.fileAssociations)); + } + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error("Couldn't load " +file.getName()+ " in folder " + dictionary + ": ", e); + } + } + } + + return loadedExtensions; + } + + public void enableExtension(Extension extension) { + if (!extension.isEnabled()) { + try { + extension.getExtensionLoader().enableExtension(extension); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error("Error enabling extension " + extension.getName() + ": ", e); + this.disableExtension(extension); + } + } + } + + public void disableExtension(Extension extension) { + if (extension.isEnabled()) { + try { + extension.getExtensionLoader().disableExtension(extension); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error("Error disabling extension " + extension.getName() + ": ", e); + } + } + } + + public void disableExtensions() { + for (Extension extension : this.getExtensions().values()) { + this.disableExtension(extension); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java new file mode 100644 index 000000000..c6d7b6f10 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserImpl; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; + +public class GeyserExtension implements Extension { + private boolean initialized = false; + private boolean enabled = false; + private File file = null; + private File dataFolder = null; + private ClassLoader classLoader = null; + private GeyserImpl geyser = null; + private ExtensionLoader loader; + private ExtensionDescription description = null; + + @Override + public void onLoad() { + } + + @Override + public void onEnable() { + } + + @Override + public void onDisable() { + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean value) { + if (this.enabled != value) { + this.enabled = value; + if (this.enabled) { + onEnable(); + } else { + onDisable(); + } + } + } + + @Override + public boolean isDisabled() { + return !this.enabled; + } + + @Override + public File getDataFolder() { + return this.dataFolder; + } + + @Override + public ExtensionDescription getDescription() { + return this.description; + } + + @Override + public String getName() { + return this.description.getName(); + } + + public void init(GeyserImpl geyser, ExtensionDescription description, File dataFolder, File file, ExtensionLoader loader) { + if (!this.initialized) { + this.initialized = true; + this.file = file; + this.dataFolder = dataFolder; + this.classLoader = this.getClass().getClassLoader(); + this.geyser = geyser; + this.loader = loader; + this.description = description; + } + } + + @Override + public InputStream getResource(String filename) { + if (filename == null) { + throw new IllegalArgumentException("Filename cannot be null"); + } + + try { + URL url = this.classLoader.getResource(filename); + + if (url == null) { + return null; + } + + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } + } + + @Override + public void saveResource(String filename, boolean replace) { + if (filename == null || filename.equals("")) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + + filename = filename.replace('\\', '/'); + InputStream in = getResource(filename); + if (in == null) { + throw new IllegalArgumentException("The embedded resource '" + filename + "' cannot be found in " + file); + } + + File outFile = new File(dataFolder, filename); + int lastIndex = filename.lastIndexOf('/'); + File outDir = new File(dataFolder, filename.substring(0, Math.max(lastIndex, 0))); + + if (!outDir.exists()) { + outDir.mkdirs(); + } + + try { + if (!outFile.exists() || replace) { + OutputStream out = new FileOutputStream(outFile); + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + in.close(); + } else { + this.geyser.getLogger().warning("Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists."); + } + } catch (IOException ex) { + this.geyser.getLogger().severe("Could not save " + outFile.getName() + " to " + outFile, ex); + } + } + + @Override + public GeyserImpl getGeyser() { + return this.geyser; + } + + @Override + public ClassLoader getClassLoader() { + return this.classLoader; + } + + @Override + public ExtensionLoader getExtensionLoader() { + return this.loader; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java b/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java new file mode 100644 index 000000000..0764dc1d1 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension.exception; + +public class InvalidDescriptionException extends Exception { + public InvalidDescriptionException(Throwable cause) { + super(cause); + } + + public InvalidDescriptionException(String message) { + super(message); + } + + public InvalidDescriptionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java b/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java new file mode 100644 index 000000000..f29f5d280 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension.exception; + +public class InvalidExtensionException extends Exception { + public InvalidExtensionException(Throwable cause) { + super(cause); + } + + public InvalidExtensionException(String message) { + super(message); + } + + public InvalidExtensionException(String message, Throwable cause) { + super(message, cause); + } +} From 6757437193b7ad0c72ffc9bf47e1fb7614f459f7 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:01:36 +0100 Subject: [PATCH 002/125] Moved the extension into geyser-api --- .../geyser/api}/extension/Extension.java | 17 ++-- .../api/extension/ExtensionDescription.java | 36 ++++++++ .../geyser/api/extension/ExtensionLoader.java | 41 +++++++++ .../geyser/api/extension/ExtensionLogger.java | 90 +++++++++++++++++++ .../api}/extension/GeyserExtension.java | 43 +++++---- .../InvalidDescriptionException.java | 2 +- .../exception/InvalidExtensionException.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 8 +- ...r.java => GeyserExtensionClassLoader.java} | 18 ++-- ...n.java => GeyserExtensionDescription.java} | 22 +++-- ...Loader.java => GeyserExtensionLoader.java} | 51 ++++++----- .../extension/GeyserExtensionLogger.java | 88 ++++++++++++++++++ ...nager.java => GeyserExtensionManager.java} | 65 +++++++------- 13 files changed, 382 insertions(+), 101 deletions(-) rename {core/src/main/java/org/geysermc/geyser => api/geyser/src/main/java/org/geysermc/geyser/api}/extension/Extension.java (83%) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java rename {core/src/main/java/org/geysermc/geyser => api/geyser/src/main/java/org/geysermc/geyser/api}/extension/GeyserExtension.java (81%) rename {core/src/main/java/org/geysermc/geyser => api/geyser/src/main/java/org/geysermc/geyser/api}/extension/exception/InvalidDescriptionException.java (96%) rename {core/src/main/java/org/geysermc/geyser => api/geyser/src/main/java/org/geysermc/geyser/api}/extension/exception/InvalidExtensionException.java (96%) rename core/src/main/java/org/geysermc/geyser/extension/{ExtensionClassLoader.java => GeyserExtensionClassLoader.java} (80%) rename core/src/main/java/org/geysermc/geyser/extension/{ExtensionDescription.java => GeyserExtensionDescription.java} (85%) rename core/src/main/java/org/geysermc/geyser/extension/{ExtensionLoader.java => GeyserExtensionLoader.java} (74%) create mode 100644 core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java rename core/src/main/java/org/geysermc/geyser/extension/{ExtensionManager.java => GeyserExtensionManager.java} (79%) diff --git a/core/src/main/java/org/geysermc/geyser/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java similarity index 83% rename from core/src/main/java/org/geysermc/geyser/extension/Extension.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java index a911e56ad..8a820d8ac 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/Extension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -23,9 +23,9 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.extension; +package org.geysermc.geyser.api.extension; -import org.geysermc.geyser.GeyserImpl; +import org.geysermc.api.GeyserApiBase; import java.io.File; import java.io.InputStream; @@ -37,14 +37,15 @@ public interface Extension { boolean isEnabled(); boolean isDisabled(); - File getDataFolder(); - ExtensionDescription getDescription(); - String getName(); + File dataFolder(); + ExtensionDescription description(); + String name(); InputStream getResource(String filename); void saveResource(String filename, boolean replace); - GeyserImpl getGeyser(); - ClassLoader getClassLoader(); - ExtensionLoader getExtensionLoader(); + ClassLoader classLoader(); + ExtensionLoader extensionLoader(); + ExtensionLogger logger(); + GeyserApiBase geyserApi(); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java new file mode 100644 index 000000000..0f752e72d --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import java.util.*; + +public interface ExtensionDescription { + String name(); + String main(); + List ApiVersions(); + String version(); + List authors(); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java new file mode 100644 index 000000000..c558957eb --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; + +import java.io.File; +import java.util.regex.Pattern; + +public interface ExtensionLoader { + GeyserExtension loadExtension(File file) throws InvalidExtensionException; + ExtensionDescription extensionDescription(File file) throws InvalidDescriptionException; + Pattern[] extensionFilters(); + Class classByName(final String name) throws ClassNotFoundException; + void enableExtension(Extension extension); + void disableExtension(Extension extension); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java new file mode 100644 index 000000000..6b5d86153 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.extension; + +public interface ExtensionLogger { + /** + * Get the logger prefix + * @return the logger prefix + */ + String prefix(); + + /** + * Logs a severe message to console + * + * @param message the message to log + */ + void severe(String message); + + /** + * Logs a severe message and an exception to console + * + * @param message the message to log + * @param error the error to throw + */ + void severe(String message, Throwable error); + + /** + * Logs an error message to console + * + * @param message the message to log + */ + void error(String message); + + /** + * Logs an error message and an exception to console + * + * @param message the message to log + * @param error the error to throw + */ + void error(String message, Throwable error); + + /** + * Logs a warning message to console + * + * @param message the message to log + */ + void warning(String message); + + /** + * Logs an info message to console + * + * @param message the message to log + */ + void info(String message); + + /** + * Logs a debug message to console + * + * @param message the message to log + */ + void debug(String message); + + /** + * If debug is enabled for this logger + */ + boolean isDebug(); +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java similarity index 81% rename from core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java index c6d7b6f10..3ed66b444 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java @@ -23,9 +23,9 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.extension; +package org.geysermc.geyser.api.extension; -import org.geysermc.geyser.GeyserImpl; +import org.geysermc.api.GeyserApiBase; import java.io.*; import java.net.URL; import java.net.URLConnection; @@ -36,9 +36,10 @@ public class GeyserExtension implements Extension { private File file = null; private File dataFolder = null; private ClassLoader classLoader = null; - private GeyserImpl geyser = null; private ExtensionLoader loader; + private ExtensionLogger logger; private ExtensionDescription description = null; + private GeyserApiBase api = null; @Override public void onLoad() { @@ -74,29 +75,30 @@ public class GeyserExtension implements Extension { } @Override - public File getDataFolder() { + public File dataFolder() { return this.dataFolder; } @Override - public ExtensionDescription getDescription() { + public ExtensionDescription description() { return this.description; } @Override - public String getName() { - return this.description.getName(); + public String name() { + return this.description.name(); } - public void init(GeyserImpl geyser, ExtensionDescription description, File dataFolder, File file, ExtensionLoader loader) { + public void init(GeyserApiBase api, ExtensionLogger logger, ExtensionLoader loader, ExtensionDescription description, File dataFolder, File file) { if (!this.initialized) { this.initialized = true; this.file = file; this.dataFolder = dataFolder; this.classLoader = this.getClass().getClassLoader(); - this.geyser = geyser; this.loader = loader; + this.logger = logger; this.description = description; + this.api = api; } } @@ -152,25 +154,30 @@ public class GeyserExtension implements Extension { out.close(); in.close(); } else { - this.geyser.getLogger().warning("Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists."); + this.logger.warning("Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists."); } } catch (IOException ex) { - this.geyser.getLogger().severe("Could not save " + outFile.getName() + " to " + outFile, ex); + this.logger.severe("Could not save " + outFile.getName() + " to " + outFile, ex); } } @Override - public GeyserImpl getGeyser() { - return this.geyser; - } - - @Override - public ClassLoader getClassLoader() { + public ClassLoader classLoader() { return this.classLoader; } @Override - public ExtensionLoader getExtensionLoader() { + public ExtensionLoader extensionLoader() { return this.loader; } + + @Override + public ExtensionLogger logger() { + return this.logger; + } + + @Override + public GeyserApiBase geyserApi() { + return this.api; + } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java similarity index 96% rename from core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java index 0764dc1d1..ca2fabad9 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidDescriptionException.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.extension.exception; +package org.geysermc.geyser.api.extension.exception; public class InvalidDescriptionException extends Exception { public InvalidDescriptionException(Throwable cause) { diff --git a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java similarity index 96% rename from core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java index f29f5d280..1053e6d50 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/exception/InvalidExtensionException.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.extension.exception; +package org.geysermc.geyser.api.extension.exception; public class InvalidExtensionException extends Exception { public InvalidExtensionException(Throwable cause) { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 82a5fd354..7e61b3af7 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -52,7 +52,7 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.command.CommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; -import org.geysermc.geyser.extension.ExtensionManager; +import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.ConnectorServerEventHandler; import org.geysermc.geyser.pack.ResourcePack; @@ -155,6 +155,8 @@ public class GeyserImpl implements GeyserApi { MessageTranslator.init(); MinecraftLocale.init(); + GeyserExtensionManager.init(); + start(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -198,8 +200,6 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); - ExtensionManager.init(); - if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { @@ -460,7 +460,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - ExtensionManager.getExtensionManager().disableExtensions(); + GeyserExtensionManager.getExtensionManager().disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java similarity index 80% rename from core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java rename to core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 3261adc01..a1ccc063e 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/ExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -25,7 +25,9 @@ package org.geysermc.geyser.extension; -import org.geysermc.geyser.extension.exception.InvalidExtensionException; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; import java.io.File; import java.net.MalformedURLException; import java.net.URL; @@ -34,28 +36,28 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -public class ExtensionClassLoader extends URLClassLoader { - private ExtensionLoader loader; +public class GeyserExtensionClassLoader extends URLClassLoader { + private GeyserExtensionLoader loader; private Map classes = new HashMap<>(); public GeyserExtension extension; - public ExtensionClassLoader(ExtensionLoader loader, ClassLoader parent, ExtensionDescription description, File file) throws InvalidExtensionException, MalformedURLException { + public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, ExtensionDescription description, File file) throws InvalidExtensionException, MalformedURLException { super(new URL[] { file.toURI().toURL() }, parent); this.loader = loader; try { Class jarClass; try { - jarClass = Class.forName(description.getMain(), true, this); + jarClass = Class.forName(description.main(), true, this); } catch (ClassNotFoundException ex) { - throw new InvalidExtensionException("Class " + description.getMain() + " not found, extension cannot be loaded", ex); + throw new InvalidExtensionException("Class " + description.main() + " not found, extension cannot be loaded", ex); } Class extensionClass; try { extensionClass = jarClass.asSubclass(GeyserExtension.class); } catch (ClassCastException ex) { - throw new InvalidExtensionException("Main class " + description.getMain() + " should extends GeyserExtension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); + throw new InvalidExtensionException("Main class " + description.main() + " should extends GeyserExtension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); } extension = extensionClass.newInstance(); @@ -78,7 +80,7 @@ public class ExtensionClassLoader extends URLClassLoader { Class result = classes.get(name); if(result == null) { if(checkGlobal) { - result = loader.getClassByName(name); + result = loader.classByName(name); } if(result == null) { result = super.findClass(name); diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java similarity index 85% rename from core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java rename to core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index 9f714dc11..ee215f2e4 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/ExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -25,20 +25,19 @@ package org.geysermc.geyser.extension; -import org.geysermc.geyser.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; - import java.util.*; -public class ExtensionDescription { +public class GeyserExtensionDescription implements org.geysermc.geyser.api.extension.ExtensionDescription { private String name; private String main; private List api; private String version; private final List authors = new ArrayList<>(); - public ExtensionDescription(String yamlString) throws InvalidDescriptionException { + public GeyserExtensionDescription(String yamlString) throws InvalidDescriptionException { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(dumperOptions); @@ -72,23 +71,28 @@ public class ExtensionDescription { } } - public String getName() { + @Override + public String name() { return this.name; } - public String getMain() { + @Override + public String main() { return this.main; } - public List getAPIVersions() { + @Override + public List ApiVersions() { return api; } - public String getVersion() { + @Override + public String version() { return this.version; } - public List getAuthors() { + @Override + public List authors() { return this.authors; } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java similarity index 74% rename from core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java rename to core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 15795c2c5..9b741fb2d 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/ExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -25,9 +25,13 @@ package org.geysermc.geyser.extension; +import org.geysermc.api.Geyser; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.extension.exception.InvalidDescriptionException; -import org.geysermc.geyser.extension.exception.InvalidExtensionException; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionLoader; +import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; import java.io.*; import java.util.HashMap; import java.util.Map; @@ -35,10 +39,11 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; -public class ExtensionLoader { +public class GeyserExtensionLoader implements ExtensionLoader { private final Map classes = new HashMap<>(); - private final Map classLoaders = new HashMap<>(); + private final Map classLoaders = new HashMap<>(); + @Override public GeyserExtension loadExtension(File file) throws InvalidExtensionException { if (file == null) { throw new InvalidExtensionException("File is null"); @@ -48,37 +53,39 @@ public class ExtensionLoader { throw new InvalidExtensionException(new FileNotFoundException(file.getPath()) + " does not exist"); } - final ExtensionDescription description; + final GeyserExtensionDescription description; try { - description = getExtensionDescription(file); + description = extensionDescription(file); } catch (InvalidDescriptionException e) { throw new InvalidExtensionException(e); } final File parentFile = file.getParentFile(); - final File dataFolder = new File(parentFile, description.getName()); + final File dataFolder = new File(parentFile, description.name()); if (dataFolder.exists() && !dataFolder.isDirectory()) { - throw new InvalidExtensionException("The folder " + dataFolder.getPath() + " is not a directory and is the data folder for the extension " + description.getName() + "!"); + throw new InvalidExtensionException("The folder " + dataFolder.getPath() + " is not a directory and is the data folder for the extension " + description.name() + "!"); } - final ExtensionClassLoader loader; + final GeyserExtensionClassLoader loader; try { - loader = new ExtensionClassLoader(this, getClass().getClassLoader(), description, file); + loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), description, file); } catch (Throwable e) { throw new InvalidExtensionException(e); } - classLoaders.put(description.getName(), loader); + classLoaders.put(description.name(), loader); setup(loader.extension, description, dataFolder, file); return loader.extension; } - private void setup(GeyserExtension extension, ExtensionDescription description, File dataFolder, File file) { - extension.init(GeyserImpl.getInstance(), description, dataFolder, file, this); + private void setup(GeyserExtension extension, GeyserExtensionDescription description, File dataFolder, File file) { + GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); + extension.init(Geyser.api(), logger, this, description, dataFolder, file); extension.onLoad(); } - public ExtensionDescription getExtensionDescription(File file) throws InvalidDescriptionException { + @Override + public GeyserExtensionDescription extensionDescription(File file) throws InvalidDescriptionException { JarFile jarFile = null; InputStream stream = null; @@ -105,7 +112,7 @@ public class ExtensionLoader { temp = bufferedReader.readLine(); } - return new ExtensionDescription(builder.toString()); + return new GeyserExtensionDescription(builder.toString()); } catch (IOException e) { throw new InvalidDescriptionException(e); } finally { @@ -124,14 +131,16 @@ public class ExtensionLoader { } } - public Pattern[] getExtensionFilters() { + @Override + public Pattern[] extensionFilters() { return new Pattern[] { Pattern.compile("^.+\\.jar$") }; } - public Class getClassByName(final String name) throws ClassNotFoundException{ + @Override + public Class classByName(final String name) throws ClassNotFoundException{ Class clazz = classes.get(name); try { - for(ExtensionClassLoader loader : classLoaders.values()) { + for(GeyserExtensionClassLoader loader : classLoaders.values()) { try { clazz = loader.findClass(name,false); } catch(NullPointerException e) { @@ -153,19 +162,21 @@ public class ExtensionLoader { Class clazz = classes.remove(name); } + @Override public void enableExtension(Extension extension) { if (extension instanceof GeyserExtension) { if(!extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.getDescription().getName()); + GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.description().name()); ((GeyserExtension) extension).setEnabled(true); } } } + @Override public void disableExtension(Extension extension) { if (extension instanceof GeyserExtension) { if(extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.getDescription().getName()); + GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.description().name()); ((GeyserExtension) extension).setEnabled(false); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java new file mode 100644 index 000000000..225d6cda3 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.api.extension.ExtensionLogger; + +public class GeyserExtensionLogger implements ExtensionLogger { + private GeyserLogger logger; + private String loggerPrefix; + + public GeyserExtensionLogger(GeyserLogger logger, String prefix) { + this.logger = logger; + this.loggerPrefix = prefix; + } + + @Override + public String prefix() { + return this.loggerPrefix; + } + + private String addPrefix(String message) { + return "[" + this.loggerPrefix + "] " + message; + } + + @Override + public void severe(String message) { + this.logger.severe(this.addPrefix(message)); + } + + @Override + public void severe(String message, Throwable error) { + this.logger.severe(this.addPrefix(message), error); + } + + @Override + public void error(String message) { + this.logger.error(this.addPrefix(message)); + } + + @Override + public void error(String message, Throwable error) { + this.logger.error(this.addPrefix(message), error); + } + + @Override + public void warning(String message) { + this.logger.warning(this.addPrefix(message)); + } + + @Override + public void info(String message) { + this.logger.info(this.addPrefix(message)); + } + + @Override + public void debug(String message) { + this.logger.debug(this.addPrefix(message)); + } + + @Override + public boolean isDebug() { + return this.logger.isDebug(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java similarity index 79% rename from core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java rename to core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index ad757d499..c06feba1e 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/ExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -26,35 +26,36 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.GeyserImpl; - +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.GeyserExtension; import java.io.File; -import java.io.FilenameFilter; import java.lang.reflect.Constructor; import java.util.*; import java.util.regex.Pattern; -public class ExtensionManager { - private static ExtensionManager extensionManager = null; +public class GeyserExtensionManager { + private static GeyserExtensionManager geyserExtensionManager = null; protected Map extensions = new LinkedHashMap<>(); - protected Map fileAssociations = new HashMap<>(); + protected Map fileAssociations = new HashMap<>(); public static void init() { GeyserImpl.getInstance().getLogger().info("Loading extensions..."); - extensionManager = new ExtensionManager(); - extensionManager.registerInterface(ExtensionLoader.class); - extensionManager.loadExtensions(new File("extensions")); - GeyserImpl.getInstance().getLogger().info("Loaded " + extensionManager.extensions.size() + " extensions."); + geyserExtensionManager = new GeyserExtensionManager(); + geyserExtensionManager.registerInterface(GeyserExtensionLoader.class); + geyserExtensionManager.loadExtensions(new File("extensions")); + GeyserImpl.getInstance().getLogger().info("Loaded " + geyserExtensionManager.extensions.size() + " extensions."); - for (Extension extension : extensionManager.getExtensions().values()) { + for (Extension extension : geyserExtensionManager.getExtensions().values()) { if (!extension.isEnabled()) { - extensionManager.enableExtension(extension); + geyserExtensionManager.enableExtension(extension); } } } - public static ExtensionManager getExtensionManager() { - return extensionManager; + public static GeyserExtensionManager getExtensionManager() { + return geyserExtensionManager; } public Extension getExtension(String name) { @@ -68,11 +69,11 @@ public class ExtensionManager { return this.extensions; } - public void registerInterface(Class loader) { - ExtensionLoader instance; + public void registerInterface(Class loader) { + GeyserExtensionLoader instance; - if (ExtensionLoader.class.isAssignableFrom(loader)) { - Constructor constructor; + if (GeyserExtensionLoader.class.isAssignableFrom(loader)) { + Constructor constructor; try { constructor = loader.getConstructor(); @@ -88,7 +89,7 @@ public class ExtensionManager { throw new IllegalArgumentException("Class " + loader.getName() + " does not implement interface ExtensionLoader"); } - Pattern[] patterns = instance.getExtensionFilters(); + Pattern[] patterns = instance.extensionFilters(); synchronized (this) { for (Pattern pattern : patterns) { @@ -97,17 +98,17 @@ public class ExtensionManager { } } - public GeyserExtension loadExtension(File file, Map loaders) { - for (ExtensionLoader loader : (loaders == null ? this.fileAssociations : loaders).values()) { - for (Pattern pattern : loader.getExtensionFilters()) { + public GeyserExtension loadExtension(File file, Map loaders) { + for (GeyserExtensionLoader loader : (loaders == null ? this.fileAssociations : loaders).values()) { + for (Pattern pattern : loader.extensionFilters()) { if (pattern.matcher(file.getName()).matches()) { try { - ExtensionDescription description = loader.getExtensionDescription(file); + ExtensionDescription description = loader.extensionDescription(file); if (description != null) { GeyserExtension extension = loader.loadExtension(file); if (extension != null) { - this.extensions.put(extension.getDescription().getName(), extension); + this.extensions.put(extension.description().name(), extension); return extension; } @@ -143,9 +144,9 @@ public class ExtensionManager { Map extensions = new LinkedHashMap<>(); Map loadedExtensions = new LinkedHashMap<>(); - for (final ExtensionLoader loader : this.fileAssociations.values()) { + for (final GeyserExtensionLoader loader : this.fileAssociations.values()) { for (File file : dictionary.listFiles((dir, name) -> { - for (Pattern pattern : loader.getExtensionFilters()) { + for (Pattern pattern : loader.extensionFilters()) { if (pattern.matcher(name).matches()) { return true; } @@ -157,9 +158,9 @@ public class ExtensionManager { } try { - ExtensionDescription description = loader.getExtensionDescription(file); + ExtensionDescription description = loader.extensionDescription(file); if (description != null) { - String name = description.getName(); + String name = description.name(); if (extensions.containsKey(name) || this.getExtension(name) != null) { GeyserImpl.getInstance().getLogger().warning("Found duplicate extension '" + name + "', ignoring '" + file.getName() + "'"); @@ -168,7 +169,7 @@ public class ExtensionManager { boolean compatible = false; - for (String version : description.getAPIVersions()) { + for (String version : description.ApiVersions()) { try { //Check the format: majorVersion.minorVersion.patch if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", version)) { @@ -217,9 +218,9 @@ public class ExtensionManager { public void enableExtension(Extension extension) { if (!extension.isEnabled()) { try { - extension.getExtensionLoader().enableExtension(extension); + extension.extensionLoader().enableExtension(extension); } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Error enabling extension " + extension.getName() + ": ", e); + GeyserImpl.getInstance().getLogger().error("Error enabling extension " + extension.name() + ": ", e); this.disableExtension(extension); } } @@ -228,9 +229,9 @@ public class ExtensionManager { public void disableExtension(Extension extension) { if (extension.isEnabled()) { try { - extension.getExtensionLoader().disableExtension(extension); + extension.extensionLoader().disableExtension(extension); } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Error disabling extension " + extension.getName() + ": ", e); + GeyserImpl.getInstance().getLogger().error("Error disabling extension " + extension.name() + ": ", e); } } } From 805f7f666a68aee6950c9bda6ed5d25dc20bfc40 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 13:50:54 +0100 Subject: [PATCH 003/125] Added javadocs & fixed API version & more --- .../geyser/api/extension/Extension.java | 51 ---------- .../api/extension/ExtensionDescription.java | 33 ++++++- .../geyser/api/extension/ExtensionLoader.java | 42 +++++++- .../geyser/api/extension/ExtensionLogger.java | 1 + .../geyser/api/extension/GeyserExtension.java | 95 ++++++++++++++----- .../InvalidDescriptionException.java | 3 + .../exception/InvalidExtensionException.java | 3 + .../extension/GeyserExtensionDescription.java | 21 ++-- .../extension/GeyserExtensionLoader.java | 28 +++--- .../extension/GeyserExtensionManager.java | 91 +++++++++--------- 10 files changed, 214 insertions(+), 154 deletions(-) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java deleted file mode 100644 index 8a820d8ac..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.extension; - -import org.geysermc.api.GeyserApiBase; -import java.io.File; -import java.io.InputStream; - -public interface Extension { - void onLoad(); - void onEnable(); - void onDisable(); - - boolean isEnabled(); - boolean isDisabled(); - - File dataFolder(); - ExtensionDescription description(); - String name(); - - InputStream getResource(String filename); - void saveResource(String filename, boolean replace); - - ClassLoader classLoader(); - ExtensionLoader extensionLoader(); - ExtensionLogger logger(); - GeyserApiBase geyserApi(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index 0f752e72d..d32300e09 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -25,12 +25,41 @@ package org.geysermc.geyser.api.extension; -import java.util.*; +import java.util.List; public interface ExtensionDescription { + /** + * Gets the extension's name + * + * @return the extension's name + */ String name(); + + /** + * Gets the extension's main class + * + * @return the extension's main class + */ String main(); - List ApiVersions(); + + /** + * Gets the extension's api version + * + * @return the extension's api version + */ + String ApiVersion(); + + /** + * Gets the extension's description + * + * @return the extension's description + */ String version(); + + /** + * Gets the extension's authors + * + * @return the extension's authors + */ List authors(); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index c558957eb..291a34daf 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -27,15 +27,47 @@ package org.geysermc.geyser.api.extension; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; - import java.io.File; -import java.util.regex.Pattern; public interface ExtensionLoader { + /** + * Loads an extension from a given file + * + * @param file the file to load the extension from + * @return the loaded extension + * @throws InvalidExtensionException + */ GeyserExtension loadExtension(File file) throws InvalidExtensionException; + + /** + * Gets an extension's description from a given file + * + * @param file the file to get the description from + * @return the extension's description + * @throws InvalidDescriptionException + */ ExtensionDescription extensionDescription(File file) throws InvalidDescriptionException; - Pattern[] extensionFilters(); + + /** + * Gets a class by its name from the extension's classloader + * + * @param name the name of the class + * @return the class + * @throws ClassNotFoundException + */ Class classByName(final String name) throws ClassNotFoundException; - void enableExtension(Extension extension); - void disableExtension(Extension extension); + + /** + * Enables an extension + * + * @param extension the extension to enable + */ + void enableExtension(GeyserExtension extension); + + /** + * Disables an extension + * + * @param extension the extension to disable + */ + void disableExtension(GeyserExtension extension); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java index 6b5d86153..60ee45572 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.api.extension; public interface ExtensionLogger { /** * Get the logger prefix + * * @return the logger prefix */ String prefix(); diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java index 3ed66b444..a3f911580 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java @@ -30,34 +30,52 @@ import java.io.*; import java.net.URL; import java.net.URLConnection; -public class GeyserExtension implements Extension { +public class GeyserExtension { private boolean initialized = false; private boolean enabled = false; private File file = null; private File dataFolder = null; private ClassLoader classLoader = null; - private ExtensionLoader loader; - private ExtensionLogger logger; + private ExtensionLoader loader = null; + private ExtensionLogger logger = null; private ExtensionDescription description = null; private GeyserApiBase api = null; - @Override + /** + * Called when the extension is loaded + */ public void onLoad() { + } - @Override + /** + * Called when the extension is enabled + */ public void onEnable() { + } - @Override + /** + * Called when the extension is disabled + */ public void onDisable() { + } - @Override + /** + * Gets if the extension is enabled + * + * @return true if the extension is enabled + */ public boolean isEnabled() { return this.enabled; } + /** + * Gets if the extension is enabled + * + * @return true if the extension is enabled + */ public void setEnabled(boolean value) { if (this.enabled != value) { this.enabled = value; @@ -69,27 +87,34 @@ public class GeyserExtension implements Extension { } } - @Override - public boolean isDisabled() { - return !this.enabled; - } - - @Override + /** + * Gets the extension's data folder + * + * @return the extension's data folder + */ public File dataFolder() { return this.dataFolder; } - @Override + /** + * Gets the extension's description + * + * @return the extension's description + */ public ExtensionDescription description() { return this.description; } - @Override + /** + * Gets the extension's name + * + * @return the extension's name + */ public String name() { return this.description.name(); } - public void init(GeyserApiBase api, ExtensionLogger logger, ExtensionLoader loader, ExtensionDescription description, File dataFolder, File file) { + public void init(GeyserApiBase api, ExtensionLoader loader, ExtensionLogger logger, ExtensionDescription description, File dataFolder, File file) { if (!this.initialized) { this.initialized = true; this.file = file; @@ -102,7 +127,12 @@ public class GeyserExtension implements Extension { } } - @Override + /** + * Gets a resource from the extension jar file + * + * @param filename the file name + * @return the input stream + */ public InputStream getResource(String filename) { if (filename == null) { throw new IllegalArgumentException("Filename cannot be null"); @@ -123,7 +153,12 @@ public class GeyserExtension implements Extension { } } - @Override + /** + * Saves a resource from the extension jar file to the extension's data folder + * + * @param filename the file name + * @param replace whether to replace the file if it already exists + */ public void saveResource(String filename, boolean replace) { if (filename == null || filename.equals("")) { throw new IllegalArgumentException("ResourcePath cannot be null or empty"); @@ -161,22 +196,38 @@ public class GeyserExtension implements Extension { } } - @Override + /** + * Gets the extension's class loader + * + * @return the extension's class loader + */ public ClassLoader classLoader() { return this.classLoader; } - @Override + /** + * Gets the extension's loader + * + * @return the extension's loader + */ public ExtensionLoader extensionLoader() { return this.loader; } - @Override + /** + * Gets the extension's logger + * + * @return the extension's logger + */ public ExtensionLogger logger() { return this.logger; } - @Override + /** + * Gets the {@link GeyserApiBase} instance + * + * @return the {@link GeyserApiBase} instance + */ public GeyserApiBase geyserApi() { return this.api; } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java index ca2fabad9..1fe88e9e9 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java @@ -25,6 +25,9 @@ package org.geysermc.geyser.api.extension.exception; +/** + * Thrown when an extension's description is invalid. + */ public class InvalidDescriptionException extends Exception { public InvalidDescriptionException(Throwable cause) { super(cause); diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java index 1053e6d50..7fb6b6922 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java @@ -25,6 +25,9 @@ package org.geysermc.geyser.api.extension.exception; +/** + * Thrown when an extension is invalid. + */ public class InvalidExtensionException extends Exception { public InvalidExtensionException(Throwable cause) { super(cause); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index ee215f2e4..e2a09c598 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -33,7 +33,7 @@ import java.util.*; public class GeyserExtensionDescription implements org.geysermc.geyser.api.extension.ExtensionDescription { private String name; private String main; - private List api; + private String api; private String version; private final List authors = new ArrayList<>(); @@ -47,19 +47,18 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten private void loadMap(Map yamlMap) throws InvalidDescriptionException { this.name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); if (this.name.equals("")) { - throw new InvalidDescriptionException("Invalid extension name"); + throw new InvalidDescriptionException("Invalid extension name, cannot be empty"); } this.name = this.name.replace(" ", "_"); this.version = String.valueOf(yamlMap.get("version")); this.main = (String) yamlMap.get("main"); Object api = yamlMap.get("api"); - if (api instanceof List) { - this.api = (List) api; + if (api instanceof String) { + this.api = (String) api; } else { - List list = new ArrayList<>(); - list.add((String) api); - this.api = list; + this.api = "0.0.0"; + throw new InvalidDescriptionException("Invalid api version format, should be a string: major.minor.patch"); } if (yamlMap.containsKey("author")) { @@ -67,7 +66,11 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten } if (yamlMap.containsKey("authors")) { - this.authors.addAll((Collection) yamlMap.get("authors")); + try { + this.authors.addAll((Collection) yamlMap.get("authors")); + } catch (Exception e) { + throw new InvalidDescriptionException("Invalid authors format, should be a list of strings", e); + } } } @@ -82,7 +85,7 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten } @Override - public List ApiVersions() { + public String ApiVersion() { return api; } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 9b741fb2d..a7e4bac3d 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.extension; import org.geysermc.api.Geyser; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.api.extension.GeyserExtension; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; @@ -80,7 +79,7 @@ public class GeyserExtensionLoader implements ExtensionLoader { private void setup(GeyserExtension extension, GeyserExtensionDescription description, File dataFolder, File file) { GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); - extension.init(Geyser.api(), logger, this, description, dataFolder, file); + extension.init(Geyser.api(), this, logger, description, dataFolder, file); extension.onLoad(); } @@ -131,7 +130,6 @@ public class GeyserExtensionLoader implements ExtensionLoader { } } - @Override public Pattern[] extensionFilters() { return new Pattern[] { Pattern.compile("^.+\\.jar$") }; } @@ -152,33 +150,29 @@ public class GeyserExtensionLoader implements ExtensionLoader { } } - public void setClass(String name, final Class clazz) { + void setClass(String name, final Class clazz) { if(!classes.containsKey(name)) { classes.put(name,clazz); } } - protected void removeClass(String name) { + void removeClass(String name) { Class clazz = classes.remove(name); } @Override - public void enableExtension(Extension extension) { - if (extension instanceof GeyserExtension) { - if(!extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.description().name()); - ((GeyserExtension) extension).setEnabled(true); - } + public void enableExtension(GeyserExtension extension) { + if (!extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.description().name()); + extension.setEnabled(true); } } @Override - public void disableExtension(Extension extension) { - if (extension instanceof GeyserExtension) { - if(extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.description().name()); - ((GeyserExtension) extension).setEnabled(false); - } + public void disableExtension(GeyserExtension extension) { + if (extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.description().name()); + extension.setEnabled(false); } } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index c06feba1e..edda15ade 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -26,9 +26,9 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.GeyserExtension; + import java.io.File; import java.lang.reflect.Constructor; import java.util.*; @@ -37,35 +37,34 @@ import java.util.regex.Pattern; public class GeyserExtensionManager { private static GeyserExtensionManager geyserExtensionManager = null; - protected Map extensions = new LinkedHashMap<>(); + protected Map extensions = new LinkedHashMap<>(); protected Map fileAssociations = new HashMap<>(); public static void init() { GeyserImpl.getInstance().getLogger().info("Loading extensions..."); + geyserExtensionManager = new GeyserExtensionManager(); geyserExtensionManager.registerInterface(GeyserExtensionLoader.class); geyserExtensionManager.loadExtensions(new File("extensions")); - GeyserImpl.getInstance().getLogger().info("Loaded " + geyserExtensionManager.extensions.size() + " extensions."); - for (Extension extension : geyserExtensionManager.getExtensions().values()) { - if (!extension.isEnabled()) { - geyserExtensionManager.enableExtension(extension); - } - } + String plural = geyserExtensionManager.extensions.size() == 1 ? "" : "s"; + GeyserImpl.getInstance().getLogger().info("Loaded " + geyserExtensionManager.extensions.size() + " extension" + plural); + + geyserExtensionManager.enableExtensions(); } public static GeyserExtensionManager getExtensionManager() { return geyserExtensionManager; } - public Extension getExtension(String name) { + public GeyserExtension getExtension(String name) { if (this.extensions.containsKey(name)) { return this.extensions.get(name); } return null; } - public Map getExtensions() { + public Map getExtensions() { return this.extensions; } @@ -124,8 +123,8 @@ public class GeyserExtensionManager { return null; } - public Map loadExtensions(File dictionary) { - if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { + public Map loadExtensions(File dictionary) { + if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { // If your IDE says this is always true, ignore it, it isn't. GeyserImpl.getInstance().getLogger().error("Cannot load extensions in a development environment, aborting extension loading"); return new HashMap<>(); } @@ -134,6 +133,8 @@ public class GeyserExtensionManager { return new HashMap<>(); } + String[] apiVersion = GeyserImpl.VERSION.split("\\."); + if (!dictionary.exists()) { dictionary.mkdir(); } @@ -142,7 +143,7 @@ public class GeyserExtensionManager { } Map extensions = new LinkedHashMap<>(); - Map loadedExtensions = new LinkedHashMap<>(); + Map loadedExtensions = new LinkedHashMap<>(); for (final GeyserExtensionLoader loader : this.fileAssociations.values()) { for (File file : dictionary.listFiles((dir, name) -> { @@ -167,47 +168,35 @@ public class GeyserExtensionManager { continue; } - boolean compatible = false; - - for (String version : description.ApiVersions()) { - try { - //Check the format: majorVersion.minorVersion.patch - if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", version)) { - throw new IllegalArgumentException(); - } - } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error("Could't load extension " + name + ": Wrong API format"); - continue; + try { + //Check the format: majorVersion.minorVersion.patch + if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", description.ApiVersion())) { + throw new IllegalArgumentException(); } - - String[] versionArray = version.split("\\."); - String[] apiVersion = GeyserImpl.VERSION.split("\\."); - - //Completely different API version - if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); - continue; - } - - //If the extension requires new API features, being backwards compatible - if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); - continue; - } - - compatible = true; - break; + } catch (NullPointerException | IllegalArgumentException e) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version format, should be 'majorVersion.minorVersion.patch', current version: " + apiVersion[0] + "." + apiVersion[1]); + continue; } - if (!compatible) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name +": Incompatible API version"); + String[] versionArray = description.ApiVersion().split("\\."); + + //Completely different API version + if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + continue; + } + + //If the extension requires new API features, being backwards compatible + if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { + GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + continue; } extensions.put(name, file); loadedExtensions.put(name, this.loadExtension(file, this.fileAssociations)); } } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Couldn't load " +file.getName()+ " in folder " + dictionary + ": ", e); + GeyserImpl.getInstance().getLogger().error("Couldn't load " + file.getName() + " in folder " + dictionary.getAbsolutePath() + ": ", e); } } } @@ -215,7 +204,7 @@ public class GeyserExtensionManager { return loadedExtensions; } - public void enableExtension(Extension extension) { + public void enableExtension(GeyserExtension extension) { if (!extension.isEnabled()) { try { extension.extensionLoader().enableExtension(extension); @@ -226,7 +215,7 @@ public class GeyserExtensionManager { } } - public void disableExtension(Extension extension) { + public void disableExtension(GeyserExtension extension) { if (extension.isEnabled()) { try { extension.extensionLoader().disableExtension(extension); @@ -236,8 +225,14 @@ public class GeyserExtensionManager { } } + public void enableExtensions() { + for (GeyserExtension extension : this.getExtensions().values()) { + this.enableExtension(extension); + } + } + public void disableExtensions() { - for (Extension extension : this.getExtensions().values()) { + for (GeyserExtension extension : this.getExtensions().values()) { this.disableExtension(extension); } } From f3a331981fdf26afbc61b38709a27aa1e2a0a395 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 15:31:28 +0100 Subject: [PATCH 004/125] Added extension dump data & make plugins be enabled on reload --- .../api/extension/ExtensionDescription.java | 5 +++- .../geyser/api/extension/ExtensionLoader.java | 3 +++ .../geyser/api/extension/ExtensionLogger.java | 3 +++ .../geyser/api/extension/GeyserExtension.java | 3 +++ .../java/org/geysermc/geyser/GeyserImpl.java | 3 ++- .../org/geysermc/geyser/dump/DumpInfo.java | 24 +++++++++++++++---- .../extension/GeyserExtensionDescription.java | 2 +- .../extension/GeyserExtensionManager.java | 6 ++--- 8 files changed, 39 insertions(+), 10 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index d32300e09..4426a4a92 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -27,6 +27,9 @@ package org.geysermc.geyser.api.extension; import java.util.List; +/** + * This is the Geyer extension description + */ public interface ExtensionDescription { /** * Gets the extension's name @@ -47,7 +50,7 @@ public interface ExtensionDescription { * * @return the extension's api version */ - String ApiVersion(); + String apiVersion(); /** * Gets the extension's description diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index 291a34daf..1301493d5 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -29,6 +29,9 @@ import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; import java.io.File; +/** + * The extension loader is responsible for loading, unloading, enabling and disabling extensions + */ public interface ExtensionLoader { /** * Loads an extension from a given file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java index 60ee45572..17e108455 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java @@ -25,6 +25,9 @@ package org.geysermc.geyser.api.extension; +/** + * This is the Geyser extension logger + */ public interface ExtensionLogger { /** * Get the logger prefix diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java index a3f911580..210452c3c 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java @@ -30,6 +30,9 @@ import java.io.*; import java.net.URL; import java.net.URLConnection; +/** + * This class is to be extended by a Geyser extension + */ public class GeyserExtension { private boolean initialized = false; private boolean enabled = false; diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 7e61b3af7..b563892cc 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -460,7 +460,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - GeyserExtensionManager.getExtensionManager().disableExtensions(); + GeyserExtensionManager.getInstance().disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } @@ -468,6 +468,7 @@ public class GeyserImpl implements GeyserApi { @Override public void reload() { shutdown(); + GeyserExtensionManager.getInstance().enableExtensions(); bootstrap.onEnable(); } diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 2734c7443..316f095c0 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -36,6 +36,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.network.MinecraftProtocol; @@ -54,10 +56,7 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; +import java.util.*; import java.util.stream.Collectors; @Getter @@ -76,6 +75,7 @@ public class DumpInfo { private LogsInfo logsInfo; private final BootstrapDumpInfo bootstrapInfo; private final FlagsInfo flagsInfo; + private final List extensionInfo; public DumpInfo(boolean addLog) { this.versionInfo = new VersionInfo(); @@ -125,6 +125,11 @@ public class DumpInfo { this.bootstrapInfo = GeyserImpl.getInstance().getBootstrap().getDumpInfo(); this.flagsInfo = new FlagsInfo(); + + this.extensionInfo = new ArrayList<>(); + for (GeyserExtension extension : GeyserExtensionManager.getInstance().getExtensions().values()) { + this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().main(), extension.description().authors(), extension.description().apiVersion())); + } } @Getter @@ -277,4 +282,15 @@ public class DumpInfo { this.flags = ManagementFactory.getRuntimeMXBean().getInputArguments(); } } + + @Getter + @AllArgsConstructor + public static class ExtensionInfo { + public boolean enabled; + public String name; + public String version; + public String main; + public List authors; + public String apiVersion; + } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index e2a09c598..228b88426 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -85,7 +85,7 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten } @Override - public String ApiVersion() { + public String apiVersion() { return api; } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index edda15ade..db0515906 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -53,7 +53,7 @@ public class GeyserExtensionManager { geyserExtensionManager.enableExtensions(); } - public static GeyserExtensionManager getExtensionManager() { + public static GeyserExtensionManager getInstance() { return geyserExtensionManager; } @@ -170,7 +170,7 @@ public class GeyserExtensionManager { try { //Check the format: majorVersion.minorVersion.patch - if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", description.ApiVersion())) { + if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", description.apiVersion())) { throw new IllegalArgumentException(); } } catch (NullPointerException | IllegalArgumentException e) { @@ -178,7 +178,7 @@ public class GeyserExtensionManager { continue; } - String[] versionArray = description.ApiVersion().split("\\."); + String[] versionArray = description.apiVersion().split("\\."); //Completely different API version if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { From cb18c969d7f2cb1e8e2f18bc708caf80c6f5d61d Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 15:32:49 +0100 Subject: [PATCH 005/125] I forgot the "s" in Geyser --- .../org/geysermc/geyser/api/extension/ExtensionDescription.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index 4426a4a92..487df3926 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.api.extension; import java.util.List; /** - * This is the Geyer extension description + * This is the Geyser extension description */ public interface ExtensionDescription { /** From 8bb8e48a55050dbeff523ad2568f322d3382b632 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 16:40:51 +0100 Subject: [PATCH 006/125] Fixed what Konicai asked --- .../geyser/api/extension/GeyserExtension.java | 4 +-- .../java/org/geysermc/geyser/GeyserImpl.java | 14 +++++++-- .../org/geysermc/geyser/dump/DumpInfo.java | 6 ++-- .../extension/GeyserExtensionClassLoader.java | 6 ++-- .../extension/GeyserExtensionDescription.java | 29 +++++++++++++++++++ .../extension/GeyserExtensionLoader.java | 18 ++---------- .../extension/GeyserExtensionManager.java | 19 +++--------- 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java index 210452c3c..bd53bafd3 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java @@ -75,9 +75,7 @@ public class GeyserExtension { } /** - * Gets if the extension is enabled - * - * @return true if the extension is enabled + * Enables or disables the extension */ public void setEnabled(boolean value) { if (this.enabled != value) { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index b563892cc..2fbcbaddd 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -123,6 +123,8 @@ public class GeyserImpl implements GeyserApi { private final PlatformType platformType; private final GeyserBootstrap bootstrap; + private final GeyserExtensionManager extensionManager; + private Metrics metrics; private static GeyserImpl instance; @@ -155,7 +157,8 @@ public class GeyserImpl implements GeyserApi { MessageTranslator.init(); MinecraftLocale.init(); - GeyserExtensionManager.init(); + extensionManager = new GeyserExtensionManager(); + extensionManager.init(); start(); @@ -200,6 +203,8 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); + extensionManager.enableExtensions(); + if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { @@ -460,7 +465,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - GeyserExtensionManager.getInstance().disableExtensions(); + extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } @@ -468,7 +473,6 @@ public class GeyserImpl implements GeyserApi { @Override public void reload() { shutdown(); - GeyserExtensionManager.getInstance().enableExtensions(); bootstrap.onEnable(); } @@ -514,6 +518,10 @@ public class GeyserImpl implements GeyserApi { return bootstrap.getWorldManager(); } + public GeyserExtensionManager getExtensionManager() { + return extensionManager; + } + public static GeyserImpl getInstance() { return instance; } diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 316f095c0..3377f7ee5 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -127,8 +127,8 @@ public class DumpInfo { this.flagsInfo = new FlagsInfo(); this.extensionInfo = new ArrayList<>(); - for (GeyserExtension extension : GeyserExtensionManager.getInstance().getExtensions().values()) { - this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().main(), extension.description().authors(), extension.description().apiVersion())); + for (GeyserExtension extension : GeyserImpl.getInstance().getExtensionManager().getExtensions().values()) { + this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().apiVersion(), extension.description().main(), extension.description().authors())); } } @@ -289,8 +289,8 @@ public class DumpInfo { public boolean enabled; public String name; public String version; + public String apiVersion; public String main; public List authors; - public String apiVersion; } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index a1ccc063e..3ce0a00ae 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -78,11 +78,11 @@ public class GeyserExtensionClassLoader extends URLClassLoader { throw new ClassNotFoundException(name); } Class result = classes.get(name); - if(result == null) { - if(checkGlobal) { + if (result == null) { + if (checkGlobal) { result = loader.classByName(name); } - if(result == null) { + if (result == null) { result = super.findClass(name); if (result != null) { loader.setClass(name, result); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index 228b88426..31d6c3a46 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -28,6 +28,10 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.*; public class GeyserExtensionDescription implements org.geysermc.geyser.api.extension.ExtensionDescription { @@ -37,7 +41,32 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten private String version; private final List authors = new ArrayList<>(); + public GeyserExtensionDescription(InputStream inputStream) throws InvalidDescriptionException { + try { + InputStreamReader reader = new InputStreamReader(inputStream); + StringBuilder builder = new StringBuilder(); + String temp; + BufferedReader bufferedReader = new BufferedReader(reader); + temp = bufferedReader.readLine(); + while (temp != null) { + if (builder.length() != 0) { + builder.append("\n"); + } + builder.append(temp); + temp = bufferedReader.readLine(); + } + + this.loadString(builder.toString()); + } catch (IOException e) { + throw new InvalidDescriptionException(e); + } + } + public GeyserExtensionDescription(String yamlString) throws InvalidDescriptionException { + this.loadString(yamlString); + } + + private void loadString(String yamlString) throws InvalidDescriptionException { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(dumperOptions); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index a7e4bac3d..cffd513a1 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -97,21 +97,7 @@ public class GeyserExtensionLoader implements ExtensionLoader { } stream = jarFile.getInputStream(descriptionEntry); - - InputStreamReader reader = new InputStreamReader(stream); - StringBuilder builder = new StringBuilder(); - String temp; - BufferedReader bufferedReader = new BufferedReader(reader); - temp = bufferedReader.readLine(); - while (temp != null) { - if (builder.length() != 0) { - builder.append("\n"); - } - builder.append(temp); - temp = bufferedReader.readLine(); - } - - return new GeyserExtensionDescription(builder.toString()); + return new GeyserExtensionDescription(stream); } catch (IOException e) { throw new InvalidDescriptionException(e); } finally { @@ -151,7 +137,7 @@ public class GeyserExtensionLoader implements ExtensionLoader { } void setClass(String name, final Class clazz) { - if(!classes.containsKey(name)) { + if (!classes.containsKey(name)) { classes.put(name,clazz); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index db0515906..6dcfca0b5 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -28,33 +28,22 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.GeyserExtension; - import java.io.File; import java.lang.reflect.Constructor; import java.util.*; import java.util.regex.Pattern; public class GeyserExtensionManager { - private static GeyserExtensionManager geyserExtensionManager = null; - protected Map extensions = new LinkedHashMap<>(); protected Map fileAssociations = new HashMap<>(); - public static void init() { + public void init() { GeyserImpl.getInstance().getLogger().info("Loading extensions..."); - geyserExtensionManager = new GeyserExtensionManager(); - geyserExtensionManager.registerInterface(GeyserExtensionLoader.class); - geyserExtensionManager.loadExtensions(new File("extensions")); + this.registerInterface(GeyserExtensionLoader.class); + this.loadExtensions(new File("extensions")); - String plural = geyserExtensionManager.extensions.size() == 1 ? "" : "s"; - GeyserImpl.getInstance().getLogger().info("Loaded " + geyserExtensionManager.extensions.size() + " extension" + plural); - - geyserExtensionManager.enableExtensions(); - } - - public static GeyserExtensionManager getInstance() { - return geyserExtensionManager; + GeyserImpl.getInstance().getLogger().info("Loaded " + this.extensions.size() + " extension(s)"); } public GeyserExtension getExtension(String name) { From f8c173aae8ed9ba70443b04ea3f48c6b5a4b2ed3 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 16:47:25 +0100 Subject: [PATCH 007/125] Actually did what Konicai wanted --- .../extension/GeyserExtensionDescription.java | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index 31d6c3a46..b14291b01 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -42,28 +42,10 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten private final List authors = new ArrayList<>(); public GeyserExtensionDescription(InputStream inputStream) throws InvalidDescriptionException { - try { - InputStreamReader reader = new InputStreamReader(inputStream); - StringBuilder builder = new StringBuilder(); - String temp; - BufferedReader bufferedReader = new BufferedReader(reader); - temp = bufferedReader.readLine(); - while (temp != null) { - if (builder.length() != 0) { - builder.append("\n"); - } - builder.append(temp); - temp = bufferedReader.readLine(); - } - - this.loadString(builder.toString()); - } catch (IOException e) { - throw new InvalidDescriptionException(e); - } - } - - public GeyserExtensionDescription(String yamlString) throws InvalidDescriptionException { - this.loadString(yamlString); + DumperOptions dumperOptions = new DumperOptions(); + dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(dumperOptions); + this.loadMap(yaml.loadAs(inputStream, LinkedHashMap.class)); } private void loadString(String yamlString) throws InvalidDescriptionException { From 0ccd85ccfbb0735844503639ca994a69cbe3bb5b Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Wed, 12 Jan 2022 18:43:10 +0100 Subject: [PATCH 008/125] Use Geyser locale for log messages --- .../extension/GeyserExtensionClassLoader.java | 6 ++--- .../extension/GeyserExtensionDescription.java | 10 -------- .../extension/GeyserExtensionLoader.java | 7 +++--- .../extension/GeyserExtensionLogger.java | 4 +-- .../extension/GeyserExtensionManager.java | 25 ++++++++++--------- core/src/main/resources/languages | 2 +- core/src/main/resources/mappings | 2 +- 7 files changed, 24 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 3ce0a00ae..67363a40f 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -37,8 +37,8 @@ import java.util.Map; import java.util.Set; public class GeyserExtensionClassLoader extends URLClassLoader { - private GeyserExtensionLoader loader; - private Map classes = new HashMap<>(); + private final GeyserExtensionLoader loader; + private final Map classes = new HashMap<>(); public GeyserExtension extension; public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, ExtensionDescription description, File file) throws InvalidExtensionException, MalformedURLException { @@ -74,7 +74,7 @@ public class GeyserExtensionClassLoader extends URLClassLoader { } protected Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { - if (name.startsWith("org.geysermc.geyser.") || name.startsWith("org.geysermc.connector.") || name.startsWith("net.minecraft.")) { + if (name.startsWith("org.geysermc.geyser.") || name.startsWith("org.geysermc.connector.") || name.startsWith("org.geysermc.platform.") || name.startsWith("org.geysermc.floodgate.") || name.startsWith("org.geysermc.api.") || name.startsWith("org.geysermc.processor.") || name.startsWith("net.minecraft.")) { throw new ClassNotFoundException(name); } Class result = classes.get(name); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index b14291b01..f8a3d9bbe 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -28,10 +28,7 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; -import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.util.*; public class GeyserExtensionDescription implements org.geysermc.geyser.api.extension.ExtensionDescription { @@ -48,13 +45,6 @@ public class GeyserExtensionDescription implements org.geysermc.geyser.api.exten this.loadMap(yaml.loadAs(inputStream, LinkedHashMap.class)); } - private void loadString(String yamlString) throws InvalidDescriptionException { - DumperOptions dumperOptions = new DumperOptions(); - dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - Yaml yaml = new Yaml(dumperOptions); - this.loadMap(yaml.loadAs(yamlString, LinkedHashMap.class)); - } - private void loadMap(Map yamlMap) throws InvalidDescriptionException { this.name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); if (this.name.equals("")) { diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index cffd513a1..f029eb797 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -31,6 +31,7 @@ import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.api.extension.GeyserExtension; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; +import org.geysermc.geyser.text.GeyserLocale; import java.io.*; import java.util.HashMap; import java.util.Map; @@ -143,13 +144,13 @@ public class GeyserExtensionLoader implements ExtensionLoader { } void removeClass(String name) { - Class clazz = classes.remove(name); + classes.remove(name); } @Override public void enableExtension(GeyserExtension extension) { if (!extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Enabled extension " + extension.description().name()); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); extension.setEnabled(true); } } @@ -157,7 +158,7 @@ public class GeyserExtensionLoader implements ExtensionLoader { @Override public void disableExtension(GeyserExtension extension) { if (extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info("Disabled extension " + extension.description().name()); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); extension.setEnabled(false); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java index 225d6cda3..fe23417f8 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java @@ -29,8 +29,8 @@ import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.api.extension.ExtensionLogger; public class GeyserExtensionLogger implements ExtensionLogger { - private GeyserLogger logger; - private String loggerPrefix; + private final GeyserLogger logger; + private final String loggerPrefix; public GeyserExtensionLogger(GeyserLogger logger, String prefix) { this.logger = logger; diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index 6dcfca0b5..169053182 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.extension; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.text.GeyserLocale; import java.io.File; import java.lang.reflect.Constructor; import java.util.*; @@ -38,12 +39,12 @@ public class GeyserExtensionManager { protected Map fileAssociations = new HashMap<>(); public void init() { - GeyserImpl.getInstance().getLogger().info("Loading extensions..."); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); this.registerInterface(GeyserExtensionLoader.class); this.loadExtensions(new File("extensions")); - GeyserImpl.getInstance().getLogger().info("Loaded " + this.extensions.size() + " extension(s)"); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.done", this.extensions.size())); } public GeyserExtension getExtension(String name) { @@ -102,7 +103,7 @@ public class GeyserExtensionManager { } } } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Could not load extension", e); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed"), e); return null; } } @@ -114,11 +115,11 @@ public class GeyserExtensionManager { public Map loadExtensions(File dictionary) { if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { // If your IDE says this is always true, ignore it, it isn't. - GeyserImpl.getInstance().getLogger().error("Cannot load extensions in a development environment, aborting extension loading"); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_dev_environment")); return new HashMap<>(); } if (!GeyserImpl.VERSION.contains(".")) { - GeyserImpl.getInstance().getLogger().error("Something went wrong with the Geyser version number, aborting extension loading"); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); return new HashMap<>(); } @@ -153,7 +154,7 @@ public class GeyserExtensionManager { String name = description.name(); if (extensions.containsKey(name) || this.getExtension(name) != null) { - GeyserImpl.getInstance().getLogger().warning("Found duplicate extension '" + name + "', ignoring '" + file.getName() + "'"); + GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, file.getName())); continue; } @@ -163,7 +164,7 @@ public class GeyserExtensionManager { throw new IllegalArgumentException(); } } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version format, should be 'majorVersion.minorVersion.patch', current version: " + apiVersion[0] + "." + apiVersion[1]); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); continue; } @@ -171,13 +172,13 @@ public class GeyserExtensionManager { //Completely different API version if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); continue; } //If the extension requires new API features, being backwards compatible if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { - GeyserImpl.getInstance().getLogger().error("Couldn't load extension " + name + ": Wrong API version, current version: " + apiVersion[0] + "." + apiVersion[1]); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); continue; } @@ -185,7 +186,7 @@ public class GeyserExtensionManager { loadedExtensions.put(name, this.loadExtension(file, this.fileAssociations)); } } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Couldn't load " + file.getName() + " in folder " + dictionary.getAbsolutePath() + ": ", e); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", file.getName(), dictionary.getAbsolutePath()), e); } } } @@ -198,7 +199,7 @@ public class GeyserExtensionManager { try { extension.extensionLoader().enableExtension(extension); } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Error enabling extension " + extension.name() + ": ", e); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.failed", extension.name()), e); this.disableExtension(extension); } } @@ -209,7 +210,7 @@ public class GeyserExtensionManager { try { extension.extensionLoader().disableExtension(extension); } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error("Error disabling extension " + extension.name() + ": ", e); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.failed", extension.name()), e); } } } diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 1a50238c1..94c185193 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 1a50238c1c743579a1dbd93c57d02b5da3be14fa +Subproject commit 94c1851931f2319a7e7f42c2fe9066b78235bc39 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 82ad7ba27..b60cfcdd4 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 82ad7ba279c68eb11a0b1a969c9efb3228c59227 +Subproject commit b60cfcdd40cd58a93143b489fc9153a347e48c41 From 142bb95c069fd92f457b805e9ff4deb5723fc15e Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 15 Jan 2022 11:56:40 -0600 Subject: [PATCH 009/125] Fix package name for `Connection` --- api/base/src/main/java/org/geysermc/api/GeyserApiBase.java | 2 +- .../org/geysermc/api/{session => connection}/Connection.java | 2 +- .../org/geysermc/geyser/api/connection/GeyserConnection.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename api/base/src/main/java/org/geysermc/api/{session => connection}/Connection.java (97%) diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index 3549a912a..bf38f58b9 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -27,7 +27,7 @@ package org.geysermc.api; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.api.session.Connection; +import org.geysermc.api.connection.Connection; import java.util.List; import java.util.UUID; diff --git a/api/base/src/main/java/org/geysermc/api/session/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java similarity index 97% rename from api/base/src/main/java/org/geysermc/api/session/Connection.java rename to api/base/src/main/java/org/geysermc/api/connection/Connection.java index ccf3f7122..d3879f36d 100644 --- a/api/base/src/main/java/org/geysermc/api/session/Connection.java +++ b/api/base/src/main/java/org/geysermc/api/connection/Connection.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.api.session; +package org.geysermc.api.connection; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 79260ac95..b673b60f6 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.api.connection; -import org.geysermc.api.session.Connection; +import org.geysermc.api.connection.Connection; /** * Represents a player session used in Geyser. From 778f004d9983b2e9597df159a2e262baf06b5e83 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 15 Jan 2022 16:27:35 -0600 Subject: [PATCH 010/125] Slight cleanups and make Extension an interface --- api/geyser/pom.xml | 9 + .../org/geysermc/geyser/api/GeyserApi.java | 18 ++ .../geyser/api/extension/Extension.java | 136 ++++++++++ .../api/extension/ExtensionDescription.java | 8 + .../geyser/api/extension/ExtensionLoader.java | 84 +++--- .../api/extension/ExtensionManager.java | 120 +++++++++ .../geyser/api/extension/GeyserExtension.java | 235 ---------------- api/pom.xml | 1 + core/pom.xml | 1 - .../java/org/geysermc/geyser/GeyserImpl.java | 18 +- .../org/geysermc/geyser/dump/DumpInfo.java | 6 +- .../extension/GeyserExtensionClassLoader.java | 37 +-- .../extension/GeyserExtensionContainer.java | 50 ++++ .../extension/GeyserExtensionDescription.java | 66 ++--- .../extension/GeyserExtensionLoader.java | 253 ++++++++++++------ .../extension/GeyserExtensionManager.java | 241 ++++++----------- .../geysermc/geyser/registry/Registries.java | 9 +- pom.xml | 2 + 18 files changed, 708 insertions(+), 586 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml index 89349e8ac..5c598f65b 100644 --- a/api/geyser/pom.xml +++ b/api/geyser/pom.xml @@ -6,6 +6,7 @@ org.geysermc api-parent 2.0.0-SNAPSHOT + ../pom.xml 4.0.0 @@ -14,6 +15,8 @@ 16 16 + + 4.9.3 @@ -23,6 +26,12 @@ 3.19.0 provided + + net.kyori + adventure-api + ${adventure.version} + compile + org.geysermc base-api diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 074918881..d5fd09381 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -27,8 +27,10 @@ package org.geysermc.geyser.api; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.extension.ExtensionManager; import java.util.List; import java.util.UUID; @@ -78,4 +80,20 @@ public interface GeyserApi extends GeyserApiBase { */ @NonNull List onlineConnections(); + + /** + * Gets the {@link ExtensionManager}. + * + * @return the extension manager + */ + ExtensionManager extensionManager(); + + /** + * Gets the current {@link GeyserApiBase} instance. + * + * @return the current geyser api instance + */ + static GeyserApi api() { + return Geyser.api(GeyserApi.class); + } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java new file mode 100644 index 000000000..097cabdc3 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.geysermc.api.GeyserApiBase; +import org.geysermc.geyser.api.GeyserApi; + +import java.nio.file.Path; + +/** + * Represents an extension within Geyser. + */ +public interface Extension { + + /** + * Called when the extension is loaded + */ + default void onLoad() { + } + + /** + * Called when the extension is enabled + */ + default void onEnable() { + } + + /** + * Called when the extension is disabled + */ + default void onDisable() { + } + + /** + * Gets if the extension is enabled + * + * @return true if the extension is enabled + */ + default boolean isEnabled() { + return this.extensionLoader().isEnabled(this); + } + + /** + * Enables or disables the extension + * + * @param enabled if the extension should be enabled + */ + default void setEnabled(boolean enabled) { + this.extensionLoader().setEnabled(this, enabled); + } + + /** + * Gets the extension's data folder + * + * @return the extension's data folder + */ + default Path dataFolder() { + return this.extensionLoader().dataFolder(this); + } + + /** + * Gets the {@link ExtensionManager}. + * + * @return the extension manager + */ + default ExtensionManager extensionManager() { + return this.geyserApi().extensionManager(); + } + + /** + * Gets the extension's name + * + * @return the extension's name + */ + default String name() { + return this.description().name(); + } + + /** + * Gets this extension's {@link ExtensionDescription}. + * + * @return the extension's description + */ + default ExtensionDescription description() { + return this.extensionLoader().description(this); + } + + /** + * Gets the extension's logger + * + * @return the extension's logger + */ + default ExtensionLogger logger() { + return this.extensionLoader().logger(this); + } + + /** + * Gets the {@link ExtensionLoader}. + * + * @return the extension loader + */ + default ExtensionLoader extensionLoader() { + return this.extensionManager().extensionLoader(this); + } + + /** + * Gets the {@link GeyserApiBase} instance + * + * @return the geyser api instance + */ + default GeyserApi geyserApi() { + return GeyserApi.api(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index 487df3926..e77411144 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -25,17 +25,21 @@ package org.geysermc.geyser.api.extension; +import org.checkerframework.checker.nullness.qual.NonNull; + import java.util.List; /** * This is the Geyser extension description */ public interface ExtensionDescription { + /** * Gets the extension's name * * @return the extension's name */ + @NonNull String name(); /** @@ -43,6 +47,7 @@ public interface ExtensionDescription { * * @return the extension's main class */ + @NonNull String main(); /** @@ -50,6 +55,7 @@ public interface ExtensionDescription { * * @return the extension's api version */ + @NonNull String apiVersion(); /** @@ -57,6 +63,7 @@ public interface ExtensionDescription { * * @return the extension's description */ + @NonNull String version(); /** @@ -64,5 +71,6 @@ public interface ExtensionDescription { * * @return the extension's authors */ + @NonNull List authors(); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index 1301493d5..651afd9eb 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -25,52 +25,72 @@ package org.geysermc.geyser.api.extension; -import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; -import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; -import java.io.File; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.nio.file.Path; /** * The extension loader is responsible for loading, unloading, enabling and disabling extensions */ -public interface ExtensionLoader { - /** - * Loads an extension from a given file - * - * @param file the file to load the extension from - * @return the loaded extension - * @throws InvalidExtensionException - */ - GeyserExtension loadExtension(File file) throws InvalidExtensionException; +public abstract class ExtensionLoader { /** - * Gets an extension's description from a given file + * Gets if the given {@link Extension} is enabled. * - * @param file the file to get the description from - * @return the extension's description - * @throws InvalidDescriptionException + * @param extension the extension + * @return if the extension is enabled */ - ExtensionDescription extensionDescription(File file) throws InvalidDescriptionException; + protected abstract boolean isEnabled(@NonNull Extension extension); /** - * Gets a class by its name from the extension's classloader - * - * @param name the name of the class - * @return the class - * @throws ClassNotFoundException - */ - Class classByName(final String name) throws ClassNotFoundException; - - /** - * Enables an extension + * Sets if the given {@link Extension} is enabled. * * @param extension the extension to enable + * @param enabled if the extension should be enabled */ - void enableExtension(GeyserExtension extension); + protected abstract void setEnabled(@NonNull Extension extension, boolean enabled); /** - * Disables an extension + * Gets the given {@link Extension}'s data folder. * - * @param extension the extension to disable + * @param extension the extension + * @return the data folder of the given extension */ - void disableExtension(GeyserExtension extension); -} + @NonNull + protected abstract Path dataFolder(@NonNull Extension extension); + + /** + * Gets the given {@link Extension}'s {@link ExtensionDescription}. + * + * @param extension the extension + * @return the description of the given extension + */ + @NonNull + protected abstract ExtensionDescription description(@NonNull Extension extension); + + /** + * Gets the {@link ExtensionLogger} for the given {@link Extension}. + * + * @param extension the extension + * @return the extension logger for the given extension + */ + @NonNull + protected abstract ExtensionLogger logger(@NonNull Extension extension); + + /** + * Loads all extensions. + * + * @param extensionManager the extension manager + */ + protected abstract void loadAllExtensions(@NonNull ExtensionManager extensionManager); + + /** + * Registers the given {@link Extension} with the given {@link ExtensionManager}. + * + * @param extension the extension + * @param extensionManager the extension manager + */ + protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { + extensionManager.register(extension, this); + } +} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java new file mode 100644 index 000000000..65387a8c7 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Map; + +/** + * Manages Geyser {@link Extension}s + */ +public abstract class ExtensionManager { + + /** + * Gets an extension with the given name. + * + * @param name the name of the extension + * @return an extension with the given name + */ + @Nullable + public abstract Extension extension(@NotNull String name); + + /** + * Enables the given {@link Extension}. + * + * @param extension the extension to enable + */ + public abstract void enable(@NonNull Extension extension); + + /** + * Disables the given {@link Extension}. + * + * @param extension the extension to disable + */ + public abstract void disable(@NonNull Extension extension); + + /** + * Gets the {@link ExtensionLoader} responsible for loading + * the given {@link Extension}. + * + * @return the extension loader for loading the given extension + */ + @Nullable + public abstract ExtensionLoader extensionLoader(@NotNull Extension extension); + + /** + * Gets all the {@link Extension}s currently loaded. + * + * @return all the extensions currently loaded + */ + @NonNull + public abstract Collection extensions(); + + /** + * Gets the {@link ExtensionLoader} with the given identifier. + * + * @param identifier the identifier + * @return the extension loader at the given identifier + */ + @Nullable + public abstract ExtensionLoader extensionLoader(@NonNull Key identifier); + + /** + * Registers an {@link ExtensionLoader} with the given identifier. + * + * @param identifier the identifier + * @param extensionLoader the extension loader + */ + public abstract void registerExtensionLoader(@NonNull Key identifier, @NotNull ExtensionLoader extensionLoader); + + /** + * Gets all the currently registered {@link ExtensionLoader}s. + * + * @return all the currently registered extension loaders + */ + @NonNull + public abstract Map extensionLoaders(); + + /** + * Registers an {@link Extension} with the given {@link ExtensionLoader}. + * + * @param extension the extension + * @param loader the loader + */ + public abstract void register(@NotNull Extension extension, @NotNull ExtensionLoader loader); + + /** + * Loads all extensions from the given {@link ExtensionLoader}. + */ + protected final void loadAllExtensions(@NotNull ExtensionLoader extensionLoader) { + extensionLoader.loadAllExtensions(this); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java deleted file mode 100644 index bd53bafd3..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/GeyserExtension.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.extension; - -import org.geysermc.api.GeyserApiBase; -import java.io.*; -import java.net.URL; -import java.net.URLConnection; - -/** - * This class is to be extended by a Geyser extension - */ -public class GeyserExtension { - private boolean initialized = false; - private boolean enabled = false; - private File file = null; - private File dataFolder = null; - private ClassLoader classLoader = null; - private ExtensionLoader loader = null; - private ExtensionLogger logger = null; - private ExtensionDescription description = null; - private GeyserApiBase api = null; - - /** - * Called when the extension is loaded - */ - public void onLoad() { - - } - - /** - * Called when the extension is enabled - */ - public void onEnable() { - - } - - /** - * Called when the extension is disabled - */ - public void onDisable() { - - } - - /** - * Gets if the extension is enabled - * - * @return true if the extension is enabled - */ - public boolean isEnabled() { - return this.enabled; - } - - /** - * Enables or disables the extension - */ - public void setEnabled(boolean value) { - if (this.enabled != value) { - this.enabled = value; - if (this.enabled) { - onEnable(); - } else { - onDisable(); - } - } - } - - /** - * Gets the extension's data folder - * - * @return the extension's data folder - */ - public File dataFolder() { - return this.dataFolder; - } - - /** - * Gets the extension's description - * - * @return the extension's description - */ - public ExtensionDescription description() { - return this.description; - } - - /** - * Gets the extension's name - * - * @return the extension's name - */ - public String name() { - return this.description.name(); - } - - public void init(GeyserApiBase api, ExtensionLoader loader, ExtensionLogger logger, ExtensionDescription description, File dataFolder, File file) { - if (!this.initialized) { - this.initialized = true; - this.file = file; - this.dataFolder = dataFolder; - this.classLoader = this.getClass().getClassLoader(); - this.loader = loader; - this.logger = logger; - this.description = description; - this.api = api; - } - } - - /** - * Gets a resource from the extension jar file - * - * @param filename the file name - * @return the input stream - */ - public InputStream getResource(String filename) { - if (filename == null) { - throw new IllegalArgumentException("Filename cannot be null"); - } - - try { - URL url = this.classLoader.getResource(filename); - - if (url == null) { - return null; - } - - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - return connection.getInputStream(); - } catch (IOException ex) { - return null; - } - } - - /** - * Saves a resource from the extension jar file to the extension's data folder - * - * @param filename the file name - * @param replace whether to replace the file if it already exists - */ - public void saveResource(String filename, boolean replace) { - if (filename == null || filename.equals("")) { - throw new IllegalArgumentException("ResourcePath cannot be null or empty"); - } - - filename = filename.replace('\\', '/'); - InputStream in = getResource(filename); - if (in == null) { - throw new IllegalArgumentException("The embedded resource '" + filename + "' cannot be found in " + file); - } - - File outFile = new File(dataFolder, filename); - int lastIndex = filename.lastIndexOf('/'); - File outDir = new File(dataFolder, filename.substring(0, Math.max(lastIndex, 0))); - - if (!outDir.exists()) { - outDir.mkdirs(); - } - - try { - if (!outFile.exists() || replace) { - OutputStream out = new FileOutputStream(outFile); - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - out.close(); - in.close(); - } else { - this.logger.warning("Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists."); - } - } catch (IOException ex) { - this.logger.severe("Could not save " + outFile.getName() + " to " + outFile, ex); - } - } - - /** - * Gets the extension's class loader - * - * @return the extension's class loader - */ - public ClassLoader classLoader() { - return this.classLoader; - } - - /** - * Gets the extension's loader - * - * @return the extension's loader - */ - public ExtensionLoader extensionLoader() { - return this.loader; - } - - /** - * Gets the extension's logger - * - * @return the extension's logger - */ - public ExtensionLogger logger() { - return this.logger; - } - - /** - * Gets the {@link GeyserApiBase} instance - * - * @return the {@link GeyserApiBase} instance - */ - public GeyserApiBase geyserApi() { - return this.api; - } -} diff --git a/api/pom.xml b/api/pom.xml index b3d0262ea..f66b982fb 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,6 +7,7 @@ org.geysermc geyser-parent 2.0.0-SNAPSHOT + ../pom.xml api-parent diff --git a/core/pom.xml b/core/pom.xml index 4da6bdbe0..4837660f9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -11,7 +11,6 @@ core - 4.9.3 8.5.2 2.12.4 4.1.66.Final diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 2fbcbaddd..6e740cb44 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -157,8 +157,9 @@ public class GeyserImpl implements GeyserApi { MessageTranslator.init(); MinecraftLocale.init(); - extensionManager = new GeyserExtensionManager(); - extensionManager.init(); + /* Load Extensions */ + this.extensionManager = new GeyserExtensionManager(); + this.extensionManager.init(); start(); @@ -203,7 +204,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); - extensionManager.enableExtensions(); + this.extensionManager.enableExtensions(); if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting @@ -465,7 +466,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - extensionManager.disableExtensions(); + this.extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } @@ -488,6 +489,11 @@ public class GeyserImpl implements GeyserApi { return !"DEV".equals(GeyserImpl.VERSION); } + @Override + public GeyserExtensionManager extensionManager() { + return this.extensionManager; + } + public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); @@ -518,10 +524,6 @@ public class GeyserImpl implements GeyserApi { return bootstrap.getWorldManager(); } - public GeyserExtensionManager getExtensionManager() { - return extensionManager; - } - public static GeyserImpl getInstance() { return instance; } diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 3377f7ee5..28e81d8e7 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -36,8 +36,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.extension.GeyserExtension; -import org.geysermc.geyser.extension.GeyserExtensionManager; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.network.MinecraftProtocol; @@ -127,7 +127,7 @@ public class DumpInfo { this.flagsInfo = new FlagsInfo(); this.extensionInfo = new ArrayList<>(); - for (GeyserExtension extension : GeyserImpl.getInstance().getExtensionManager().getExtensions().values()) { + for (Extension extension : GeyserApi.api().extensionManager().extensions()) { this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().apiVersion(), extension.description().main(), extension.description().authors())); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 67363a40f..426cd1de7 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -25,24 +25,25 @@ package org.geysermc.geyser.extension; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; -import org.geysermc.geyser.api.extension.GeyserExtension; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; -import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Set; public class GeyserExtensionClassLoader extends URLClassLoader { private final GeyserExtensionLoader loader; - private final Map classes = new HashMap<>(); - public GeyserExtension extension; + private final Map> classes = new HashMap<>(); + private final Extension extension; - public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, ExtensionDescription description, File file) throws InvalidExtensionException, MalformedURLException { - super(new URL[] { file.toURI().toURL() }, parent); + public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, ExtensionDescription description, Path path) throws InvalidExtensionException, MalformedURLException { + super(new URL[] { path.toUri().toURL() }, parent); this.loader = loader; try { @@ -53,15 +54,15 @@ public class GeyserExtensionClassLoader extends URLClassLoader { throw new InvalidExtensionException("Class " + description.main() + " not found, extension cannot be loaded", ex); } - Class extensionClass; + Class extensionClass; try { - extensionClass = jarClass.asSubclass(GeyserExtension.class); + extensionClass = jarClass.asSubclass(Extension.class); } catch (ClassCastException ex) { throw new InvalidExtensionException("Main class " + description.main() + " should extends GeyserExtension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); } - extension = extensionClass.newInstance(); - } catch (IllegalAccessException ex) { + this.extension = extensionClass.getConstructor().newInstance(); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { throw new InvalidExtensionException("No public constructor", ex); } catch (InstantiationException ex) { throw new InvalidExtensionException("Abnormal extension type", ex); @@ -74,26 +75,28 @@ public class GeyserExtensionClassLoader extends URLClassLoader { } protected Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { - if (name.startsWith("org.geysermc.geyser.") || name.startsWith("org.geysermc.connector.") || name.startsWith("org.geysermc.platform.") || name.startsWith("org.geysermc.floodgate.") || name.startsWith("org.geysermc.api.") || name.startsWith("org.geysermc.processor.") || name.startsWith("net.minecraft.")) { + if (name.startsWith("org.geysermc.geyser.") || name.startsWith("net.minecraft.")) { throw new ClassNotFoundException(name); } - Class result = classes.get(name); + Class result = this.classes.get(name); if (result == null) { if (checkGlobal) { - result = loader.classByName(name); + result = this.loader.classByName(name); } + if (result == null) { result = super.findClass(name); if (result != null) { - loader.setClass(name, result); + this.loader.setClass(name, result); } } - classes.put(name, result); + + this.classes.put(name, result); } return result; } - Set getClasses() { - return classes.keySet(); + public Extension extension() { + return this.extension; } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java new file mode 100644 index 000000000..5b2e01842 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.ExtensionLoader; +import org.geysermc.geyser.api.extension.ExtensionLogger; + +import java.nio.file.Path; + +@Accessors(fluent = true) +@Getter +@RequiredArgsConstructor +public class GeyserExtensionContainer { + private final Extension extension; + private final Path dataFolder; + private final ExtensionDescription description; + private final ExtensionLoader loader; + private final ExtensionLogger logger; + + @Getter(AccessLevel.NONE) protected boolean enabled; +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index f8a3d9bbe..5c8ff0ae2 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -25,78 +25,52 @@ package org.geysermc.geyser.extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; -import java.io.InputStream; +import java.io.Reader; import java.util.*; -public class GeyserExtensionDescription implements org.geysermc.geyser.api.extension.ExtensionDescription { - private String name; - private String main; - private String api; - private String version; - private final List authors = new ArrayList<>(); - - public GeyserExtensionDescription(InputStream inputStream) throws InvalidDescriptionException { +public record GeyserExtensionDescription(String name, String main, String apiVersion, String version, List authors) implements ExtensionDescription { + @SuppressWarnings("unchecked") + public static GeyserExtensionDescription fromYaml(Reader reader) throws InvalidDescriptionException { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - Yaml yaml = new Yaml(dumperOptions); - this.loadMap(yaml.loadAs(inputStream, LinkedHashMap.class)); - } - private void loadMap(Map yamlMap) throws InvalidDescriptionException { - this.name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); - if (this.name.equals("")) { + Yaml yaml = new Yaml(dumperOptions); + Map yamlMap = yaml.loadAs(reader, LinkedHashMap.class); + + String name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); + if (name.isBlank()) { throw new InvalidDescriptionException("Invalid extension name, cannot be empty"); } - this.name = this.name.replace(" ", "_"); - this.version = String.valueOf(yamlMap.get("version")); - this.main = (String) yamlMap.get("main"); + + name = name.replace(" ", "_"); + String version = String.valueOf(yamlMap.get("version")); + String main = (String) yamlMap.get("main"); + String apiVersion; Object api = yamlMap.get("api"); if (api instanceof String) { - this.api = (String) api; + apiVersion = (String) api; } else { - this.api = "0.0.0"; throw new InvalidDescriptionException("Invalid api version format, should be a string: major.minor.patch"); } + List authors = new ArrayList<>(); if (yamlMap.containsKey("author")) { - this.authors.add((String) yamlMap.get("author")); + authors.add((String) yamlMap.get("author")); } if (yamlMap.containsKey("authors")) { try { - this.authors.addAll((Collection) yamlMap.get("authors")); + authors.addAll((Collection) yamlMap.get("authors")); } catch (Exception e) { throw new InvalidDescriptionException("Invalid authors format, should be a list of strings", e); } } - } - @Override - public String name() { - return this.name; - } - - @Override - public String main() { - return this.main; - } - - @Override - public String apiVersion() { - return api; - } - - @Override - public String version() { - return this.version; - } - - @Override - public List authors() { - return this.authors; + return new GeyserExtensionDescription(name, main, apiVersion, version, authors); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index f029eb797..a539dcb15 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -25,95 +25,81 @@ package org.geysermc.geyser.extension; -import org.geysermc.api.Geyser; +import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.ExtensionLoader; -import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.api.extension.ExtensionLogger; +import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; import org.geysermc.geyser.text.GeyserLocale; -import java.io.*; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.util.Objects; import java.util.regex.Pattern; -public class GeyserExtensionLoader implements ExtensionLoader { - private final Map classes = new HashMap<>(); +@RequiredArgsConstructor +public class GeyserExtensionLoader extends ExtensionLoader { + private static final Path EXTENSION_DIRECTORY = Paths.get("extensions"); + private static final Pattern API_VERSION_PATTERN = Pattern.compile("^[0-9]+\\.[0-9]+\\.[0-9]+$"); + + private final Map> classes = new HashMap<>(); private final Map classLoaders = new HashMap<>(); + private final Map extensionContainers = new HashMap<>(); - @Override - public GeyserExtension loadExtension(File file) throws InvalidExtensionException { - if (file == null) { - throw new InvalidExtensionException("File is null"); + public GeyserExtensionContainer loadExtension(Path path) throws InvalidExtensionException, InvalidDescriptionException { + if (path == null) { + throw new InvalidExtensionException("Path is null"); } - if (!file.exists()) { - throw new InvalidExtensionException(new FileNotFoundException(file.getPath()) + " does not exist"); + if (Files.notExists(path)) { + throw new InvalidExtensionException(new NoSuchFileException(path.toString()) + " does not exist"); } - final GeyserExtensionDescription description; - try { - description = extensionDescription(file); - } catch (InvalidDescriptionException e) { - throw new InvalidExtensionException(e); - } - - final File parentFile = file.getParentFile(); - final File dataFolder = new File(parentFile, description.name()); - if (dataFolder.exists() && !dataFolder.isDirectory()) { - throw new InvalidExtensionException("The folder " + dataFolder.getPath() + " is not a directory and is the data folder for the extension " + description.name() + "!"); + GeyserExtensionDescription description = this.extensionDescription(path); + Path parentFile = path.getParent(); + Path dataFolder = parentFile.resolve(description.name()); + if (Files.exists(dataFolder) && !Files.isDirectory(dataFolder)) { + throw new InvalidExtensionException("The folder " + dataFolder + " is not a directory and is the data folder for the extension " + description.name() + "!"); } final GeyserExtensionClassLoader loader; try { - loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), description, file); + loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), description, path); } catch (Throwable e) { throw new InvalidExtensionException(e); } - classLoaders.put(description.name(), loader); - setup(loader.extension, description, dataFolder, file); - return loader.extension; + this.classLoaders.put(description.name(), loader); + return this.setup(loader.extension(), description, dataFolder); } - private void setup(GeyserExtension extension, GeyserExtensionDescription description, File dataFolder, File file) { + private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder) { GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); - extension.init(Geyser.api(), this, logger, description, dataFolder, file); + GeyserExtensionContainer container = new GeyserExtensionContainer(extension, dataFolder, description, this, logger); extension.onLoad(); + return container; } - @Override - public GeyserExtensionDescription extensionDescription(File file) throws InvalidDescriptionException { - JarFile jarFile = null; - InputStream stream = null; - - try { - jarFile = new JarFile(file); - - JarEntry descriptionEntry = jarFile.getJarEntry("extension.yml"); - if (descriptionEntry == null) { - throw new InvalidDescriptionException(new FileNotFoundException("extension.yml") + " does not exist in the jar file!"); - } - - stream = jarFile.getInputStream(descriptionEntry); - return new GeyserExtensionDescription(stream); - } catch (IOException e) { - throw new InvalidDescriptionException(e); - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException e) { - } - } - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - } - } + public GeyserExtensionDescription extensionDescription(Path path) throws InvalidDescriptionException { + Map environment = new HashMap<>(); + try (FileSystem fileSystem = FileSystems.newFileSystem(path, environment, null)) { + Path extensionYml = fileSystem.getPath("extension.yml"); + return GeyserExtensionDescription.fromYaml(Files.newBufferedReader(extensionYml)); + } catch (IOException ex) { + throw new InvalidDescriptionException("Failed to load extension description for " + path, ex); } } @@ -121,14 +107,13 @@ public class GeyserExtensionLoader implements ExtensionLoader { return new Pattern[] { Pattern.compile("^.+\\.jar$") }; } - @Override public Class classByName(final String name) throws ClassNotFoundException{ - Class clazz = classes.get(name); + Class clazz = this.classes.get(name); try { - for(GeyserExtensionClassLoader loader : classLoaders.values()) { + for(GeyserExtensionClassLoader loader : this.classLoaders.values()) { try { clazz = loader.findClass(name,false); - } catch(NullPointerException e) { + } catch(NullPointerException ignored) { } } return clazz; @@ -138,28 +123,132 @@ public class GeyserExtensionLoader implements ExtensionLoader { } void setClass(String name, final Class clazz) { - if (!classes.containsKey(name)) { - classes.put(name,clazz); - } - } - - void removeClass(String name) { - classes.remove(name); - } - - @Override - public void enableExtension(GeyserExtension extension) { - if (!extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); - extension.setEnabled(true); + if (!this.classes.containsKey(name)) { + this.classes.put(name,clazz); } } @Override - public void disableExtension(GeyserExtension extension) { - if (extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); - extension.setEnabled(false); + protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) { + // noinspection ConstantConditions + if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_dev_environment")); + return; } + + if (!GeyserImpl.VERSION.contains(".")) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); + return; + } + + String[] apiVersion = GeyserImpl.VERSION.split("\\."); + + try { + if (Files.notExists(EXTENSION_DIRECTORY)) { + Files.createDirectory(EXTENSION_DIRECTORY); + } + + Map extensions = new LinkedHashMap<>(); + Map loadedExtensions = new LinkedHashMap<>(); + + Pattern[] extensionFilters = this.extensionFilters(); + + Files.walk(EXTENSION_DIRECTORY).forEach(path -> { + if (Files.isDirectory(path)) { + return; + } + + for (Pattern filter : extensionFilters) { + if (!filter.matcher(path.getFileName().toString()).matches()) { + return; + } + } + + try { + ExtensionDescription description = this.extensionDescription(path); + if (description == null) { + return; + } + + String name = description.name(); + if (extensions.containsKey(name) || extensionManager.extension(name) != null) { + GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString())); + return; + } + + try { + // Check the format: majorVersion.minorVersion.patch + if (!API_VERSION_PATTERN.matcher(description.apiVersion()).matches()) { + throw new IllegalArgumentException(); + } + } catch (NullPointerException | IllegalArgumentException e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + String[] versionArray = description.apiVersion().split("\\."); + + // Completely different API version + if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + // If the extension requires new API features, being backwards compatible + if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + extensions.put(name, path); + loadedExtensions.put(name, this.loadExtension(path)); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e); + } + }); + + for (GeyserExtensionContainer container : loadedExtensions.values()) { + this.extensionContainers.put(container.extension(), container); + this.register(container.extension(), extensionManager); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + protected boolean isEnabled(@NonNull Extension extension) { + return this.extensionContainers.get(extension).enabled; + } + + @Override + protected void setEnabled(@NonNull Extension extension, boolean enabled) { + boolean isEnabled = this.extensionContainers.get(extension).enabled; + if (isEnabled != enabled) { + this.extensionContainers.get(extension).enabled = enabled; + if (enabled) { + extension.onEnable(); + } else { + extension.onDisable(); + } + } + } + + @NonNull + @Override + protected Path dataFolder(@NonNull Extension extension) { + return this.extensionContainers.get(extension).dataFolder(); + } + + @NonNull + @Override + protected ExtensionDescription description(@NonNull Extension extension) { + return this.extensionContainers.get(extension).description(); + } + + @NonNull + @Override + protected ExtensionLogger logger(@NonNull Extension extension) { + return this.extensionContainers.get(extension).logger(); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index 169053182..a72712f16 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -25,205 +25,126 @@ package org.geysermc.geyser.extension; +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.extension.ExtensionDescription; -import org.geysermc.geyser.api.extension.GeyserExtension; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionManager; +import org.geysermc.geyser.api.extension.ExtensionLoader; +import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.text.GeyserLocale; -import java.io.File; -import java.lang.reflect.Constructor; -import java.util.*; -import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; -public class GeyserExtensionManager { - protected Map extensions = new LinkedHashMap<>(); - protected Map fileAssociations = new HashMap<>(); +import java.util.*; + +public class GeyserExtensionManager extends ExtensionManager { + private static final Key BASE_KEY = Key.key("geysermc", "base"); + + private final Map extensions = new LinkedHashMap<>(); + private final Map extensionsLoaders = new LinkedHashMap<>(); public void init() { GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); - this.registerInterface(GeyserExtensionLoader.class); - this.loadExtensions(new File("extensions")); + this.registerExtensionLoader(BASE_KEY, new GeyserExtensionLoader()); + + for (ExtensionLoader loader : this.extensionLoaders().values()) { + this.loadAllExtensions(loader); + } GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.done", this.extensions.size())); } - public GeyserExtension getExtension(String name) { + @Override + public Extension extension(@NonNull String name) { if (this.extensions.containsKey(name)) { return this.extensions.get(name); } - return null; - } - - public Map getExtensions() { - return this.extensions; - } - - public void registerInterface(Class loader) { - GeyserExtensionLoader instance; - - if (GeyserExtensionLoader.class.isAssignableFrom(loader)) { - Constructor constructor; - - try { - constructor = loader.getConstructor(); - instance = constructor.newInstance(); - } catch (NoSuchMethodException ex) { // This should never happen - String className = loader.getName(); - - throw new IllegalArgumentException("Class " + className + " does not have a public constructor", ex); - } catch (Exception ex) { // This should never happen - throw new IllegalArgumentException("Unexpected exception " + ex.getClass().getName() + " while attempting to construct a new instance of " + loader.getName(), ex); - } - } else { - throw new IllegalArgumentException("Class " + loader.getName() + " does not implement interface ExtensionLoader"); - } - - Pattern[] patterns = instance.extensionFilters(); - - synchronized (this) { - for (Pattern pattern : patterns) { - fileAssociations.put(pattern, instance); - } - } - } - - public GeyserExtension loadExtension(File file, Map loaders) { - for (GeyserExtensionLoader loader : (loaders == null ? this.fileAssociations : loaders).values()) { - for (Pattern pattern : loader.extensionFilters()) { - if (pattern.matcher(file.getName()).matches()) { - try { - ExtensionDescription description = loader.extensionDescription(file); - if (description != null) { - GeyserExtension extension = loader.loadExtension(file); - - if (extension != null) { - this.extensions.put(extension.description().name(), extension); - - return extension; - } - } - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed"), e); - return null; - } - } - } - } return null; } - public Map loadExtensions(File dictionary) { - if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { // If your IDE says this is always true, ignore it, it isn't. - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_dev_environment")); - return new HashMap<>(); - } - if (!GeyserImpl.VERSION.contains(".")) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); - return new HashMap<>(); - } - - String[] apiVersion = GeyserImpl.VERSION.split("\\."); - - if (!dictionary.exists()) { - dictionary.mkdir(); - } - if (!dictionary.isDirectory()) { - return new HashMap<>(); - } - - Map extensions = new LinkedHashMap<>(); - Map loadedExtensions = new LinkedHashMap<>(); - - for (final GeyserExtensionLoader loader : this.fileAssociations.values()) { - for (File file : dictionary.listFiles((dir, name) -> { - for (Pattern pattern : loader.extensionFilters()) { - if (pattern.matcher(name).matches()) { - return true; - } - } - return false; - })) { - if (file.isDirectory()) { - continue; - } - - try { - ExtensionDescription description = loader.extensionDescription(file); - if (description != null) { - String name = description.name(); - - if (extensions.containsKey(name) || this.getExtension(name) != null) { - GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, file.getName())); - continue; - } - - try { - //Check the format: majorVersion.minorVersion.patch - if (!Pattern.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$", description.apiVersion())) { - throw new IllegalArgumentException(); - } - } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); - continue; - } - - String[] versionArray = description.apiVersion().split("\\."); - - //Completely different API version - if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); - continue; - } - - //If the extension requires new API features, being backwards compatible - if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); - continue; - } - - extensions.put(name, file); - loadedExtensions.put(name, this.loadExtension(file, this.fileAssociations)); - } - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", file.getName(), dictionary.getAbsolutePath()), e); - } - } - } - - return loadedExtensions; - } - - public void enableExtension(GeyserExtension extension) { + @Override + public void enable(@NonNull Extension extension) { if (!extension.isEnabled()) { try { - extension.extensionLoader().enableExtension(extension); + this.enableExtension(extension); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.failed", extension.name()), e); - this.disableExtension(extension); + this.disable(extension); } } } - public void disableExtension(GeyserExtension extension) { + @Override + public void disable(@NonNull Extension extension) { if (extension.isEnabled()) { try { - extension.extensionLoader().disableExtension(extension); + this.disableExtension(extension); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.failed", extension.name()), e); } } } + public void enableExtension(Extension extension) { + if (!extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); + extension.setEnabled(true); + } + } + + private void disableExtension(@NonNull Extension extension) { + if (extension.isEnabled()) { + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); + extension.setEnabled(false); + } + } + public void enableExtensions() { - for (GeyserExtension extension : this.getExtensions().values()) { - this.enableExtension(extension); + for (Extension extension : this.extensions()) { + this.enable(extension); } } public void disableExtensions() { - for (GeyserExtension extension : this.getExtensions().values()) { - this.disableExtension(extension); + for (Extension extension : this.extensions()) { + this.disable(extension); } } + + @Override + public ExtensionLoader extensionLoader(@NonNull Extension extension) { + return this.extensionsLoaders.get(extension); + } + + @NonNull + @Override + public Collection extensions() { + return Collections.unmodifiableCollection(this.extensions.values()); + } + + @Nullable + @Override + public ExtensionLoader extensionLoader(@NonNull Key identifier) { + return Registries.EXTENSION_LOADERS.get(identifier); + } + + @Override + public void registerExtensionLoader(@NonNull Key identifier, @NotNull ExtensionLoader extensionLoader) { + Registries.EXTENSION_LOADERS.register(identifier, extensionLoader); + } + + @NonNull + @Override + public Map extensionLoaders() { + return Collections.unmodifiableMap(Registries.EXTENSION_LOADERS.get()); + } + + @Override + public void register(@NotNull Extension extension, @NotNull ExtensionLoader loader) { + this.extensionsLoaders.put(extension, loader); + this.extensions.put(extension.name(), extension); + } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 3b0c1f138..173bfbd1a 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -31,7 +31,6 @@ import com.github.steveice10.mc.protocol.data.game.level.event.SoundEvent; import com.github.steveice10.mc.protocol.data.game.level.particle.ParticleType; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; import com.github.steveice10.packetlib.packet.Packet; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; @@ -42,6 +41,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.kyori.adventure.key.Key; +import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; import org.geysermc.geyser.translator.collision.BlockCollision; @@ -62,7 +63,6 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.IntFunction; /** * Holds all the common registries in Geyser. @@ -113,6 +113,11 @@ public final class Registries { */ public static final SimpleMappedRegistry> ENTITY_DEFINITIONS = SimpleMappedRegistry.create(RegistryLoaders.empty(() -> new EnumMap<>(EntityType.class))); + /** + * A map containing all the extension loaders. + */ + public static final SimpleMappedRegistry EXTENSION_LOADERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); + /** * A map containing all Java entity identifiers and their respective Geyser definitions */ diff --git a/pom.xml b/pom.xml index 004d58666..db512e477 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,8 @@ UTF-8 16 16 + + 4.9.3 From b82c66168883045896bae92866a5ecb580b10ca8 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 15 Jan 2022 22:54:08 -0600 Subject: [PATCH 011/125] Implement simple event system --- api/geyser/pom.xml | 6 + .../org/geysermc/geyser/api/GeyserApi.java | 9 ++ .../api/connection/GeyserConnection.java | 2 +- .../geyser/api/event/Cancellable.java | 46 ++++++++ .../org/geysermc/geyser/api/event/Event.java | 41 +++++++ .../geysermc/geyser/api/event/EventBus.java | 100 ++++++++++++++++ .../geyser/api/event/EventSubscription.java | 93 +++++++++++++++ .../geysermc/geyser/api/event/Subscribe.java | 99 ++++++++++++++++ .../lifecycle/GeyserPostInitializeEvent.java | 34 ++++++ .../event/lifecycle/GeyserShutdownEvent.java | 34 ++++++ core/pom.xml | 7 ++ .../java/org/geysermc/geyser/GeyserImpl.java | 15 +++ .../geysermc/geyser/event/GeyserEventBus.java | 110 ++++++++++++++++++ .../geyser/event/GeyserEventSubscription.java | 82 +++++++++++++ 14 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml index 5c598f65b..88f1cafdc 100644 --- a/api/geyser/pom.xml +++ b/api/geyser/pom.xml @@ -32,6 +32,12 @@ ${adventure.version} compile + + net.kyori + event-api + 3.0.0 + provided + org.geysermc base-api diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index d5fd09381..dbdee8b4e 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; import java.util.List; @@ -88,6 +89,14 @@ public interface GeyserApi extends GeyserApiBase { */ ExtensionManager extensionManager(); + /** + * Gets the {@link EventBus} for handling + * Geyser events. + * + * @return the event bus + */ + EventBus eventBus(); + /** * Gets the current {@link GeyserApiBase} instance. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index b673b60f6..036c7c591 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.api.connection; import org.geysermc.api.connection.Connection; /** - * Represents a player session used in Geyser. + * Represents a player connection used in Geyser. */ public interface GeyserConnection extends Connection { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java new file mode 100644 index 000000000..94d0b832d --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +/** + * Represents a cancellable event. + */ +public interface Cancellable { + + /** + * Gets if the event is cancelled. + * + * @return if the event is cancelled + */ + boolean isCancelled(); + + /** + * Cancels the event. + * + * @param cancelled if the event is cancelled + */ + void setCancelled(boolean cancelled); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java new file mode 100644 index 000000000..c32e1701e --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +/** + * Represents an event. + */ +public interface Event { + + /** + * Gets if the event is async. + * + * @return if the event is async + */ + default boolean isAsync() { + return false; + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java new file mode 100644 index 000000000..a26e8c653 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; +import java.util.function.Consumer; + +/** + * Represents a bus capable of subscribing + * or "listening" to events and firing them. + */ +public interface EventBus { + + /** + * Subscribes to the given event see {@link EventSubscription}. + * + * @param eventClass the class of the event + * @param consumer the consumer for handling the event + * @param the event class + * @return the event subscription + */ + @NonNull + EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer); + + /** + * Subscribes to the given event see {@link EventSubscription}. + * + * The difference between this method and {@link #subscribe(Class, Consumer)} + * is that this method takes in an extension parameter which allows for + * the event to be unsubscribed upon extension disable and reloads. + * + * @param extension the extension to subscribe the event to + * @param eventClass the class of the event + * @param consumer the consumer for handling the event + * @param the event class + * @return the event subscription + */ + @NonNull + EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer); + + /** + * Unsubscribes the given {@link EventSubscription}. + * + * @param subscription the event subscription + */ + void unsubscribe(@NonNull EventSubscription subscription); + + /** + * Registers events for the given listener. + * + * @param extension the extension registering the event + * @param eventHolder the listener + */ + void register(@NonNull Extension extension, @NonNull Object eventHolder); + + /** + * Fires the given {@link Event}. + * + * @param event the event to fire + * + * @return true if the event successfully fired + */ + boolean fire(@NonNull Event event); + + /** + * Gets the subscriptions for the given event class. + * + * @param eventClass the event class + * @param the value + * @return the subscriptions for the event class + */ + @NonNull + Set> subscriptions(@NonNull Class eventClass); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java new file mode 100644 index 000000000..65939a7b0 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.function.Consumer; + +/** + * Represents a subscribed listener to a {@link Event}. Wraps around + * the event and is capable of unsubscribing from the event or give + * information about it. + * + * @param the class of the event + */ +public interface EventSubscription { + + /** + * Gets the event class. + * + * @return the event class + */ + @NonNull + Class eventClass(); + + /** + * Gets the consumer responsible for handling + * this event. + * + * @return the consumer responsible for this event + */ + @NonNull + Consumer eventConsumer(); + + /** + * Gets the {@link Extension} that owns this + * event subscription. + * + * @return the extension that owns this subscription + */ + @NonNull + Extension owner(); + + /** + * Gets the priority of this event subscription. + * + * @return the priority of this event subscription + */ + Subscribe.Priority priority(); + + /** + * Gets if this event subscription is active. + * + * @return if this event subscription is active + */ + boolean isActive(); + + /** + * Unsubscribes from this event listener + */ + void unsubscribe(); + + /** + * Invokes the given event + * + * @param event the event + */ + void invoke(@NonNull T event) throws Throwable; +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java new file mode 100644 index 000000000..581aa6fa0 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation used to signify the given method is + * an {@link Event}. Only should be applied to methods + * where the class containing them is designated for + * events specifically. + * + * When using {@link EventBus#subscribe}, this annotation should + * not be applied whatsoever as it will have no use and potentially + * throw errors due to it being used wrongly. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Subscribe { + + /** + * The {@link Priority} of the event + * + * @return the priority of the event + */ + @NonNull + Priority priority() default Priority.NORMAL; + + /** + * Represents the priority of an event. + */ + @Accessors(fluent = true) + @Getter + @AllArgsConstructor + enum Priority { + + /** + * The lowest priority. Called first to + * allow for other events to customize + * the outcome + */ + LOWEST(net.kyori.event.PostOrders.FIRST), + + /** + * The second lowest priority. + */ + LOW(net.kyori.event.PostOrders.EARLY), + + /** + * Normal priority. Event is neither + * important nor unimportant + */ + NORMAL(net.kyori.event.PostOrders.NORMAL), + + /** + * The second highest priority + */ + HIGH(net.kyori.event.PostOrders.LATE), + + /** + * The highest of importance! Event is called + * last and has the final say in the outcome + */ + HIGHEST(net.kyori.event.PostOrders.LAST); + + private final int postOrder; + } +} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java new file mode 100644 index 000000000..94d3d8cb8 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.geysermc.geyser.api.event.Event; + +/** + * Called when Geyser has completed initializing. + */ +public class GeyserPostInitializeEvent implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java new file mode 100644 index 000000000..baf8b3445 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.geysermc.geyser.api.event.Event; + +/** + * Called when Geyser is shutting down. + */ +public class GeyserShutdownEvent implements Event { +} diff --git a/core/pom.xml b/core/pom.xml index 4837660f9..6f4e0fa3c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -244,6 +244,13 @@ ${adventure.version} compile + + + net.kyori + event-api + 3.0.0 + compile + junit diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 6e740cb44..573934b91 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -49,9 +49,13 @@ import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; import org.geysermc.geyser.command.CommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.ConnectorServerEventHandler; @@ -123,6 +127,7 @@ public class GeyserImpl implements GeyserApi { private final PlatformType platformType; private final GeyserBootstrap bootstrap; + private final EventBus eventBus; private final GeyserExtensionManager extensionManager; private Metrics metrics; @@ -158,6 +163,7 @@ public class GeyserImpl implements GeyserApi { MinecraftLocale.init(); /* Load Extensions */ + this.eventBus = new GeyserEventBus(); this.extensionManager = new GeyserExtensionManager(); this.extensionManager.init(); @@ -411,6 +417,8 @@ public class GeyserImpl implements GeyserApi { } newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); + + this.eventBus.fire(new GeyserPostInitializeEvent()); } @Override @@ -466,6 +474,8 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); + this.eventBus.fire(new GeyserShutdownEvent()); + this.extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); @@ -494,6 +504,11 @@ public class GeyserImpl implements GeyserApi { return this.extensionManager; } + @Override + public EventBus eventBus() { + return this.eventBus; + } + public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java new file mode 100644 index 000000000..93a042826 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event; + +import net.kyori.event.SimpleEventBus; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventSubscription; +import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.geyser.api.extension.Extension; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class GeyserEventBus implements EventBus { + private final SimpleEventBus bus = new SimpleEventBus<>(Event.class); + + @NonNull + @Override + public EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer) { + return this.subscribe(eventClass, consumer, null, Subscribe.Priority.NORMAL); + } + + @NonNull + @Override + public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { + return this.subscribe(eventClass, consumer, extension, Subscribe.Priority.NORMAL); + } + + @Override + public void unsubscribe(@NonNull EventSubscription subscription) { + this.bus.unregister((GeyserEventSubscription) subscription); + } + + @SuppressWarnings("unchecked") + @Override + public void register(@NonNull Extension extension, @NonNull Object eventHolder) { + for (Method method : eventHolder.getClass().getMethods()) { + if (!method.isAnnotationPresent(Subscribe.class)) { + continue; + } + + if (method.getParameterCount() > 1) { + continue; + } + + if (!Event.class.isAssignableFrom(method.getParameters()[0].getType())) { + continue; + } + + Subscribe subscribe = method.getAnnotation(Subscribe.class); + this.subscribe((Class) method.getParameters()[0].getType(), (event) -> { + try { + method.invoke(eventHolder, event); + } catch (IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + } + }, extension, subscribe.priority()); + } + } + + @Override + public boolean fire(@NonNull Event event) { + return this.bus.post(event).wasSuccessful(); + } + + @SuppressWarnings("unchecked") + @NonNull + @Override + public Set> subscriptions(@NonNull Class eventClass) { + return bus.subscribers().values() + .stream() + .filter(sub -> sub instanceof EventSubscription && ((EventSubscription) sub).eventClass().isAssignableFrom(eventClass)) + .map(sub -> ((EventSubscription) sub)) + .collect(Collectors.toSet()); + } + + private EventSubscription subscribe(Class eventClass, Consumer handler, Extension extension, Subscribe.Priority priority) { + GeyserEventSubscription eventSubscription = new GeyserEventSubscription<>(this, eventClass, handler, extension, priority); + this.bus.register(eventClass, eventSubscription); + return eventSubscription; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java new file mode 100644 index 000000000..e160291c9 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.kyori.event.EventSubscriber; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventSubscription; +import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.function.Consumer; + +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +public class GeyserEventSubscription implements EventSubscription, EventSubscriber { + private final EventBus eventBus; + private final Class eventClass; + private final Consumer eventConsumer; + private final Extension owner; + private final Subscribe.Priority priority; + @Getter(AccessLevel.NONE) private boolean active; + + @Override + public boolean isActive() { + return this.active; + } + + @Override + public void unsubscribe() { + if (!this.active) { + return; + } + + this.active = false; + this.eventBus.unsubscribe(this); + } + + @Override + public void invoke(@NonNull T event) throws Throwable { + try { + this.eventConsumer.accept(event); + } catch (Throwable ex) { + this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); + ex.printStackTrace(); + } + } + + @Override + public int postOrder() { + return this.priority.postOrder(); + } +} From 57345fa102cd45cf3b7f2e59b40c2bad64145e8b Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 15 Jan 2022 23:01:40 -0600 Subject: [PATCH 012/125] Event owner can be null --- .../java/org/geysermc/geyser/api/event/EventSubscription.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java index 65939a7b0..daa05ab21 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.extension.Extension; import java.util.function.Consumer; @@ -62,7 +63,7 @@ public interface EventSubscription { * * @return the extension that owns this subscription */ - @NonNull + @Nullable Extension owner(); /** From 30303d5f16bf10212bcbc5a52f2fc7bf50b068db Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 15:09:53 -0600 Subject: [PATCH 013/125] Implement support for adding Geyser subcommands --- api/geyser/pom.xml | 14 +- .../org/geysermc/geyser/api/GeyserApi.java | 8 + .../geysermc/geyser/api/command/Command.java | 124 +++++++++ .../geyser/api/command/CommandExecutor.java | 44 +++ .../geyser/api/command/CommandManager.java | 67 +++++ .../geyser/api/command/CommandSource.java | 46 ++-- .../api/connection/GeyserConnection.java | 3 +- .../geysermc/geyser/api/event/EventBus.java | 20 +- .../geyser/api/event/EventSubscription.java | 2 +- .../geyser/api/event/ExtensionEventBus.java | 61 +++++ .../api/event/connection/ConnectionEvent.java | 47 ++++ .../downstream/ServerDefineCommandsEvent.java | 81 ++++++ .../lifecycle/GeyserDefineCommandsEvent.java | 42 +++ .../geyser/api/extension/Extension.java | 10 + .../geyser/api/extension/ExtensionLoader.java | 10 + .../bungeecord/GeyserBungeePlugin.java | 5 +- ...ndSender.java => BungeeCommandSource.java} | 10 +- .../command/GeyserBungeeCommandExecutor.java | 16 +- .../command/GeyserBungeeCommandManager.java | 6 +- .../platform/spigot/GeyserSpigotPlugin.java | 9 +- .../command/GeyserSpigotCommandExecutor.java | 14 +- .../command/GeyserSpigotCommandManager.java | 6 +- ...ndSender.java => SpigotCommandSource.java} | 8 +- .../platform/sponge/GeyserSpongePlugin.java | 5 +- .../command/GeyserSpongeCommandExecutor.java | 12 +- .../command/GeyserSpongeCommandManager.java | 6 +- ...ndSender.java => SpongeCommandSource.java} | 4 +- .../standalone/GeyserStandaloneBootstrap.java | 11 +- .../standalone/GeyserStandaloneLogger.java | 6 +- ...va => GeyserStandaloneCommandManager.java} | 8 +- .../standalone/gui/GeyserStandaloneGUI.java | 28 +- .../velocity/GeyserVelocityPlugin.java | 5 +- .../GeyserVelocityCommandExecutor.java | 16 +- .../command/GeyserVelocityCommandManager.java | 6 +- ...Sender.java => VelocityCommandSource.java} | 10 +- .../network/session/GeyserSession.java | 2 +- .../org/geysermc/geyser/GeyserBootstrap.java | 4 +- .../java/org/geysermc/geyser/GeyserImpl.java | 14 +- .../geyser/command/CommandManager.java | 122 --------- .../geyser/command/GeyserCommand.java | 28 +- ...ecutor.java => GeyserCommandExecutor.java} | 18 +- .../geyser/command/GeyserCommandManager.java | 256 ++++++++++++++++++ .../geyser/command/GeyserCommandSource.java | 43 +++ .../defaults/AdvancedTooltipsCommand.java | 6 +- .../command/defaults/AdvancementsCommand.java | 4 +- .../geyser/command/defaults/DumpCommand.java | 26 +- .../geyser/command/defaults/HelpCommand.java | 17 +- .../geyser/command/defaults/ListCommand.java | 6 +- .../command/defaults/OffhandCommand.java | 4 +- .../command/defaults/ReloadCommand.java | 6 +- .../command/defaults/SettingsCommand.java | 4 +- .../command/defaults/StatisticsCommand.java | 4 +- .../geyser/command/defaults/StopCommand.java | 6 +- .../command/defaults/VersionCommand.java | 14 +- .../geysermc/geyser/entity/type/Entity.java | 2 +- .../geysermc/geyser/event/GeyserEventBus.java | 13 +- .../extension/GeyserExtensionContainer.java | 2 + .../extension/GeyserExtensionLoader.java | 14 +- .../extension/GeyserExtensionManager.java | 7 +- .../event/GeyserExtensionEventBus.java | 87 ++++++ .../updater/AnvilInventoryUpdater.java | 4 +- .../geyser/network/UpstreamPacketHandler.java | 4 +- .../geyser/session/GeyserSession.java | 12 +- .../geyser/session/SessionManager.java | 2 +- .../session/cache/AdvancementsCache.java | 8 +- .../geyser/session/cache/BossBar.java | 4 +- .../inventory/item/ItemTranslator.java | 6 +- .../item/nbt/AxolotlBucketTranslator.java | 2 +- .../item/nbt/BasicItemTranslator.java | 2 +- .../item/nbt/PlayerHeadTranslator.java | 2 +- .../nbt/TropicalFishBucketTranslator.java | 8 +- .../BedrockCommandRequestTranslator.java | 4 +- .../bedrock/BedrockFilterTextTranslator.java | 4 +- .../protocol/java/JavaChatTranslator.java | 2 +- .../protocol/java/JavaCommandsTranslator.java | 16 +- .../java/JavaDisconnectTranslator.java | 2 +- .../java/JavaLoginDisconnectTranslator.java | 2 +- .../protocol/java/JavaLoginTranslator.java | 2 +- .../JavaUpdateAdvancementsTranslator.java | 4 +- .../inventory/JavaOpenScreenTranslator.java | 4 +- .../java/level/JavaGameEventTranslator.java | 2 +- .../java/level/JavaLevelEventTranslator.java | 2 +- .../JavaSetPlayerTeamTranslator.java | 8 +- .../title/JavaSetActionBarTextTranslator.java | 2 +- .../title/JavaSetSubtitleTextTranslator.java | 2 +- .../title/JavaSetTitleTextTranslator.java | 2 +- .../translator/text/MessageTranslator.java | 2 +- .../geyser/util/LoginEncryptionUtils.java | 12 +- .../geysermc/geyser/util/SettingsUtils.java | 2 +- .../geysermc/geyser/util/StatisticsUtils.java | 2 +- 90 files changed, 1207 insertions(+), 402 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java rename core/src/main/java/org/geysermc/geyser/command/CommandSender.java => api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java (66%) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java rename bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/{BungeeCommandSender.java => BungeeCommandSource.java} (89%) rename bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/{SpigotCommandSender.java => SpigotCommandSource.java} (95%) rename bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/{SpongeCommandSender.java => SpongeCommandSource.java} (94%) rename bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/{GeyserCommandManager.java => GeyserStandaloneCommandManager.java} (85%) rename bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/{VelocityCommandSender.java => VelocityCommandSource.java} (90%) delete mode 100644 core/src/main/java/org/geysermc/geyser/command/CommandManager.java rename core/src/main/java/org/geysermc/geyser/command/{CommandExecutor.java => GeyserCommandExecutor.java} (85%) create mode 100644 core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java create mode 100644 core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java create mode 100644 core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml index 88f1cafdc..b2f448589 100644 --- a/api/geyser/pom.xml +++ b/api/geyser/pom.xml @@ -26,6 +26,12 @@ 3.19.0 provided + + net.kyori + event-api + 3.0.0 + provided + net.kyori adventure-api @@ -33,10 +39,10 @@ compile - net.kyori - event-api - 3.0.0 - provided + org.yaml + snakeyaml + 1.27 + compile org.geysermc diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index dbdee8b4e..2bddc9ef5 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; +import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; @@ -89,6 +90,13 @@ public interface GeyserApi extends GeyserApiBase { */ ExtensionManager extensionManager(); + /** + * Gets the {@link CommandManager}. + * + * @return the command manager + */ + CommandManager commandManager(); + /** * Gets the {@link EventBus} for handling * Geyser events. diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java new file mode 100644 index 000000000..2c48473c6 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.command; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; + +import java.util.Collections; +import java.util.List; + +/** + * Represents a command. + */ +public interface Command { + + /** + * Gets the command name. + * + * @return the command name + */ + @NonNull + String name(); + + /** + * Gets the command description. + * + * @return the command description + */ + @NonNull + String description(); + + /** + * Gets the permission node associated with + * this command. + * + * @return the permission node for this command + */ + @NonNull + String permission(); + + /** + * Gets the aliases for this command. + * + * @return the aliases for this command + */ + @NonNull + List aliases(); + + /** + * Gets if this command is executable on console. + * + * @return if this command is executable on console + */ + boolean isExecutableOnConsole(); + + /** + * Gets the subcommands associated with this + * command. Mainly used within the Geyser Standalone + * GUI to know what subcommands are supported. + * + * @return the subcommands associated with this command + */ + @NonNull + default List subCommands() { + return Collections.emptyList(); + } + + /** + * Used to send a deny message to Java players if this command can only be used by Bedrock players. + * + * @return true if this command can only be used by Bedrock players. + */ + default boolean isBedrockOnly() { + return false; + } + + static Command.Builder builder(Class sourceType) { + return GeyserApi.api().commandManager().provideBuilder(sourceType); + } + + interface Builder { + + Builder name(String name); + + Builder description(String description); + + Builder permission(String permission); + + Builder aliases(List aliases); + + Builder executableOnConsole(boolean executableOnConsole); + + Builder subCommands(List subCommands); + + Builder bedrockOnly(boolean bedrockOnly); + + Builder executor(CommandExecutor executor); + + Command build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java new file mode 100644 index 000000000..d384d097c --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.command; + +/** + * Handles executing a command. + * + * @param the command source + */ +public interface CommandExecutor { + + /** + * Executes the given {@link Command} with the given + * {@link CommandSource}. + * + * @param source the command source + * @param command the command + * @param args the arguments + */ + void execute(T source, Command command, String[] args); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java new file mode 100644 index 000000000..864ba71a4 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.command; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Map; + +/** + * Manages Bedrock commands within Geyser. + */ +public abstract class CommandManager { + + /** + * Provides a {@link Command.Builder}. + * + * @param sourceType the command source type + * @param the type + * @return a command builder + */ + protected abstract Command.Builder provideBuilder(Class sourceType); + + /** + * Registers the given {@link Command}. + * + * @param command the command to register + */ + public abstract void register(@NonNull Command command); + + /** + * Unregisters the given {@link Command}. + * + * @param command the command to unregister + */ + public abstract void unregister(@NonNull Command command); + + /** + * Gets all the registered {@link Command}s. + * + * @return all the registered commands + */ + @NonNull + public abstract Map commands(); +} diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandSender.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java similarity index 66% rename from core/src/main/java/org/geysermc/geyser/command/CommandSender.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java index d9d1bcfbc..aabf0c4e8 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandSender.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java @@ -23,45 +23,57 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.command; - -import org.geysermc.geyser.text.GeyserLocale; +package org.geysermc.geyser.api.command; /** - * Implemented on top of any class that can send a command. - * For example, it wraps around Spigot's CommandSender class. + * Represents an instance capable of sending commands. */ -public interface CommandSender { +public interface CommandSource { + /** + * The name of the command source. + * + * @return the name of the command source + */ String name(); + /** + * Sends the given message to the command source + * + * @param message the message to send + */ + void sendMessage(String message); + + /** + * Sends the given messages to the command source + * + * @param messages the messages to send + */ default void sendMessage(String[] messages) { for (String message : messages) { sendMessage(message); } } - void sendMessage(String message); - /** - * @return true if the specified sender is from the console. + * If this source is the console. + * + * @return true if this source is the console */ boolean isConsole(); /** - * Returns the locale of the command sender. Defaults to the default locale at {@link GeyserLocale#getDefaultLocale()}. - * - * @return the locale of the command sender. + * Returns the locale of the command source. + * + * @return the locale of the command source. */ - default String getLocale() { - return GeyserLocale.getDefaultLocale(); - } + String locale(); /** - * Checks if the CommandSender has a permission + * Checks if this command source has the given permission * * @param permission The permission node to check - * @return true if the CommandSender has the requested permission, false if not + * @return true if this command source has a permission */ boolean hasPermission(String permission); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 036c7c591..13fd60407 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -26,9 +26,10 @@ package org.geysermc.geyser.api.connection; import org.geysermc.api.connection.Connection; +import org.geysermc.geyser.api.command.CommandSource; /** * Represents a player connection used in Geyser. */ -public interface GeyserConnection extends Connection { +public interface GeyserConnection extends Connection, CommandSource { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java index a26e8c653..0352dcc9e 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -40,18 +40,7 @@ public interface EventBus { /** * Subscribes to the given event see {@link EventSubscription}. * - * @param eventClass the class of the event - * @param consumer the consumer for handling the event - * @param the event class - * @return the event subscription - */ - @NonNull - EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer); - - /** - * Subscribes to the given event see {@link EventSubscription}. - * - * The difference between this method and {@link #subscribe(Class, Consumer)} + * The difference between this method and {@link ExtensionEventBus#subscribe(Class, Consumer)} * is that this method takes in an extension parameter which allows for * the event to be unsubscribed upon extension disable and reloads. * @@ -79,6 +68,13 @@ public interface EventBus { */ void register(@NonNull Extension extension, @NonNull Object eventHolder); + /** + * Unregisters all events from a given {@link Extension}. + * + * @param extension the extension + */ + void unregisterAll(@NonNull Extension extension); + /** * Fires the given {@link Event}. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java index daa05ab21..2870c0ba6 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java @@ -63,7 +63,7 @@ public interface EventSubscription { * * @return the extension that owns this subscription */ - @Nullable + @NonNull Extension owner(); /** diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java new file mode 100644 index 000000000..db0209e5f --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.function.Consumer; + +/** + * An {@link EventBus} with additional methods that implicitly + * set the extension instance. + * + */ +public interface ExtensionEventBus extends EventBus { + + /** + * Subscribes to the given event see {@link EventSubscription}. + * + * @param eventClass the class of the event + * @param consumer the consumer for handling the event + * @param the event class + * @return the event subscription + */ + @NonNull + EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer); + + /** + * Registers events for the given listener. + * + * @param eventHolder the listener + */ + void register(@NonNull Object eventHolder); + + /** + * Unregisters all events for this extension. + */ + void unregisterAll(); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java new file mode 100644 index 000000000..9dcb68908 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.connection; + +import lombok.RequiredArgsConstructor; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.Event; + +/** + * An event that contains a {@link GeyserConnection}. + */ +@RequiredArgsConstructor +public abstract class ConnectionEvent implements Event { + private final GeyserConnection connection; + + /** + * Gets the {@link GeyserConnection}. + * + * @return the connection + */ + public GeyserConnection connection() { + return this.connection; + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java new file mode 100644 index 000000000..559631acf --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.downstream; + +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.Cancellable; +import org.geysermc.geyser.api.event.connection.ConnectionEvent; + +import java.util.Set; + +/** + * Called when the downstream server defines the commands available on the server. + */ +public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable { + private final Set commands; + private boolean cancelled; + + public ServerDefineCommandsEvent(GeyserConnection connection, Set commands) { + super(connection); + this.commands = commands; + } + + /** + * A mutable collection of the commands sent over. + * + * @return a mutable collection of the commands sent over + */ + public Set commands() { + return this.commands; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public interface CommandInfo { + + /** + * Gets the name of the command. + * + * @return the name of the command + */ + String name(); + + /** + * Gets the description of the command. + * + * @return the description of the command + */ + String description(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java new file mode 100644 index 000000000..7f64a4622 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandManager; +import org.geysermc.geyser.api.event.Event; + +import java.util.Map; + +/** + * Called when commands are defined within Geyser. + * + * @param commandManager the command manager + * @param commands a mutable list of the currently + * registered default commands + */ +public record GeyserDefineCommandsEvent(CommandManager commandManager, Map commands) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java index 097cabdc3..357165ffe 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.api.extension; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.event.ExtensionEventBus; import java.nio.file.Path; @@ -80,6 +81,15 @@ public interface Extension { return this.extensionLoader().dataFolder(this); } + /** + * Gets the {@link ExtensionEventBus}. + * + * @return the extension event bus + */ + default ExtensionEventBus eventBus() { + return this.extensionLoader().eventBus(this); + } + /** * Gets the {@link ExtensionManager}. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index 651afd9eb..c84c37919 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.api.extension; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.ExtensionEventBus; import java.nio.file.Path; @@ -68,6 +69,15 @@ public abstract class ExtensionLoader { @NonNull protected abstract ExtensionDescription description(@NonNull Extension extension); + /** + * Gets the given {@link Extension}'s {@link ExtensionEventBus}. + * + * @param extension the extension + * @return the extension's event bus + */ + @NonNull + protected abstract ExtensionEventBus eventBus(@NonNull Extension extension); + /** * Gets the {@link ExtensionLogger} for the given {@link Extension}. * diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index f35082359..bb90e5000 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -30,7 +30,7 @@ import net.md_5.bungee.api.plugin.Plugin; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -126,6 +126,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserInjector.initializeLocalChannel(this); this.geyserCommandManager = new GeyserBungeeCommandManager(geyser); + this.geyserCommandManager.init(); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); @@ -157,7 +158,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java similarity index 89% rename from bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java rename to bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java index 05df8ba97..801fc8777 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java @@ -27,17 +27,17 @@ package org.geysermc.geyser.platform.bungeecord.command; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.GeyserLocale; -public class BungeeCommandSender implements CommandSender { +public class BungeeCommandSource implements GeyserCommandSource { private final net.md_5.bungee.api.CommandSender handle; - public BungeeCommandSender(net.md_5.bungee.api.CommandSender handle) { + public BungeeCommandSource(net.md_5.bungee.api.CommandSender handle) { this.handle = handle; // Ensure even Java players' languages are loaded - GeyserLocale.loadGeyserLocale(getLocale()); + GeyserLocale.loadGeyserLocale(this.locale()); } @Override @@ -56,7 +56,7 @@ public class BungeeCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { if (handle instanceof ProxiedPlayer player) { String locale = player.getLocale().getLanguage() + "_" + player.getLocale().getCountry(); return GeyserLocale.formatLocale(locale); diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java index 5bb323aac..87e5f9e13 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java @@ -30,7 +30,7 @@ import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.TabExecutor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; +import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -39,30 +39,30 @@ import java.util.Arrays; import java.util.Collections; public class GeyserBungeeCommandExecutor extends Command implements TabExecutor { - private final CommandExecutor commandExecutor; + private final GeyserCommandExecutor commandExecutor; public GeyserBungeeCommandExecutor(GeyserImpl geyser) { super("geyser"); - this.commandExecutor = new CommandExecutor(geyser); + this.commandExecutor = new GeyserCommandExecutor(geyser); } @Override public void execute(CommandSender sender, String[] args) { - BungeeCommandSender commandSender = new BungeeCommandSender(sender); + BungeeCommandSource commandSender = new BungeeCommandSource(sender); GeyserSession session = this.commandExecutor.getGeyserSession(commandSender); if (args.length > 0) { GeyserCommand command = this.commandExecutor.getCommand(args[0]); if (command != null) { - if (!sender.hasPermission(command.getPermission())) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale()); + if (!sender.hasPermission(command.permission())) { + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return; } if (command.isBedrockOnly() && session == null) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.getLocale()); + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return; @@ -77,7 +77,7 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor @Override public Iterable onTabComplete(CommandSender sender, String[] args) { if (args.length == 1) { - return commandExecutor.tabComplete(new BungeeCommandSender(sender)); + return commandExecutor.tabComplete(new BungeeCommandSource(sender)); } else { return Collections.emptyList(); } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java index 019544c28..e0fd7a4ac 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.bungeecord.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserBungeeCommandManager extends CommandManager { +public class GeyserBungeeCommandManager extends GeyserCommandManager { public GeyserBungeeCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // no support for command descriptions in bungee } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index bdf28a203..e0ad866c8 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -34,7 +34,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -48,7 +48,7 @@ import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; -import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; +import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigot1_11CraftingListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; @@ -161,6 +161,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); + this.geyserCommandManager.init(); boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; if (isViaVersion) { @@ -182,7 +183,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0"); // Set if we need to use a different method for getting a player's locale - SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12); + SpigotCommandSource.setUseLegacyLocaleMethod(isPre1_12); // We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib // To do their job injecting, then connect into *that* @@ -267,7 +268,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java index b1bcfcaf8..35cb2d03a 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java @@ -30,7 +30,7 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; +import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -39,7 +39,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -public class GeyserSpigotCommandExecutor extends CommandExecutor implements TabExecutor { +public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implements TabExecutor { public GeyserSpigotCommandExecutor(GeyserImpl geyser) { super(geyser); @@ -47,20 +47,20 @@ public class GeyserSpigotCommandExecutor extends CommandExecutor implements TabE @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - SpigotCommandSender commandSender = new SpigotCommandSender(sender); + SpigotCommandSource commandSender = new SpigotCommandSource(sender); GeyserSession session = getGeyserSession(commandSender); if (args.length > 0) { GeyserCommand geyserCommand = getCommand(args[0]); if (geyserCommand != null) { - if (!sender.hasPermission(geyserCommand.getPermission())) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale()); + if (!sender.hasPermission(geyserCommand.permission())) { + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return true; } if (geyserCommand.isBedrockOnly() && session == null) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale())); return true; } geyserCommand.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); @@ -76,7 +76,7 @@ public class GeyserSpigotCommandExecutor extends CommandExecutor implements TabE @Override public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { if (args.length == 1) { - return tabComplete(new SpigotCommandSender(sender)); + return tabComplete(new SpigotCommandSource(sender)); } return Collections.emptyList(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java index 103390ab8..22cb95c32 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java @@ -29,11 +29,11 @@ import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandMap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import java.lang.reflect.Field; -public class GeyserSpigotCommandManager extends CommandManager { +public class GeyserSpigotCommandManager extends GeyserCommandManager { private static CommandMap COMMAND_MAP; @@ -52,7 +52,7 @@ public class GeyserSpigotCommandManager extends CommandManager { } @Override - public String getDescription(String command) { + public String description(String command) { Command cmd = COMMAND_MAP.getCommand(command.replace("/", "")); return cmd != null ? cmd.getDescription() : ""; } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java similarity index 95% rename from bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java rename to bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java index a05a6ebe0..839e4d88e 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java @@ -28,13 +28,13 @@ package org.geysermc.geyser.platform.spigot.command; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.GeyserLocale; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class SpigotCommandSender implements CommandSender { +public class SpigotCommandSource implements GeyserCommandSource { /** * Whether to use {@code Player.getLocale()} or {@code Player.spigot().getLocale()}, depending on version. @@ -46,7 +46,7 @@ public class SpigotCommandSender implements CommandSender { private final org.bukkit.command.CommandSender handle; private final String locale; - public SpigotCommandSender(org.bukkit.command.CommandSender handle) { + public SpigotCommandSource(org.bukkit.command.CommandSender handle) { this.handle = handle; this.locale = getSpigotLocale(); // Ensure even Java players' languages are loaded @@ -69,7 +69,7 @@ public class SpigotCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { return locale; } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java index f5d6613c7..894368e5e 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java @@ -29,7 +29,7 @@ import com.google.inject.Inject; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -120,6 +120,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { } this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser); + this.geyserCommandManager.init(); Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser), "geyser"); } @@ -139,7 +140,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java index 825d0bf78..c8338e440 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.platform.sponge.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandExecutor; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.session.GeyserSession; @@ -45,7 +45,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -public class GeyserSpongeCommandExecutor extends CommandExecutor implements CommandCallable { +public class GeyserSpongeCommandExecutor extends GeyserCommandExecutor implements CommandCallable { public GeyserSpongeCommandExecutor(GeyserImpl geyser) { super(geyser); @@ -53,14 +53,14 @@ public class GeyserSpongeCommandExecutor extends CommandExecutor implements Comm @Override public CommandResult process(CommandSource source, String arguments) { - CommandSender commandSender = new SpongeCommandSender(source); + GeyserCommandSource commandSender = new SpongeCommandSource(source); GeyserSession session = getGeyserSession(commandSender); String[] args = arguments.split(" "); if (args.length > 0) { GeyserCommand command = getCommand(args[0]); if (command != null) { - if (!source.hasPermission(command.getPermission())) { + if (!source.hasPermission(command.permission())) { // Not ideal to use log here but we dont get a session source.sendMessage(Text.of(ChatColor.RED + GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return CommandResult.success(); @@ -80,7 +80,7 @@ public class GeyserSpongeCommandExecutor extends CommandExecutor implements Comm @Override public List getSuggestions(CommandSource source, String arguments, @Nullable Location targetPosition) { if (arguments.split(" ").length == 1) { - return tabComplete(new SpongeCommandSender(source)); + return tabComplete(new SpongeCommandSource(source)); } return Collections.emptyList(); } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java index dce39870d..8e981f72a 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java @@ -26,12 +26,12 @@ package org.geysermc.geyser.platform.sponge.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandMapping; import org.spongepowered.api.text.Text; -public class GeyserSpongeCommandManager extends CommandManager { +public class GeyserSpongeCommandManager extends GeyserCommandManager { private final org.spongepowered.api.command.CommandManager handle; public GeyserSpongeCommandManager(org.spongepowered.api.command.CommandManager handle, GeyserImpl geyser) { @@ -41,7 +41,7 @@ public class GeyserSpongeCommandManager extends CommandManager { } @Override - public String getDescription(String command) { + public String description(String command) { return handle.get(command).map(CommandMapping::getCallable) .map(callable -> callable.getShortDescription(Sponge.getServer().getConsole()).orElse(Text.EMPTY)) .orElse(Text.EMPTY).toPlain(); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java similarity index 94% rename from bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java rename to bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java index f57f3e276..664727c50 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java @@ -27,13 +27,13 @@ package org.geysermc.geyser.platform.sponge.command; import lombok.AllArgsConstructor; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.source.ConsoleSource; import org.spongepowered.api.text.Text; @AllArgsConstructor -public class SpongeCommandSender implements CommandSender { +public class SpongeCommandSource implements GeyserCommandSource { private CommandSource handle; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 43ab4b3fe..ca41d3c1d 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -41,15 +41,15 @@ import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.platform.standalone.command.GeyserCommandManager; import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import java.io.File; @@ -63,7 +63,7 @@ import java.util.stream.Collectors; public class GeyserStandaloneBootstrap implements GeyserBootstrap { - private GeyserCommandManager geyserCommandManager; + private GeyserStandaloneCommandManager geyserCommandManager; private GeyserStandaloneConfiguration geyserConfig; private GeyserStandaloneLogger geyserLogger; private IGeyserPingPassthrough geyserPingPassthrough; @@ -215,7 +215,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); geyser = GeyserImpl.start(PlatformType.STANDALONE, this); - geyserCommandManager = new GeyserCommandManager(geyser); + geyserCommandManager = new GeyserStandaloneCommandManager(geyser); + geyserCommandManager.init(); if (gui != null) { gui.setupInterface(geyserLogger, geyserCommandManager); @@ -260,7 +261,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return geyserCommandManager; } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index 3bd2a3960..f7ce6d052 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -31,11 +31,11 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.ChatColor; @Log4j2 -public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, CommandSender { +public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { @Override protected boolean isRunning() { @@ -44,7 +44,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey @Override protected void runCommand(String line) { - GeyserImpl.getInstance().getCommandManager().runCommand(this, line); + GeyserImpl.getInstance().commandManager().runCommand(this, line); } @Override diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java similarity index 85% rename from bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java rename to bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java index 03d780f3c..e7b4cbe37 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.standalone.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserCommandManager extends CommandManager { +public class GeyserStandaloneCommandManager extends GeyserCommandManager { - public GeyserCommandManager(GeyserImpl geyser) { + public GeyserStandaloneCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // this is not sent over the protocol, so we return none } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java index 44faabdf5..5901da470 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java @@ -26,11 +26,12 @@ package org.geysermc.geyser.platform.standalone.gui; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger; -import org.geysermc.geyser.platform.standalone.command.GeyserCommandManager; +import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager; import javax.swing.*; import javax.swing.table.DefaultTableModel; @@ -255,33 +256,34 @@ public class GeyserStandaloneGUI { * @param geyserStandaloneLogger The current logger * @param geyserCommandManager The commands manager */ - public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserCommandManager geyserCommandManager) { + public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserStandaloneCommandManager geyserCommandManager) { commandsMenu.removeAll(); optionsMenu.removeAll(); - for (Map.Entry command : geyserCommandManager.getCommands().entrySet()) { + for (Map.Entry entry : geyserCommandManager.getCommands().entrySet()) { // Remove the offhand command and any alias commands to prevent duplicates in the list - if (!command.getValue().isExecutableOnConsole() || command.getValue().getAliases().contains(command.getKey())) { + if (!entry.getValue().isExecutableOnConsole() || entry.getValue().aliases().contains(entry.getKey())) { continue; } + GeyserCommand command = (GeyserCommand) entry.getValue(); // Create the button that runs the command - boolean hasSubCommands = command.getValue().hasSubCommands(); + boolean hasSubCommands = !entry.getValue().subCommands().isEmpty(); // Add an extra menu if there are more commands that can be run - JMenuItem commandButton = hasSubCommands ? new JMenu(command.getValue().getName()) : new JMenuItem(command.getValue().getName()); - commandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription()); + JMenuItem commandButton = hasSubCommands ? new JMenu(entry.getValue().name()) : new JMenuItem(entry.getValue().name()); + commandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description()); if (!hasSubCommands) { - commandButton.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{ })); + commandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ })); } else { // Add a submenu that's the same name as the menu can't be pressed - JMenuItem otherCommandButton = new JMenuItem(command.getValue().getName()); - otherCommandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription()); - otherCommandButton.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{ })); + JMenuItem otherCommandButton = new JMenuItem(entry.getValue().name()); + otherCommandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description()); + otherCommandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ })); commandButton.add(otherCommandButton); // Add a menu option for all possible subcommands - for (String subCommandName : command.getValue().getSubCommands()) { + for (String subCommandName : entry.getValue().subCommands()) { JMenuItem item = new JMenuItem(subCommandName); - item.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{subCommandName})); + item.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{subCommandName})); commandButton.add(item); } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 6645ef595..a79fbb30b 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -38,6 +38,7 @@ import lombok.Getter; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -145,6 +146,8 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { // Will be initialized after the proxy has been bound this.geyserCommandManager = new GeyserVelocityCommandManager(geyser); + this.geyserCommandManager.init(); + this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser)); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); @@ -174,7 +177,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } @Override - public org.geysermc.geyser.command.CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java index 30f6c2efd..d30d9ae9e 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.platform.velocity.command; import com.velocitypowered.api.command.SimpleCommand; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandExecutor; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.session.GeyserSession; @@ -38,7 +38,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -public class GeyserVelocityCommandExecutor extends CommandExecutor implements SimpleCommand { +public class GeyserVelocityCommandExecutor extends GeyserCommandExecutor implements SimpleCommand { public GeyserVelocityCommandExecutor(GeyserImpl geyser) { super(geyser); @@ -46,18 +46,18 @@ public class GeyserVelocityCommandExecutor extends CommandExecutor implements Si @Override public void execute(Invocation invocation) { - CommandSender sender = new VelocityCommandSender(invocation.source()); + GeyserCommandSource sender = new VelocityCommandSource(invocation.source()); GeyserSession session = getGeyserSession(sender); if (invocation.arguments().length > 0) { GeyserCommand command = getCommand(invocation.arguments()[0]); if (command != null) { - if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).getPermission())) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).permission())) { + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } if (command.isBedrockOnly() && session == null) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale())); return; } command.execute(session, sender, invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]); @@ -71,7 +71,7 @@ public class GeyserVelocityCommandExecutor extends CommandExecutor implements Si public List suggest(Invocation invocation) { // Velocity seems to do the splitting a bit differently. This results in the same behaviour in bungeecord/spigot. if (invocation.arguments().length == 0 || invocation.arguments().length == 1) { - return tabComplete(new VelocityCommandSender(invocation.source())); + return tabComplete(new VelocityCommandSource(invocation.source())); } return Collections.emptyList(); } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java index b42c8f76e..6f9faba8f 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.velocity.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserVelocityCommandManager extends CommandManager { +public class GeyserVelocityCommandManager extends GeyserCommandManager { public GeyserVelocityCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // no support for command descriptions in velocity } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java similarity index 90% rename from bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java rename to bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java index d5e4804ee..fa70d1cf7 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java @@ -29,19 +29,19 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.api.proxy.Player; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.GeyserLocale; import java.util.Locale; -public class VelocityCommandSender implements CommandSender { +public class VelocityCommandSource implements GeyserCommandSource { private final CommandSource handle; - public VelocityCommandSender(CommandSource handle) { + public VelocityCommandSource(CommandSource handle) { this.handle = handle; // Ensure even Java players' languages are loaded - GeyserLocale.loadGeyserLocale(getLocale()); + GeyserLocale.loadGeyserLocale(this.locale()); } @Override @@ -65,7 +65,7 @@ public class VelocityCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { if (handle instanceof Player) { Locale locale = ((Player) handle).getPlayerSettings().getLocale(); return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry()); diff --git a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 932761d4b..e24146cb2 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -136,7 +136,7 @@ public class GeyserSession { } public String getLocale() { - return this.handle.getLocale(); + return this.handle.locale(); } public void sendUpstreamPacket(BedrockPacket packet) { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index 54ca36787..bc6a07ae3 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -25,7 +25,7 @@ package org.geysermc.geyser; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.GeyserWorldManager; @@ -72,7 +72,7 @@ public interface GeyserBootstrap { * * @return The current CommandManager */ - CommandManager getGeyserCommandManager(); + GeyserCommandManager getGeyserCommandManager(); /** * Returns the current PingPassthrough manager diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 573934b91..b685741e7 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -52,7 +52,7 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.event.GeyserEventBus; @@ -470,12 +470,11 @@ public class GeyserImpl implements GeyserApi { skinUploader.close(); } newsHandler.shutdown(); - this.getCommandManager().getCommands().clear(); + this.commandManager().getCommands().clear(); ResourcePack.PACKS.clear(); this.eventBus.fire(new GeyserShutdownEvent()); - this.extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); @@ -504,6 +503,11 @@ public class GeyserImpl implements GeyserApi { return this.extensionManager; } + @Override + public GeyserCommandManager commandManager() { + return this.bootstrap.getGeyserCommandManager(); + } + @Override public EventBus eventBus() { return this.eventBus; @@ -531,10 +535,6 @@ public class GeyserImpl implements GeyserApi { return bootstrap.getGeyserConfig(); } - public CommandManager getCommandManager() { - return bootstrap.getGeyserCommandManager(); - } - public WorldManager getWorldManager() { return bootstrap.getWorldManager(); } diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandManager.java b/core/src/main/java/org/geysermc/geyser/command/CommandManager.java deleted file mode 100644 index 60af8c4e5..000000000 --- a/core/src/main/java/org/geysermc/geyser/command/CommandManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.command; - -import lombok.Getter; - -import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.defaults.*; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.GeyserLocale; - -import java.util.*; - -public abstract class CommandManager { - - @Getter - private final Map commands = new HashMap<>(); - - private final GeyserImpl geyser; - - public CommandManager(GeyserImpl geyser) { - this.geyser = geyser; - - registerCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help")); - registerCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); - registerCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); - registerCommand(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); - registerCommand(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); - registerCommand(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); - registerCommand(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); - registerCommand(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); - registerCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); - registerCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); - if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { - registerCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); - } - } - - public void registerCommand(GeyserCommand command) { - commands.put(command.getName(), command); - geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.getName())); - - if (command.getAliases().isEmpty()) - return; - - for (String alias : command.getAliases()) - commands.put(alias, command); - } - - public void runCommand(CommandSender sender, String command) { - if (!command.startsWith("geyser ")) - return; - - command = command.trim().replace("geyser ", ""); - String label; - String[] args; - - if (!command.contains(" ")) { - label = command.toLowerCase(); - args = new String[0]; - } else { - label = command.substring(0, command.indexOf(" ")).toLowerCase(); - String argLine = command.substring(command.indexOf(" ") + 1); - args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; - } - - GeyserCommand cmd = commands.get(label); - if (cmd == null) { - geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); - return; - } - - if (sender instanceof GeyserSession) { - cmd.execute((GeyserSession) sender, sender, args); - } else { - if (!cmd.isBedrockOnly()) { - cmd.execute(null, sender, args); - } else { - geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only")); - } - } - } - - /** - * @return a list of all subcommands under {@code /geyser}. - */ - public List getCommandNames() { - return Arrays.asList(geyser.getCommandManager().getCommands().keySet().toArray(new String[0])); - } - - /** - * Returns the description of the given command - * - * @param command Command to get the description for - * @return Command description - */ - public abstract String getDescription(String command); -} diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java index 20451b5e8..2b3a855aa 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java @@ -27,7 +27,9 @@ package org.geysermc.geyser.command; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; +import lombok.experimental.Accessors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nullable; @@ -35,9 +37,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +@Accessors(fluent = true) @Getter @RequiredArgsConstructor -public abstract class GeyserCommand { +public abstract class GeyserCommand implements Command { protected final String name; /** @@ -46,16 +49,16 @@ public abstract class GeyserCommand { protected final String description; protected final String permission; - @Setter private List aliases = new ArrayList<>(); - public abstract void execute(@Nullable GeyserSession session, CommandSender sender, String[] args); + public abstract void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args); /** * If false, hides the command from being shown on the Geyser Standalone GUI. * * @return true if the command can be run on the server console */ + @Override public boolean isExecutableOnConsole() { return true; } @@ -65,25 +68,22 @@ public abstract class GeyserCommand { * * @return a list of all possible subcommands, or empty if none. */ - public List getSubCommands() { + @NonNull + @Override + public List subCommands() { return Collections.emptyList(); } /** - * Shortcut to {@link #getSubCommands()}{@code .isEmpty()}. + * Shortcut to {@link #subCommands()} ()}{@code .isEmpty()}. * * @return true if there are subcommand present for this command. */ public boolean hasSubCommands() { - return !getSubCommands().isEmpty(); + return !this.subCommands().isEmpty(); } - /** - * Used to send a deny message to Java players if this command can only be used by Bedrock players. - * - * @return true if this command can only be used by Bedrock players. - */ - public boolean isBedrockOnly() { - return false; + public void setAliases(List aliases) { + this.aliases = aliases; } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java similarity index 85% rename from core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java rename to core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java index 5fa5f688b..3d08600d1 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.command; import lombok.AllArgsConstructor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nullable; @@ -39,16 +40,16 @@ import java.util.Map; * Represents helper functions for listening to {@code /geyser} commands. */ @AllArgsConstructor -public class CommandExecutor { +public class GeyserCommandExecutor { protected final GeyserImpl geyser; public GeyserCommand getCommand(String label) { - return geyser.getCommandManager().getCommands().get(label); + return (GeyserCommand) geyser.commandManager().commands().get(label); } @Nullable - public GeyserSession getGeyserSession(CommandSender sender) { + public GeyserSession getGeyserSession(GeyserCommandSource sender) { if (sender.isConsole()) { return null; } @@ -70,20 +71,19 @@ public class CommandExecutor { * If the command sender does not have the permission for a given command, the command will not be shown. * @return A list of command names to include in the tab complete */ - public List tabComplete(CommandSender sender) { + public List tabComplete(GeyserCommandSource sender) { if (getGeyserSession(sender) != null) { // Bedrock doesn't get tab completions or argument suggestions return Collections.emptyList(); } List availableCommands = new ArrayList<>(); - Map commands = geyser.getCommandManager().getCommands(); + Map commands = geyser.commandManager().getCommands(); // Only show commands they have permission to use - for (Map.Entry entry : commands.entrySet()) { - GeyserCommand geyserCommand = entry.getValue(); - if (sender.hasPermission(geyserCommand.getPermission())) { - + for (Map.Entry entry : commands.entrySet()) { + Command geyserCommand = entry.getValue(); + if (sender.hasPermission(geyserCommand.permission())) { if (geyserCommand.isBedrockOnly()) { // Don't show commands the JE player can't run continue; diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java new file mode 100644 index 000000000..9cce0e9ce --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.command; + +import lombok.Getter; + +import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.common.PlatformType; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandExecutor; +import org.geysermc.geyser.api.command.CommandManager; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; +import org.geysermc.geyser.command.defaults.*; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.GeyserLocale; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +@RequiredArgsConstructor +public abstract class GeyserCommandManager extends CommandManager { + + @Getter + private final Map commands = new HashMap<>(); + + private final GeyserImpl geyser; + + public void init() { + register(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help")); + register(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); + register(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); + register(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); + register(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); + register(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); + register(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); + register(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); + register(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); + register(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); + if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { + register(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); + } + + this.geyser.eventBus().fire(new GeyserDefineCommandsEvent(this, this.commands)); + } + + @Override + public void register(@NonNull Command command) { + this.commands.put(command.name(), command); + this.geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.name())); + + if (command.aliases().isEmpty()) { + return; + } + + for (String alias : command.aliases()) { + this.commands.put(alias, command); + } + } + + @Override + public void unregister(@NonNull Command command) { + this.commands.remove(command.name(), command); + + if (command.aliases().isEmpty()) { + return; + } + + for (String alias : command.aliases()) { + this.commands.remove(alias, command); + } + } + + @NotNull + @Override + public Map commands() { + return Collections.unmodifiableMap(this.commands); + } + + public void runCommand(GeyserCommandSource sender, String command) { + if (!command.startsWith("geyser ")) + return; + + command = command.trim().replace("geyser ", ""); + String label; + String[] args; + + if (!command.contains(" ")) { + label = command.toLowerCase(); + args = new String[0]; + } else { + label = command.substring(0, command.indexOf(" ")).toLowerCase(); + String argLine = command.substring(command.indexOf(" ") + 1); + args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; + } + + Command cmd = commands.get(label); + if (cmd == null) { + geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); + return; + } + + if (cmd instanceof GeyserCommand) { + if (sender instanceof GeyserSession) { + ((GeyserCommand) cmd).execute((GeyserSession) sender, sender, args); + } else { + if (!cmd.isBedrockOnly()) { + ((GeyserCommand) cmd).execute(null, sender, args); + } else { + geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only")); + } + } + } + } + + /** + * Returns the description of the given command + * + * @param command Command to get the description for + * @return Command description + */ + public abstract String description(String command); + + @Override + protected Command.Builder provideBuilder(Class sourceType) { + return new CommandBuilder<>(sourceType); + } + + @RequiredArgsConstructor + public static class CommandBuilder implements Command.Builder { + private final Class sourceType; + private String name; + private String description = ""; + private String permission = ""; + private List aliases; + private boolean executableOnConsole = true; + private List subCommands; + private boolean bedrockOnly; + private CommandExecutor executor; + + public CommandBuilder name(String name) { + this.name = name; + return this; + } + + public CommandBuilder description(String description) { + this.description = description; + return this; + } + + public CommandBuilder permission(String permission) { + this.permission = permission; + return this; + } + + public CommandBuilder aliases(List aliases) { + this.aliases = aliases; + return this; + } + + public CommandBuilder executableOnConsole(boolean executableOnConsole) { + this.executableOnConsole = executableOnConsole; + return this; + } + + public CommandBuilder subCommands(List subCommands) { + this.subCommands = subCommands; + return this; + } + + public CommandBuilder bedrockOnly(boolean bedrockOnly) { + this.bedrockOnly = bedrockOnly; + return this; + } + + public CommandBuilder executor(CommandExecutor executor) { + this.executor = executor; + return this; + } + + public GeyserCommand build() { + if (this.name == null || this.name.isBlank()) { + throw new IllegalArgumentException("Command cannot be null or blank!"); + } + + return new GeyserCommand(this.name, this.description, this.permission) { + + @SuppressWarnings("unchecked") + @Override + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { + Class sourceType = CommandBuilder.this.sourceType; + CommandExecutor executor = CommandBuilder.this.executor; + if (sourceType.isInstance(session)) { + executor.execute((T) session, this, args); + return; + } + + if (sourceType.isInstance(sender)) { + executor.execute((T) sender, this, args); + return; + } + + GeyserImpl.getInstance().getLogger().debug("Ignoring command " + this.name + " due to no suitable sender."); + } + + @NonNull + @Override + public List aliases() { + return CommandBuilder.this.aliases == null ? Collections.emptyList() : CommandBuilder.this.aliases; + } + + @NonNull + @Override + public List subCommands() { + return CommandBuilder.this.subCommands == null ? Collections.emptyList() : CommandBuilder.this.subCommands; + } + + @Override + public boolean isBedrockOnly() { + return CommandBuilder.this.bedrockOnly; + } + + @Override + public boolean isExecutableOnConsole() { + return CommandBuilder.this.executableOnConsole; + } + }; + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java new file mode 100644 index 000000000..eabccc243 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.command; + +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.text.GeyserLocale; + +/** + * Implemented on top of any class that can send a command. + * For example, it wraps around Spigot's CommandSender class. + */ +public interface GeyserCommandSource extends CommandSource { + + /** + * {@inheritDoc} + */ + default String locale() { + return GeyserLocale.getDefaultLocale(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java index 18546c914..350b98f04 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.command.defaults; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; @@ -36,11 +36,11 @@ public class AdvancedTooltipsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { String onOrOff = session.isAdvancedTooltips() ? "off" : "on"; session.setAdvancedTooltips(!session.isAdvancedTooltips()); - session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.getLocale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.getLocale())); + session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.locale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.locale())); session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java index 169158572..2aea5f4df 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.command.defaults; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; @@ -35,7 +35,7 @@ public class AdvancementsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { session.getAdvancementsCache().buildAndShowMenuForm(); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java index bd98d2b31..32382526a 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java @@ -29,9 +29,10 @@ import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.AsteriskSerializer; @@ -58,10 +59,10 @@ public class DumpCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { // Only allow the console to create dumps on Geyser Standalone if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } @@ -80,7 +81,7 @@ public class DumpCommand extends GeyserCommand { AsteriskSerializer.showSensitive = showSensitive; - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale())); String dumpData; try { if (offlineDump) { @@ -92,7 +93,7 @@ public class DumpCommand extends GeyserCommand { dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog)); } } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); return; } @@ -100,21 +101,21 @@ public class DumpCommand extends GeyserCommand { String uploadedDumpUrl = ""; if (offlineDump) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.locale())); try { FileOutputStream outputStream = new FileOutputStream(GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile()); outputStream.write(dumpData.getBytes()); outputStream.close(); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.write_error_short"), e); return; } uploadedDumpUrl = "dump.json"; } else { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale())); String response; JsonNode responseNode; @@ -122,27 +123,28 @@ public class DumpCommand extends GeyserCommand { response = WebUtils.post(DUMP_URL + "documents", dumpData); responseNode = MAPPER.readTree(response); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); return; } if (!responseNode.has("key")) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.getLocale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); return; } uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); } - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.getLocale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); if (!sender.isConsole()) { geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.commands.dump.created", sender.name(), uploadedDumpUrl)); } } + @NonNull @Override - public List getSubCommands() { + public List subCommands() { return Arrays.asList("offline", "full", "logs"); } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java index 85682b294..fccb1b267 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java @@ -27,7 +27,8 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.session.GeyserSession; @@ -54,25 +55,25 @@ public class HelpCommand extends GeyserCommand { * @param args Not used. */ @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { int page = 1; int maxPage = 1; - String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.getLocale(), page, maxPage); + String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage); sender.sendMessage(header); - Map cmds = geyser.getCommandManager().getCommands(); - for (Map.Entry entry : cmds.entrySet()) { - GeyserCommand cmd = entry.getValue(); + Map cmds = geyser.commandManager().getCommands(); + for (Map.Entry entry : cmds.entrySet()) { + Command cmd = entry.getValue(); // Standalone hack-in since it doesn't have a concept of permissions - if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.getPermission())) { + if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.permission())) { // Only list commands the player can actually run if (cmd.isBedrockOnly() && session == null) { continue; } sender.sendMessage(ChatColor.YELLOW + "/geyser " + entry.getKey() + ChatColor.WHITE + ": " + - GeyserLocale.getPlayerLocaleString(cmd.getDescription(), sender.getLocale())); + GeyserLocale.getPlayerLocaleString(cmd.description(), sender.locale())); } } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java index f1004c3fb..8382d7a29 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -44,8 +44,8 @@ public class ListCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { - String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.getLocale(), + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { + String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.locale(), geyser.getSessionManager().size(), geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::name).collect(Collectors.joining(" "))); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java index 1d29d5122..a9f265f6e 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java @@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.BlockUtils; @@ -41,7 +41,7 @@ public class OffhandCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session == null) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java index b6a728382..31e17faad 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java @@ -27,7 +27,7 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -42,12 +42,12 @@ public class ReloadCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { return; } - String message = GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", sender.getLocale()); + String message = GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", sender.locale()); sender.sendMessage(message); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java index 58d778ba9..a8e8a374e 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.SettingsUtils; @@ -37,7 +37,7 @@ public class SettingsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { session.sendForm(SettingsUtils.buildForm(session)); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java index e54b0fb9b..c898f32a9 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.command.defaults; import com.github.steveice10.mc.protocol.data.game.ClientCommand; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; @@ -39,7 +39,7 @@ public class StatisticsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session == null) return; session.setWaitingForStatistics(true); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java index 903e3bf4b..b038fa0ff 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java @@ -27,7 +27,7 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -46,9 +46,9 @@ public class StopCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 6ec816b12..89bea3343 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.command.defaults; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.session.GeyserSession; @@ -54,7 +54,7 @@ public class VersionCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { String bedrockVersions; List supportedCodecs = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS; if (supportedCodecs.size() > 1) { @@ -70,12 +70,12 @@ public class VersionCommand extends GeyserCommand { javaVersions = supportedJavaVersions.get(0); } - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", sender.getLocale(), + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", sender.locale(), GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); // Disable update checking in dev mode and for players in Geyser Standalone if (GeyserImpl.getInstance().productionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale())); try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("git.properties")) { Properties gitProp = new Properties(); gitProp.load(stream); @@ -86,17 +86,17 @@ public class VersionCommand extends GeyserCommand { int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim()); int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number")); if (latestBuildNum == buildNum) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); } else { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated", - sender.getLocale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/")); + sender.locale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/")); } } else { throw new AssertionError("buildNumber missing"); } } catch (IOException | AssertionError | NumberFormatException e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e); - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale())); } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index adeccdd01..77a1a199c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -356,7 +356,7 @@ public class Entity { public void setDisplayName(EntityMetadata, ?> entityMetadata) { Optional name = entityMetadata.getValue(); if (name.isPresent()) { - nametag = MessageTranslator.convertMessage(name.get(), session.getLocale()); + nametag = MessageTranslator.convertMessage(name.get(), session.locale()); dirtyMetadata.put(EntityData.NAMETAG, nametag); } else if (!nametag.isEmpty()) { // Clear nametag diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index 93a042826..eaf71e087 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.event; +import net.kyori.event.EventSubscriber; import net.kyori.event.SimpleEventBus; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.event.Event; @@ -37,17 +38,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; public class GeyserEventBus implements EventBus { private final SimpleEventBus bus = new SimpleEventBus<>(Event.class); - @NonNull - @Override - public EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer) { - return this.subscribe(eventClass, consumer, null, Subscribe.Priority.NORMAL); - } - @NonNull @Override public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { @@ -86,6 +82,11 @@ public class GeyserEventBus implements EventBus { } } + @Override + public void unregisterAll(@NonNull Extension extension) { + this.bus.unregister((Predicate>) subscriber -> extension.equals(((GeyserEventSubscription) subscriber).owner())); + } + @Override public boolean fire(@NonNull Event event) { return this.bus.post(event).wasSuccessful(); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java index 5b2e01842..a26415a13 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java @@ -29,6 +29,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import org.geysermc.geyser.api.event.ExtensionEventBus; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.ExtensionLoader; @@ -45,6 +46,7 @@ public class GeyserExtensionContainer { private final ExtensionDescription description; private final ExtensionLoader loader; private final ExtensionLogger logger; + private final ExtensionEventBus eventBus; @Getter(AccessLevel.NONE) protected boolean enabled; } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index a539dcb15..972b106d6 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.extension; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.ExtensionEventBus; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.ExtensionLoader; @@ -35,6 +36,7 @@ import org.geysermc.geyser.api.extension.ExtensionLogger; import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; +import org.geysermc.geyser.extension.event.GeyserExtensionEventBus; import org.geysermc.geyser.text.GeyserLocale; import java.io.IOException; @@ -83,12 +85,12 @@ public class GeyserExtensionLoader extends ExtensionLoader { } this.classLoaders.put(description.name(), loader); - return this.setup(loader.extension(), description, dataFolder); + return this.setup(loader.extension(), description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), loader.extension())); } - private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder) { + private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder, ExtensionEventBus eventBus) { GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); - GeyserExtensionContainer container = new GeyserExtensionContainer(extension, dataFolder, description, this, logger); + GeyserExtensionContainer container = new GeyserExtensionContainer(extension, dataFolder, description, this, logger, eventBus); extension.onLoad(); return container; } @@ -246,6 +248,12 @@ public class GeyserExtensionLoader extends ExtensionLoader { return this.extensionContainers.get(extension).description(); } + @NonNull + @Override + protected ExtensionEventBus eventBus(@NonNull Extension extension) { + return this.extensionContainers.get(extension).eventBus(); + } + @NonNull @Override protected ExtensionLogger logger(@NonNull Extension extension) { diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index a72712f16..936cde471 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -90,15 +90,18 @@ public class GeyserExtensionManager extends ExtensionManager { public void enableExtension(Extension extension) { if (!extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); extension.setEnabled(true); + GeyserImpl.getInstance().eventBus().register(extension, extension); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); } } private void disableExtension(@NonNull Extension extension) { if (extension.isEnabled()) { - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); + GeyserImpl.getInstance().eventBus().unregisterAll(extension); + extension.setEnabled(false); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java new file mode 100644 index 000000000..4104871fa --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventSubscription; +import org.geysermc.geyser.api.event.ExtensionEventBus; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; +import java.util.function.Consumer; + +public record GeyserExtensionEventBus(EventBus eventBus, + Extension extension) implements ExtensionEventBus { + @NonNull + @Override + public EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer) { + return this.eventBus.subscribe(this.extension, eventClass, consumer); + } + + @Override + public void register(@NonNull Object eventHolder) { + this.eventBus.register(this.extension, eventHolder); + } + + @Override + public void unregisterAll() { + this.eventBus.unregisterAll(this.extension); + } + + @NonNull + @Override + public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { + return this.eventBus.subscribe(extension, eventClass, consumer); + } + + @Override + public void unsubscribe(@NonNull EventSubscription subscription) { + this.eventBus.unsubscribe(subscription); + } + + @Override + public void register(@NonNull Extension extension, @NonNull Object eventHolder) { + this.eventBus.register(extension, eventHolder); + } + + @Override + public void unregisterAll(@NonNull Extension extension) { + this.eventBus.unregisterAll(extension); + } + + @Override + public boolean fire(@NonNull Event event) { + return this.eventBus.fire(event); + } + + @NonNull + @Override + public Set> subscriptions(@NonNull Class eventClass) { + return this.eventBus.subscriptions(eventClass); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java index d6f72b8d0..d203b0311 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java @@ -115,7 +115,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { // Changing the item in the input slot resets the name field on Bedrock, but // does not result in a FilterTextPacket - String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getNbt()), session.getLocale()); + String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getNbt()), session.locale()); ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(originalName); session.sendDownstreamPacket(renameItemPacket); @@ -427,7 +427,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { String originalName = ItemUtils.getCustomName(anvilContainer.getInput().getNbt()); if (bedrock && originalName != null && anvilContainer.getNewName() != null) { // Check text and formatting - String legacyOriginalName = MessageTranslator.convertMessageLenient(originalName, session.getLocale()); + String legacyOriginalName = MessageTranslator.convertMessageLenient(originalName, session.locale()); return !legacyOriginalName.equals(anvilContainer.getNewName()); } return !Objects.equals(originalName, ItemUtils.getCustomName(anvilContainer.getResult().getNbt())); diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index f547c4dce..5686e3d98 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -112,7 +112,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks()); session.sendUpstreamPacket(resourcePacksInfo); - GeyserLocale.loadGeyserLocale(session.getLocale()); + GeyserLocale.loadGeyserLocale(session.locale()); return true; } @@ -208,7 +208,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { if (session.isLoggingIn()) { SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.getLocale())); + titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.locale())); titlePacket.setFadeInTime(0); titlePacket.setFadeOutTime(1); titlePacket.setStayTime(2); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 7ea65e49e..591934d6d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -79,7 +79,7 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.entity.InteractiveTagManager; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; @@ -122,7 +122,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Getter -public class GeyserSession implements GeyserConnection, CommandSender { +public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final GeyserImpl geyser; private final UpstreamSession upstream; @@ -876,7 +876,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { if (cause instanceof UnexpectedEncryptionException) { if (remoteAuthType != AuthType.FLOODGATE) { // Server expects online mode - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. geyser.getLogger().warning(GeyserLocale.getLocaleStringLog( geyser.getPlatformType() == PlatformType.STANDALONE ? @@ -886,14 +886,14 @@ public class GeyserSession implements GeyserConnection, CommandSender { )); } else { // Likely that Floodgate is not configured correctly. - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale()); if (geyser.getPlatformType() == PlatformType.STANDALONE) { geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone")); } } } else if (cause instanceof ConnectException) { // Server is offline, probably - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", locale()); } else { disconnectMessage = MessageTranslator.convertMessageLenient(event.getReason()); } @@ -1156,7 +1156,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { } @Override - public String getLocale() { + public String locale() { return clientData.getLanguageCode(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java index 8cfc73d7e..fc6c37356 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java @@ -80,7 +80,7 @@ public final class SessionManager { public void disconnectAll(String message) { Collection sessions = getAllSessions(); for (GeyserSession session : sessions) { - session.disconnect(GeyserLocale.getPlayerLocaleString(message, session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString(message, session.locale())); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java index 9d3e4f5aa..5d5779ae7 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java @@ -72,14 +72,14 @@ public class AdvancementsCache { public void buildAndShowMenuForm() { SimpleForm.Builder builder = SimpleForm.builder() - .translator(MinecraftLocale::getLocaleString, session.getLocale()) + .translator(MinecraftLocale::getLocaleString, session.locale()) .title("gui.advancements"); boolean hasAdvancements = false; for (Map.Entry advancement : storedAdvancements.entrySet()) { if (advancement.getValue().getParentId() == null) { // No parent means this is a root advancement hasAdvancements = true; - builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.getLocale())); + builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.locale())); } } @@ -128,7 +128,7 @@ public class AdvancementsCache { */ public void buildAndShowListForm() { GeyserAdvancement categoryAdvancement = storedAdvancements.get(currentAdvancementCategoryId); - String language = session.getLocale(); + String language = session.locale(); SimpleForm.Builder builder = SimpleForm.builder() @@ -190,7 +190,7 @@ public class AdvancementsCache { */ public void buildAndShowInfoForm(GeyserAdvancement advancement) { // Cache language for easier access - String language = session.getLocale(); + String language = session.locale(); String earned = isEarned(advancement) ? "yes" : "no"; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java b/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java index 7cfeaa165..cd1bc4c98 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java @@ -57,7 +57,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.CREATE); - bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.locale())); bossEventPacket.setHealthPercentage(health); bossEventPacket.setColor(color); bossEventPacket.setOverlay(overlay); @@ -71,7 +71,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME); - bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.locale())); session.sendUpstreamPacket(bossEventPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index 6a2182279..34e9364ce 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -157,7 +157,7 @@ public abstract class ItemTranslator { nbt = translateDisplayProperties(session, nbt, bedrockItem); if (session.isAdvancedTooltips()) { - nbt = addAdvancedTooltips(nbt, session.getItemMappings().getMapping(stack), session.getLocale()); + nbt = addAdvancedTooltips(nbt, session.getItemMappings().getMapping(stack), session.locale()); } ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); @@ -474,7 +474,7 @@ public abstract class ItemTranslator { String name = ((StringTag) display.get("Name")).getValue(); // Get the translated name and prefix it with a reset char - name = MessageTranslator.convertMessageLenient(name, session.getLocale()); + name = MessageTranslator.convertMessageLenient(name, session.locale()); // Add the new name tag display.put(new StringTag("Name", name)); @@ -500,7 +500,7 @@ public abstract class ItemTranslator { String translationKey = mapping.getTranslationString(); // Reset formatting since Bedrock defaults to italics - display.put(new StringTag("Name", "§r§" + translationColor + MinecraftLocale.getLocaleString(translationKey, session.getLocale()))); + display.put(new StringTag("Name", "§r§" + translationColor + MinecraftLocale.getLocaleString(translationKey, session.locale()))); } return tag; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java index c3abf2495..4a91110dc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java @@ -42,7 +42,7 @@ public class AxolotlBucketTranslator extends NbtItemStackTranslator { // Bedrock Edition displays the properties of the axolotl. Java does not. // To work around this, set the custom name to the Axolotl translation and it's displayed correctly itemTag.put(new ByteTag("AppendCustomName", (byte) 1)); - itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.axolotl", session.getLocale()))); + itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.axolotl", session.locale()))); // Boilerplate required so the nametag does not appear as "Bucket of " itemTag.put(new StringTag("ColorID", "")); itemTag.put(new StringTag("BodyID", "")); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java index 42cfc0439..d50a8c579 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java @@ -61,7 +61,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator { List lore = new ArrayList<>(); for (Tag tag : listTag.getValue()) { if (!(tag instanceof StringTag)) continue; - lore.add(new StringTag("", MessageTranslator.convertMessageLenient(((StringTag) tag).getValue(), session.getLocale()))); + lore.add(new StringTag("", MessageTranslator.convertMessageLenient(((StringTag) tag).getValue(), session.locale()))); } displayTag.put(new ListTag("Lore", lore)); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java index 680be00fd..44308aeee 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java @@ -56,7 +56,7 @@ public class PlayerHeadTranslator extends NbtItemStackTranslator { } // Add correct name of player skull // TODO: It's always yellow, even with a custom name. Handle? - String displayName = "\u00a7r\u00a7e" + MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.getLocale()).replace("%s", name.getValue()); + String displayName = "\u00a7r\u00a7e" + MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.locale()).replace("%s", name.getValue()); if (!itemTag.contains("display")) { itemTag.put(new CompoundTag("display")); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java index dbacc75fe..5fd101e8b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java @@ -50,7 +50,7 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { // Prevent name from appearing as "Bucket of" itemTag.put(new ByteTag("AppendCustomName", (byte) 1)); - itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.getLocale()))); + itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.locale()))); // Add Java's client side lore tag Tag bucketVariantTag = itemTag.get("BucketVariantTag"); if (bucketVariantTag instanceof IntTag) { @@ -66,10 +66,10 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { int predefinedVariantId = TropicalFishEntity.getPredefinedId(varNumber); if (predefinedVariantId != -1) { Component tooltip = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE); - lore.add(0, new StringTag("", MessageTranslator.convertMessage(tooltip, session.getLocale()))); + lore.add(0, new StringTag("", MessageTranslator.convertMessage(tooltip, session.locale()))); } else { Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(varNumber), LORE_STYLE); - lore.add(0, new StringTag("", MessageTranslator.convertMessage(typeTooltip, session.getLocale()))); + lore.add(0, new StringTag("", MessageTranslator.convertMessage(typeTooltip, session.locale()))); byte baseColor = TropicalFishEntity.getBaseColor(varNumber); byte patternColor = TropicalFishEntity.getPatternColor(varNumber); @@ -78,7 +78,7 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE)) .append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor), LORE_STYLE)); } - lore.add(1, new StringTag("", MessageTranslator.convertMessage(colorTooltip, session.getLocale()))); + lore.add(1, new StringTag("", MessageTranslator.convertMessage(colorTooltip, session.locale()))); } ListTag loreTag = displayTag.get("Lore"); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java index 73ca9b222..1a955c1a8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java @@ -27,7 +27,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -42,7 +42,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator }); textPacket.setNeedsTranslation(false); - textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage(), session.getLocale())); + textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage(), session.locale())); session.sendUpstreamPacket(textPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java index 28ebca926..18712dbf5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java @@ -43,7 +43,8 @@ import lombok.Getter; import lombok.ToString; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -112,7 +113,7 @@ public class JavaCommandsTranslator extends PacketTranslator commandData = new ArrayList<>(); IntSet commandNodes = new IntOpenHashSet(); @@ -141,15 +142,20 @@ public class JavaCommandsTranslator extends PacketTranslator new HashSet<>()).add(node.getName().toLowerCase()); } + ServerDefineCommandsEvent event = new ServerDefineCommandsEvent(session, commands.keySet()); + session.getGeyser().eventBus().fire(event); + if (event.isCancelled()) { + return; + } + // The command flags, not sure what these do apart from break things List flags = Collections.emptyList(); // Loop through all the found commands - for (Map.Entry> entry : commands.entrySet()) { String commandName = entry.getValue().iterator().next(); // We know this has a value @@ -236,7 +242,7 @@ public class JavaCommandsTranslator extends PacketTranslator 256) { - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length())); + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.too_long", session.locale(), message.length())); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index 5a1063a10..8fd079702 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -226,7 +226,7 @@ public class LoginEncryptionUtils { session.sendForm( SimpleForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) + .translator(GeyserLocale::getPlayerLocaleString, session.locale()) .title("geyser.auth.login.form.notice.title") .content("geyser.auth.login.form.notice.desc") .optionalButton("geyser.auth.login.form.notice.btn_login.mojang", isPasswordAuthEnabled) @@ -257,14 +257,14 @@ public class LoginEncryptionUtils { return; } - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.locale())); })); } public static void buildAndShowLoginDetailsWindow(GeyserSession session) { session.sendForm( CustomForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) + .translator(GeyserLocale::getPlayerLocaleString, session.locale()) .title("geyser.auth.login.form.details.title") .label("geyser.auth.login.form.details.desc") .input("geyser.auth.login.form.details.email", "account@geysermc.org", "") @@ -286,7 +286,7 @@ public class LoginEncryptionUtils { public static void buildAndShowMicrosoftAuthenticationWindow(GeyserSession session) { session.sendForm( SimpleForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) + .translator(GeyserLocale::getPlayerLocaleString, session.locale()) .title("geyser.auth.login.form.notice.btn_login.microsoft") .button("geyser.auth.login.method.browser") .button("geyser.auth.login.method.password") @@ -303,7 +303,7 @@ public class LoginEncryptionUtils { } else if (response.getClickedButtonId() == 1) { buildAndShowLoginDetailsWindow(session); } else { - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.locale())); } })); } @@ -326,7 +326,7 @@ public class LoginEncryptionUtils { } if (response.getClickedButtonId() == 1) { - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.locale())); } }) ); diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index ea3412451..75c26cade 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -45,7 +45,7 @@ public class SettingsUtils { */ public static CustomForm buildForm(GeyserSession session) { // Cache the language for cleaner access - String language = session.getLocale(); + String language = session.locale(); CustomForm.Builder builder = CustomForm.builder() .translator(SettingsUtils::translateEntry, language) diff --git a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java index 447661e21..3324ec577 100644 --- a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java @@ -53,7 +53,7 @@ public class StatisticsUtils { */ public static void buildAndSendStatisticsMenu(GeyserSession session) { // Cache the language for cleaner access - String language = session.getLocale(); + String language = session.locale(); session.sendForm( SimpleForm.builder() From 2277b98dfd8935600e4474fb0c42fd3d90e99102 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 15:16:52 -0600 Subject: [PATCH 014/125] Rename Priority to PostOrder --- .../geysermc/geyser/api/event/EventSubscription.java | 7 +++---- .../org/geysermc/geyser/api/event/Subscribe.java | 12 +++++------- .../org/geysermc/geyser/event/GeyserEventBus.java | 8 ++++---- .../geyser/event/GeyserEventSubscription.java | 4 ++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java index 2870c0ba6..8603dac37 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.extension.Extension; import java.util.function.Consumer; @@ -67,11 +66,11 @@ public interface EventSubscription { Extension owner(); /** - * Gets the priority of this event subscription. + * Gets the post order of this event subscription. * - * @return the priority of this event subscription + * @return the post order of this event subscription */ - Subscribe.Priority priority(); + Subscribe.PostOrder order(); /** * Gets if this event subscription is active. diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java index 581aa6fa0..6b3cfe638 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java @@ -28,7 +28,6 @@ package org.geysermc.geyser.api.event; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.experimental.Accessors; -import org.checkerframework.checker.nullness.qual.NonNull; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -50,20 +49,19 @@ import java.lang.annotation.Target; public @interface Subscribe { /** - * The {@link Priority} of the event + * The {@link PostOrder} of the event * - * @return the priority of the event + * @return the post order of the event */ - @NonNull - Priority priority() default Priority.NORMAL; + Subscribe.PostOrder postOrder() default PostOrder.NORMAL; /** - * Represents the priority of an event. + * Represents the post order of an event. */ @Accessors(fluent = true) @Getter @AllArgsConstructor - enum Priority { + enum PostOrder { /** * The lowest priority. Called first to diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index eaf71e087..edda59e95 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -47,7 +47,7 @@ public class GeyserEventBus implements EventBus { @NonNull @Override public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { - return this.subscribe(eventClass, consumer, extension, Subscribe.Priority.NORMAL); + return this.subscribe(eventClass, consumer, extension, Subscribe.PostOrder.NORMAL); } @Override @@ -78,7 +78,7 @@ public class GeyserEventBus implements EventBus { } catch (IllegalAccessException | InvocationTargetException ex) { ex.printStackTrace(); } - }, extension, subscribe.priority()); + }, extension, subscribe.postOrder()); } } @@ -103,8 +103,8 @@ public class GeyserEventBus implements EventBus { .collect(Collectors.toSet()); } - private EventSubscription subscribe(Class eventClass, Consumer handler, Extension extension, Subscribe.Priority priority) { - GeyserEventSubscription eventSubscription = new GeyserEventSubscription<>(this, eventClass, handler, extension, priority); + private EventSubscription subscribe(Class eventClass, Consumer handler, Extension extension, Subscribe.PostOrder postOrder) { + GeyserEventSubscription eventSubscription = new GeyserEventSubscription<>(this, eventClass, handler, extension, postOrder); this.bus.register(eventClass, eventSubscription); return eventSubscription; } diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java index e160291c9..33fe5a9e1 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java @@ -47,7 +47,7 @@ public class GeyserEventSubscription implements EventSubscripti private final Class eventClass; private final Consumer eventConsumer; private final Extension owner; - private final Subscribe.Priority priority; + private final Subscribe.PostOrder order; @Getter(AccessLevel.NONE) private boolean active; @Override @@ -77,6 +77,6 @@ public class GeyserEventSubscription implements EventSubscripti @Override public int postOrder() { - return this.priority.postOrder(); + return this.order.postOrder(); } } From 4c297092a58be847be7bc9ffd302985848dc9bbd Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 15:31:32 -0600 Subject: [PATCH 015/125] Update PostOrder names and don't use lombok in API --- .../geysermc/geyser/api/event/Subscribe.java | 28 +++++++++++-------- .../api/event/connection/ConnectionEvent.java | 8 ++++-- .../downstream/ServerDefineCommandsEvent.java | 4 ++- .../lifecycle/GeyserDefineCommandsEvent.java | 3 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java index 6b3cfe638..488fa0ea3 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java @@ -25,10 +25,6 @@ package org.geysermc.geyser.api.event; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.experimental.Accessors; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -58,9 +54,6 @@ public @interface Subscribe { /** * Represents the post order of an event. */ - @Accessors(fluent = true) - @Getter - @AllArgsConstructor enum PostOrder { /** @@ -68,12 +61,12 @@ public @interface Subscribe { * allow for other events to customize * the outcome */ - LOWEST(net.kyori.event.PostOrders.FIRST), + FIRST(net.kyori.event.PostOrders.FIRST), /** * The second lowest priority. */ - LOW(net.kyori.event.PostOrders.EARLY), + EARLY(net.kyori.event.PostOrders.EARLY), /** * Normal priority. Event is neither @@ -84,14 +77,27 @@ public @interface Subscribe { /** * The second highest priority */ - HIGH(net.kyori.event.PostOrders.LATE), + LATE(net.kyori.event.PostOrders.LATE), /** * The highest of importance! Event is called * last and has the final say in the outcome */ - HIGHEST(net.kyori.event.PostOrders.LAST); + LAST(net.kyori.event.PostOrders.LAST); private final int postOrder; + + PostOrder(int postOrder) { + this.postOrder = postOrder; + } + + /** + * The numerical post order value. + * + * @return numerical post order value + */ + public int postOrder() { + return this.postOrder; + } } } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java index 9dcb68908..48f3acdb7 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java @@ -25,22 +25,26 @@ package org.geysermc.geyser.api.event.connection; -import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.Event; /** * An event that contains a {@link GeyserConnection}. */ -@RequiredArgsConstructor public abstract class ConnectionEvent implements Event { private final GeyserConnection connection; + public ConnectionEvent(@NonNull GeyserConnection connection) { + this.connection = connection; + } + /** * Gets the {@link GeyserConnection}. * * @return the connection */ + @NonNull public GeyserConnection connection() { return this.connection; } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java index 559631acf..ba7254c94 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.api.event.downstream; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.Cancellable; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -38,7 +39,7 @@ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancel private final Set commands; private boolean cancelled; - public ServerDefineCommandsEvent(GeyserConnection connection, Set commands) { + public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull Set commands) { super(connection); this.commands = commands; } @@ -48,6 +49,7 @@ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancel * * @return a mutable collection of the commands sent over */ + @NonNull public Set commands() { return this.commands; } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java index 7f64a4622..1a2c7b4d4 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.api.event.lifecycle; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.event.Event; @@ -38,5 +39,5 @@ import java.util.Map; * @param commands a mutable list of the currently * registered default commands */ -public record GeyserDefineCommandsEvent(CommandManager commandManager, Map commands) implements Event { +public record GeyserDefineCommandsEvent(@NonNull CommandManager commandManager, @NonNull Map commands) implements Event { } From ac18ef605469dd6efd81cb275af45d62a38b0be6 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 15:58:47 -0600 Subject: [PATCH 016/125] Remove adventure usage in API Unfortunately due to various platforms we support not having adventure support, we are unable to fully implement adventure into our API without having issues with shading or conflicts with other plugins. May look into what we can do in regards to classloading in the future but unfortunately it may not be a possibility at this point in time to support adventure inside of the API. --- api/geyser/pom.xml | 12 +++-------- .../api/extension/ExtensionManager.java | 16 +++++++------- .../extension/GeyserExtensionManager.java | 21 +++++++++---------- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml index b2f448589..435643b0a 100644 --- a/api/geyser/pom.xml +++ b/api/geyser/pom.xml @@ -33,9 +33,9 @@ provided - net.kyori - adventure-api - ${adventure.version} + org.geysermc + base-api + 2.0.0-SNAPSHOT compile @@ -44,11 +44,5 @@ 1.27 compile - - org.geysermc - base-api - 2.0.0-SNAPSHOT - compile - \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java index 65387a8c7..65d6c66da 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java @@ -25,10 +25,8 @@ package org.geysermc.geyser.api.extension; -import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Map; @@ -45,7 +43,7 @@ public abstract class ExtensionManager { * @return an extension with the given name */ @Nullable - public abstract Extension extension(@NotNull String name); + public abstract Extension extension(@NonNull String name); /** * Enables the given {@link Extension}. @@ -68,7 +66,7 @@ public abstract class ExtensionManager { * @return the extension loader for loading the given extension */ @Nullable - public abstract ExtensionLoader extensionLoader(@NotNull Extension extension); + public abstract ExtensionLoader extensionLoader(@NonNull Extension extension); /** * Gets all the {@link Extension}s currently loaded. @@ -85,7 +83,7 @@ public abstract class ExtensionManager { * @return the extension loader at the given identifier */ @Nullable - public abstract ExtensionLoader extensionLoader(@NonNull Key identifier); + public abstract ExtensionLoader extensionLoader(@NonNull String identifier); /** * Registers an {@link ExtensionLoader} with the given identifier. @@ -93,7 +91,7 @@ public abstract class ExtensionManager { * @param identifier the identifier * @param extensionLoader the extension loader */ - public abstract void registerExtensionLoader(@NonNull Key identifier, @NotNull ExtensionLoader extensionLoader); + public abstract void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader); /** * Gets all the currently registered {@link ExtensionLoader}s. @@ -101,7 +99,7 @@ public abstract class ExtensionManager { * @return all the currently registered extension loaders */ @NonNull - public abstract Map extensionLoaders(); + public abstract Map extensionLoaders(); /** * Registers an {@link Extension} with the given {@link ExtensionLoader}. @@ -109,12 +107,12 @@ public abstract class ExtensionManager { * @param extension the extension * @param loader the loader */ - public abstract void register(@NotNull Extension extension, @NotNull ExtensionLoader loader); + public abstract void register(@NonNull Extension extension, @NonNull ExtensionLoader loader); /** * Loads all extensions from the given {@link ExtensionLoader}. */ - protected final void loadAllExtensions(@NotNull ExtensionLoader extensionLoader) { + protected final void loadAllExtensions(@NonNull ExtensionLoader extensionLoader) { extensionLoader.loadAllExtensions(this); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index 936cde471..c125d010b 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -34,12 +34,12 @@ import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.text.GeyserLocale; -import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.stream.Collectors; public class GeyserExtensionManager extends ExtensionManager { - private static final Key BASE_KEY = Key.key("geysermc", "base"); + private static final Key BASE_EXTENSION_LOADER_KEY = Key.key("geysermc", "base"); private final Map extensions = new LinkedHashMap<>(); private final Map extensionsLoaders = new LinkedHashMap<>(); @@ -47,8 +47,7 @@ public class GeyserExtensionManager extends ExtensionManager { public void init() { GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); - this.registerExtensionLoader(BASE_KEY, new GeyserExtensionLoader()); - + Registries.EXTENSION_LOADERS.register(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader()); for (ExtensionLoader loader : this.extensionLoaders().values()) { this.loadAllExtensions(loader); } @@ -130,23 +129,23 @@ public class GeyserExtensionManager extends ExtensionManager { @Nullable @Override - public ExtensionLoader extensionLoader(@NonNull Key identifier) { - return Registries.EXTENSION_LOADERS.get(identifier); + public ExtensionLoader extensionLoader(@NonNull String identifier) { + return Registries.EXTENSION_LOADERS.get(Key.key(identifier)); } @Override - public void registerExtensionLoader(@NonNull Key identifier, @NotNull ExtensionLoader extensionLoader) { - Registries.EXTENSION_LOADERS.register(identifier, extensionLoader); + public void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader) { + Registries.EXTENSION_LOADERS.register(Key.key(identifier), extensionLoader); } @NonNull @Override - public Map extensionLoaders() { - return Collections.unmodifiableMap(Registries.EXTENSION_LOADERS.get()); + public Map extensionLoaders() { + return Registries.EXTENSION_LOADERS.get().entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue)); } @Override - public void register(@NotNull Extension extension, @NotNull ExtensionLoader loader) { + public void register(@NonNull Extension extension, @NonNull ExtensionLoader loader) { this.extensionsLoaders.put(extension, loader); this.extensions.put(extension.name(), extension); } From 115b10362812d48b471ab0e43a9e3e21aa548fc3 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 16:35:27 -0600 Subject: [PATCH 017/125] Add extensions command --- .../geyser/command/GeyserCommandManager.java | 1 + .../command/defaults/ExtensionsCommand.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index 9cce0e9ce..0214a44fd 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -63,6 +63,7 @@ public abstract class GeyserCommandManager extends CommandManager { register(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); register(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); register(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); + register(new ExtensionsCommand(geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions")); if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { register(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java new file mode 100644 index 000000000..30d422b23 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.command.defaults; + +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.text.GeyserLocale; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.List; + +public class ExtensionsCommand extends GeyserCommand { + private final GeyserImpl geyser; + + public ExtensionsCommand(GeyserImpl geyser, String name, String description, String permission) { + super(name, description, permission); + + this.geyser = geyser; + } + + @Override + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { + // TODO: Pagination + int page = 1; + int maxPage = 1; + String header = GeyserLocale.getPlayerLocaleString("geyser.commands.extensions.header", sender.locale(), page, maxPage); + sender.sendMessage(header); + + this.geyser.extensionManager().extensions().stream().sorted(Comparator.comparing(Extension::name)).forEach(extension -> { + String extensionName = (extension.isEnabled() ? ChatColor.GREEN : ChatColor.RED) + extension.name(); + sender.sendMessage("- " + extensionName + ChatColor.RESET + " v" + extension.description().version() + formatAuthors(extension.description().authors())); + }); + } + + private String formatAuthors(List authors) { + return authors.isEmpty() ? "" : " by: " + String.join(", ", authors); + } +} From 34d1dfde5e7ed8d9c46eef4794afd3d9e8f8e021 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 16:43:57 -0600 Subject: [PATCH 018/125] Add extensions permission to Spigot plugin.yml --- bootstrap/spigot/src/main/resources/plugin.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index 18773402e..88b2ea1a1 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -40,3 +40,6 @@ permissions: geyser.command.version: description: Shows the current Geyser version and checks for updates. default: op + geyser.command.extensions: + description: Shows all the loaded extensions. + default: op From ac134b84f243590a8b763cbeefae3f5ac2bf8d4b Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 18:28:39 -0600 Subject: [PATCH 019/125] Remove onEnable, onDisable and onLoad and replace it with lifecycle events --- .../lifecycle/GeyserPostInitializeEvent.java | 8 +++- .../lifecycle/GeyserPreInitializeEvent.java | 40 +++++++++++++++++++ .../event/lifecycle/GeyserShutdownEvent.java | 6 ++- .../geyser/api/extension/Extension.java | 18 --------- .../java/org/geysermc/geyser/GeyserImpl.java | 7 +++- .../extension/GeyserExtensionLoader.java | 14 +------ 6 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java index 94d3d8cb8..94e42e075 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -25,10 +25,16 @@ package org.geysermc.geyser.api.event.lifecycle; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.extension.ExtensionManager; /** * Called when Geyser has completed initializing. + * + * @param extensionManager the extension manager + * @param eventBus the event bus */ -public class GeyserPostInitializeEvent implements Event { +public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java new file mode 100644 index 000000000..fa130c883 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.extension.ExtensionManager; + +/** + * Called when Geyser is starting to initialize. + * + * @param extensionManager the extension manager + * @param eventBus the event bus + */ +public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java index baf8b3445..a0fc2294b 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -25,10 +25,14 @@ package org.geysermc.geyser.api.event.lifecycle; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.extension.ExtensionManager; /** * Called when Geyser is shutting down. */ -public class GeyserShutdownEvent implements Event { +public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull CommandManager commandManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java index 357165ffe..2982a76fb 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -36,24 +36,6 @@ import java.nio.file.Path; */ public interface Extension { - /** - * Called when the extension is loaded - */ - default void onLoad() { - } - - /** - * Called when the extension is enabled - */ - default void onEnable() { - } - - /** - * Called when the extension is disabled - */ - default void onDisable() { - } - /** * Gets if the extension is enabled * diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index b685741e7..47aa905ea 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -51,6 +51,7 @@ import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -167,6 +168,8 @@ public class GeyserImpl implements GeyserApi { this.extensionManager = new GeyserExtensionManager(); this.extensionManager.init(); + this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); + start(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -418,7 +421,7 @@ public class GeyserImpl implements GeyserApi { newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); - this.eventBus.fire(new GeyserPostInitializeEvent()); + this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); } @Override @@ -474,7 +477,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - this.eventBus.fire(new GeyserShutdownEvent()); + this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.commandManager(), this.eventBus)); this.extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 972b106d6..00e0e6701 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -90,9 +90,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder, ExtensionEventBus eventBus) { GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); - GeyserExtensionContainer container = new GeyserExtensionContainer(extension, dataFolder, description, this, logger, eventBus); - extension.onLoad(); - return container; + return new GeyserExtensionContainer(extension, dataFolder, description, this, logger, eventBus); } public GeyserExtensionDescription extensionDescription(Path path) throws InvalidDescriptionException { @@ -225,15 +223,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { @Override protected void setEnabled(@NonNull Extension extension, boolean enabled) { - boolean isEnabled = this.extensionContainers.get(extension).enabled; - if (isEnabled != enabled) { - this.extensionContainers.get(extension).enabled = enabled; - if (enabled) { - extension.onEnable(); - } else { - extension.onDisable(); - } - } + this.extensionContainers.get(extension).enabled = enabled; } @NonNull From 5abf989139be312b504e976890296e75dc6d3eca Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 16 Jan 2022 22:52:27 -0600 Subject: [PATCH 020/125] Use lambda metadata factory for events stonks --- .../geyser/api/event/EventSubscription.java | 11 ---- ...on.java => AbstractEventSubscription.java} | 24 ++------ .../geyser/event/BaseEventSubscription.java | 58 ++++++++++++++++++ .../event/GeneratedEventSubscription.java | 60 +++++++++++++++++++ .../geysermc/geyser/event/GeyserEventBus.java | 32 ++++++---- .../extension/GeyserExtensionClassLoader.java | 1 - .../extension/GeyserExtensionLoader.java | 4 +- 7 files changed, 146 insertions(+), 44 deletions(-) rename core/src/main/java/org/geysermc/geyser/event/{GeyserEventSubscription.java => AbstractEventSubscription.java} (72%) create mode 100644 core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java index 8603dac37..9a04b697c 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java @@ -28,8 +28,6 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.extension.Extension; -import java.util.function.Consumer; - /** * Represents a subscribed listener to a {@link Event}. Wraps around * the event and is capable of unsubscribing from the event or give @@ -47,15 +45,6 @@ public interface EventSubscription { @NonNull Class eventClass(); - /** - * Gets the consumer responsible for handling - * this event. - * - * @return the consumer responsible for this event - */ - @NonNull - Consumer eventConsumer(); - /** * Gets the {@link Extension} that owns this * event subscription. diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java similarity index 72% rename from core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java rename to core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java index 33fe5a9e1..69dc74935 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscription.java +++ b/core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java @@ -30,24 +30,20 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import net.kyori.event.EventSubscriber; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventSubscription; import org.geysermc.geyser.api.event.Subscribe; import org.geysermc.geyser.api.extension.Extension; -import java.util.function.Consumer; - @Getter @Accessors(fluent = true) @RequiredArgsConstructor -public class GeyserEventSubscription implements EventSubscription, EventSubscriber { - private final EventBus eventBus; - private final Class eventClass; - private final Consumer eventConsumer; - private final Extension owner; - private final Subscribe.PostOrder order; +public abstract class AbstractEventSubscription implements EventSubscription, EventSubscriber { + protected final EventBus eventBus; + protected final Class eventClass; + protected final Extension owner; + protected final Subscribe.PostOrder order; @Getter(AccessLevel.NONE) private boolean active; @Override @@ -65,16 +61,6 @@ public class GeyserEventSubscription implements EventSubscripti this.eventBus.unsubscribe(this); } - @Override - public void invoke(@NonNull T event) throws Throwable { - try { - this.eventConsumer.accept(event); - } catch (Throwable ex) { - this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); - ex.printStackTrace(); - } - } - @Override public int postOrder() { return this.order.postOrder(); diff --git a/core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java new file mode 100644 index 000000000..79df94e5d --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event; + +import lombok.Getter; +import lombok.experimental.Accessors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.function.Consumer; + +@Getter +@Accessors(fluent = true) +public class BaseEventSubscription extends AbstractEventSubscription { + private final Consumer eventConsumer; + + public BaseEventSubscription(EventBus eventBus, Class eventClass, Extension owner, Subscribe.PostOrder order, Consumer eventConsumer) { + super(eventBus, eventClass, owner, order); + + this.eventConsumer = eventConsumer; + } + + @Override + public void invoke(@NonNull T event) throws Throwable { + try { + this.eventConsumer.accept(event); + } catch (Throwable ex) { + this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); + ex.printStackTrace(); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java new file mode 100644 index 000000000..b1ba7bf8b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event; + +import lombok.Getter; +import lombok.experimental.Accessors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.function.BiConsumer; + +@Getter +@Accessors(fluent = true) +public class GeneratedEventSubscription extends AbstractEventSubscription { + private final Object eventHolder; + private final BiConsumer eventConsumer; + + public GeneratedEventSubscription(EventBus eventBus, Class eventClass, Extension owner, Subscribe.PostOrder order, Object eventHolder, BiConsumer eventConsumer) { + super(eventBus, eventClass, owner, order); + + this.eventHolder = eventHolder; + this.eventConsumer = eventConsumer; + } + + @Override + public void invoke(@NonNull T event) throws Throwable { + try { + this.eventConsumer.accept(this.eventHolder, event); + } catch (Throwable ex) { + this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); + ex.printStackTrace(); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index edda59e95..60e354ac9 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -33,15 +33,19 @@ import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventSubscription; import org.geysermc.geyser.api.event.Subscribe; import org.geysermc.geyser.api.extension.Extension; +import org.lanternpowered.lmbda.LambdaFactory; -import java.lang.reflect.InvocationTargetException; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; public class GeyserEventBus implements EventBus { + private static final MethodHandles.Lookup CALLER = MethodHandles.lookup(); + private final SimpleEventBus bus = new SimpleEventBus<>(Event.class); @NonNull @@ -52,7 +56,7 @@ public class GeyserEventBus implements EventBus { @Override public void unsubscribe(@NonNull EventSubscription subscription) { - this.bus.unregister((GeyserEventSubscription) subscription); + this.bus.unregister((AbstractEventSubscription) subscription); } @SuppressWarnings("unchecked") @@ -72,19 +76,19 @@ public class GeyserEventBus implements EventBus { } Subscribe subscribe = method.getAnnotation(Subscribe.class); - this.subscribe((Class) method.getParameters()[0].getType(), (event) -> { - try { - method.invoke(eventHolder, event); - } catch (IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - } - }, extension, subscribe.postOrder()); + + try { + Class type = (Class) method.getParameters()[0].getType(); + this.subscribe(type, eventHolder, LambdaFactory.createBiConsumer(CALLER.unreflect(method)), extension, subscribe.postOrder()); + } catch (IllegalAccessException ex) { + ex.printStackTrace(); + } } } @Override public void unregisterAll(@NonNull Extension extension) { - this.bus.unregister((Predicate>) subscriber -> extension.equals(((GeyserEventSubscription) subscriber).owner())); + this.bus.unregister((Predicate>) subscriber -> extension.equals(((AbstractEventSubscription) subscriber).owner())); } @Override @@ -104,7 +108,13 @@ public class GeyserEventBus implements EventBus { } private EventSubscription subscribe(Class eventClass, Consumer handler, Extension extension, Subscribe.PostOrder postOrder) { - GeyserEventSubscription eventSubscription = new GeyserEventSubscription<>(this, eventClass, handler, extension, postOrder); + BaseEventSubscription eventSubscription = new BaseEventSubscription<>(this, eventClass, extension, postOrder, handler); + this.bus.register(eventClass, eventSubscription); + return eventSubscription; + } + + private EventSubscription subscribe(Class eventClass, Object eventHolder, BiConsumer handler, Extension extension, Subscribe.PostOrder postOrder) { + GeneratedEventSubscription eventSubscription = new GeneratedEventSubscription<>(this, eventClass, extension, postOrder, eventHolder, handler); this.bus.register(eventClass, eventSubscription); return eventSubscription; } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 426cd1de7..28b9930b4 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -35,7 +35,6 @@ import java.net.URLClassLoader; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; -import java.util.Set; public class GeyserExtensionClassLoader extends URLClassLoader { private final GeyserExtensionLoader loader; diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 00e0e6701..7092d1fc2 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -110,14 +110,14 @@ public class GeyserExtensionLoader extends ExtensionLoader { public Class classByName(final String name) throws ClassNotFoundException{ Class clazz = this.classes.get(name); try { - for(GeyserExtensionClassLoader loader : this.classLoaders.values()) { + for (GeyserExtensionClassLoader loader : this.classLoaders.values()) { try { clazz = loader.findClass(name,false); } catch(NullPointerException ignored) { } } return clazz; - } catch(NullPointerException s) { + } catch (NullPointerException s) { return null; } } From 6321ecc1669d12680d71633f10c7f234404f5992 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 16:38:55 -0600 Subject: [PATCH 021/125] Initial move to gradle --- Jenkinsfile | 6 +- ap/build.gradle.kts | 0 .../geysermc/processor/ClassProcessor.java | 1 + .../javax.annotation.processing.Processor | 0 api/base/build.gradle.kts | 1 + api/base/pom.xml | 27 -- api/geyser/build.gradle.kts | 3 + api/geyser/pom.xml | 48 --- .../org/geysermc/geyser/api/GeyserApi.java | 4 +- api/pom.xml | 25 -- bootstrap/bungeecord/build.gradle.kts | 35 ++ bootstrap/bungeecord/pom.xml | 103 ----- .../bungeecord/GeyserBungeePlugin.java | 2 +- .../bungeecord/src/main/resources/bungee.yml | 8 +- bootstrap/pom.xml | 49 --- bootstrap/spigot/build.gradle.kts | 46 +++ bootstrap/spigot/pom.xml | 127 ------ .../spigot/src/main/resources/plugin.yml | 8 +- bootstrap/sponge/build.gradle.kts | 36 ++ bootstrap/sponge/pom.xml | 101 ----- bootstrap/standalone/build.gradle.kts | 33 ++ bootstrap/standalone/pom.xml | 140 ------- .../standalone/GeyserStandaloneBootstrap.java | 2 + .../standalone/GeyserStandaloneLogger.java | 8 +- .../standalone/src/main/resources/log4j2.xml | 2 +- bootstrap/velocity/build.gradle.kts | 68 ++++ bootstrap/velocity/pom.xml | 106 ----- build-logic/build.gradle.kts | 21 + build-logic/src/main/kotlin/Versions.kt | 44 +++ build-logic/src/main/kotlin/extensions.kt | 79 ++++ .../kotlin/geyser.api-conventions.gradle.kts | 9 + .../kotlin/geyser.base-conventions.gradle.kts | 41 ++ .../main/kotlin/geyser.build-logic.gradle.kts | 0 .../geyser.platform-conventions.gradle.kts | 4 + .../geyser.shadow-conventions.gradle.kts | 55 +++ build.gradle.kts | 43 ++ common/build.gradle.kts | 3 + common/pom.xml | 31 -- core/build.gradle.kts | 86 ++++ core/pom.xml | 374 ------------------ .../geysermc/connector/GeyserConnector.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 33 +- .../command/defaults/VersionCommand.java | 13 +- core/src/main/resources/languages | 2 +- gradle.properties | 6 + gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 +++++++++++ gradlew.bat | 89 +++++ pom.xml | 95 ----- settings.gradle.kts | 73 ++++ 50 files changed, 1052 insertions(+), 1279 deletions(-) create mode 100644 ap/build.gradle.kts rename {core => ap}/src/main/resources/META-INF/services/javax.annotation.processing.Processor (100%) create mode 100644 api/base/build.gradle.kts delete mode 100644 api/base/pom.xml create mode 100644 api/geyser/build.gradle.kts delete mode 100644 api/geyser/pom.xml delete mode 100644 api/pom.xml create mode 100644 bootstrap/bungeecord/build.gradle.kts delete mode 100644 bootstrap/bungeecord/pom.xml delete mode 100644 bootstrap/pom.xml create mode 100644 bootstrap/spigot/build.gradle.kts delete mode 100644 bootstrap/spigot/pom.xml create mode 100644 bootstrap/sponge/build.gradle.kts delete mode 100644 bootstrap/sponge/pom.xml create mode 100644 bootstrap/standalone/build.gradle.kts delete mode 100644 bootstrap/standalone/pom.xml create mode 100644 bootstrap/velocity/build.gradle.kts delete mode 100644 bootstrap/velocity/pom.xml create mode 100644 build-logic/build.gradle.kts create mode 100644 build-logic/src/main/kotlin/Versions.kt create mode 100644 build-logic/src/main/kotlin/extensions.kt create mode 100644 build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts create mode 100644 build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts create mode 100644 build-logic/src/main/kotlin/geyser.build-logic.gradle.kts create mode 100644 build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts create mode 100644 build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts create mode 100644 build.gradle.kts create mode 100644 common/build.gradle.kts delete mode 100644 common/pom.xml create mode 100644 core/build.gradle.kts delete mode 100644 core/pom.xml create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 pom.xml create mode 100644 settings.gradle.kts diff --git a/Jenkinsfile b/Jenkinsfile index 481c02310..b8566b2f7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any tools { - maven 'Maven 3' + gradle 'Gradle 7' jdk 'Java 16' } options { @@ -11,11 +11,11 @@ pipeline { stage ('Build') { steps { sh 'git submodule update --init --recursive' - sh 'mvn clean package' + sh './gradlew shadowJar' } post { success { - archiveArtifacts artifacts: 'bootstrap/**/target/*.jar', excludes: 'bootstrap/**/target/original-*.jar', fingerprint: true + archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/original-*.jar', fingerprint: true } } } diff --git a/ap/build.gradle.kts b/ap/build.gradle.kts new file mode 100644 index 000000000..e69de29bb diff --git a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java b/ap/src/main/java/org/geysermc/processor/ClassProcessor.java index a6259a853..0f730aaba 100644 --- a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java +++ b/ap/src/main/java/org/geysermc/processor/ClassProcessor.java @@ -163,6 +163,7 @@ public class ClassProcessor extends AbstractProcessor { this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath); return Files.newBufferedReader(this.outputPath); } + FileObject obj = this.processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", this.annotationClassName); if (obj != null) { this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + obj.toUri()); diff --git a/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor similarity index 100% rename from core/src/main/resources/META-INF/services/javax.annotation.processing.Processor rename to ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts new file mode 100644 index 000000000..d7500fdaa --- /dev/null +++ b/api/base/build.gradle.kts @@ -0,0 +1 @@ +provided("net.kyori", "event-api", Versions.eventVersion) \ No newline at end of file diff --git a/api/base/pom.xml b/api/base/pom.xml deleted file mode 100644 index 17edb1a85..000000000 --- a/api/base/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - org.geysermc - api-parent - 2.0.1-SNAPSHOT - - 4.0.0 - - base-api - - - 16 - 16 - - - - - org.checkerframework - checker-qual - 3.19.0 - provided - - - \ No newline at end of file diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts new file mode 100644 index 000000000..f9f8e66a8 --- /dev/null +++ b/api/geyser/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api(projects.api) +} \ No newline at end of file diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml deleted file mode 100644 index de9c63e83..000000000 --- a/api/geyser/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - org.geysermc - api-parent - 2.0.1-SNAPSHOT - ../pom.xml - - 4.0.0 - - geyser-api - - - 16 - 16 - - 4.9.3 - - - - - org.checkerframework - checker-qual - 3.19.0 - provided - - - net.kyori - event-api - 3.0.0 - provided - - - org.geysermc - base-api - 2.0.1-SNAPSHOT - compile - - - org.yaml - snakeyaml - 1.27 - compile - - - \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 2bddc9ef5..b5a0c7897 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -55,9 +55,9 @@ public interface GeyserApi extends GeyserApiBase { * Gets if this Geyser instance is running in an IDE. This only needs to be used in cases where files * expected to be in a jarfile are not present. * - * @return true if the version number is not 'DEV'. + * @return if we are in a production environment */ - boolean productionEnvironment(); + boolean isProductionEnvironment(); /** * {@inheritDoc} diff --git a/api/pom.xml b/api/pom.xml deleted file mode 100644 index b6d865cb4..000000000 --- a/api/pom.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - ../pom.xml - - - api-parent - pom - - - 16 - 16 - - - - base - geyser - - \ No newline at end of file diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts new file mode 100644 index 000000000..873df692a --- /dev/null +++ b/bootstrap/bungeecord/build.gradle.kts @@ -0,0 +1,35 @@ +val bungeeVersion = "a7c6ede"; + +dependencies { + api(projects.core) +} + +platformRelocate("net.md_5.bungee.jni") +platformRelocate("com.fasterxml.jackson") +platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound +platformRelocate("net.kyori") + +// These dependencies are already present on the platform +provided("com.github.SpigotMC.BungeeCord", "bungeecord-proxy", bungeeVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-BungeeCord") + + dependencies { + exclude(dependency("com.google.*:.*")) + exclude(dependency("org.yaml:.*")) + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-resolver-dns:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml deleted file mode 100644 index 45a08c7db..000000000 --- a/bootstrap/bungeecord/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.1-SNAPSHOT - - bootstrap-bungeecord - - - - org.geysermc - core - 2.0.1-SNAPSHOT - compile - - - - com.github.SpigotMC.BungeeCord - bungeecord-proxy - a7c6ede - provided - - - - ${outputName}-BungeeCord - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0-SNAPSHOT - - - package - - shade - - - - - net.md_5.bungee.jni - org.geysermc.geyser.platform.bungeecord.shaded.jni - - - com.fasterxml.jackson - org.geysermc.geyser.platform.bungeecord.shaded.jackson - - - - io.netty.channel.kqueue - org.geysermc.geyser.platform.bungeecord.shaded.io.netty.channel.kqueue - - - net.kyori - org.geysermc.geyser.platform.bungeecord.shaded.kyori - - - - - - - - - com.google.*:* - org.yaml:* - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-resolver-dns:* - - - - - - - diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index bb90e5000..0aa82d9cd 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.bungeecord; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.plugin.Plugin; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; @@ -40,7 +41,6 @@ import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandManager; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; diff --git a/bootstrap/bungeecord/src/main/resources/bungee.yml b/bootstrap/bungeecord/src/main/resources/bungee.yml index 7390a4623..1e18b8da4 100644 --- a/bootstrap/bungeecord/src/main/resources/bungee.yml +++ b/bootstrap/bungeecord/src/main/resources/bungee.yml @@ -1,5 +1,5 @@ main: org.geysermc.geyser.platform.bungeecord.GeyserBungeePlugin -name: ${outputName}-BungeeCord -author: ${project.organization.name} -website: ${project.organization.url} -version: ${project.version} \ No newline at end of file +name: ${name}-BungeeCord +author: ${author} +website: ${url} +version: ${version} \ No newline at end of file diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml deleted file mode 100644 index 58c651455..000000000 --- a/bootstrap/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - - bootstrap-parent - pom - - - - spigot-public - https://hub.spigotmc.org/nexus/content/repositories/public/ - - - sponge-repo - https://repo.spongepowered.org/repository/maven-public/ - - - bungeecord-repo - https://oss.sonatype.org/content/repositories/snapshots - - - velocity-repo - https://repo.velocitypowered.com/snapshots/ - - - - - - org.geysermc - ap - 2.0.1-SNAPSHOT - provided - - - - - bungeecord - spigot - sponge - standalone - velocity - - \ No newline at end of file diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts new file mode 100644 index 000000000..affb243b3 --- /dev/null +++ b/bootstrap/spigot/build.gradle.kts @@ -0,0 +1,46 @@ +val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet +val viaVersion = "4.0.0" +val adaptersVersion = "1.3-SNAPSHOT" + +dependencies { + api(projects.core) + + implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) +} + +platformRelocate("it.unimi.dsi.fastutil") +platformRelocate("com.fasterxml.jackson") +platformRelocate("net.kyori") +platformRelocate("org.objectweb.asm") + +// These dependencies are already present on the platform +provided("io.papermc.paper", "paper-api", paperVersion) +provided("com.viaversion", "viaversion", viaVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.spigot.GeyserSpigotMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Spigot") + + dependencies { + exclude(dependency("com.google.*:.*")) + exclude(dependency("org.yaml:.*")) + + // We cannot shade Netty, or else native libraries will not load + // Needed because older Spigot builds do not provide the haproxy module + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-codec-dns:.*")) + exclude(dependency("io.netty:netty-resolver-dns:.*")) + exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml deleted file mode 100644 index 6eda527f3..000000000 --- a/bootstrap/spigot/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.1-SNAPSHOT - - bootstrap-spigot - - - - papermc - https://papermc.io/repo/repository/maven-public/ - - - viaversion-repo - https://repo.viaversion.com - - - - - - org.geysermc - core - 2.0.1-SNAPSHOT - compile - - - io.papermc.paper - paper-api - 1.18.1-R0.1-SNAPSHOT - provided - - - com.viaversion - viaversion - 4.0.0 - provided - - - org.geysermc.geyser.adapters - spigot-all - 1.3-SNAPSHOT - - - - ${outputName}-Spigot - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.spigot.GeyserSpigotMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0-SNAPSHOT - - - package - - shade - - - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.spigot.shaded.fastutil - - - com.fasterxml.jackson - org.geysermc.geyser.platform.spigot.shaded.jackson - - - net.kyori - org.geysermc.geyser.platform.spigot.shaded.kyori - - - org.objectweb.asm - org.geysermc.geyser.platform.spigot.shaded.asm - - - - - - - - - com.google.*:* - org.yaml:* - - - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-transport-native-kqueue:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-codec-dns:* - io.netty:netty-resolver-dns:* - io.netty:netty-resolver-dns-native-macos:* - - - - - - - \ No newline at end of file diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index 88b2ea1a1..27f51acd1 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -1,8 +1,8 @@ main: org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin -name: ${outputName}-Spigot -author: ${project.organization.name} -website: ${project.organization.url} -version: ${project.version} +name: ${name}-Spigot +author: ${author} +website: ${url} +version: ${version} softdepend: ["ViaVersion", "floodgate"] api-version: 1.13 commands: diff --git a/bootstrap/sponge/build.gradle.kts b/bootstrap/sponge/build.gradle.kts new file mode 100644 index 000000000..2850b2c5e --- /dev/null +++ b/bootstrap/sponge/build.gradle.kts @@ -0,0 +1,36 @@ +val spongeVersion = "7.1.0" + +dependencies { + api(projects.core) +} + +platformRelocate("com.fasterxml.jackson") +platformRelocate("io.netty") +platformRelocate("it.unimi.dsi.fastutil") +platformRelocate("com.google.common") +platformRelocate("com.google.guava") +platformRelocate("net.kyori") + +// Exclude these dependencies +exclude("com.google.code.gson:*") +exclude("org.yaml:*") +exclude("org.slf4j:*") +exclude("org.ow2.asm:*") + +// These dependencies are already present on the platform +provided("org.spongepowered", "spongeapi", spongeVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.sponge.GeyserSpongeMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Sponge") + + dependencies { + exclude(dependency("com.google.code.gson:.*")) + exclude(dependency("org.yaml:.*")) + exclude(dependency("org.slf4j:.*")) + exclude(dependency("org.ow2.asm:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml deleted file mode 100644 index ab3b7d970..000000000 --- a/bootstrap/sponge/pom.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.1-SNAPSHOT - - bootstrap-sponge - - - - org.geysermc - core - 2.0.1-SNAPSHOT - compile - - - org.spongepowered - spongeapi - 7.1.0 - provided - - - - ${outputName}-Sponge - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.sponge.GeyserSpongeMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0-SNAPSHOT - - - package - - shade - - - - - com.fasterxml.jackson - org.geysermc.geyser.platform.sponge.shaded.jackson - - - io.netty - org.geysermc.geyser.platform.sponge.shaded.netty - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.sponge.shaded.fastutil - - - com.google.common - org.geysermc.geyser.platform.sponge.shaded.google.common - - - com.google.guava - org.geysermc.geyser.platform.sponge.shaded.google.guava - - - net.kyori - org.geysermc.geyser.platform.sponge.shaded.kyori - - - - - - - - - com.google.code.gson:* - org.yaml:* - org.slf4j:* - org.ow2.asm:* - - - - - - - \ No newline at end of file diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts new file mode 100644 index 000000000..977a4a7a6 --- /dev/null +++ b/bootstrap/standalone/build.gradle.kts @@ -0,0 +1,33 @@ +import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer + +val terminalConsoleVersion = "1.2.0" +val jlineVersion = "3.20.0" + +dependencies { + api(projects.core) + + implementation("net.minecrell", "terminalconsoleappender", terminalConsoleVersion) { + exclude("org.apache.logging.log4j", "log4j-core") + exclude("org.jline", "jline-reader") + exclude("org.jline", "jline-terminal") + exclude("org.jline", "jline-terminal-jna") + } + + implementation("org.jline", "jline-terminal", jlineVersion) + implementation("org.jline", "jline-terminal-jna", jlineVersion) + implementation("org.jline", "jline-reader", jlineVersion) + + implementation("org.apache.logging.log4j", "log4j-api", Versions.log4jVersion) + implementation("org.apache.logging.log4j", "log4j-core", Versions.log4jVersion) + implementation("org.apache.logging.log4j", "log4j-slf4j18-impl", Versions.log4jVersion) +} + +application { + mainClass.set("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap") +} + +tasks.withType { + archiveBaseName.set("Geyser") + + transform(Log4j2PluginsCacheFileTransformer()) +} \ No newline at end of file diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml deleted file mode 100644 index 881c87e6c..000000000 --- a/bootstrap/standalone/pom.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.1-SNAPSHOT - - bootstrap-standalone - - - 2.17.1 - - - - - org.geysermc - core - 2.0.1-SNAPSHOT - compile - - - net.minecrell - terminalconsoleappender - 1.2.0 - - - org.apache.logging.log4j - log4j-core - - - org.jline - jline-reader - - - org.jline - jline-terminal-jna - - - org.jline - jline-terminal - - - - - org.jline - jline-terminal - 3.20.0 - - - org.jline - jline-terminal-jna - 3.20.0 - - - org.jline - jline-reader - 3.20.0 - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j18-impl - ${log4j.version} - - - - ${outputName} - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0-SNAPSHOT - - - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.8.1 - - - - - package - - shade - - - false - - - - - - - *:* - - META-INF/versions/9/module-info.class - - - - - - org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap - - true - - - - - - ${project.build.directory}/dependency-reduced-pom.xml - - - - - diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index ca41d3c1d..9869fa478 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; @@ -179,6 +180,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { logger.removeAppender(appender); } } + if (useGui && gui == null) { gui = new GeyserStandaloneGUI(); gui.redirectSystemStreams(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index f7ce6d052..31b395a61 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.platform.standalone; -import lombok.extern.log4j.Log4j2; +import lombok.extern.slf4j.Slf4j; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; @@ -34,7 +34,7 @@ import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.ChatColor; -@Log4j2 +@Slf4j public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { @Override @@ -54,12 +54,12 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey @Override public void severe(String message) { - log.fatal(ChatColor.DARK_RED + message); + log.error(ChatColor.DARK_RED + message); } @Override public void severe(String message, Throwable error) { - log.fatal(ChatColor.DARK_RED + message, error); + log.error(ChatColor.DARK_RED + message, error); } @Override diff --git a/bootstrap/standalone/src/main/resources/log4j2.xml b/bootstrap/standalone/src/main/resources/log4j2.xml index cd101f306..0738acdcd 100644 --- a/bootstrap/standalone/src/main/resources/log4j2.xml +++ b/bootstrap/standalone/src/main/resources/log4j2.xml @@ -16,7 +16,7 @@ - + diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts new file mode 100644 index 000000000..f9fcafb2c --- /dev/null +++ b/bootstrap/velocity/build.gradle.kts @@ -0,0 +1,68 @@ +val velocityVersion = "3.0.0" + +dependencies { + api(projects.core) +} + +platformRelocate("com.fasterxml.jackson") +platformRelocate("it.unimi.fastutil") +platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl") + +exclude("com.google.*:*") + +// Needed because Velocity provides every dependency except netty-resolver-dns +exclude("io.netty:netty-transport-native-epoll:*") +exclude("io.netty:netty-transport-native-unix-common:*") +exclude("io.netty:netty-transport-native-kqueue:*") +exclude("io.netty:netty-handler:*") +exclude("io.netty:netty-common:*") +exclude("io.netty:netty-buffer:*") +exclude("io.netty:netty-resolver:*") +exclude("io.netty:netty-transport:*") +exclude("io.netty:netty-codec:*") +exclude("io.netty:netty-codec-haproxy:*") +exclude("org.slf4j:*") +exclude("org.ow2.asm:*") + +// Exclude all Kyori dependencies except the legacy NBT serializer +exclude("net.kyori:adventure-api:*") +exclude("net.kyori:examination-api:*") +exclude("net.kyori:examination-string:*") +exclude("net.kyori:adventure-text-serializer-gson:*") +exclude("net.kyori:adventure-text-serializer-legacy:*") +exclude("net.kyori:adventure-nbt:*") + +// These dependencies are already present on the platform +provided("com.velocitypowered", "velocity-api", velocityVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.velocity.GeyserVelocityMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Velocity") + + dependencies { + exclude(dependency("com.google.*:.*")) + // Needed because Velocity provides every dependency except netty-resolver-dns + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-codec-haproxy:.*")) + exclude(dependency("org.slf4j:.*")) + exclude(dependency("org.ow2.asm:.*")) + // Exclude all Kyori dependencies except the legacy NBT serializer + exclude(dependency("net.kyori:adventure-api:.*")) + exclude(dependency("net.kyori:examination-api:.*")) + exclude(dependency("net.kyori:examination-string:.*")) + exclude(dependency("net.kyori:adventure-text-serializer-gson:.*")) + exclude(dependency("net.kyori:adventure-text-serializer-legacy:.*")) + exclude(dependency("net.kyori:adventure-nbt:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml deleted file mode 100644 index ff052471d..000000000 --- a/bootstrap/velocity/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.1-SNAPSHOT - - bootstrap-velocity - - - - org.geysermc - core - 2.0.1-SNAPSHOT - compile - - - com.velocitypowered - velocity-api - 3.0.0 - provided - - - - ${outputName}-Velocity - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.velocity.GeyserVelocityMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0-SNAPSHOT - - - package - - shade - - - - - com.fasterxml.jackson - org.geysermc.geyser.platform.velocity.shaded.jackson - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.velocity.shaded.fastutil - - - net.kyori.adventure.text.serializer.gson.legacyimpl - org.geysermc.geyser.platform.velocity.shaded.kyori.legacyimpl - - - - - - - - - com.google.*:* - - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-transport-native-kqueue:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-codec-haproxy:* - org.slf4j:* - org.ow2.asm:* - - net.kyori:adventure-api:* - net.kyori:examination-api:* - net.kyori:examination-string:* - net.kyori:adventure-text-serializer-gson:* - net.kyori:adventure-text-serializer-legacy:* - net.kyori:adventure-nbt:* - - - - - - - \ No newline at end of file diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 000000000..25cbfe9de --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,21 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("net.kyori", "indra-common", "2.0.6") + implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1") + implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1") +} + +tasks.withType { + kotlinOptions { + jvmTarget = "16" + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt new file mode 100644 index 000000000..c6348bc65 --- /dev/null +++ b/build-logic/src/main/kotlin/Versions.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +object Versions { + const val jacksonVersion= "2.12.4" + const val fastutilVersion= "8.5.2" + const val nettyVersion= "4.1.66.Final" + const val guavaVersion= "29.0-jre" + const val nbtVersion= "2.1.0" + const val websocketVersion= "1.5.1" + const val protocolVersion= "0cd24c0" + const val raknetVersion= "1.6.28-SNAPSHOT" + const val mcauthlibVersion= "6c99331" + const val mcprotocollibversion= "6a23a780" + const val packetlibVersion= "2.1-SNAPSHOT" + const val adventureVersion= "4.9.3" + const val eventVersion= "3.0.0" + const val junitVersion= "4.13.1" + const val checkerQualVersion= "3.19.0" + const val cumulusVersion = "1.0-SNAPSHOT" + const val log4jVersion = "2.17.1" +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt new file mode 100644 index 000000000..a2c90cd1f --- /dev/null +++ b/build-logic/src/main/kotlin/extensions.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import net.kyori.indra.git.IndraGitExtension +import org.gradle.api.Project +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.the + +fun Project.lastCommitHash(): String? = + the().commit()?.name?.substring(0, 7) + +// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project +// some properties might be specific to Jenkins +fun Project.branchName(): String = + System.getProperty("GIT_BRANCH", "local/dev") +fun Project.commitHashAbbrev(): String = + System.getProperty("GIT_COMMIT", "0000000") +fun Project.versionName(): String = + System.getProperty("GIT_VERSION", "local/dev") +fun Project.buildNumber(): Int = + Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")) + +fun Project.relocate(pattern: String) { + tasks.named("shadowJar") { + relocate(pattern, "org.geysermc.geyser.shaded.$pattern") + } +} + +fun Project.exclude(group: String) { + tasks.named("shadowJar") { + exclude(group) + } +} + +fun Project.platformRelocate(pattern: String) { + tasks.named("shadowJar") { + relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") + } +} + +val providedDependencies = mutableMapOf>() + +fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) { + providedDependencies.getOrPut(project.name) { mutableSetOf() } + .add("${calcExclusion(pattern, 0b100, excludedOn)}:" + + "${calcExclusion(name, 0b10, excludedOn)}:" + + calcExclusion(version, 0b1, excludedOn)) + dependencies.add("compileOnlyApi", "$pattern:$name:$version") +} + +fun Project.provided(dependency: ProjectDependency) = + provided(dependency.group!!, dependency.name, dependency.version!!) + +private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String = + if (excludedOn and bit > 0) section else "" \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts new file mode 100644 index 000000000..0781436c4 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("geyser.shadow-conventions") +} + +tasks { + shadowJar { + archiveBaseName.set(archiveBaseName.get() + "-api") + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts new file mode 100644 index 000000000..211455d31 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -0,0 +1,41 @@ +plugins { + `java-library` + `maven-publish` +} + +dependencies { + compileOnly("org.checkerframework", "checker-qual", Versions.checkerQualVersion) +} + +tasks { + processResources { + filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) { + expand( + "id" to "Geyser", + "name" to "Geyser", + "version" to project.version, + "description" to project.description, + "url" to "https://geysermc.org", + "author" to "GeyserMC" + ) + } + } + compileJava { + options.encoding = Charsets.UTF_8.name() + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_16 + targetCompatibility = JavaVersion.VERSION_16 + + withSourcesJar() +} + +publishing { + publications.create("mavenJava") { + groupId = project.group as String + artifactId = project.name + version = project.version as String + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts b/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts new file mode 100644 index 000000000..e69de29bb diff --git a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts new file mode 100644 index 000000000..07968f231 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts @@ -0,0 +1,4 @@ +plugins { + application + id("geyser.shadow-conventions") +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts new file mode 100644 index 000000000..ddd427897 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts @@ -0,0 +1,55 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id("geyser.base-conventions") + id("com.github.johnrengelman.shadow") + id("com.jfrog.artifactory") +} + +tasks { + named("jar") { + archiveClassifier.set("unshaded") + from(project.rootProject.file("LICENSE")) + } + val shadowJar = named("shadowJar") { + archiveBaseName.set(project.name) + archiveVersion.set("") + archiveClassifier.set("") + + val sJar: ShadowJar = this + + doFirst { + providedDependencies[project.name]?.forEach { string -> + sJar.dependencies { + println("Excluding $string from ${project.name}") + exclude(dependency(string)) + } + } + } + } + named("build") { + dependsOn(shadowJar) + } +} + +publishing { + publications.named("mavenJava") { + artifact(tasks["shadowJar"]) + artifact(tasks["sourcesJar"]) + } +} + +artifactory { + publish { + repository { + setRepoKey("maven-snapshots") + setMavenCompatible(true) + } + defaults { + publishConfigs("archives") + setPublishArtifacts(true) + setPublishPom(true) + setPublishIvy(false) + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..573687c7f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + `java-library` + id("geyser.build-logic") + id("io.freefair.lombok") version "6.3.0" apply false +} + +allprojects { + group = "org.geysermc" + version = "2.1.0-SNAPSHOT" + description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers." +} + +val platforms = setOf( + projects.bungeecord, + projects.spigot, + projects.sponge, + projects.standalone, + projects.velocity +).map { it.dependencyProject } + +val api: Project = projects.api.dependencyProject + +subprojects { + apply { + plugin("java-library") + plugin("io.freefair.lombok") + plugin("geyser.build-logic") + } + + val relativePath = projectDir.relativeTo(rootProject.projectDir).path + + if (relativePath.contains("api")) { + group = rootProject.group as String + ".api" + plugins.apply("geyser.api-conventions") + } else { + group = rootProject.group as String + ".geyser" + when (this) { + in platforms -> plugins.apply("geyser.platform-conventions") + api -> plugins.apply("geyser.shadow-conventions") + else -> plugins.apply("geyser.base-conventions") + } + } +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 000000000..205b20c0e --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) +} \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index fde2605bc..000000000 --- a/common/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - - common - - - - 8 - 8 - - - - - org.geysermc.cumulus - cumulus - 1.0-SNAPSHOT - - - com.google.code.gson - gson - 2.8.6 - - - \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 000000000..0a1883bc4 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,86 @@ +import net.kyori.blossom.BlossomExtension + +plugins { + id("net.kyori.blossom") +} + +dependencies { + api(projects.geyserApi) + api(projects.common) + + // Jackson JSON and YAML serialization + api("com.fasterxml.jackson.core", "jackson-annotations", Versions.jacksonVersion) + api("com.fasterxml.jackson.core", "jackson-databind", Versions.jacksonVersion) + api("com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml", Versions.jacksonVersion) + api("com.google.guava", "guava", Versions.guavaVersion) + + api("com.nukkitx", "nbt", Versions.nbtVersion) + + // Fastutil Maps + implementation("com.nukkitx.fastutil", "fastutil-int-int-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-long-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-byte-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-boolean-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-object-int-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-object-object-maps", Versions.fastutilVersion) + + // Network libraries + implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) + + api("com.github.CloudburstMC.Protocol", "bedrock-v486", Versions.protocolVersion) { + exclude("com.nukkitx.network", "raknet") + exclude("com.nukkitx", "nbt") + } + + api("com.github.RednedEpic", "MCAuthLib", Versions.mcauthlibVersion) + api("com.github.GeyserMC", "MCProtocolLib", Versions.mcprotocollibversion) { + exclude("com.github.steveice10", "packetlib") + exclude("com.github.steveice10", "mcauthlib") + } + + api("com.github.steveice10", "packetlib", Versions.packetlibVersion) { + exclude("io.netty", "netty-all") + // This is still experimental - additionally, it could only really benefit standalone + exclude("io.netty.incubator", "netty-incubator-transport-native-io_uring") + } + + implementation("com.nukkitx.network", "raknet", Versions.raknetVersion) { + exclude("io.netty", "*"); + } + + implementation("io.netty", "netty-resolver-dns", Versions.nettyVersion) + implementation("io.netty", "netty-resolver-dns-native-macos", Versions.nettyVersion, null, "osx-x86_64") + implementation("io.netty", "netty-codec-haproxy", Versions.nettyVersion) + + // Network dependencies we are updating ourselves + api("io.netty", "netty-handler", Versions.nettyVersion) + + implementation("io.netty", "netty-transport-native-epoll", Versions.nettyVersion, null, "linux-x86_64") + implementation("io.netty", "netty-transport-native-epoll", Versions.nettyVersion, null, "linux-aarch_64") + implementation("io.netty", "netty-transport-native-kqueue", Versions.nettyVersion, null, "osx-x86_64") + + // Adventure text serialization + implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) + implementation("net.kyori", "adventure-text-serializer-plain", Versions.adventureVersion) + + // Kyori Misc + implementation("net.kyori", "event-api", Versions.eventVersion) + + // Test + testImplementation("junit", "junit", Versions.junitVersion) + + // Annotation Processors + annotationProcessor(projects.ap) +} + +provided(projects.ap) + +configure { + val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" + val gitVersion = "git-${branchName()}-${commitHashAbbrev()}" + + replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) + replaceToken("\${gitVersion}", gitVersion, mainFile) + replaceToken("\${buildNumber}", buildNumber(), mainFile) + replaceToken("\${branch}", branchName(), mainFile) +} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml deleted file mode 100644 index 31f9a075f..000000000 --- a/core/pom.xml +++ /dev/null @@ -1,374 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - - core - - - 8.5.2 - 2.12.4 - 4.1.66.Final - - - - - org.geysermc - ap - 2.0.1-SNAPSHOT - provided - - - org.geysermc - geyser-api - 2.0.1-SNAPSHOT - compile - - - org.geysermc - common - 2.0.1-SNAPSHOT - compile - - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - compile - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - compile - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - compile - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - compile - - - com.google.guava - guava - 29.0-jre - compile - - - - com.nukkitx - nbt - - 2.1.0 - compile - - - com.nukkitx.fastutil - fastutil-int-int-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-long-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-byte-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-boolean-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-object-int-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-object-object-maps - ${fastutil.version} - compile - - - - org.java-websocket - Java-WebSocket - 1.5.1 - compile - - - com.github.CloudburstMC.Protocol - bedrock-v486 - 0cd24c0 - compile - - - com.nukkitx.network - raknet - - - com.nukkitx - nbt - - - - - com.nukkitx.network - raknet - 1.6.28-20211202.034102-5 - compile - - - io.netty - * - - - - - com.github.RednedEpic - MCAuthLib - 6c99331 - compile - - - com.github.GeyserMC - MCProtocolLib - 6a23a780 - compile - - - com.github.steveice10 - packetlib - - - com.github.steveice10 - mcauthlib - - - - - com.github.steveice10 - packetlib - 2.1-SNAPSHOT - compile - - - io.netty - netty-all - - - - io.netty.incubator - netty-incubator-transport-native-io_uring - - - - - io.netty - netty-resolver-dns - ${netty.version} - compile - - - io.netty - netty-resolver-dns-native-macos - ${netty.version} - compile - osx-x86_64 - - - io.netty - netty-codec-haproxy - ${netty.version} - compile - - - - io.netty - netty-handler - ${netty.version} - compile - - - io.netty - netty-transport-native-epoll - ${netty.version} - compile - linux-x86_64 - - - io.netty - netty-transport-native-epoll - ${netty.version} - compile - linux-aarch_64 - - - io.netty - netty-transport-native-kqueue - ${netty.version} - compile - osx-x86_64 - - - - net.kyori - adventure-text-serializer-legacy - ${adventure.version} - compile - - - net.kyori - adventure-text-serializer-plain - ${adventure.version} - compile - - - - net.kyori - event-api - 3.0.0 - compile - - - - junit - junit - 4.13.1 - test - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - **/services/javax.annotation.processing.Processor - - - - - pl.project13.maven - git-commit-id-plugin - 4.0.0 - - - get-the-git-infos - - revision - - - - - true - ${project.build.outputDirectory}/git.properties - properties - false - false - false - true - false - - git.user.* - git.*.user.* - git.closest.* - git.commit.id.describe - git.commit.id.describe-short - git.commit.message.short - - flat - - true - - - - - com.google.code.maven-replacer-plugin - replacer - 1.5.3 - - - add-version - process-sources - - replace - - - - ${project.basedir}/src/main/java/org/geysermc/geyser/GeyserImpl.java - - - - String VERSION = ".*" - String VERSION = "${project.version} (" + GIT_VERSION + ")" - - - String GIT_VERSION = ".*" - - String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}" - - - - - - - remove-version - process-classes - - replace - - - - ${project.basedir}/src/main/java/org/geysermc/geyser/GeyserImpl.java - - - - String VERSION = ".*" - String VERSION = "DEV" - - - String GIT_VERSION = ".*" - String GIT_VERSION = "DEV" - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - - - -Dfile.encoding=${project.build.sourceEncoding} - - - - - diff --git a/core/src/main/java/org/geysermc/connector/GeyserConnector.java b/core/src/main/java/org/geysermc/connector/GeyserConnector.java index b3307a134..d9ff694bd 100644 --- a/core/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/core/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -91,6 +91,6 @@ public class GeyserConnector { } public boolean isProductionEnvironment() { - return GeyserImpl.getInstance().productionEnvironment(); + return GeyserImpl.getInstance().isProductionEnvironment(); } } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 68bb69b46..681b38449 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -99,8 +99,11 @@ public class GeyserImpl implements GeyserApi { .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); public static final String NAME = "Geyser"; - public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs - public static final String VERSION = "DEV"; // A fallback for running in IDEs + public static final String GIT_VERSION = "${gitVersion}"; // A fallback for running in IDEs + public static final String VERSION = "${version}"; // A fallback for running in IDEs + + public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}"); + public static final String BRANCH = "${branch}"; /** * Oauth client ID for Microsoft authentication @@ -268,25 +271,7 @@ public class GeyserImpl implements GeyserApi { } } - String branch = "unknown"; - int buildNumber = -1; - if (this.productionEnvironment()) { - try (InputStream stream = bootstrap.getResource("git.properties")) { - Properties gitProperties = new Properties(); - gitProperties.load(stream); - branch = gitProperties.getProperty("git.branch"); - String build = gitProperties.getProperty("git.build.number"); - if (build != null) { - buildNumber = Integer.parseInt(build); - } - } catch (Throwable e) { - logger.error("Failed to read git.properties", e); - } - } else { - logger.debug("Not getting git properties for the news handler as we are in a development environment."); - } - - this.newsHandler = new NewsHandler(branch, buildNumber); + this.newsHandler = new NewsHandler(BRANCH, BUILD_NUMBER); CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether @@ -499,9 +484,9 @@ public class GeyserImpl implements GeyserApi { * @return true if the version number is not 'DEV'. */ @Override - public boolean productionEnvironment() { - //noinspection ConstantConditions - changes in production - return !"DEV".equals(GeyserImpl.VERSION); + public boolean isProductionEnvironment() { + // noinspection ConstantConditions - changes in production + return !"git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 89bea3343..6dd2a4c41 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -74,17 +74,14 @@ public class VersionCommand extends GeyserCommand { GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); // Disable update checking in dev mode and for players in Geyser Standalone - if (GeyserImpl.getInstance().productionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { + if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale())); - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("git.properties")) { - Properties gitProp = new Properties(); - gitProp.load(stream); - + try { String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" + - URLEncoder.encode(gitProp.getProperty("git.branch"), StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); + URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); if (buildXML.startsWith("")) { int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim()); - int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number")); + int buildNum = GeyserImpl.BUILD_NUMBER; if (latestBuildNum == buildNum) { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); } else { @@ -94,7 +91,7 @@ public class VersionCommand extends GeyserCommand { } else { throw new AssertionError("buildNumber missing"); } - } catch (IOException | AssertionError | NumberFormatException e) { + } catch (IOException e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e); sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale())); } diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 94c185193..cef75b344 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 94c1851931f2319a7e7f42c2fe9066b78235bc39 +Subproject commit cef75b34461d195d50bfcd274b499bd9d641c5aa diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..8f6ac8e85 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +group=org.geysermc +version=2.1.0-SNAPSHOT + +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.vfs.watch=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..41dfb8790 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 000000000..1b6c78733 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 191a40704..000000000 --- a/pom.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - 4.0.0 - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - pom - Geyser - Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers. - https://geysermc.org - - - Geyser - UTF-8 - UTF-8 - 16 - 16 - - 4.9.3 - - - - GeyserMC - https://github.com/GeyserMC/Geyser/blob/master/pom.xml - - - - scm:git:https://github.com/GeyserMC/Geyser.git - scm:git:git@github.com:GeyserMC/Geyser.git - https://github.com/GeyserMC/Geyser - - - - ap - api - bootstrap - common - core - - - - - - apache.snapshots - https://repository.apache.org/snapshots/ - - - - - - - apache.snapshots - https://repository.apache.org/snapshots/ - - - jitpack.io - https://jitpack.io - - - opencollab-release-repo - https://repo.opencollab.dev/maven-releases/ - - true - - - false - - - - opencollab-snapshot-repo - https://repo.opencollab.dev/maven-snapshots/ - - false - - - true - - - - sonatype - https://oss.sonatype.org/content/repositories/snapshots/ - - - - - - org.projectlombok - lombok - 1.18.20 - provided - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..51c1b479d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,73 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + // Floodgate, Cumulus etc. + maven("https://repo.opencollab.dev/maven-releases") { + mavenContent { releasesOnly() } + } + maven("https://repo.opencollab.dev/maven-snapshots") { + mavenContent { snapshotsOnly() } + } + + // Paper, Velocity + maven("https://papermc.io/repo/repository/maven-public") + // Spigot + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } + } + + // BungeeCord + maven("https://oss.sonatype.org/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } + } + + // Minecraft + maven("https://libraries.minecraft.net") { + name = "minecraft" + mavenContent { releasesOnly() } + } + + mavenLocal() + mavenCentral() + + maven("https://jitpack.io") { + content { includeGroupByRegex("com\\.github\\..*") } + } + } +} + +pluginManagement { + repositories { + gradlePluginPortal() + } + plugins { + id("net.kyori.blossom") version "1.2.0" + id("net.kyori.indra") + id("net.kyori.indra.git") + } + includeBuild("build-logic") +} + +rootProject.name = "geyser-parent" + +include(":ap") +include(":api") +include(":geyser-api") +include(":bungeecord") +include(":spigot") +include(":sponge") +include(":standalone") +include(":velocity") +include(":common") +include(":core") + +// Specify project dirs +project(":api").projectDir = file("api/base") +project(":geyser-api").projectDir = file("api/geyser") +project(":bungeecord").projectDir = file("bootstrap/bungeecord") +project(":spigot").projectDir = file("bootstrap/spigot") +project(":sponge").projectDir = file("bootstrap/sponge") +project(":standalone").projectDir = file("bootstrap/standalone") +project(":velocity").projectDir = file("bootstrap/velocity") \ No newline at end of file From f00c18c9fc8381dc1ca232284ab30c7ec6c0f2a9 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 16:43:15 -0600 Subject: [PATCH 022/125] This is what floodgate uses; lets use it --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index b8566b2f7..82fdcfb18 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,7 +11,7 @@ pipeline { stage ('Build') { steps { sh 'git submodule update --init --recursive' - sh './gradlew shadowJar' + sh './gradlew clean build' } post { success { From fe6c87c95590e16d5d10714d4ca229ea5da767af Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 16:44:17 -0600 Subject: [PATCH 023/125] Update these as well --- Jenkinsfile | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 82fdcfb18..14e0fd4e3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -26,23 +26,25 @@ pipeline { } steps { - rtMavenDeployer( + rtGradleDeployer( id: "maven-deployer", serverId: "opencollab-artifactory", releaseRepo: "maven-releases", snapshotRepo: "maven-snapshots" ) - rtMavenResolver( - id: "maven-resolver", - serverId: "opencollab-artifactory", - releaseRepo: "maven-deploy-release", - snapshotRepo: "maven-deploy-snapshot" + rtGradleResolver( + id: "GRADLE_RESOLVER", + serverId: "opencollab-artifactory" ) - rtMavenRun( - pom: 'pom.xml', - goals: 'javadoc:jar source:jar install -pl :core -am -DskipTests', - deployerId: "maven-deployer", - resolverId: "maven-resolver" + rtGradleRun( + usesPlugin: true, + tool: 'Gradle 7', + rootDir: "", + useWrapper: true, + buildFile: 'build.gradle.kts', + tasks: 'build artifactoryPublish', + deployerId: "GRADLE_DEPLOYER", + resolverId: "GRADLE_RESOLVER" ) rtPublishBuildInfo( serverId: "opencollab-artifactory" From d1c7fe9f7e22991917c2361274ca42279d1d980e Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 16:51:34 -0600 Subject: [PATCH 024/125] Make gradlew executable --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 865ee5684c98e512faac379f7ba36a20d03b47cb Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 16:52:52 -0600 Subject: [PATCH 025/125] Add the wrapper too --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 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 literal 0 HcmV?d00001 From bf20f2821ddf89f4413528fd669fa23b3690233f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 17:17:34 -0600 Subject: [PATCH 026/125] Publish jar for ap & other fixes --- ap/pom.xml | 14 -------------- .../processor/BlockEntityProcessor.java | 2 +- .../{ => geyser}/processor/ClassProcessor.java | 2 +- .../processor/CollisionRemapperProcessor.java | 2 +- .../processor/ItemRemapperProcessor.java | 2 +- .../processor/PacketTranslatorProcessor.java | 2 +- .../processor/SoundHandlerProcessor.java | 2 +- .../services/javax.annotation.processing.Processor | 10 +++++----- .../main/kotlin/geyser.base-conventions.gradle.kts | 2 ++ build.gradle.kts | 2 ++ 10 files changed, 15 insertions(+), 25 deletions(-) delete mode 100644 ap/pom.xml rename ap/src/main/java/org/geysermc/{ => geyser}/processor/BlockEntityProcessor.java (97%) rename ap/src/main/java/org/geysermc/{ => geyser}/processor/ClassProcessor.java (99%) rename ap/src/main/java/org/geysermc/{ => geyser}/processor/CollisionRemapperProcessor.java (97%) rename ap/src/main/java/org/geysermc/{ => geyser}/processor/ItemRemapperProcessor.java (97%) rename ap/src/main/java/org/geysermc/{ => geyser}/processor/PacketTranslatorProcessor.java (97%) rename ap/src/main/java/org/geysermc/{ => geyser}/processor/SoundHandlerProcessor.java (97%) diff --git a/ap/pom.xml b/ap/pom.xml deleted file mode 100644 index dce28a7d7..000000000 --- a/ap/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.1-SNAPSHOT - - - ap - 2.0.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/geyser/processor/BlockEntityProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java index 7ab760cec..f9ba68302 100644 --- a/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java similarity index 99% rename from ap/src/main/java/org/geysermc/processor/ClassProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java index 0f730aaba..cd9f24ba0 100644 --- a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; diff --git a/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java index 971abd984..84e2e2ffd 100644 --- a/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java index 39d5f9fdf..2dd00506d 100644 --- a/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java index 97687e981..9b99d679b 100644 --- a/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java index 3e6a7c412..c35c0ee4e 100644 --- a/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 463d1efad..1f6475b61 100644 --- a/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1,5 +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 +org.geysermc.geyser.processor.BlockEntityProcessor +org.geysermc.geyser.processor.CollisionRemapperProcessor +org.geysermc.geyser.processor.ItemRemapperProcessor +org.geysermc.geyser.processor.PacketTranslatorProcessor +org.geysermc.geyser.processor.SoundHandlerProcessor \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 211455d31..2f57a97db 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -37,5 +37,7 @@ publishing { groupId = project.group as String artifactId = project.name version = project.version as String + + artifact(tasks["jar"]) } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 573687c7f..6037f65d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.accessors.dm.ApProjectDependency + plugins { `java-library` id("geyser.build-logic") From 83c2b72008c3b320496a6ef706357733b470241f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 17:20:29 -0600 Subject: [PATCH 027/125] Try using shadow conventions for now? --- build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts | 2 -- build.gradle.kts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 2f57a97db..211455d31 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -37,7 +37,5 @@ publishing { groupId = project.group as String artifactId = project.name version = project.version as String - - artifact(tasks["jar"]) } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6037f65d3..9207dfd3f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,7 +39,7 @@ subprojects { when (this) { in platforms -> plugins.apply("geyser.platform-conventions") api -> plugins.apply("geyser.shadow-conventions") - else -> plugins.apply("geyser.base-conventions") + else -> plugins.apply("geyser.shadow-conventions") } } } \ No newline at end of file From 12b3bcd6b87225955a2732062ab16b4d8e7cf704 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 17:26:50 -0600 Subject: [PATCH 028/125] Use api for the time being since that worked locally --- build.gradle.kts | 2 +- core/build.gradle.kts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9207dfd3f..6037f65d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,7 +39,7 @@ subprojects { when (this) { in platforms -> plugins.apply("geyser.platform-conventions") api -> plugins.apply("geyser.shadow-conventions") - else -> plugins.apply("geyser.shadow-conventions") + else -> plugins.apply("geyser.base-conventions") } } } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 0a1883bc4..1400c405f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -70,11 +70,11 @@ dependencies { testImplementation("junit", "junit", Versions.junitVersion) // Annotation Processors + api(projects.ap) + annotationProcessor(projects.ap) } -provided(projects.ap) - configure { val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" val gitVersion = "git-${branchName()}-${commitHashAbbrev()}" From 55ac39abc3e27d2d6d8cdb93860236bb5950549c Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 17:31:58 -0600 Subject: [PATCH 029/125] Exclude the right jars --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 14e0fd4e3..09bccf5d2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { } post { success { - archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/original-*.jar', fingerprint: true + archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/*-sources.jar', excludes: 'bootstrap/**/build/libs/*-unshaded.jar', fingerprint: true } } } From b20546c51563475e31727cb1a356c4409063eaf8 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 17:34:05 -0600 Subject: [PATCH 030/125] Fix syntax --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 09bccf5d2..24acee78a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { } post { success { - archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/*-sources.jar', excludes: 'bootstrap/**/build/libs/*-unshaded.jar', fingerprint: true + archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/*-sources.jar,bootstrap/**/build/libs/*-unshaded.jar', fingerprint: true } } } From 7428998d71417f4d5592df8662d4632605221263 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 22:43:06 -0600 Subject: [PATCH 031/125] Fix variable replacement by using Indra --- build-logic/src/main/kotlin/extensions.kt | 16 ---------------- core/build.gradle.kts | 10 +++++++--- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index a2c90cd1f..ae8d578df 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -24,25 +24,9 @@ */ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.kyori.indra.git.IndraGitExtension import org.gradle.api.Project import org.gradle.api.artifacts.ProjectDependency import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.the - -fun Project.lastCommitHash(): String? = - the().commit()?.name?.substring(0, 7) - -// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project -// some properties might be specific to Jenkins -fun Project.branchName(): String = - System.getProperty("GIT_BRANCH", "local/dev") -fun Project.commitHashAbbrev(): String = - System.getProperty("GIT_COMMIT", "0000000") -fun Project.versionName(): String = - System.getProperty("GIT_VERSION", "local/dev") -fun Project.buildNumber(): Int = - Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")) fun Project.relocate(pattern: String) { tasks.named("shadowJar") { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1400c405f..b163d3794 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,7 +1,9 @@ +import net.kyori.indra.git.IndraGitExtension import net.kyori.blossom.BlossomExtension plugins { id("net.kyori.blossom") + id("net.kyori.indra.git") } dependencies { @@ -76,11 +78,13 @@ dependencies { } configure { + val indra = the() + val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val gitVersion = "git-${branchName()}-${commitHashAbbrev()}" + val gitVersion = "git-${indra.branchName()}-${indra.commit()?.name?.substring(0, 7)}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) - replaceToken("\${buildNumber}", buildNumber(), mainFile) - replaceToken("\${branch}", branchName(), mainFile) + replaceToken("\${buildNumber}", Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")), mainFile) + replaceToken("\${branch}", indra.branchName(), mainFile) } \ No newline at end of file From 9bcd62937d566866d90f0199531ac9eb3fc5652f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 22:49:27 -0600 Subject: [PATCH 032/125] Handle null better --- core/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b163d3794..e14d69711 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -81,10 +81,10 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val gitVersion = "git-${indra.branchName()}-${indra.commit()?.name?.substring(0, 7)}" + val gitVersion = "git-${indra.branchName() ?: "DEV"}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) replaceToken("\${buildNumber}", Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")), mainFile) - replaceToken("\${branch}", indra.branchName(), mainFile) + replaceToken("\${branch}", indra.branchName() ?: "DEV", mainFile) } \ No newline at end of file From 28dca21892c7f718a6d020a47762961428a7dab1 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 22:54:56 -0600 Subject: [PATCH 033/125] Try this for branch name --- core/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e14d69711..e908c40db 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -81,10 +81,10 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val gitVersion = "git-${indra.branchName() ?: "DEV"}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" + val gitVersion = "git-${indra.branch()?.name ?: "DEV"}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) replaceToken("\${buildNumber}", Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")), mainFile) - replaceToken("\${branch}", indra.branchName() ?: "DEV", mainFile) + replaceToken("\${branch}", indra.branch()?.name ?: "DEV", mainFile) } \ No newline at end of file From e1e4b5059c67f63e85e67d8c7b52728afee4fe72 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 23:01:24 -0600 Subject: [PATCH 034/125] Try command line --- core/build.gradle.kts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e908c40db..91ad66cab 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,6 +1,8 @@ import net.kyori.indra.git.IndraGitExtension import net.kyori.blossom.BlossomExtension +import java.io.ByteArrayOutputStream + plugins { id("net.kyori.blossom") id("net.kyori.indra.git") @@ -81,10 +83,20 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val gitVersion = "git-${indra.branch()?.name ?: "DEV"}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" + val gitVersion = "git-${branchName()}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) replaceToken("\${buildNumber}", Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")), mainFile) - replaceToken("\${branch}", indra.branch()?.name ?: "DEV", mainFile) + replaceToken("\${branch}", branchName(), mainFile) +} + +fun Project.branchName(): String { + val out = ByteArrayOutputStream() + exec { + commandLine = listOf("git", "rev-parse", "--abbrev-ref", "HEAD") + standardOutput = out + } + + return out.toString(Charsets.UTF_8.name()).trim() } \ No newline at end of file From 2a667ed0963a599129e57c5db667c76c8f2432c2 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 27 Feb 2022 23:11:27 -0600 Subject: [PATCH 035/125] Use System.getenv instead of System.getProperty --- core/build.gradle.kts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 91ad66cab..ac21acd2b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -87,16 +87,12 @@ configure { replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) - replaceToken("\${buildNumber}", Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1")), mainFile) + replaceToken("\${buildNumber}", buildNumber(), mainFile) replaceToken("\${branch}", branchName(), mainFile) } -fun Project.branchName(): String { - val out = ByteArrayOutputStream() - exec { - commandLine = listOf("git", "rev-parse", "--abbrev-ref", "HEAD") - standardOutput = out - } +fun Project.branchName(): String = + System.getenv("GIT_BRANCH") ?: "local/dev" - return out.toString(Charsets.UTF_8.name()).trim() -} \ No newline at end of file +fun Project.buildNumber(): Int = + Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") \ No newline at end of file From f53c3b71c074b20f60462be7c2d7ea66ea8bd96a Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 20:46:30 -0500 Subject: [PATCH 036/125] Update dependencies --- bootstrap/spigot/build.gradle.kts | 2 +- build-logic/src/main/kotlin/Versions.kt | 34 ++++++++++++------------- core/build.gradle.kts | 6 ++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index affb243b3..767a20d71 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,6 +1,6 @@ val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet val viaVersion = "4.0.0" -val adaptersVersion = "1.3-SNAPSHOT" +val adaptersVersion = "1.4-SNAPSHOT" dependencies { api(projects.core) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index c6348bc65..b7b639031 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -24,21 +24,21 @@ */ object Versions { - const val jacksonVersion= "2.12.4" - const val fastutilVersion= "8.5.2" - const val nettyVersion= "4.1.66.Final" - const val guavaVersion= "29.0-jre" - const val nbtVersion= "2.1.0" - const val websocketVersion= "1.5.1" - const val protocolVersion= "0cd24c0" - const val raknetVersion= "1.6.28-SNAPSHOT" - const val mcauthlibVersion= "6c99331" - const val mcprotocollibversion= "6a23a780" - const val packetlibVersion= "2.1-SNAPSHOT" - const val adventureVersion= "4.9.3" - const val eventVersion= "3.0.0" - const val junitVersion= "4.13.1" - const val checkerQualVersion= "3.19.0" - const val cumulusVersion = "1.0-SNAPSHOT" - const val log4jVersion = "2.17.1" + const val jacksonVersion = "2.12.4" + const val fastutilVersion = "8.5.2" + const val nettyVersion = "4.1.66.Final" + const val guavaVersion = "29.0-jre" + const val nbtVersion = "2.1.0" + const val websocketVersion = "1.5.1" + const val protocolVersion = "0cd24c0" + const val raknetVersion = "1.6.28-SNAPSHOT" + const val mcauthlibVersion = "d9d773e" + const val mcprotocollibversion = "0771504" + const val packetlibVersion = "2.1-SNAPSHOT" + const val adventureVersion = "4.9.3" + const val eventVersion = "3.0.0" + const val junitVersion = "4.13.1" + const val checkerQualVersion = "3.19.0" + const val cumulusVersion = "1.0-SNAPSHOT" + const val log4jVersion = "2.17.1" } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index ac21acd2b..28490a3af 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -36,10 +36,10 @@ dependencies { exclude("com.nukkitx", "nbt") } - api("com.github.RednedEpic", "MCAuthLib", Versions.mcauthlibVersion) + api("com.github.GeyserMC", "MCAuthLib", Versions.mcauthlibVersion) api("com.github.GeyserMC", "MCProtocolLib", Versions.mcprotocollibversion) { - exclude("com.github.steveice10", "packetlib") - exclude("com.github.steveice10", "mcauthlib") + exclude("com.github.GeyserMC", "packetlib") + exclude("com.github.GeyserMC", "mcauthlib") } api("com.github.steveice10", "packetlib", Versions.packetlibVersion) { From 1232c02c8e2fb810efd49ee36694da4daaa5fde2 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 21:13:37 -0500 Subject: [PATCH 037/125] Do not compile ap into jar --- core/build.gradle.kts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 28490a3af..da1a6cd3e 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,8 +1,6 @@ import net.kyori.indra.git.IndraGitExtension import net.kyori.blossom.BlossomExtension -import java.io.ByteArrayOutputStream - plugins { id("net.kyori.blossom") id("net.kyori.indra.git") @@ -74,7 +72,7 @@ dependencies { testImplementation("junit", "junit", Versions.junitVersion) // Annotation Processors - api(projects.ap) + compileOnly(projects.ap) annotationProcessor(projects.ap) } From 9939a26a5bbbe6902fe4e7dd8779998946ebaefa Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 21:55:29 -0500 Subject: [PATCH 038/125] Add RemoteServer API --- .../org/geysermc/geyser/api/GeyserApi.java | 9 +++ .../geyser/api/network}/AuthType.java | 16 +---- .../geyser/api/network/RemoteServer.java | 67 +++++++++++++++++++ .../bungeecord/GeyserBungeePlugin.java | 6 +- .../spigot/GeyserSpigotPingPassthrough.java | 4 +- .../platform/spigot/GeyserSpigotPlugin.java | 10 +-- .../spigot/GeyserSpigotVersionChecker.java | 14 ++-- .../GeyserSpigotLegacyNativeWorldManager.java | 4 +- .../manager/GeyserSpigotWorldManager.java | 4 +- .../sponge/GeyserSpongePingPassthrough.java | 4 +- .../velocity/GeyserVelocityPlugin.java | 6 +- .../network/session/GeyserSession.java | 4 +- .../geysermc/geyser/FloodgateKeyLoader.java | 4 +- .../java/org/geysermc/geyser/GeyserImpl.java | 25 +++++-- .../command/defaults/VersionCommand.java | 10 ++- .../configuration/GeyserConfiguration.java | 2 +- .../GeyserJacksonConfiguration.java | 11 ++- .../org/geysermc/geyser/dump/DumpInfo.java | 12 ++-- .../network/ConnectorServerEventHandler.java | 8 +-- ...necraftProtocol.java => GameProtocol.java} | 13 +++- .../geyser/network/QueryPacketHandler.java | 2 +- .../geyser/network/RemoteServerImpl.java | 32 +++++++++ .../geyser/network/UpstreamPacketHandler.java | 10 +-- .../ping/GeyserLegacyPingPassthrough.java | 4 +- .../loader/EnchantmentRegistryLoader.java | 4 +- .../loader/PotionMixRegistryLoader.java | 4 +- .../geyser/registry/type/ItemMapping.java | 4 +- .../geyser/session/GeyserSession.java | 33 ++++----- .../org/geysermc/geyser/skin/SkinManager.java | 2 +- .../geysermc/geyser/text/MinecraftLocale.java | 4 +- .../inventory/item/BannerTranslator.java | 4 +- .../inventory/item/CompassTranslator.java | 4 +- .../inventory/item/PotionTranslator.java | 4 +- .../inventory/item/TippedArrowTranslator.java | 6 +- .../entity/CampfireBlockEntityTranslator.java | 4 +- ...SetLocalPlayerAsInitializedTranslator.java | 4 +- .../java/JavaCustomPayloadTranslator.java | 4 +- .../java/JavaGameProfileTranslator.java | 6 +- .../java/JavaLoginDisconnectTranslator.java | 4 +- .../protocol/java/JavaLoginTranslator.java | 4 +- 40 files changed, 249 insertions(+), 127 deletions(-) rename {core/src/main/java/org/geysermc/geyser/session/auth => api/geyser/src/main/java/org/geysermc/geyser/api/network}/AuthType.java (78%) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java rename core/src/main/java/org/geysermc/geyser/network/{MinecraftProtocol.java => GameProtocol.java} (93%) create mode 100644 core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index b5a0c7897..59a254ecc 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -33,6 +33,7 @@ import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; +import org.geysermc.geyser.api.network.RemoteServer; import java.util.List; import java.util.UUID; @@ -105,6 +106,14 @@ public interface GeyserApi extends GeyserApiBase { */ EventBus eventBus(); + /** + * Get's the default {@link RemoteServer} configured + * within the config file that is used by default. + * + * @return the default remote server used within Geyser + */ + RemoteServer getDefaultRemoteServer(); + /** * Gets the current {@link GeyserApiBase} instance. * diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java similarity index 78% rename from core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java index 1edbd0f29..f52a2e7ba 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java @@ -23,20 +23,15 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.session.auth; +package org.geysermc.geyser.api.network; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; import lombok.Getter; -import java.io.IOException; - @Getter public enum AuthType { OFFLINE, ONLINE, - FLOODGATE; + HYBRID; public static final AuthType[] VALUES = values(); @@ -60,11 +55,4 @@ public enum AuthType { } return ONLINE; } - - public static class Deserializer extends JsonDeserializer { - @Override - public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return getByName(p.getValueAsString()); - } - } } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java new file mode 100644 index 000000000..b13ae5930 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.network; + +/** + * Represents the Java server that Geyser is connecting to. + */ +public interface RemoteServer { + + /** + * Gets the IP address of the remote server. + * + * @return the IP address of the remote server + */ + String address(); + + /** + * Gets the port of the remote server. + * + * @return the port of the remote server + */ + int port(); + + /** + * Gets the protocol version of the remote server. + * + * @return the protocol version of the remote server + */ + int protocolVersion(); + + /** + * Gets the Minecraft version of the remote server. + * + * @return the Minecraft version of the remote server + */ + String minecraftVersion(); + + /** + * Gets the {@link AuthType} required by the remote server. + * + * @return the auth type required by the remote server + */ + AuthType authType(); +} diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index 0aa82d9cd..79e18ad82 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -32,7 +32,7 @@ import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -109,13 +109,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && getProxy().getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + geyserConfig.getRemote().setAuthType(AuthType.HYBRID); } geyserConfig.loadFloodgate(this); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java index 19153b750..ca1d4160f 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java @@ -31,7 +31,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.util.CachedServerIcon; import org.geysermc.geyser.ping.GeyserPingInfo; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import javax.annotation.Nonnull; @@ -52,7 +52,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough { Bukkit.getPluginManager().callEvent(event); GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()), - new GeyserPingInfo.Version(Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion()) // thanks Spigot for not exposing this, just default to latest + new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion()) // thanks Spigot for not exposing this, just default to latest ); Bukkit.getOnlinePlayers().stream().map(Player::getName).forEach(geyserPingInfo.getPlayerList()::add); return geyserPingInfo; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 7f1c4aa06..c7088cfa6 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -40,7 +40,7 @@ import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; @@ -49,7 +49,7 @@ import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.geyser.platform.spigot.world.manager.*; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; @@ -136,14 +136,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && Bukkit.getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + geyserConfig.getRemote().setAuthType(AuthType.HYBRID); } geyserConfig.loadFloodgate(this); @@ -344,7 +344,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { */ private boolean isViaVersionNeeded() { ProtocolVersion serverVersion = getServerProtocolVersion(); - List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(), + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(GameProtocol.getJavaProtocolVersion(), serverVersion.getVersion()); if (protocolList == null) { // No translation needed! diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java index 923209e59..0212ff9b0 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java @@ -29,7 +29,7 @@ import com.viaversion.viaversion.api.Via; import org.bukkit.Bukkit; import org.bukkit.UnsafeValues; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; import java.lang.reflect.InvocationTargetException; @@ -48,7 +48,7 @@ public final class GeyserSpigotVersionChecker { try { // This method is only present on later versions of Paper UnsafeValues.class.getMethod("getProtocolVersion"); - if (Bukkit.getUnsafe().getProtocolVersion() != MinecraftProtocol.getJavaProtocolVersion()) { + if (Bukkit.getUnsafe().getProtocolVersion() != GameProtocol.getJavaProtocolVersion()) { sendOutdatedMessage(logger); } return; @@ -82,7 +82,7 @@ public final class GeyserSpigotVersionChecker { } return; } - if (protocolVersion != MinecraftProtocol.getJavaProtocolVersion()) { + if (protocolVersion != GameProtocol.getJavaProtocolVersion()) { sendOutdatedMessage(logger); } return; @@ -94,13 +94,13 @@ public final class GeyserSpigotVersionChecker { private static void checkViaVersionSupportedVersions(GeyserLogger logger) { // Run after ViaVersion has obtained the server protocol version Via.getPlatform().runSync(() -> { - if (Via.getAPI().getSupportedVersions().contains(MinecraftProtocol.getJavaProtocolVersion())) { + if (Via.getAPI().getSupportedVersions().contains(GameProtocol.getJavaProtocolVersion())) { // Via supports this protocol version; we will be able to connect. return; } - if (Via.getAPI().getFullSupportedVersions().contains(MinecraftProtocol.getJavaProtocolVersion())) { + if (Via.getAPI().getFullSupportedVersions().contains(GameProtocol.getJavaProtocolVersion())) { // ViaVersion supports our protocol, but the user has blocked them from connecting. - logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.viaversion.blocked", MinecraftProtocol.getAllSupportedJavaVersions())); + logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.viaversion.blocked", GameProtocol.getAllSupportedJavaVersions())); return; } // Else, presumably, ViaVersion is not updated. @@ -114,7 +114,7 @@ public final class GeyserSpigotVersionChecker { } private static void sendOutdatedMessage(GeyserLogger logger) { - logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.no_supported_protocol", MinecraftProtocol.getAllSupportedJavaVersions(), VIAVERSION_DOWNLOAD_URL)); + logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.no_supported_protocol", GameProtocol.getAllSupportedJavaVersions(), VIAVERSION_DOWNLOAD_URL)); } private GeyserSpigotVersionChecker() { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java index 2e0491db8..816edb231 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java @@ -32,7 +32,7 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin; @@ -50,7 +50,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl IntList allBlockStates = adapter.getAllBlockStates(); oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size()); ProtocolVersion serverVersion = plugin.getServerProtocolVersion(); - List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(), + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(GameProtocol.getJavaProtocolVersion(), serverVersion.getVersion()); for (int oldBlockId : allBlockStates) { int newBlockId = oldBlockId; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java index a03549444..b9bbba9d3 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -38,7 +38,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; import org.geysermc.geyser.level.GeyserWorldManager; @@ -57,7 +57,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { /** * The current client protocol version for ViaVersion usage. */ - protected static final int CLIENT_PROTOCOL_VERSION = MinecraftProtocol.getJavaProtocolVersion(); + protected static final int CLIENT_PROTOCOL_VERSION = GameProtocol.getJavaProtocolVersion(); private final Plugin plugin; diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java index 7c01f18ce..63900d615 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.platform.sponge; import org.geysermc.geyser.ping.GeyserPingInfo; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.spongepowered.api.MinecraftVersion; import org.spongepowered.api.Sponge; @@ -73,7 +73,7 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough { ), new GeyserPingInfo.Version( event.getResponse().getVersion().getName(), - MinecraftProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge + GameProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge ); event.getResponse().getPlayers().get().getProfiles().stream() .map(GameProfile::getName) diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index a79fbb30b..f4f6985f3 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -45,7 +45,7 @@ import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandManager; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import org.jetbrains.annotations.Nullable; @@ -128,14 +128,14 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } catch (ClassNotFoundException ignored) { } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { + if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } else if (geyserConfig.isAutoconfiguredRemote() && proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + geyserConfig.getRemote().setAuthType(AuthType.HYBRID); } geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); diff --git a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index aa60e25ba..21aa35efc 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -56,11 +56,11 @@ public class GeyserSession { } public String getRemoteAddress() { - return this.handle.getRemoteAddress(); + return this.handle.remoteServer().address(); } public int getRemotePort() { - return this.handle.getRemotePort(); + return this.handle.remoteServer().port(); } public int getRenderDistance() { diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index 6d47c38c6..cb10b717d 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -26,7 +26,7 @@ package org.geysermc.geyser; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.text.GeyserLocale; import java.nio.file.Files; @@ -34,7 +34,7 @@ import java.nio.file.Path; public class FloodgateKeyLoader { public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { - if (config.getRemote().getAuthType() != AuthType.FLOODGATE) { + if (config.getRemote().getAuthType() != AuthType.HYBRID) { return geyserDataFolder.resolve(config.getFloodgateKeyFile()); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 7a908cc55..bdf68beef 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -55,6 +55,7 @@ import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; +import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; @@ -62,6 +63,8 @@ import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.ConnectorServerEventHandler; +import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.network.RemoteServerImpl; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; @@ -69,7 +72,7 @@ import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.SessionManager; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.text.GeyserLocale; @@ -83,7 +86,6 @@ import javax.naming.directory.InitialDirContext; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -142,6 +144,8 @@ public class GeyserImpl implements GeyserApi { private final EventBus eventBus; private final GeyserExtensionManager extensionManager; + private final RemoteServer remoteServer; + private Metrics metrics; private PendingMicrosoftAuthentication pendingMicrosoftAuthentication; @@ -200,6 +204,14 @@ public class GeyserImpl implements GeyserApi { } } + this.remoteServer = new RemoteServerImpl( + config.getRemote().getAddress(), + config.getRemote().getPort(), + GameProtocol.getJavaProtocolVersion(), + GameProtocol.getJavaMinecraftVersion(), + config.getRemote().getAuthType() + ); + double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " "; if (isGui) { @@ -212,7 +224,7 @@ public class GeyserImpl implements GeyserApi { if (platformType == PlatformType.STANDALONE) { logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); - } else if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + } else if (config.getRemote().getAuthType() == AuthType.HYBRID) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } } @@ -270,7 +282,7 @@ public class GeyserImpl implements GeyserApi { // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; - if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + if (config.getRemote().getAuthType() == AuthType.HYBRID) { try { Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); @@ -558,6 +570,11 @@ public class GeyserImpl implements GeyserApi { return this.eventBus; } + @Override + public RemoteServer getDefaultRemoteServer() { + return null; + } + public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 6dd2a4c41..eabd3800c 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -30,18 +30,16 @@ import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.WebUtils; import java.io.IOException; -import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.Properties; public class VersionCommand extends GeyserCommand { @@ -56,14 +54,14 @@ public class VersionCommand extends GeyserCommand { @Override public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { String bedrockVersions; - List supportedCodecs = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS; + List supportedCodecs = GameProtocol.SUPPORTED_BEDROCK_CODECS; if (supportedCodecs.size() > 1) { bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion(); } else { - bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion(); + bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion(); } String javaVersions; - List supportedJavaVersions = MinecraftProtocol.getJavaVersions(); + List supportedJavaVersions = GameProtocol.getJavaVersions(); if (supportedJavaVersions.size() > 1) { javaVersions = supportedJavaVersions.get(0) + " - " + supportedJavaVersions.get(supportedJavaVersions.size() - 1); } else { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index 7bb73a648..be9e3e374 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -27,7 +27,7 @@ package org.geysermc.geyser.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.network.CIDRMatcher; import org.geysermc.geyser.text.GeyserLocale; diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 463350441..f152efa53 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -35,7 +35,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.network.CIDRMatcher; import org.geysermc.geyser.text.GeyserLocale; @@ -208,7 +208,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private int port = 25565; @Setter - @JsonDeserialize(using = AuthType.Deserializer.class) + @JsonDeserialize(using = AuthTypeDeserializer.class) @JsonProperty("auth-type") private AuthType authType = AuthType.ONLINE; @@ -274,4 +274,11 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration } } } + + public static class AuthTypeDeserializer extends JsonDeserializer { + @Override + public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return AuthType.getByName(p.getValueAsString()); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 28e81d8e7..4c2f5d651 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -40,7 +40,7 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; @@ -216,11 +216,11 @@ public class DumpInfo { private final int javaProtocol; MCInfo() { - this.bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getMinecraftVersion).toList(); - this.bedrockProtocols = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getProtocolVersion).toList(); - this.defaultBedrockProtocol = MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); - this.javaVersions = MinecraftProtocol.getJavaVersions(); - this.javaProtocol = MinecraftProtocol.getJavaProtocolVersion(); + this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getMinecraftVersion).toList(); + this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getProtocolVersion).toList(); + this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); + this.javaVersions = GameProtocol.getJavaVersions(); + this.javaProtocol = GameProtocol.getJavaProtocolVersion(); } } diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index 892ddcb64..02f17aa48 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -50,7 +50,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { /* The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client */ - private static final int MINECRAFT_VERSION_BYTES_LENGTH = MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; + private static final int MINECRAFT_VERSION_BYTES_LENGTH = GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length; /** * The MOTD, sub-MOTD and Minecraft version ({@link #MINECRAFT_VERSION_BYTES_LENGTH}) combined cannot reach this length. @@ -104,8 +104,8 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setEdition("MCPE"); pong.setGameType("Survival"); // Can only be Survival or Creative as of 1.16.210.59 pong.setNintendoLimited(false); - pong.setProtocolVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); - pong.setVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. + pong.setProtocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); + pong.setVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. pong.setIpv4Port(config.getBedrock().getPort()); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { @@ -167,7 +167,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public void onSessionCreation(@Nonnull BedrockServerSession bedrockServerSession) { try { - bedrockServerSession.setPacketCodec(MinecraftProtocol.DEFAULT_BEDROCK_CODEC); + bedrockServerSession.setPacketCodec(GameProtocol.DEFAULT_BEDROCK_CODEC); bedrockServerSession.setLogging(true); bedrockServerSession.setCompressionLevel(geyser.getConfig().getBedrock().getCompressionLevel()); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(geyser, new GeyserSession(geyser, bedrockServerSession, eventLoopGroup.next()))); diff --git a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java similarity index 93% rename from core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java rename to core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 7ab381375..4164976b6 100644 --- a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -37,7 +37,7 @@ import java.util.*; /** * Contains information about the supported protocols in Geyser. */ -public final class MinecraftProtocol { +public final class GameProtocol { /** * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. @@ -103,6 +103,15 @@ public final class MinecraftProtocol { return DEFAULT_JAVA_CODEC.getProtocolVersion(); } + /** + * Gets the supported Minecraft: Java Edition version. + * + * @return the supported Minecraft: Java Edition version + */ + public static String getJavaMinecraftVersion() { + return DEFAULT_JAVA_CODEC.getMinecraftVersion(); + } + /** * @return a string showing all supported Bedrock versions for this Geyser instance */ @@ -127,6 +136,6 @@ public final class MinecraftProtocol { return joiner.toString(); } - private MinecraftProtocol() { + private GameProtocol() { } } diff --git a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java index 8b08098f2..f11851c1b 100644 --- a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java @@ -175,7 +175,7 @@ public class QueryPacketHandler { gameData.put("hostname", motd); gameData.put("gametype", "SMP"); gameData.put("game_id", "MINECRAFT"); - gameData.put("version", GeyserImpl.NAME + " (" + GeyserImpl.GIT_VERSION + ") " + MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); + gameData.put("version", GeyserImpl.NAME + " (" + GeyserImpl.GIT_VERSION + ") " + GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); gameData.put("plugins", ""); gameData.put("map", map); gameData.put("numplayers", currentPlayerCount); diff --git a/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java b/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java new file mode 100644 index 000000000..a0d919c3a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.network; + +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.api.network.RemoteServer; + +public record RemoteServerImpl(String address, int port, int protocolVersion, String minecraftVersion, AuthType authType) implements RemoteServer { +} diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index f3ffaeeff..8714dfb37 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.pack.ResourcePack; @@ -69,14 +69,14 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } - BedrockPacketCodec packetCodec = MinecraftProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); + BedrockPacketCodec packetCodec = GameProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); if (packetCodec == null) { - String supportedVersions = MinecraftProtocol.getAllSupportedBedrockVersions(); - if (loginPacket.getProtocolVersion() > MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); + if (loginPacket.getProtocolVersion() > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { // Too early to determine session locale session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); return true; - } else if (loginPacket.getProtocolVersion() < MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + } else if (loginPacket.getProtocolVersion() < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index d438110d0..c3a242501 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -29,7 +29,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.nukkitx.nbt.util.VarInts; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -83,7 +83,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); DataOutputStream handshake = new DataOutputStream(byteArrayStream); handshake.write(0x0); - VarInts.writeUnsignedInt(handshake, MinecraftProtocol.getJavaProtocolVersion()); + VarInts.writeUnsignedInt(handshake, GameProtocol.getJavaProtocolVersion()); VarInts.writeUnsignedInt(handshake, address.length()); handshake.writeBytes(address); handshake.writeShort(port); diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java index e566ff37c..8ad09bf88 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java @@ -30,7 +30,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.EnchantmentData; import org.geysermc.geyser.registry.type.ItemMapping; @@ -77,7 +77,7 @@ public class EnchantmentRegistryLoader implements RegistryLoader appliedItems; public CompassTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getItems() .values() .stream() diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index 54a6deadb..3e07eb1db 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -45,7 +45,7 @@ public class PotionTranslator extends ItemTranslator { private final List appliedItems; public PotionTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getItems() .values() .stream() diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index 35e8baa07..0b0571c66 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.inventory.item.TippedArrowPotion; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -44,12 +44,12 @@ public class TippedArrowTranslator extends ItemTranslator { private final List appliedItems; - private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:tipped_arrow") .getJavaId(); public TippedArrowTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getItems() .values() .stream() diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java index 53e1af8a5..6ec0effca 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -48,7 +48,7 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator { protected NbtMap getItem(CompoundTag tag) { // TODO: Version independent mappings - ItemMapping mapping = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue()); + ItemMapping mapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue()); NbtMapBuilder tagBuilder = NbtMap.builder() .putString("Name", mapping.getBedrockIdentifier()) .putByte("Count", (byte) tag.get("Count").getValue()) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java index 8641a35ff..8b86be69b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -41,7 +41,7 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat if (!session.getUpstream().isInitialized()) { session.getUpstream().setInitialized(true); - if (session.getRemoteAuthType() == AuthType.ONLINE) { + if (session.remoteServer().authType() == AuthType.ONLINE) { if (!session.isLoggedIn()) { if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.name())) { if (session.getGeyser().refreshTokenFor(session.name()) == null) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java index 33fb4f15c..f084c1d84 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java @@ -32,7 +32,7 @@ import com.nukkitx.protocol.bedrock.packet.TransferPacket; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -49,7 +49,7 @@ public class JavaCustomPayloadTranslator extends PacketTranslator Date: Sat, 19 Mar 2022 22:00:04 -0500 Subject: [PATCH 039/125] Remove unecessary getter --- .../main/java/org/geysermc/geyser/api/network/AuthType.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java index f52a2e7ba..4144f02c1 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java @@ -25,9 +25,6 @@ package org.geysermc.geyser.api.network; -import lombok.Getter; - -@Getter public enum AuthType { OFFLINE, ONLINE, From f8e966266540415578e1b15dbef0eb8f6a93b365 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 22:30:12 -0500 Subject: [PATCH 040/125] Add BedrockListener API and fix other stuffs --- .../org/geysermc/geyser/api/GeyserApi.java | 11 ++- .../geyser/api/network/BedrockListener.java | 74 +++++++++++++++++++ .../java/org/geysermc/geyser/GeyserImpl.java | 20 ++++- .../geyser/network/BedrockListenerImpl.java | 31 ++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java create mode 100644 core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 59a254ecc..e103d0525 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -33,6 +33,7 @@ import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; +import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; import java.util.List; @@ -112,7 +113,15 @@ public interface GeyserApi extends GeyserApiBase { * * @return the default remote server used within Geyser */ - RemoteServer getDefaultRemoteServer(); + RemoteServer defaultRemoteServer(); + + /** + * Gets the {@link BedrockListener} used for listening + * for Minecraft: Bedrock Edition client connections. + * + * @return the listener used for Bedrock client connectins + */ + BedrockListener bedrockListener(); /** * Gets the current {@link GeyserApiBase} instance. diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java new file mode 100644 index 000000000..648f83e47 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.network; + +/** + * The listener that handles connections from Minecraft: + * Bedrock Edition. + */ +public interface BedrockListener { + + /** + * Gets the address used for listening for Bedrock + * connections from. + * + * @return the listening address + */ + String address(); + + /** + * Gets the port used for listening for Bedrock + * connections from. + * + * @return the listening port + */ + int port(); + + /** + * Gets the primary MOTD shown to Bedrock players. + *

+ * This is the first line that will be displayed. + * + * @return the primary MOTD shown to Bedrock players. + */ + String primaryMotd(); + + /** + * Gets the secondary MOTD shown to Bedrock players. + *

+ * This is the second line that will be displayed. + * + * @return the secondary MOTD shown to Bedrock players. + */ + String secondaryMotd(); + + /** + * Gets the server name that is sent to Bedrock clients. + * + * @return the server sent to Bedrock clients + */ + String serverName(); +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index bdf68beef..a4787127f 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -55,6 +55,7 @@ import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; +import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -62,6 +63,7 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; +import org.geysermc.geyser.network.BedrockListenerImpl; import org.geysermc.geyser.network.ConnectorServerEventHandler; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.RemoteServerImpl; @@ -145,6 +147,7 @@ public class GeyserImpl implements GeyserApi { private final GeyserExtensionManager extensionManager; private final RemoteServer remoteServer; + private final BedrockListener bedrockListener; private Metrics metrics; @@ -212,6 +215,14 @@ public class GeyserImpl implements GeyserApi { config.getRemote().getAuthType() ); + this.bedrockListener = new BedrockListenerImpl( + config.getBedrock().getAddress(), + config.getBedrock().getPort(), + config.getBedrock().getMotd1(), + config.getBedrock().getMotd2(), + config.getBedrock().getServerName() + ); + double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " "; if (isGui) { @@ -571,8 +582,13 @@ public class GeyserImpl implements GeyserApi { } @Override - public RemoteServer getDefaultRemoteServer() { - return null; + public RemoteServer defaultRemoteServer() { + return this.remoteServer; + } + + @Override + public BedrockListener bedrockListener() { + return this.bedrockListener; } public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { diff --git a/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java b/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java new file mode 100644 index 000000000..e617be36a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.network; + +import org.geysermc.geyser.api.network.BedrockListener; + +public record BedrockListenerImpl(String address, int port, String primaryMotd, String secondaryMotd, String serverName) implements BedrockListener { +} From 95747d5649a73ce7b0bc35ffd10aa234015c15fd Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 22:35:41 -0500 Subject: [PATCH 041/125] Add maxPlayers API --- .../src/main/java/org/geysermc/geyser/api/GeyserApi.java | 9 +++++++++ core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index e103d0525..4366e9ed6 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -123,6 +123,15 @@ public interface GeyserApi extends GeyserApiBase { */ BedrockListener bedrockListener(); + /** + * Gets the maximum number of players that + * can join this Geyser instance. + * + * @return the maximum number of players that + * can join this Geyser instance + */ + int maxPlayers(); + /** * Gets the current {@link GeyserApiBase} instance. * diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index a4787127f..54be9f79d 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -591,6 +591,11 @@ public class GeyserImpl implements GeyserApi { return this.bedrockListener; } + @Override + public int maxPlayers() { + return this.getConfig().getMaxPlayers(); + } + public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); From 6f0ff0f83d6343f0b14dce6d006d683cc43c9685 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 23:03:26 -0500 Subject: [PATCH 042/125] Update pull request workflow to use Gradle --- .github/workflows/pullrequest.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 9621fa1d0..f5bb4c042 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -9,47 +9,43 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Set up JDK 16 uses: actions/setup-java@v1 with: + distribution: 'temurin' java-version: 16 + cache: 'gradle' - name: submodules-init uses: snickerbockers/submodules-init@v4 - - name: Build with Maven - run: mvn -B package -T 2C + - name: Build with Gradle + run: ./gradlew build - name: Archive artifacts (Geyser Standalone) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Standalone - path: bootstrap/standalone/target/Geyser.jar + path: bootstrap/standalone/build/libs/Geyser.jar - name: Archive artifacts (Geyser Spigot) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Spigot - path: bootstrap/spigot/target/Geyser-Spigot.jar + path: bootstrap/spigot/build/libs/Geyser-Spigot.jar - name: Archive artifacts (Geyser BungeeCord) uses: actions/upload-artifact@v2 if: success() with: name: Geyser BungeeCord - path: bootstrap/bungeecord/target/Geyser-BungeeCord.jar + path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar - name: Archive artifacts (Geyser Sponge) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Sponge - path: bootstrap/sponge/target/Geyser-Sponge.jar + path: bootstrap/sponge/build/libs/Geyser-Sponge.jar - name: Archive artifacts (Geyser Velocity) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Velocity - path: bootstrap/velocity/target/Geyser-Velocity.jar + path: bootstrap/velocity/build/libs/Geyser-Velocity.jar From 3e8863ccf31a6c8a4207a1e6df8896fd78530b21 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 23:20:10 -0500 Subject: [PATCH 043/125] Add .gradle to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 401002e1d..9fc9258d7 100644 --- a/.gitignore +++ b/.gitignore @@ -235,6 +235,9 @@ nbdist/ # End of https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all,visualstudiocode +### Gradle ### +.gradle + ### Geyser ### run/ config.yml From b26879f76c9f4d777630c206af7c1920e5a4ccdd Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 23:24:57 -0500 Subject: [PATCH 044/125] Add deprecated annotaiton so gradle stops complaining --- .../org/geysermc/connector/network/session/auth/AuthData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java b/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java index 6b8e53d8e..cca7aa48c 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java +++ b/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java @@ -33,6 +33,7 @@ import java.util.UUID; * * @deprecated legacy code */ +@Deprecated public class AuthData { private final org.geysermc.geyser.session.auth.AuthData handle; From 1b1621c424e069c8590a2a9c22c3fb7c63a52b06 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 23:29:24 -0500 Subject: [PATCH 045/125] Add ViaVersion repo --- settings.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 51c1b479d..0a7e08a4a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,11 @@ dependencyResolutionManagement { mavenLocal() mavenCentral() + // ViaVersion + maven("https://repo.viaversion.com") { + name = "viaversion" + } + maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } } From 43ab71bdaa9bc2281ac680de5fe6aaabdce41f9e Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 19 Mar 2022 23:46:56 -0500 Subject: [PATCH 046/125] Add Sponge repository --- settings.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0a7e08a4a..9f210114c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,6 +37,9 @@ dependencyResolutionManagement { name = "viaversion" } + // Sponge + maven("https://repo.spongepowered.org/repository/maven-public/") + maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } } From d613ac65a79781882ae196cbb1a06c7c38aced78 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 20 Mar 2022 00:05:25 -0500 Subject: [PATCH 047/125] Just catch NoSuchFileException for missing processors --- .../java/org/geysermc/geyser/processor/ClassProcessor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java index cd9f24ba0..e1da50f25 100644 --- a/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java @@ -39,6 +39,7 @@ 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; @@ -167,7 +168,11 @@ public class ClassProcessor extends AbstractProcessor { FileObject obj = this.processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", this.annotationClassName); if (obj != null) { this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + obj.toUri()); - return new BufferedReader(obj.openReader(false)); + try { + return new BufferedReader(obj.openReader(false)); + } catch (NoSuchFileException ignored) { + return null; + } } return null; } From d1cedbb823ceb626118682d51c5079217f7222a2 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 20 Apr 2022 12:36:10 -0400 Subject: [PATCH 048/125] Generate velocity-plugins.json and relocate fastutil (#2940) --- bootstrap/velocity/build.gradle.kts | 3 ++- build.gradle.kts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts index f9fcafb2c..ab2f85b85 100644 --- a/bootstrap/velocity/build.gradle.kts +++ b/bootstrap/velocity/build.gradle.kts @@ -1,11 +1,12 @@ val velocityVersion = "3.0.0" dependencies { + annotationProcessor("com.velocitypowered", "velocity-api", velocityVersion) api(projects.core) } platformRelocate("com.fasterxml.jackson") -platformRelocate("it.unimi.fastutil") +platformRelocate("it.unimi.dsi.fastutil") platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl") exclude("com.google.*:*") diff --git a/build.gradle.kts b/build.gradle.kts index 6037f65d3..b10d7c357 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,6 +10,10 @@ allprojects { group = "org.geysermc" version = "2.1.0-SNAPSHOT" description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers." + + tasks.withType { + options.encoding = "UTF-8" + } } val platforms = setOf( From 03b067e23e0d2690fd71fa30f1e8ae616836997e Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:37:50 -0400 Subject: [PATCH 049/125] Merge master into extensions (#2941) * Don't always store cert/client data used for skin uploaded This takes up a decent 30K of memory that we don't use after the skin is uploaded. The GameProfileTranslator cannot be run more than once per session. * Make all moon phases visible The fix to prevent integer overflows also prevented moon phases from being visible until now. Fixes #2927 * SetTimeTranslator: cast from long on the entire modulus This should fix some inaccuracies with time on older worlds. * Bump version; drop 1.17.40; support 1.18.30 * Actually bump to 2.0.3-SNAPSHOT * Fix message being sent still if a single escape character is sent * Replace instances of configs using `generateduuid` for Metrics * Fix some merge mistakes Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- README.md | 2 +- build-logic/src/main/kotlin/Versions.kt | 2 +- core/build.gradle.kts | 2 +- .../GeyserJacksonConfiguration.java | 2 +- .../entity/type/player/PlayerEntity.java | 2 + .../entity/type/player/SkullPlayerEntity.java | 2 + .../geyser/level/BedrockDimension.java | 41 + .../level/block/BlockPositionIterator.java | 2 - .../geysermc/geyser/network/GameProtocol.java | 13 +- .../geyser/network/UpstreamPacketHandler.java | 7 +- .../populator/BlockRegistryPopulator.java | 72 +- .../populator/ItemRegistryPopulator.java | 30 +- .../geyser/session/GeyserSession.java | 13 +- .../geyser/session/auth/AuthData.java | 13 +- .../session/auth/BedrockClientData.java | 6 + .../geyser/session/cache/ChunkCache.java | 5 +- .../bedrock/BedrockTextTranslator.java | 12 +- .../player/BedrockMovePlayerTranslator.java | 13 +- .../java/JavaGameProfileTranslator.java | 8 +- .../JavaLevelChunkWithLightTranslator.java | 39 +- .../java/level/JavaSetTimeTranslator.java | 5 +- .../org/geysermc/geyser/util/ChunkUtils.java | 29 +- .../geysermc/geyser/util/DimensionUtils.java | 2 +- .../geyser/util/LoginEncryptionUtils.java | 6 +- ...e.1_17_40.nbt => block_palette.1_18_0.nbt} | Bin .../bedrock/block_palette.1_18_30.nbt | Bin 0 -> 45536 bytes ...17_40.json => creative_items.1_18_30.json} | 2529 +++++++++-------- ....json => runtime_item_states.1_18_30.json} | 264 +- 28 files changed, 1648 insertions(+), 1473 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java rename core/src/main/resources/bedrock/{block_palette.1_17_40.nbt => block_palette.1_18_0.nbt} (100%) create mode 100644 core/src/main/resources/bedrock/block_palette.1_18_30.nbt rename core/src/main/resources/bedrock/{creative_items.1_17_40.json => creative_items.1_18_30.json} (84%) rename core/src/main/resources/bedrock/{runtime_item_states.1_17_40.json => runtime_item_states.1_18_30.json} (96%) diff --git a/README.md b/README.md index 23bde93d2..bbb9532a5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.17.41 + 1.18.0 - 1.18.10 and Minecraft Java 1.18.2. +### Currently supporting Minecraft Bedrock 1.18.0 - 1.18.30 and Minecraft Java 1.18.2. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 96d98b18b..ce8593adc 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -30,7 +30,7 @@ object Versions { const val guavaVersion = "29.0-jre" const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "0cd24c0" + const val protocolVersion = "29ecd7a" const val raknetVersion = "1.6.28-SNAPSHOT" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "0771504" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 31018c454..03260fc8f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) - api("com.github.CloudburstMC.Protocol", "bedrock-v486", Versions.protocolVersion) { + api("com.github.CloudburstMC.Protocol", "bedrock-v503", Versions.protocolVersion) { exclude("com.nukkitx.network", "raknet") exclude("com.nukkitx", "nbt") } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index f152efa53..630e2ffd6 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -36,8 +36,8 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.network.CIDRMatcher; +import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.text.GeyserLocale; import java.io.IOException; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 58f04a756..0d6c0dac1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -37,6 +37,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.AttributeData; +import com.nukkitx.protocol.bedrock.data.GameType; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -126,6 +127,7 @@ public class PlayerEntity extends LivingEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.setGameType(GameType.SURVIVAL); //TODO addPlayerPacket.getMetadata().putFlags(flags); dirtyMetadata.apply(addPlayerPacket.getMetadata()); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index ce1615816..f1a447b57 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.player; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.data.GameType; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -84,6 +85,7 @@ public class SkullPlayerEntity extends PlayerEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.setGameType(GameType.SURVIVAL); addPlayerPacket.getMetadata().putFlags(flags); dirtyMetadata.apply(addPlayerPacket.getMetadata()); diff --git a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java new file mode 100644 index 000000000..78c6b2c6a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.level; + +/** + * A data structure to represent what Bedrock believes are the height requirements for a specific dimension. + * As of 1.18.30, biome count is representative of the height of the world, and out-of-bounds chunks can crash + * the client. + * + * @param minY The minimum height Bedrock Edition will accept. + * @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. + * @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's. + */ +public record BedrockDimension(int minY, int height, boolean doUpperHeightWarn) { + public static BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true); + public static BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false); + public static BedrockDimension THE_END = new BedrockDimension(0, 256, true); +} diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java index 425c78f18..d22150ccf 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.level.block; import com.nukkitx.network.util.Preconditions; -import lombok.Getter; - public class BlockPositionIterator { private final int minX; diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 4164976b6..1f28a0264 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -28,11 +28,14 @@ package org.geysermc.geyser.network; import com.github.steveice10.mc.protocol.codec.MinecraftCodec; import com.github.steveice10.mc.protocol.codec.PacketCodec; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringJoiner; /** * Contains information about the supported protocols in Geyser. @@ -42,7 +45,7 @@ public final class GameProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v486.V486_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v503.V503_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -55,11 +58,11 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v471.V471_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v475.V475_CODEC.toBuilder().minecraftVersion("1.18.0/1.18.1/1.18.2").build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v486.V486_CODEC.toBuilder() .minecraftVersion("1.18.10/1.18.12") // 1.18.11 is also supported, but was only on Switch and since that auto-updates it's not needed .build()); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } /** diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 8714dfb37..66cda8f16 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -32,16 +32,17 @@ import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.pack.ResourcePackManifest; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.*; +import org.geysermc.geyser.util.LoginEncryptionUtils; +import org.geysermc.geyser.util.MathUtils; import java.io.FileInputStream; import java.io.InputStream; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index 8238bcea1..d8aa6a456 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -28,8 +28,9 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; +import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -60,35 +61,50 @@ public class BlockRegistryPopulator { private static final ImmutableMap, BiFunction> BLOCK_MAPPERS; private static final BiFunction EMPTY_MAPPER = (bedrockIdentifier, statesBuilder) -> null; + private static final BiFunction V486_MAPPER = (bedrockIdentifier, statesBuilder) -> { + statesBuilder.remove("no_drop_bit"); // Used in skulls + if (bedrockIdentifier.equals("minecraft:glow_lichen")) { + // Moved around north, south, west + int bits = (int) statesBuilder.get("multi_face_direction_bits"); + boolean north = (bits & (1 << 2)) != 0; + boolean south = (bits & (1 << 3)) != 0; + boolean west = (bits & (1 << 4)) != 0; + if (north) { + bits |= 1 << 4; + } else { + bits &= ~(1 << 4); + } + if (south) { + bits |= 1 << 2; + } else { + bits &= ~(1 << 2); + } + if (west) { + bits |= 1 << 3; + } else { + bits &= ~(1 << 3); + } + statesBuilder.put("multi_face_direction_bits", bits); + } + return null; + }; + static { ImmutableMap.Builder, BiFunction> stateMapperBuilder = ImmutableMap., BiFunction>builder() - .put(ObjectIntPair.of("1_17_40", Bedrock_v471.V471_CODEC.getProtocolVersion()), EMPTY_MAPPER) - .put(ObjectIntPair.of("1_18_10", Bedrock_v486.V486_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { - statesBuilder.remove("no_drop_bit"); // Used in skulls - if (bedrockIdentifier.equals("minecraft:glow_lichen")) { - // Moved around north, south, west - int bits = (int) statesBuilder.get("multi_face_direction_bits"); - boolean north = (bits & (1 << 2)) != 0; - boolean south = (bits & (1 << 3)) != 0; - boolean west = (bits & (1 << 4)) != 0; - if (north) { - bits |= 1 << 4; - } else { - bits &= ~(1 << 4); - } - if (south) { - bits |= 1 << 2; - } else { - bits &= ~(1 << 2); - } - if (west) { - bits |= 1 << 3; - } else { - bits &= ~(1 << 3); - } - statesBuilder.put("multi_face_direction_bits", bits); - } - return null; + .put(ObjectIntPair.of("1_18_0", Bedrock_v475.V475_CODEC.getProtocolVersion()), EMPTY_MAPPER) + .put(ObjectIntPair.of("1_18_10", Bedrock_v486.V486_CODEC.getProtocolVersion()), V486_MAPPER) + .put(ObjectIntPair.of("1_18_30", Bedrock_v503.V503_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { + // Apply these fixes too + V486_MAPPER.apply(bedrockIdentifier, statesBuilder); + return switch (bedrockIdentifier) { + case "minecraft:pistonArmCollision" -> "minecraft:piston_arm_collision"; + case "minecraft:stickyPistonArmCollision" -> "minecraft:sticky_piston_arm_collision"; + case "minecraft:movingBlock" -> "minecraft:moving_block"; + case "minecraft:tripWire" -> "minecraft:trip_wire"; + case "minecraft:seaLantern" -> "minecraft:sea_lantern"; + case "minecraft:concretePowder" -> "minecraft:concrete_powder"; + default -> null; + }; }); BLOCK_MAPPERS = stateMapperBuilder.build(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 0e12669e3..37b6c49f4 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -35,10 +35,12 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; -import it.unimi.dsi.fastutil.ints.*; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.*; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; @@ -58,19 +60,16 @@ import java.util.*; * Populates the item registries. */ public class ItemRegistryPopulator { - private static final Map PALETTE_VERSIONS; - - static { - PALETTE_VERSIONS = new Object2ObjectOpenHashMap<>(); - PALETTE_VERSIONS.put("1_17_40", new PaletteVersion(Bedrock_v471.V471_CODEC.getProtocolVersion(), Collections.emptyMap())); - PALETTE_VERSIONS.put("1_18_0", new PaletteVersion(Bedrock_v475.V475_CODEC.getProtocolVersion(), Collections.emptyMap())); - PALETTE_VERSIONS.put("1_18_10", new PaletteVersion(Bedrock_v486.V486_CODEC.getProtocolVersion(), Collections.emptyMap())); - } private record PaletteVersion(int protocolVersion, Map additionalTranslatedItems) { } public static void populate() { + Map paletteVersions = new Object2ObjectOpenHashMap<>(); + paletteVersions.put("1_18_0", new PaletteVersion(Bedrock_v475.V475_CODEC.getProtocolVersion(), Collections.emptyMap())); + paletteVersions.put("1_18_10", new PaletteVersion(Bedrock_v486.V486_CODEC.getProtocolVersion(), Collections.emptyMap())); + paletteVersions.put("1_18_30", new PaletteVersion(Bedrock_v503.V503_CODEC.getProtocolVersion(), Collections.emptyMap())); + GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); TypeReference> mappingItemsType = new TypeReference<>() { }; @@ -88,7 +87,7 @@ public class ItemRegistryPopulator { Int2IntMap dyeColors = new FixedInt2IntMap(); /* Load item palette */ - for (Map.Entry palette : PALETTE_VERSIONS.entrySet()) { + for (Map.Entry palette : paletteVersions.entrySet()) { TypeReference> paletteEntriesType = new TypeReference<>() {}; // Used to get the Bedrock namespaced ID (in instances where there are small differences) @@ -232,12 +231,15 @@ public class ItemRegistryPopulator { } String bedrockIdentifier; - if (javaIdentifier.equals("minecraft:music_disc_otherside") && palette.getValue().protocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) { - bedrockIdentifier = "minecraft:music_disc_pigstep"; - } else if (javaIdentifier.equals("minecraft:globe_banner_pattern") && palette.getValue().protocolVersion() < Bedrock_v486.V486_CODEC.getProtocolVersion()) { + if (javaIdentifier.equals("minecraft:globe_banner_pattern") && palette.getValue().protocolVersion() < Bedrock_v486.V486_CODEC.getProtocolVersion()) { bedrockIdentifier = "minecraft:banner_pattern"; } else { bedrockIdentifier = mappingItem.getBedrockIdentifier(); + if (palette.getValue().protocolVersion() >= Bedrock_v503.V503_CODEC.getProtocolVersion()) { + if (bedrockIdentifier.equals("minecraft:sealantern")) { + bedrockIdentifier = "minecraft:sea_lantern"; + } + } } if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index b72fe29a2..6d8fa477b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.session; +import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException; import com.github.steveice10.mc.auth.exception.request.RequestException; @@ -66,7 +67,6 @@ import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import io.netty.channel.Channel; import io.netty.channel.EventLoop; import it.unimi.dsi.fastutil.ints.*; @@ -144,6 +144,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private AuthData authData; @Setter private BedrockClientData clientData; + /** + * Used for Floodgate skin uploading + */ + @Setter + private JsonNode certChainData; @Accessors(fluent = true) @Setter @@ -1377,7 +1382,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0)); startGamePacket.setRotation(Vector2f.from(1, 1)); - startGamePacket.setSeed(-1); + startGamePacket.setSeed(-1L); startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension)); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); @@ -1426,10 +1431,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { settings.setServerAuthoritativeBlockBreaking(false); startGamePacket.setPlayerMovementSettings(settings); - if (upstream.getProtocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) { - startGamePacket.getExperiments().add(new ExperimentData("caves_and_cliffs", true)); - } - upstream.sendPacket(startGamePacket); } diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java b/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java index 802ee3ca0..99b7ae3af 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java +++ b/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java @@ -25,18 +25,7 @@ package org.geysermc.geyser.session.auth; -import com.fasterxml.jackson.databind.JsonNode; -import org.geysermc.geyser.GeyserImpl; - import java.util.UUID; -public record AuthData(String name, UUID uuid, String xuid, - JsonNode certChainData, String clientData) { - - public void upload(GeyserImpl geyser) { - // we can't upload the skin in LoginEncryptionUtil since the global server would return - // the skin too fast, that's why we upload it after we know for sure that the target server - // is ready to handle the result of the global server - geyser.getSkinUploader().uploadSkin(certChainData, clientData); - } +public record AuthData(String name, UUID uuid, String xuid) { } diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java index b3601f6c3..07dd38491 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java +++ b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java @@ -25,9 +25,11 @@ package org.geysermc.geyser.session.auth; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; +import lombok.Setter; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.UiProfile; @@ -107,6 +109,10 @@ public final class BedrockClientData { @JsonProperty(value = "PlayFabId") private String playFabId; + @JsonIgnore + @Setter + private String originalString = null; + public DeviceOs getDeviceOs() { return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN; } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java index feb1cf3a8..91d6b33d6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java @@ -33,6 +33,7 @@ import lombok.Setter; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.chunk.GeyserChunk; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.util.MathUtils; public class ChunkCache { @@ -45,11 +46,11 @@ public class ChunkCache { private int heightY; /** - * Whether the Bedrock client believes they are in a world with a minimum of -64 and maximum of 320 + * Which dimension Bedrock understands themselves to be in. */ @Getter @Setter - private boolean isExtendedHeight = false; + private BedrockDimension bedrockDimension = BedrockDimension.OVERWORLD; public ChunkCache(GeyserSession session) { this.cache = !session.getGeyser().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java index 1a6771cc5..91ed5aa2b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java @@ -40,11 +40,8 @@ public class BedrockTextTranslator extends PacketTranslator { public void translate(GeyserSession session, TextPacket packet) { String message = packet.getMessage(); - if (message.isBlank()) { - // Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either! - return; - } - + // The order here is important - strip out illegal characters first, then check if it's blank + // (in case the message is blank after removing) if (message.indexOf(ChatColor.ESCAPE) != -1) { // Filter out all escape characters - Java doesn't let you type these StringBuilder builder = new StringBuilder(); @@ -57,6 +54,11 @@ public class BedrockTextTranslator extends PacketTranslator { message = builder.toString(); } + if (message.isBlank()) { + // Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either! + return; + } + if (MessageTranslator.isTooLong(message, session)) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 2fccbe482..a63c0f334 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -37,14 +37,12 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @Translator(packet = MovePlayerPacket.class) public class BedrockMovePlayerTranslator extends PacketTranslator { - /* The upper and lower bounds to check for the void floor that only exists in Bedrock. These are the constants for the overworld. */ - private static final int BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y = -104; - private static final int BEDROCK_OVERWORLD_VOID_FLOOR_LOWER_Y = BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y + 2; @Override public void translate(GeyserSession session, MovePlayerPacket packet) { @@ -124,11 +122,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator= (extendedWorld ? BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y : -40)) { + // The void floor is offset about 40 blocks below the bottom of the world + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); + int voidFloorLocation = bedrockDimension.minY() - 40; + if (floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation) { // Work around there being a floor at the bottom of the world and teleport the player below it // Moving from below to above the void floor works fine entity.setPosition(entity.getPosition().sub(0, 4f, 0)); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java index 3978db0df..6dd826bf7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java @@ -57,7 +57,13 @@ public class JavaGameProfileTranslator extends PacketTranslator> 4) - 1; + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); + int maxBedrockSectionY = (bedrockDimension.height() >> 4) - 1; int sectionCount; byte[] payload; ByteBuf byteBuf = null; - GeyserChunkSection[] sections = new GeyserChunkSection[javaChunks.length - (yOffset + ((overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4))]; + GeyserChunkSection[] sections = new GeyserChunkSection[javaChunks.length - (yOffset + (bedrockDimension.minY() >> 4))]; try { NetInput in = new StreamNetInput(new ByteArrayInputStream(packet.getChunkData())); @@ -113,7 +116,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator> 4)); + int bedrockSectionY = sectionY + (yOffset - (bedrockDimension.minY() >> 4)); if (bedrockSectionY < 0 || maxBedrockSectionY < bedrockSectionY) { // Ignore this chunk section since it goes outside the bounds accepted by the Bedrock client continue; @@ -309,11 +312,11 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator= Bedrock_v475.V475_CODEC.getProtocolVersion(); - int biomeCount = isNewVersion ? 25 : 32; - int dimensionOffset = (overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4; + // As of 1.18.0, Bedrock hardcodes to always read 25 biome sections + // As of 1.18.30, the hardcode may now be tied to the dimension definition + boolean isNewVersion = session.getUpstream().getProtocolVersion() >= Bedrock_v503.V503_CODEC.getProtocolVersion(); + int biomeCount = isNewVersion ? bedrockDimension.height() >> 4 : 25; + int dimensionOffset = bedrockDimension.minY() >> 4; for (int i = 0; i < biomeCount; i++) { int biomeYOffset = dimensionOffset + i; if (biomeYOffset < yOffset) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java index 581433a5f..bc4e8c1ff 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java @@ -42,7 +42,10 @@ public class JavaSetTimeTranslator extends PacketTranslator= 0) { // Client thinks there is no daylight cycle but there is diff --git a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java index 7fdf12ec9..445ffb882 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java @@ -46,23 +46,13 @@ import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity; import static org.geysermc.geyser.level.block.BlockStateValues.JAVA_AIR_ID; @UtilityClass public class ChunkUtils { - /** - * The minimum height Bedrock Edition will accept. - */ - public static final int MINIMUM_ACCEPTED_HEIGHT = 0; - public static final int MINIMUM_ACCEPTED_HEIGHT_OVERWORLD = -64; - /** - * The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. - */ - public static final int MAXIMUM_ACCEPTED_HEIGHT = 256; - public static final int MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD = 384; - /** * An empty subchunk. */ @@ -249,17 +239,20 @@ public class ChunkUtils { throw new RuntimeException("Maximum Y must be a multiple of 16!"); } - int dimension = DimensionUtils.javaToBedrock(session.getDimension()); - boolean extendedHeight = dimension == 0; - session.getChunkCache().setExtendedHeight(extendedHeight); + BedrockDimension bedrockDimension = switch (session.getDimension()) { + case DimensionUtils.THE_END -> BedrockDimension.THE_END; + case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; + default -> BedrockDimension.OVERWORLD; + }; + session.getChunkCache().setBedrockDimension(bedrockDimension); // Yell in the console if the world height is too height in the current scenario // The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled - if (minY < (extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) - || maxY > (extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT)) { + // (Ignore this for the Nether. We can't change that at the moment without the workaround. :/ ) + if (minY < bedrockDimension.minY() || (bedrockDimension.doUpperHeightWarn() && maxY > bedrockDimension.height())) { session.getGeyser().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds", - extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT, - extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT, + String.valueOf(bedrockDimension.minY()), + String.valueOf(bedrockDimension.height()), session.getDimension())); } diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index 5af5e2c2b..f1aca63e8 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -109,7 +109,7 @@ public class DimensionUtils { // we check if the player is entering the nether and apply the nether fog to fake the fact that the client // thinks they are in the end dimension. if (BEDROCK_NETHER_ID == 2) { - if (bedrockDimension == BEDROCK_NETHER_ID) { + if (NETHER.equals(javaDimension)) { session.sendFog("minecraft:fog_hell"); } else if (previousDimension == BEDROCK_NETHER_ID) { session.removeFog("minecraft:fog_hell"); diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index 3488f713c..d8d3e3c49 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -155,10 +155,11 @@ public class LoginEncryptionUtils { session.setAuthenticationData(new AuthData( extraData.get("displayName").asText(), UUID.fromString(extraData.get("identity").asText()), - extraData.get("XUID").asText(), - certChainData, clientData + extraData.get("XUID").asText() )); + session.setCertChainData(certChainData); + if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { throw new RuntimeException("Identity Public Key was not found!"); } @@ -169,6 +170,7 @@ public class LoginEncryptionUtils { JsonNode clientDataJson = JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()); BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class); + data.setOriginalString(clientData); session.setClientData(data); if (EncryptionUtils.canUseEncryption()) { diff --git a/core/src/main/resources/bedrock/block_palette.1_17_40.nbt b/core/src/main/resources/bedrock/block_palette.1_18_0.nbt similarity index 100% rename from core/src/main/resources/bedrock/block_palette.1_17_40.nbt rename to core/src/main/resources/bedrock/block_palette.1_18_0.nbt diff --git a/core/src/main/resources/bedrock/block_palette.1_18_30.nbt b/core/src/main/resources/bedrock/block_palette.1_18_30.nbt new file mode 100644 index 0000000000000000000000000000000000000000..bf7690970f792f71b62d7b7f0c9a2f80a3292a21 GIT binary patch literal 45536 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 Date: Sun, 24 Apr 2022 11:42:17 -0500 Subject: [PATCH 050/125] Rename HYBRID AuthType back to FLOODGATE --- .../java/org/geysermc/geyser/api/network/AuthType.java | 2 +- .../geyser/platform/bungeecord/GeyserBungeePlugin.java | 4 ++-- .../geyser/platform/spigot/GeyserSpigotPlugin.java | 4 ++-- .../geyser/platform/velocity/GeyserVelocityPlugin.java | 4 ++-- .../main/java/org/geysermc/geyser/FloodgateKeyLoader.java | 2 +- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 4 ++-- .../java/org/geysermc/geyser/session/GeyserSession.java | 8 ++++---- .../protocol/java/JavaCustomPayloadTranslator.java | 2 +- .../protocol/java/JavaGameProfileTranslator.java | 2 +- .../translator/protocol/java/JavaLoginTranslator.java | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java index 4144f02c1..5e1c2539d 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.api.network; public enum AuthType { OFFLINE, ONLINE, - HYBRID; + FLOODGATE; public static final AuthType[] VALUES = values(); diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index 79e18ad82..7120a94f9 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -109,13 +109,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && getProxy().getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.HYBRID); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); } geyserConfig.loadFloodgate(this); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 100d5e1f1..c5a8a4a25 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -139,14 +139,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && Bukkit.getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.HYBRID); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); } geyserConfig.loadFloodgate(this); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index f4f6985f3..184d59b3e 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -128,14 +128,14 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } catch (ClassNotFoundException ignored) { } - if (geyserConfig.getRemote().getAuthType() == AuthType.HYBRID && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { + if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } else if (geyserConfig.isAutoconfiguredRemote() && proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.HYBRID); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); } geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index cb10b717d..fc21087ae 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -34,7 +34,7 @@ import java.nio.file.Path; public class FloodgateKeyLoader { public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { - if (config.getRemote().getAuthType() != AuthType.HYBRID) { + if (config.getRemote().getAuthType() != AuthType.FLOODGATE) { return geyserDataFolder.resolve(config.getFloodgateKeyFile()); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 54be9f79d..38d567bde 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -235,7 +235,7 @@ public class GeyserImpl implements GeyserApi { if (platformType == PlatformType.STANDALONE) { logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); - } else if (config.getRemote().getAuthType() == AuthType.HYBRID) { + } else if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } } @@ -293,7 +293,7 @@ public class GeyserImpl implements GeyserApi { // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; - if (config.getRemote().getAuthType() == AuthType.HYBRID) { + if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { try { Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 3897fd61b..a4508f0b5 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -667,7 +667,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // However, this doesn't affect the final username as Floodgate is still in charge of that. // So if you have (for example) replace spaces enabled on Floodgate the spaces will re-appear. String validUsername = username; - if (this.remoteServer.authType() == AuthType.HYBRID) { + if (this.remoteServer.authType() == AuthType.FLOODGATE) { validUsername = username.replace(' ', '_'); } @@ -824,7 +824,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * After getting whatever credentials needed, we attempt to join the Java server. */ private void connectDownstream() { - boolean floodgate = this.remoteServer.authType() == AuthType.HYBRID; + boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); @@ -920,7 +920,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { UUID uuid = protocol.getProfile().getId(); if (uuid == null) { // Set what our UUID *probably* is going to be - if (remoteServer.authType() == AuthType.HYBRID) { + if (remoteServer.authType() == AuthType.FLOODGATE) { uuid = new UUID(0, Long.parseLong(authData.xuid())); } else { uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + protocol.getProfile().getName()).getBytes(StandardCharsets.UTF_8)); @@ -950,7 +950,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { String disconnectMessage; Throwable cause = event.getCause(); if (cause instanceof UnexpectedEncryptionException) { - if (remoteServer.authType() != AuthType.HYBRID) { + if (remoteServer.authType() != AuthType.FLOODGATE) { // Server expects online mode disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java index f084c1d84..094cc3979 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java @@ -49,7 +49,7 @@ public class JavaCustomPayloadTranslator extends PacketTranslator Date: Sun, 24 Apr 2022 18:02:02 +0100 Subject: [PATCH 051/125] Re-arrange groups and artefacts when publishing (#2899) * Re-arrange groups and artefacts when publishing * Publish core jar --- api/geyser/build.gradle.kts | 7 +++++++ build.gradle.kts | 1 - core/build.gradle.kts | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts index f9f8e66a8..cfd87cbee 100644 --- a/api/geyser/build.gradle.kts +++ b/api/geyser/build.gradle.kts @@ -1,3 +1,10 @@ dependencies { api(projects.api) +} + +publishing { + publications.named("mavenJava") { + groupId = rootProject.group as String + ".geyser" + artifactId = "api" + } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b10d7c357..bf6c1df4d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,6 @@ subprojects { val relativePath = projectDir.relativeTo(rootProject.projectDir).path if (relativePath.contains("api")) { - group = rootProject.group as String + ".api" plugins.apply("geyser.api-conventions") } else { group = rootProject.group as String + ".geyser" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 03260fc8f..c22df20ba 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -77,6 +77,13 @@ dependencies { annotationProcessor(projects.ap) } +publishing { + publications.named("mavenJava") { + artifact(tasks["jar"]) + artifact(tasks["sourcesJar"]) + } +} + configure { val indra = the() From 9af6f948fe3b4cb6f2b0515d777161070718efa9 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 12:07:50 -0500 Subject: [PATCH 052/125] Publish extensions branch --- Jenkinsfile | 1 + .../geysermc/geyser/extension/GeyserExtensionLoader.java | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 24acee78a..bcb8ffa24 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,6 +23,7 @@ pipeline { stage ('Deploy') { when { branch "master" + branch "feature/extensions" } steps { diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 7092d1fc2..4c3c4bf11 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -61,7 +61,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { private final Map classLoaders = new HashMap<>(); private final Map extensionContainers = new HashMap<>(); - public GeyserExtensionContainer loadExtension(Path path) throws InvalidExtensionException, InvalidDescriptionException { + public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException, InvalidDescriptionException { if (path == null) { throw new InvalidExtensionException("Path is null"); } @@ -70,7 +70,6 @@ public class GeyserExtensionLoader extends ExtensionLoader { throw new InvalidExtensionException(new NoSuchFileException(path.toString()) + " does not exist"); } - GeyserExtensionDescription description = this.extensionDescription(path); Path parentFile = path.getParent(); Path dataFolder = parentFile.resolve(description.name()); if (Files.exists(dataFolder) && !Files.isDirectory(dataFolder)) { @@ -136,6 +135,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { return; } + // noinspection ConstantConditions if (!GeyserImpl.VERSION.contains(".")) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); return; @@ -165,7 +165,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { } try { - ExtensionDescription description = this.extensionDescription(path); + GeyserExtensionDescription description = this.extensionDescription(path); if (description == null) { return; } @@ -201,7 +201,7 @@ public class GeyserExtensionLoader extends ExtensionLoader { } extensions.put(name, path); - loadedExtensions.put(name, this.loadExtension(path)); + loadedExtensions.put(name, this.loadExtension(path, description)); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e); } From 4dbfd5f66e50495979f60bd7069ea872486dc17e Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 12:11:50 -0500 Subject: [PATCH 053/125] Fix conditional for deploying --- Jenkinsfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index bcb8ffa24..df09fe47e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,8 +22,10 @@ pipeline { stage ('Deploy') { when { - branch "master" - branch "feature/extensions" + anyOf { + branch "master" + branch "feature/extensions" + } } steps { From df808b72c5c7a11873998a8092ffa94c4bbcc9a7 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 12:44:46 -0500 Subject: [PATCH 054/125] Fix gradle deployer id --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index df09fe47e..c49498f77 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -30,7 +30,7 @@ pipeline { steps { rtGradleDeployer( - id: "maven-deployer", + id: "GRADLE_DEPLOYER", serverId: "opencollab-artifactory", releaseRepo: "maven-releases", snapshotRepo: "maven-snapshots" From 38891100819bf248c3f493ed42e058fa6eb434d7 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:03:21 -0500 Subject: [PATCH 055/125] Try this? --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index c49498f77..3e485bc10 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -40,10 +40,9 @@ pipeline { serverId: "opencollab-artifactory" ) rtGradleRun( - usesPlugin: true, + usesPlugin: false, tool: 'Gradle 7', rootDir: "", - useWrapper: true, buildFile: 'build.gradle.kts', tasks: 'build artifactoryPublish', deployerId: "GRADLE_DEPLOYER", From 463fc83f785eb7dfe5ba70c54fd75fa037363439 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:16:39 -0500 Subject: [PATCH 056/125] Publishing changes --- Jenkinsfile | 10 ++++-- .../kotlin/geyser.api-conventions.gradle.kts | 2 +- .../geyser.platform-conventions.gradle.kts | 2 +- .../geyser.publish-conventions.gradle.kts | 31 +++++++++++++++++++ .../geyser.shadow-conventions.gradle.kts | 23 -------------- build.gradle.kts | 2 +- core/build.gradle.kts | 1 + 7 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts diff --git a/Jenkinsfile b/Jenkinsfile index 3e485bc10..f92778318 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,7 +11,12 @@ pipeline { stage ('Build') { steps { sh 'git submodule update --init --recursive' - sh './gradlew clean build' + rtGradleRun( + usesPlugin: true, + tool: 'Gradle 7', + buildFile: 'build.gradle.kts', + tasks: 'clean build', + ) } post { success { @@ -40,9 +45,10 @@ pipeline { serverId: "opencollab-artifactory" ) rtGradleRun( - usesPlugin: false, + usesPlugin: true, tool: 'Gradle 7', rootDir: "", + useWrapper: true, buildFile: 'build.gradle.kts', tasks: 'build artifactoryPublish', deployerId: "GRADLE_DEPLOYER", diff --git a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts index 0781436c4..31117f2da 100644 --- a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("geyser.shadow-conventions") + id("geyser.api-conventions") } tasks { diff --git a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts index 07968f231..81d224906 100644 --- a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts @@ -1,4 +1,4 @@ plugins { application - id("geyser.shadow-conventions") + id("geyser.publish-conventions") } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts new file mode 100644 index 000000000..6336e7887 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("geyser.shadow-conventions") + id("com.jfrog.artifactory") + id("maven-publish") +} + +publishing { + publications.create("mavenJava") { + groupId = project.group as String + artifactId = "Geyser-" + project.name + version = project.version as String + + artifact(tasks["shadowJar"]) + artifact(tasks["sourcesJar"]) + } +} + +artifactory { + publish { + repository { + setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases") + setMavenCompatible(true) + } + defaults { + publishConfigs("archives") + setPublishArtifacts(true) + setPublishPom(true) + setPublishIvy(false) + } + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts index ddd427897..395beb104 100644 --- a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts @@ -3,7 +3,6 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id("geyser.base-conventions") id("com.github.johnrengelman.shadow") - id("com.jfrog.artifactory") } tasks { @@ -30,26 +29,4 @@ tasks { named("build") { dependsOn(shadowJar) } -} - -publishing { - publications.named("mavenJava") { - artifact(tasks["shadowJar"]) - artifact(tasks["sourcesJar"]) - } -} - -artifactory { - publish { - repository { - setRepoKey("maven-snapshots") - setMavenCompatible(true) - } - defaults { - publishConfigs("archives") - setPublishArtifacts(true) - setPublishPom(true) - setPublishIvy(false) - } - } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index bf6c1df4d..aa35c9345 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,7 @@ subprojects { group = rootProject.group as String + ".geyser" when (this) { in platforms -> plugins.apply("geyser.platform-conventions") - api -> plugins.apply("geyser.shadow-conventions") + api -> plugins.apply("geyser.publish-conventions") else -> plugins.apply("geyser.base-conventions") } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index c22df20ba..5ab2c3932 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -4,6 +4,7 @@ import net.kyori.blossom.BlossomExtension plugins { id("net.kyori.blossom") id("net.kyori.indra.git") + id("geyser.shadow-conventions") } dependencies { From cee945a66e5183463b367008c0adceda3efb01fb Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:19:44 -0500 Subject: [PATCH 057/125] Oops --- build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts index 31117f2da..7c8f9a3d7 100644 --- a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("geyser.api-conventions") + id("geyser.publish-conventions") } tasks { From 78f36500ca5737a0d0a64d3c9e811680b09a3fbc Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:22:58 -0500 Subject: [PATCH 058/125] Add missing isSnapshot --- build-logic/src/main/kotlin/extensions.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index ae8d578df..1f9793ee4 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -28,6 +28,9 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ProjectDependency import org.gradle.kotlin.dsl.named +fun Project.isSnapshot(): Boolean = + version.toString().endsWith("-SNAPSHOT") + fun Project.relocate(pattern: String) { tasks.named("shadowJar") { relocate(pattern, "org.geysermc.geyser.shaded.$pattern") From 3091d27e0e5f47518c57fc75e4f615e3dc61476f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:35:49 -0500 Subject: [PATCH 059/125] i think i fixed it? --- api/geyser/build.gradle.kts | 4 ++++ .../src/main/kotlin/geyser.base-conventions.gradle.kts | 8 -------- build.gradle.kts | 2 -- core/build.gradle.kts | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts index cfd87cbee..dcde85337 100644 --- a/api/geyser/build.gradle.kts +++ b/api/geyser/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id("geyser.api-conventions") +} + dependencies { api(projects.api) } diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 211455d31..2ea5d88a4 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -30,12 +30,4 @@ java { targetCompatibility = JavaVersion.VERSION_16 withSourcesJar() -} - -publishing { - publications.create("mavenJava") { - groupId = project.group as String - artifactId = project.name - version = project.version as String - } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index aa35c9345..7371978d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,3 @@ -import org.gradle.accessors.dm.ApProjectDependency - plugins { `java-library` id("geyser.build-logic") diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5ab2c3932..ae21e5961 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -4,7 +4,7 @@ import net.kyori.blossom.BlossomExtension plugins { id("net.kyori.blossom") id("net.kyori.indra.git") - id("geyser.shadow-conventions") + id("geyser.publish-conventions") } dependencies { From b565ecce0a9c2a2c7e5c2068bc815cf3adef5600 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:49:03 -0500 Subject: [PATCH 060/125] Try this perhaps --- build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7371978d3..c10ea91a3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,8 +38,7 @@ subprojects { } else { group = rootProject.group as String + ".geyser" when (this) { - in platforms -> plugins.apply("geyser.platform-conventions") - api -> plugins.apply("geyser.publish-conventions") + in platforms -> plugins.apply("geyser.publish-conventions") else -> plugins.apply("geyser.base-conventions") } } From 14ac7d5dbf0950ecb9b7acf07af7e166735c1f02 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:52:02 -0500 Subject: [PATCH 061/125] Fix this --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index c10ea91a3..a51beafdc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,7 @@ subprojects { group = rootProject.group as String + ".geyser" when (this) { in platforms -> plugins.apply("geyser.publish-conventions") + api -> plugins.apply("geyser.api-conventions") else -> plugins.apply("geyser.base-conventions") } } From cbd25c01118cc9eb57c8ce64a099faa33f5007cf Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 13:53:50 -0500 Subject: [PATCH 062/125] sigh --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a51beafdc..7371978d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,8 +38,8 @@ subprojects { } else { group = rootProject.group as String + ".geyser" when (this) { - in platforms -> plugins.apply("geyser.publish-conventions") - api -> plugins.apply("geyser.api-conventions") + in platforms -> plugins.apply("geyser.platform-conventions") + api -> plugins.apply("geyser.publish-conventions") else -> plugins.apply("geyser.base-conventions") } } From 766f28ec604cb849340d4610aa16571912d4b1de Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 14:07:39 -0500 Subject: [PATCH 063/125] Publishing seems to work locally now(?) --- .../src/main/kotlin/geyser.publish-conventions.gradle.kts | 2 +- core/build.gradle.kts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts index 6336e7887..f1cb8b139 100644 --- a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts @@ -7,7 +7,7 @@ plugins { publishing { publications.create("mavenJava") { groupId = project.group as String - artifactId = "Geyser-" + project.name + artifactId = project.name version = project.version as String artifact(tasks["shadowJar"]) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index ae21e5961..fbdd3a116 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -78,13 +78,6 @@ dependencies { annotationProcessor(projects.ap) } -publishing { - publications.named("mavenJava") { - artifact(tasks["jar"]) - artifact(tasks["sourcesJar"]) - } -} - configure { val indra = the() From 735697b553c11985713e8e2e3f2525435d8dcf26 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 14:17:15 -0500 Subject: [PATCH 064/125] Allow loading extensions in dev environment --- .gitignore | 1 + .../geysermc/geyser/extension/GeyserExtensionLoader.java | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 9fc9258d7..a0e5b64e4 100644 --- a/.gitignore +++ b/.gitignore @@ -240,6 +240,7 @@ nbdist/ ### Geyser ### run/ +extensions/ config.yml logs/ key.pem diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 4c3c4bf11..ff38ea52a 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -129,12 +129,6 @@ public class GeyserExtensionLoader extends ExtensionLoader { @Override protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) { - // noinspection ConstantConditions - if (GeyserImpl.VERSION.equalsIgnoreCase("dev")) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_dev_environment")); - return; - } - // noinspection ConstantConditions if (!GeyserImpl.VERSION.contains(".")) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); From 7c8bf330a96c910292a8a8bea3a9bd1902cfe61f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 14:37:44 -0500 Subject: [PATCH 065/125] Fix loading multiple extensions (Closes #2826) --- .../extension/GeyserExtensionClassLoader.java | 14 ++++++-------- .../geyser/extension/GeyserExtensionLoader.java | 13 ++++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 28b9930b4..71311b305 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -39,12 +39,13 @@ import java.util.Map; public class GeyserExtensionClassLoader extends URLClassLoader { private final GeyserExtensionLoader loader; private final Map> classes = new HashMap<>(); - private final Extension extension; - public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, ExtensionDescription description, Path path) throws InvalidExtensionException, MalformedURLException { + public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path) throws MalformedURLException { super(new URL[] { path.toUri().toURL() }, parent); this.loader = loader; + } + public Extension load(ExtensionDescription description) throws InvalidExtensionException { try { Class jarClass; try { @@ -57,10 +58,10 @@ public class GeyserExtensionClassLoader extends URLClassLoader { try { extensionClass = jarClass.asSubclass(Extension.class); } catch (ClassCastException ex) { - throw new InvalidExtensionException("Main class " + description.main() + " should extends GeyserExtension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); + throw new InvalidExtensionException("Main class " + description.main() + " should implement Extension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); } - this.extension = extensionClass.getConstructor().newInstance(); + return extensionClass.getConstructor().newInstance(); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { throw new InvalidExtensionException("No public constructor", ex); } catch (InstantiationException ex) { @@ -77,6 +78,7 @@ public class GeyserExtensionClassLoader extends URLClassLoader { if (name.startsWith("org.geysermc.geyser.") || name.startsWith("net.minecraft.")) { throw new ClassNotFoundException(name); } + Class result = this.classes.get(name); if (result == null) { if (checkGlobal) { @@ -94,8 +96,4 @@ public class GeyserExtensionClassLoader extends URLClassLoader { } return result; } - - public Extension extension() { - return this.extension; - } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index ff38ea52a..ac8da2679 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -78,13 +78,15 @@ public class GeyserExtensionLoader extends ExtensionLoader { final GeyserExtensionClassLoader loader; try { - loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), description, path); + loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), path); } catch (Throwable e) { throw new InvalidExtensionException(e); } this.classLoaders.put(description.name(), loader); - return this.setup(loader.extension(), description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), loader.extension())); + + final Extension extension = loader.load(description); + return this.setup(extension, description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), extension)); } private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder, ExtensionEventBus eventBus) { @@ -110,10 +112,11 @@ public class GeyserExtensionLoader extends ExtensionLoader { Class clazz = this.classes.get(name); try { for (GeyserExtensionClassLoader loader : this.classLoaders.values()) { - try { - clazz = loader.findClass(name,false); - } catch(NullPointerException ignored) { + if (clazz != null) { + continue; } + + clazz = loader.findClass(name, false); } return clazz; } catch (NullPointerException s) { From 7f0e5b409f419ea378f081668eb381b9d07f1fbd Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 24 Apr 2022 14:53:47 -0500 Subject: [PATCH 066/125] Optimizations to extension loading --- core/build.gradle.kts | 1 + .../extension/GeyserExtensionClassLoader.java | 4 +- .../extension/GeyserExtensionLoader.java | 127 +++++++++--------- 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index fbdd3a116..abf703110 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { implementation("com.nukkitx.fastutil", "fastutil-int-boolean-maps", Versions.fastutilVersion) implementation("com.nukkitx.fastutil", "fastutil-object-int-maps", Versions.fastutilVersion) implementation("com.nukkitx.fastutil", "fastutil-object-object-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-object-reference-maps", Versions.fastutilVersion) // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index 71311b305..19e2765e8 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.extension; +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; @@ -38,7 +40,7 @@ import java.util.Map; public class GeyserExtensionClassLoader extends URLClassLoader { private final GeyserExtensionLoader loader; - private final Map> classes = new HashMap<>(); + private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path) throws MalformedURLException { super(new URL[] { path.toUri().toURL() }, parent); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index ac8da2679..676231c5b 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.extension; +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserImpl; @@ -51,13 +53,15 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; +import java.util.stream.Stream; @RequiredArgsConstructor public class GeyserExtensionLoader extends ExtensionLoader { private static final Path EXTENSION_DIRECTORY = Paths.get("extensions"); - private static final Pattern API_VERSION_PATTERN = Pattern.compile("^[0-9]+\\.[0-9]+\\.[0-9]+$"); + private static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); + private static final Pattern[] EXTENSION_FILTERS = new Pattern[] { Pattern.compile("^.+\\.jar$") }; - private final Map> classes = new HashMap<>(); + private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); private final Map classLoaders = new HashMap<>(); private final Map extensionContainers = new HashMap<>(); @@ -105,29 +109,27 @@ public class GeyserExtensionLoader extends ExtensionLoader { } public Pattern[] extensionFilters() { - return new Pattern[] { Pattern.compile("^.+\\.jar$") }; + return EXTENSION_FILTERS; } public Class classByName(final String name) throws ClassNotFoundException{ Class clazz = this.classes.get(name); - try { - for (GeyserExtensionClassLoader loader : this.classLoaders.values()) { - if (clazz != null) { - continue; - } - - clazz = loader.findClass(name, false); - } + if (clazz != null) { return clazz; - } catch (NullPointerException s) { - return null; } + + for (GeyserExtensionClassLoader loader : this.classLoaders.values()) { + clazz = loader.findClass(name, false); + if (clazz != null) { + break; + } + } + + return clazz; } void setClass(String name, final Class clazz) { - if (!this.classes.containsKey(name)) { - this.classes.put(name,clazz); - } + this.classes.putIfAbsent(name, clazz); } @Override @@ -149,60 +151,61 @@ public class GeyserExtensionLoader extends ExtensionLoader { Map loadedExtensions = new LinkedHashMap<>(); Pattern[] extensionFilters = this.extensionFilters(); - - Files.walk(EXTENSION_DIRECTORY).forEach(path -> { - if (Files.isDirectory(path)) { - return; - } - - for (Pattern filter : extensionFilters) { - if (!filter.matcher(path.getFileName().toString()).matches()) { - return; - } - } - - try { - GeyserExtensionDescription description = this.extensionDescription(path); - if (description == null) { + try (Stream entries = Files.walk(EXTENSION_DIRECTORY)) { + entries.forEach(path -> { + if (Files.isDirectory(path)) { return; } - String name = description.name(); - if (extensions.containsKey(name) || extensionManager.extension(name) != null) { - GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString())); - return; + for (Pattern filter : extensionFilters) { + if (!filter.matcher(path.getFileName().toString()).matches()) { + return; + } } try { - // Check the format: majorVersion.minorVersion.patch - if (!API_VERSION_PATTERN.matcher(description.apiVersion()).matches()) { - throw new IllegalArgumentException(); + GeyserExtensionDescription description = this.extensionDescription(path); + if (description == null) { + return; } - } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); - return; + + String name = description.name(); + if (extensions.containsKey(name) || extensionManager.extension(name) != null) { + GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString())); + return; + } + + try { + // Check the format: majorVersion.minorVersion.patch + if (!API_VERSION_PATTERN.matcher(description.apiVersion()).matches()) { + throw new IllegalArgumentException(); + } + } catch (NullPointerException | IllegalArgumentException e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + String[] versionArray = description.apiVersion().split("\\."); + + // Completely different API version + if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + // If the extension requires new API features, being backwards compatible + if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + return; + } + + extensions.put(name, path); + loadedExtensions.put(name, this.loadExtension(path, description)); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e); } - - String[] versionArray = description.apiVersion().split("\\."); - - // Completely different API version - if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); - return; - } - - // If the extension requires new API features, being backwards compatible - if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); - return; - } - - extensions.put(name, path); - loadedExtensions.put(name, this.loadExtension(path, description)); - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e); - } - }); + }); + } for (GeyserExtensionContainer container : loadedExtensions.values()) { this.extensionContainers.put(container.extension(), container); From 59d5a6469ca37e8958427cf25aae832d5a74674c Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 1 May 2022 12:25:24 -0500 Subject: [PATCH 067/125] Add support for modifying identifiers through the AvailableEntityIdentifiersPacket This is the first of many commits that address adding support for custom entities through the Geyser API. --- .../org/geysermc/geyser/api/GeyserApi.java | 8 ++ .../geysermc/geyser/api/command/Command.java | 55 ++++++++++- .../geyser/api/command/CommandManager.java | 9 -- .../geyser/api/entity/EntityIdentifier.java | 97 +++++++++++++++++++ .../api/event/entity/DefineEntitiesEvent.java | 46 +++++++++ .../geyser/api/provider/BuilderProvider.java | 47 +++++++++ .../geyser/api/provider/Provider.java | 29 ++++++ .../geyser/api/provider/ProviderManager.java | 41 ++++++++ .../java/org/geysermc/geyser/GeyserImpl.java | 7 ++ .../geyser/command/GeyserCommandManager.java | 5 - .../geyser/entity/GeyserEntityIdentifier.java | 85 ++++++++++++++++ .../geyser/registry/ProviderRegistries.java | 42 ++++++++ .../loader/ProviderRegistryLoader.java | 45 +++++++++ .../registry/provider/AbstractProvider.java | 39 ++++++++ .../provider/GeyserBuilderProvider.java | 64 ++++++++++++ .../provider/GeyserProviderManager.java | 36 +++++++ .../registry/provider/ProviderSupplier.java | 31 ++++++ .../geyser/session/GeyserSession.java | 34 ++++++- 18 files changed, 702 insertions(+), 18 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java create mode 100644 core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 4366e9ed6..96c23d267 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; +import org.geysermc.geyser.api.provider.ProviderManager; import java.util.List; import java.util.UUID; @@ -99,6 +100,13 @@ public interface GeyserApi extends GeyserApiBase { */ CommandManager commandManager(); + /** + * Gets the {@link ProviderManager}. + * + * @return the provider manager + */ + ProviderManager providerManager(); + /** * Gets the {@link EventBus} for handling * Geyser events. diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java index 2c48473c6..55e894dd7 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -98,27 +98,80 @@ public interface Command { } static Command.Builder builder(Class sourceType) { - return GeyserApi.api().commandManager().provideBuilder(sourceType); + return GeyserApi.api().providerManager().builderProvider().provideBuilder(Builder.class, sourceType); } interface Builder { + /** + * Sets the command name. + * + * @param name the command name + * @return the builder + */ Builder name(String name); + /** + * Sets the command description. + * + * @param description the command description + * @return the builder + */ Builder description(String description); + /** + * Sets the permission node. + * + * @param permission the permission node + * @return the builder + */ Builder permission(String permission); + /** + * Sets the aliases. + * + * @param aliases the aliases + * @return the builder + */ Builder aliases(List aliases); + /** + * Sets if this command is executable on console. + * + * @param executableOnConsole if this command is executable on console + * @return the builder + */ Builder executableOnConsole(boolean executableOnConsole); + /** + * Sets the subcommands. + * + * @param subCommands the subcommands + * @return the builder + */ Builder subCommands(List subCommands); + /** + * Sets if this command is bedrock only. + * + * @param bedrockOnly if this command is bedrock only + * @return the builder + */ Builder bedrockOnly(boolean bedrockOnly); + /** + * Sets the {@link CommandExecutor} for this command. + * + * @param executor the command executor + * @return the builder + */ Builder executor(CommandExecutor executor); + /** + * Builds the command. + * + * @return the command + */ Command build(); } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java index 864ba71a4..9f29651ba 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java @@ -34,15 +34,6 @@ import java.util.Map; */ public abstract class CommandManager { - /** - * Provides a {@link Command.Builder}. - * - * @param sourceType the command source type - * @param the type - * @return a command builder - */ - protected abstract Command.Builder provideBuilder(Class sourceType); - /** * Registers the given {@link Command}. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java b/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java new file mode 100644 index 000000000..1c82d3a63 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.entity; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; + +/** + * Represents the data sent over to a client regarding + * an entity's identifier. + */ +public interface EntityIdentifier { + + /** + * Gets whether this entity has a spawn egg or not. + * + * @return whether this entity has a spawn egg or not + */ + boolean hasSpawnEgg(); + + /** + * Gets the entity's identifier that is sent to the client. + * + * @return the entity's identifier that is sent to the client. + */ + @NonNull + String identifier(); + + /** + * Gets whether the entity is summonable or not. + * + * @return whether the entity is summonable or not + */ + boolean isSummonable(); + + @NonNull + static Builder builder() { + return GeyserApi.api().providerManager().builderProvider().provideBuilder(Builder.class); + } + + interface Builder { + + /** + * Sets whether the entity has a spawn egg or not. + * + * @param spawnEgg whether the entity has a spawn egg or not + * @return the builder + */ + Builder spawnEgg(boolean spawnEgg); + + /** + * Sets the entity's identifier that is sent to the client. + * + * @param identifier the entity's identifier that is sent to the client + * @return the builder + */ + Builder identifier(String identifier); + + /** + * Sets whether the entity is summonable or not. + * + * @param summonable whether the entity is summonable or not + * @return the builder + */ + Builder summonable(boolean summonable); + + /** + * Builds the entity identifier. + * + * @return the entity identifier + */ + EntityIdentifier build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java new file mode 100644 index 000000000..9bdae3dba --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.entity; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.entity.EntityIdentifier; +import org.geysermc.geyser.api.event.Event; + +import java.util.List; + +/** + * Called when Geyser sends a list of available entities to the + * Bedrock client. This will typically contain all the available + * entities within vanilla, but can be modified to include any custom + * entity defined through a resource pack. + * + * @param connection the {@link GeyserConnection} that is receiving the entities + * @param identifiers a mutable list of all the {@link EntityIdentifier}s + * sent to the client + */ +public record DefineEntitiesEvent(@NonNull GeyserConnection connection, @NonNull List identifiers) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java new file mode 100644 index 000000000..b05a36f88 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.provider; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Allows for obtaining instances of a builder that are + * used for constructing various data. + */ +public interface BuilderProvider extends Provider { + + /** + * Provides a builder for the specified builder type. + * + * @param builderClass the builder class + * @param the resulting type + * @param the builder type + * @return the builder instance + */ + @NonNull + B provideBuilder(@NonNull Class builderClass, @Nullable Object... args); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java new file mode 100644 index 000000000..4463efeed --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.provider; + +public interface Provider { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java new file mode 100644 index 000000000..48ec99a3f --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.provider; + +/** + * Holds a record of every {@link Provider} available + * that allows for accessing various information throughout + * the API. + */ +public interface ProviderManager { + + /** + * Returns the {@link BuilderProvider}. + * + * @return the builder provider + */ + BuilderProvider builderProvider(); +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 38d567bde..188393f4c 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -70,6 +70,7 @@ import org.geysermc.geyser.network.RemoteServerImpl; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.registry.provider.GeyserProviderManager; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; @@ -145,6 +146,7 @@ public class GeyserImpl implements GeyserApi { private final EventBus eventBus; private final GeyserExtensionManager extensionManager; + private final GeyserProviderManager providerManager = new GeyserProviderManager(); private final RemoteServer remoteServer; private final BedrockListener bedrockListener; @@ -576,6 +578,11 @@ public class GeyserImpl implements GeyserApi { return this.bootstrap.getGeyserCommandManager(); } + @Override + public GeyserProviderManager providerManager() { + return this.providerManager; + } + @Override public EventBus eventBus() { return this.eventBus; diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index 0214a44fd..c242106d8 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -148,11 +148,6 @@ public abstract class GeyserCommandManager extends CommandManager { */ public abstract String description(String command); - @Override - protected Command.Builder provideBuilder(Class sourceType) { - return new CommandBuilder<>(sourceType); - } - @RequiredArgsConstructor public static class CommandBuilder implements Command.Builder { private final Class sourceType; diff --git a/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java b/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java new file mode 100644 index 000000000..9a4a5e5d5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.entity; + +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.entity.EntityIdentifier; + +import java.util.concurrent.atomic.AtomicInteger; + +public record GeyserEntityIdentifier(NbtMap nbt) implements EntityIdentifier { + private static final AtomicInteger RUNTIME_ID_ALLOCATORS = new AtomicInteger(100000); + + @Override + public boolean hasSpawnEgg() { + return this.nbt.getBoolean("hasspawnegg"); + } + + @NonNull + @Override + public String identifier() { + return this.nbt.getString("id"); + } + + @Override + public boolean isSummonable() { + return this.nbt.getBoolean("summonable"); + } + + public static class EntityIdentifierBuilder implements EntityIdentifier.Builder { + private final NbtMapBuilder nbt = NbtMap.builder(); + + @Override + public Builder spawnEgg(boolean spawnEgg) { + this.nbt.putBoolean("hasspawnegg", spawnEgg); + return this; + } + + @Override + public Builder identifier(String identifier) { + this.nbt.putString("id", identifier); + return this; + } + + @Override + public Builder summonable(boolean summonable) { + this.nbt.putBoolean("summonable", summonable); + return this; + } + + @Override + public EntityIdentifier build() { + // Vanilla registry information + this.nbt.putString("bid", ""); + this.nbt.putInt("rid", RUNTIME_ID_ALLOCATORS.getAndIncrement()); + this.nbt.putBoolean("experimental", false); + + return new GeyserEntityIdentifier(this.nbt.build()); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java b/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java new file mode 100644 index 000000000..46b44c8da --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry; + +import org.geysermc.geyser.api.provider.Provider; +import org.geysermc.geyser.registry.loader.ProviderRegistryLoader; +import org.geysermc.geyser.registry.provider.GeyserBuilderProvider; +import org.geysermc.geyser.registry.provider.ProviderSupplier; + +/** + * Holds registries for the available {@link Provider}s + */ +public class ProviderRegistries { + + /** + * A registry containing all the providers for builders. + */ + public static final SimpleMappedRegistry, ProviderSupplier> BUILDERS = SimpleMappedRegistry.create(GeyserBuilderProvider.INSTANCE, ProviderRegistryLoader::new); +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java new file mode 100644 index 000000000..290f2991b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.loader; + +import org.geysermc.geyser.registry.provider.AbstractProvider; +import org.geysermc.geyser.registry.provider.ProviderSupplier; + +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * Registers the provider data from the provider. + */ +public class ProviderRegistryLoader implements RegistryLoader, ProviderSupplier>> { + + @Override + public Map, ProviderSupplier> load(AbstractProvider input) { + Map, ProviderSupplier> providers = new IdentityHashMap<>(); + input.registerProviders(providers); + return providers; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java new file mode 100644 index 000000000..8c5201c23 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.provider; + +import lombok.RequiredArgsConstructor; +import org.geysermc.geyser.api.provider.Provider; +import org.geysermc.geyser.registry.SimpleMappedRegistry; + +import java.util.Map; + +@RequiredArgsConstructor +public abstract class AbstractProvider implements Provider { + public abstract void registerProviders(Map, ProviderSupplier> providers); + + public abstract SimpleMappedRegistry, ProviderSupplier> providerRegistry(); +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java new file mode 100644 index 000000000..57789ae55 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.provider; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.entity.EntityIdentifier; +import org.geysermc.geyser.api.provider.BuilderProvider; +import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.entity.GeyserEntityIdentifier; +import org.geysermc.geyser.registry.ProviderRegistries; +import org.geysermc.geyser.registry.SimpleMappedRegistry; + +import java.util.Map; + +public class GeyserBuilderProvider extends AbstractProvider implements BuilderProvider { + public static GeyserBuilderProvider INSTANCE = new GeyserBuilderProvider(); + + private GeyserBuilderProvider() { + } + + @SuppressWarnings("unchecked") + @Override + public void registerProviders(Map, ProviderSupplier> providers) { + providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); + providers.put(EntityIdentifier.Builder.class, args -> new GeyserEntityIdentifier.EntityIdentifierBuilder()); + } + + @Override + public SimpleMappedRegistry, ProviderSupplier> providerRegistry() { + return ProviderRegistries.BUILDERS; + } + + @SuppressWarnings("unchecked") + @Override + public @NonNull B provideBuilder(@NonNull Class builderClass, @Nullable Object... args) { + return (B) this.providerRegistry().get(builderClass).create(args); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java new file mode 100644 index 000000000..208981d7a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.provider; + +import org.geysermc.geyser.api.provider.ProviderManager; + +public class GeyserProviderManager implements ProviderManager { + + @Override + public GeyserBuilderProvider builderProvider() { + return GeyserBuilderProvider.INSTANCE; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java b/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java new file mode 100644 index 000000000..6cb220ce4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.provider; + +public interface ProviderSupplier { + + Object create(Object... args); +} diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index a4508f0b5..8e8bbd8fc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -61,6 +61,9 @@ import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; import com.nukkitx.math.GenericMath; import com.nukkitx.math.vector.*; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.*; @@ -88,9 +91,12 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.entity.EntityIdentifier; +import org.geysermc.geyser.api.event.entity.DefineEntitiesEvent; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; +import org.geysermc.geyser.entity.GeyserEntityIdentifier; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.ItemFrameEntity; @@ -129,6 +135,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; @Getter public class GeyserSession implements GeyserConnection, GeyserCommandSource { @@ -595,9 +602,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get()); upstream.sendPacket(biomeDefinitionListPacket); - AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); - entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get()); - upstream.sendPacket(entityPacket); + this.sendAvailableEntityIdentifiers(); CreativeContentPacket creativePacket = new CreativeContentPacket(); creativePacket.setContents(this.itemMappings.getCreativeItems()); @@ -627,6 +632,29 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { upstream.sendPacket(gamerulePacket); } + public void sendAvailableEntityIdentifiers() { + NbtMap nbt = Registries.BEDROCK_ENTITY_IDENTIFIERS.get(); + List idlist = nbt.getList("idlist", NbtType.COMPOUND); + List identifiers = new ArrayList<>(idlist.size()); + for (NbtMap identifier : idlist) { + identifiers.add(new GeyserEntityIdentifier(identifier)); + } + + DefineEntitiesEvent event = new DefineEntitiesEvent(this, identifiers); + this.geyser.eventBus().fire(event); + + NbtMapBuilder builder = nbt.toBuilder(); + builder.putList("idlist", NbtType.COMPOUND, event.identifiers() + .stream() + .map(identifer -> ((GeyserEntityIdentifier) identifer).nbt()) + .collect(Collectors.toList()) + ); + + AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); + entityPacket.setIdentifiers(builder.build()); + upstream.sendPacket(entityPacket); + } + public void authenticate(String username) { authenticate(username, ""); } From fbaa6c1f5f5bd0752f633c722e8c316dd98e62d7 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 7 May 2022 10:11:21 -0500 Subject: [PATCH 068/125] Remove entity work from main extension branch for now --- .../geyser/api/entity/EntityIdentifier.java | 97 ------------------- .../api/event/entity/DefineEntitiesEvent.java | 46 --------- .../geyser/entity/GeyserEntityIdentifier.java | 85 ---------------- .../provider/GeyserBuilderProvider.java | 3 - .../geyser/session/GeyserSession.java | 30 +----- 5 files changed, 3 insertions(+), 258 deletions(-) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java delete mode 100644 core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java b/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java deleted file mode 100644 index 1c82d3a63..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/entity/EntityIdentifier.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.entity; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.GeyserApi; - -/** - * Represents the data sent over to a client regarding - * an entity's identifier. - */ -public interface EntityIdentifier { - - /** - * Gets whether this entity has a spawn egg or not. - * - * @return whether this entity has a spawn egg or not - */ - boolean hasSpawnEgg(); - - /** - * Gets the entity's identifier that is sent to the client. - * - * @return the entity's identifier that is sent to the client. - */ - @NonNull - String identifier(); - - /** - * Gets whether the entity is summonable or not. - * - * @return whether the entity is summonable or not - */ - boolean isSummonable(); - - @NonNull - static Builder builder() { - return GeyserApi.api().providerManager().builderProvider().provideBuilder(Builder.class); - } - - interface Builder { - - /** - * Sets whether the entity has a spawn egg or not. - * - * @param spawnEgg whether the entity has a spawn egg or not - * @return the builder - */ - Builder spawnEgg(boolean spawnEgg); - - /** - * Sets the entity's identifier that is sent to the client. - * - * @param identifier the entity's identifier that is sent to the client - * @return the builder - */ - Builder identifier(String identifier); - - /** - * Sets whether the entity is summonable or not. - * - * @param summonable whether the entity is summonable or not - * @return the builder - */ - Builder summonable(boolean summonable); - - /** - * Builds the entity identifier. - * - * @return the entity identifier - */ - EntityIdentifier build(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java deleted file mode 100644 index 9bdae3dba..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/entity/DefineEntitiesEvent.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.event.entity; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.entity.EntityIdentifier; -import org.geysermc.geyser.api.event.Event; - -import java.util.List; - -/** - * Called when Geyser sends a list of available entities to the - * Bedrock client. This will typically contain all the available - * entities within vanilla, but can be modified to include any custom - * entity defined through a resource pack. - * - * @param connection the {@link GeyserConnection} that is receiving the entities - * @param identifiers a mutable list of all the {@link EntityIdentifier}s - * sent to the client - */ -public record DefineEntitiesEvent(@NonNull GeyserConnection connection, @NonNull List identifiers) implements Event { -} diff --git a/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java b/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java deleted file mode 100644 index 9a4a5e5d5..000000000 --- a/core/src/main/java/org/geysermc/geyser/entity/GeyserEntityIdentifier.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.entity; - -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.entity.EntityIdentifier; - -import java.util.concurrent.atomic.AtomicInteger; - -public record GeyserEntityIdentifier(NbtMap nbt) implements EntityIdentifier { - private static final AtomicInteger RUNTIME_ID_ALLOCATORS = new AtomicInteger(100000); - - @Override - public boolean hasSpawnEgg() { - return this.nbt.getBoolean("hasspawnegg"); - } - - @NonNull - @Override - public String identifier() { - return this.nbt.getString("id"); - } - - @Override - public boolean isSummonable() { - return this.nbt.getBoolean("summonable"); - } - - public static class EntityIdentifierBuilder implements EntityIdentifier.Builder { - private final NbtMapBuilder nbt = NbtMap.builder(); - - @Override - public Builder spawnEgg(boolean spawnEgg) { - this.nbt.putBoolean("hasspawnegg", spawnEgg); - return this; - } - - @Override - public Builder identifier(String identifier) { - this.nbt.putString("id", identifier); - return this; - } - - @Override - public Builder summonable(boolean summonable) { - this.nbt.putBoolean("summonable", summonable); - return this; - } - - @Override - public EntityIdentifier build() { - // Vanilla registry information - this.nbt.putString("bid", ""); - this.nbt.putInt("rid", RUNTIME_ID_ALLOCATORS.getAndIncrement()); - this.nbt.putBoolean("experimental", false); - - return new GeyserEntityIdentifier(this.nbt.build()); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java index 57789ae55..e144fcd4f 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java @@ -29,10 +29,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.api.entity.EntityIdentifier; import org.geysermc.geyser.api.provider.BuilderProvider; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.entity.GeyserEntityIdentifier; import org.geysermc.geyser.registry.ProviderRegistries; import org.geysermc.geyser.registry.SimpleMappedRegistry; @@ -48,7 +46,6 @@ public class GeyserBuilderProvider extends AbstractProvider implements BuilderPr @Override public void registerProviders(Map, ProviderSupplier> providers) { providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); - providers.put(EntityIdentifier.Builder.class, args -> new GeyserEntityIdentifier.EntityIdentifierBuilder()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 8e8bbd8fc..bf0360e1d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -91,12 +91,9 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.entity.EntityIdentifier; -import org.geysermc.geyser.api.event.entity.DefineEntitiesEvent; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; -import org.geysermc.geyser.entity.GeyserEntityIdentifier; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.ItemFrameEntity; @@ -602,7 +599,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get()); upstream.sendPacket(biomeDefinitionListPacket); - this.sendAvailableEntityIdentifiers(); + AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); + entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get()); + upstream.sendPacket(entityPacket); CreativeContentPacket creativePacket = new CreativeContentPacket(); creativePacket.setContents(this.itemMappings.getCreativeItems()); @@ -632,29 +631,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { upstream.sendPacket(gamerulePacket); } - public void sendAvailableEntityIdentifiers() { - NbtMap nbt = Registries.BEDROCK_ENTITY_IDENTIFIERS.get(); - List idlist = nbt.getList("idlist", NbtType.COMPOUND); - List identifiers = new ArrayList<>(idlist.size()); - for (NbtMap identifier : idlist) { - identifiers.add(new GeyserEntityIdentifier(identifier)); - } - - DefineEntitiesEvent event = new DefineEntitiesEvent(this, identifiers); - this.geyser.eventBus().fire(event); - - NbtMapBuilder builder = nbt.toBuilder(); - builder.putList("idlist", NbtType.COMPOUND, event.identifiers() - .stream() - .map(identifer -> ((GeyserEntityIdentifier) identifer).nbt()) - .collect(Collectors.toList()) - ); - - AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); - entityPacket.setIdentifiers(builder.build()); - upstream.sendPacket(entityPacket); - } - public void authenticate(String username) { authenticate(username, ""); } From 795bec91d74e4bc6a1b282902ff2d57f16140106 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Sat, 11 Jun 2022 10:03:19 +0200 Subject: [PATCH 069/125] Fixed plugin.yml for spigot --- .../spigot/src/main/resources/plugin.yml | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index 013b1d96c..e28b8981d 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -8,41 +8,4 @@ api-version: 1.13 commands: geyser: description: The main command for Geyser. - usage: /geyser -<<<<<<< HEAD -permissions: - geyser.command.help: - description: Shows help for all registered commands. - default: true - geyser.command.offhand: - description: Puts an items in your offhand. - default: true - geyser.command.advancements: - description: Shows the advancements of the player on the server. - default: true - geyser.command.tooltips: - description: Toggles showing advanced tooltips on your items. - default: true - geyser.command.statistics: - description: Shows the statistics of the player on the server. - default: true - geyser.command.settings: - description: Modify user settings - default: true - geyser.command.list: - description: List all players connected through Geyser. - default: op - geyser.command.dump: - description: Dumps Geyser debug information for bug reports. - default: op - geyser.command.reload: - description: Reloads the Geyser configurations. Kicks all players when used! - default: false - geyser.command.version: - description: Shows the current Geyser version and checks for updates. - default: op - geyser.command.extensions: - description: Shows all the loaded extensions. - default: op -======= ->>>>>>> 2595eae30070fa5d221ad505eef6a9e1606fb1d1 + usage: /geyser \ No newline at end of file From 36c49a7256ae9a08aed82f7421c355b2d806a550 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Date: Sat, 2 Jul 2022 18:50:16 +0200 Subject: [PATCH 070/125] Custom item support for extensions (#2822) Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- .gitignore | 3 +- .../org/geysermc/geyser/api/GeyserApi.java | 2 +- .../geysermc/geyser/api/event/EventBus.java | 2 +- .../GeyserDefineCustomItemsEvent.java | 85 +++++ .../GeyserLoadResourcePacksEvent.java | 40 ++ .../api/item/custom/CustomItemData.java | 109 ++++++ .../api/item/custom/CustomItemOptions.java | 83 ++++ .../api/item/custom/CustomRenderOffsets.java | 51 +++ .../item/custom/NonVanillaCustomItemData.java | 188 +++++++++ .../geysermc/geyser/api/util/TriState.java | 83 ++++ bootstrap/standalone/build.gradle.kts | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 28 +- .../geyser/entity/type/ItemFrameEntity.java | 7 +- .../extension/GeyserExtensionManager.java | 24 +- .../geyser/item/GeyserCustomItemData.java | 158 ++++++++ .../geyser/item/GeyserCustomItemOptions.java | 69 ++++ .../geyser/item/GeyserCustomMappingData.java | 32 ++ .../item/GeyserNonVanillaCustomItemData.java | 303 +++++++++++++++ .../item/components/ToolBreakSpeedsUtils.java | 174 +++++++++ .../geyser/item/components/ToolTier.java | 66 ++++ .../geyser/item/components/WearableSlot.java | 47 +++ .../InvalidCustomMappingsFileException.java | 40 ++ .../item/mappings/MappingsConfigReader.java | 98 +++++ .../mappings/versions/MappingsReader.java | 93 +++++ .../mappings/versions/MappingsReader_v1.java | 123 ++++++ .../geyser/network/UpstreamPacketHandler.java | 4 +- .../geysermc/geyser/pack/ResourcePack.java | 35 +- .../geysermc/geyser/registry/Registries.java | 5 - .../CustomItemRegistryPopulator.java | 360 ++++++++++++++++++ .../populator/ItemRegistryPopulator.java | 159 +++++++- .../provider/GeyserBuilderProvider.java | 9 + .../registry/type/GeyserMappingItem.java | 2 + .../geyser/registry/type/ItemMapping.java | 9 +- .../geyser/registry/type/ItemMappings.java | 5 +- .../type/NonVanillaItemRegistration.java | 34 ++ .../geyser/session/GeyserSession.java | 6 +- .../inventory/item/CompassTranslator.java | 3 +- .../inventory/item/ItemTranslator.java | 55 ++- .../inventory/item/PotionTranslator.java | 3 +- .../inventory/item/TippedArrowTranslator.java | 3 +- .../inventory/item/nbt/BannerTranslator.java | 3 +- .../JavaMerchantOffersTranslator.java | 3 +- core/src/main/resources/mappings | 2 +- 43 files changed, 2536 insertions(+), 74 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java create mode 100644 core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java diff --git a/.gitignore b/.gitignore index 0566cb462..2b7e2972c 100644 --- a/.gitignore +++ b/.gitignore @@ -249,4 +249,5 @@ locales/ /packs/ /dump.json /saved-refresh-tokens.json -/languages/ \ No newline at end of file +/custom_mappings/ +/languages/ diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 96c23d267..d6cb3c25a 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -116,7 +116,7 @@ public interface GeyserApi extends GeyserApiBase { EventBus eventBus(); /** - * Get's the default {@link RemoteServer} configured + * Gets the default {@link RemoteServer} configured * within the config file that is used by default. * * @return the default remote server used within Geyser diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java index 0352dcc9e..b13f12300 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -76,7 +76,7 @@ public interface EventBus { void unregisterAll(@NonNull Extension extension); /** - * Fires the given {@link Event}. + * Fires the given {@link Event} and returns the result. * * @param event the event to fire * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java new file mode 100644 index 000000000..308b39d22 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; + +import java.util.*; + +/** + * Called on Geyser's startup when looking for custom items. Custom items must be registered through this event. + * + * This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config. + */ +public abstract class GeyserDefineCustomItemsEvent implements Event { + private final Multimap customItems; + private final List nonVanillaCustomItems; + + public GeyserDefineCustomItemsEvent(Multimap customItems, List nonVanillaCustomItems) { + this.customItems = customItems; + this.nonVanillaCustomItems = nonVanillaCustomItems; + } + + /** + * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. + * + * @return a multimap of all the already registered custom items + */ + public Map> getExistingCustomItems() { + return Collections.unmodifiableMap(this.customItems.asMap()); + } + + /** + * Gets the list of the already registered non-vanilla custom items. + * + * @return the list of the already registered non-vanilla custom items + */ + public List getExistingNonVanillaCustomItems() { + return Collections.unmodifiableList(this.nonVanillaCustomItems); + } + + /** + * Registers a custom item with a base Java item. This is used to register items with custom textures and properties + * based on NBT data. + * + * @param identifier the base (java) item + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + + /** + * Registers a custom item with no base item. This is used for mods. + * + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData); +} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java new file mode 100644 index 000000000..0f181aedf --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.Event; + +import java.nio.file.Path; +import java.util.List; + +/** + * Called when resource packs are loaded within Geyser. + * + * @param resourcePacks a mutable list of the currently listed resource packs + */ +public record GeyserLoadResourcePacksEvent(@NonNull List resourcePacks) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java new file mode 100644 index 000000000..7391c0680 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.GeyserApi; + +/** + * This is used to store data for a custom item. + */ +public interface CustomItemData { + /** + * Gets the item's name. + * + * @return the item's name + */ + @NonNull String name(); + + /** + * Gets the custom item options of the item. + * + * @return the custom item options of the item. + */ + CustomItemOptions customItemOptions(); + + /** + * Gets the item's display name. By default, this is the item's name. + * + * @return the item's display name + */ + @NonNull String displayName(); + + /** + * Gets the item's icon. By default, this is the item's name. + * + * @return the item's icon + */ + @NonNull String icon(); + + /** + * Gets if the item is allowed to be put into the offhand. + * + * @return true if the item is allowed to be used in the offhand, false otherwise + */ + boolean allowOffhand(); + + /** + * Gets the item's texture size. This is to resize the item if the texture is not 16x16. + * + * @return the item's texture size + */ + int textureSize(); + + /** + * Gets the item's render offsets. If it is null, the item will be rendered normally, with no offsets. + * + * @return the item's render offsets + */ + @Nullable CustomRenderOffsets renderOffsets(); + + static CustomItemData.Builder builder() { + return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemData.Builder.class); + } + + interface Builder { + /** + * Will also set the display name and icon to the provided parameter, if it is currently not set. + */ + Builder name(@NonNull String name); + + Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); + + Builder displayName(@NonNull String displayName); + + Builder icon(@NonNull String icon); + + Builder allowOffhand(boolean allowOffhand); + + Builder textureSize(int textureSize); + + Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + + CustomItemData build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java new file mode 100644 index 000000000..037f2f05e --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.util.TriState; + +import java.util.OptionalInt; + +/** + * This class represents the different ways you can register custom items + */ +public interface CustomItemOptions { + /** + * Gets if the item should be unbreakable. + * + * @return if the item should be unbreakable + */ + @NonNull TriState unbreakable(); + + /** + * Gets the item's custom model data predicate. + * + * @return the item's custom model data + */ + @NonNull OptionalInt customModelData(); + + /** + * Gets the item's damage predicate. + * + * @return the item's damage predicate + */ + @NonNull OptionalInt damagePredicate(); + + /** + * Checks if the item has at least one option set + * + * @return true if the item at least one options set + */ + default boolean hasCustomItemOptions() { + return this.unbreakable() != TriState.NOT_SET || + this.customModelData().isPresent() || + this.damagePredicate().isPresent(); + } + + static CustomItemOptions.Builder builder() { + return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemOptions.Builder.class); + } + + interface Builder { + Builder unbreakable(boolean unbreakable); + + Builder customModelData(int customModelData); + + Builder damagePredicate(int damagePredicate); + + CustomItemOptions build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java new file mode 100644 index 000000000..f81da0ae2 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This class is used to store the render offsets of custom items. + */ +public record CustomRenderOffsets(@Nullable Hand mainHand, @Nullable Hand offhand) { + /** + * The hand that is used for the offset. + */ + public record Hand(@Nullable Offset firstPerson, @Nullable Offset thirdPerson) { + } + + /** + * The offset of the item. + */ + public record Offset(@Nullable OffsetXYZ position, @Nullable OffsetXYZ rotation, @Nullable OffsetXYZ scale) { + } + + /** + * X, Y and Z positions for the offset. + */ + public record OffsetXYZ(float x, float y, float z) { + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java new file mode 100644 index 000000000..1df94f7ea --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.GeyserApi; + +import java.util.OptionalInt; +import java.util.Set; + +/** + * Represents a completely custom item that is not based on an existing vanilla Minecraft item. + */ +public interface NonVanillaCustomItemData extends CustomItemData { + /** + * Gets the java identifier for this item. + * + * @return The java identifier for this item. + */ + @NonNull String identifier(); + + /** + * Gets the java item id of the item. + * + * @return the java item id of the item + */ + @NonNegative int javaId(); + + /** + * Gets the stack size of the item. + * + * @return the stack size of the item + */ + @NonNegative int stackSize(); + + /** + * Gets the max damage of the item. + * + * @return the max damage of the item + */ + int maxDamage(); + + /** + * Gets the tool type of the item. + * + * @return the tool type of the item + */ + @Nullable String toolType(); + + /** + * Gets the tool tier of the item. + * + * @return the tool tier of the item + */ + @Nullable String toolTier(); + + /** + * Gets the armor type of the item. + * + * @return the armor type of the item + */ + @Nullable String armorType(); + + /** + * Gets the armor protection value of the item. + * + * @return the armor protection value of the item + */ + int protectionValue(); + + /** + * Gets the item's translation string. + * + * @return the item's translation string + */ + @Nullable String translationString(); + + /** + * Gets the repair materials of the item. + * + * @return the repair materials of the item + */ + @Nullable Set repairMaterials(); + + /** + * Gets the item's creative category, or tab id. + * + * @return the item's creative category + */ + @NonNull OptionalInt creativeCategory(); + + /** + * Gets the item's creative group. + * + * @return the item's creative group + */ + @Nullable String creativeGroup(); + + /** + * Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and + * normally allow the player to equip it. This is not meant for armor. + * + * @return if the item is a hat + */ + boolean isHat(); + + /** + * Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld. + * + * @return if the item is a tool + */ + boolean isTool(); + + static NonVanillaCustomItemData.Builder builder() { + return GeyserApi.api().providerManager().builderProvider().provideBuilder(NonVanillaCustomItemData.Builder.class); + } + + interface Builder extends CustomItemData.Builder { + Builder name(@NonNull String name); + + Builder identifier(@NonNull String identifier); + + Builder javaId(@NonNegative int javaId); + + Builder stackSize(@NonNegative int stackSize); + + Builder maxDamage(int maxDamage); + + Builder toolType(@Nullable String toolType); + + Builder toolTier(@Nullable String toolTier); + + Builder armorType(@Nullable String armorType); + + Builder protectionValue(int protectionValue); + + Builder translationString(@Nullable String translationString); + + Builder repairMaterials(@Nullable Set repairMaterials); + + Builder creativeCategory(int creativeCategory); + + Builder creativeGroup(@Nullable String creativeGroup); + + Builder hat(boolean isHat); + + Builder tool(boolean isTool); + + @Override + Builder displayName(@NonNull String displayName); + + @Override + Builder allowOffhand(boolean allowOffhand); + + @Override + Builder textureSize(int textureSize); + + @Override + Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + + NonVanillaCustomItemData build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java b/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java new file mode 100644 index 000000000..457a38e32 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.util; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This is a way to represent a boolean, but with a non set value added. + * This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java + */ +public enum TriState { + /** + * Describes a value that is not set, null, or not present. + */ + NOT_SET, + + /** + * Describes a true value. + */ + TRUE, + + /** + * Describes a false value. + */ + FALSE; + + /** + * Converts the TriState to a boolean. + * + * @return the boolean value of the TriState + */ + public @Nullable Boolean toBoolean() { + return switch (this) { + case TRUE -> true; + case FALSE -> false; + default -> null; + }; + } + + /** + * Creates a TriState from a boolean. + * + * @param value the Boolean value + * @return the created TriState + */ + public static @NonNull TriState fromBoolean(@Nullable Boolean value) { + return value == null ? NOT_SET : fromBoolean(value.booleanValue()); + } + + /** + * Creates a TriState from a primitive boolean. + * + * @param value the boolean value + * @return the created TriState + */ + public @NonNull static TriState fromBoolean(boolean value) { + return value ? TRUE : FALSE; + } +} diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts index 088d5dc82..d49c7c490 100644 --- a/bootstrap/standalone/build.gradle.kts +++ b/bootstrap/standalone/build.gradle.kts @@ -1,7 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer val terminalConsoleVersion = "1.2.0" -val jlineVersion = "3.10.0" +val jlineVersion = "3.21.0" dependencies { api(projects.core) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 9eeae4abb..8f3156abe 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -180,22 +180,26 @@ public class GeyserImpl implements GeyserApi { logger.info(""); logger.info("******************************************"); - /* Initialize translators and registries */ - BlockRegistries.init(); - Registries.init(); + /* Initialize event bus */ + this.eventBus = new GeyserEventBus(); + /* Load Extensions */ + this.extensionManager = new GeyserExtensionManager(); + this.extensionManager.init(); + + this.extensionManager.enableExtensions(); + this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); + + /* Initialize registries */ + Registries.init(); + BlockRegistries.init(); + + /* Initialize translators */ EntityDefinitions.init(); ItemTranslator.init(); MessageTranslator.init(); MinecraftLocale.init(); - /* Load Extensions */ - this.eventBus = new GeyserEventBus(); - this.extensionManager = new GeyserExtensionManager(); - this.extensionManager.init(); - - this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); - start(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -256,8 +260,6 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); - this.extensionManager.enableExtensions(); - if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { @@ -580,6 +582,7 @@ public class GeyserImpl implements GeyserApi { @Override public void reload() { shutdown(); + this.extensionManager.enableExtensions(); bootstrap.onEnable(); } @@ -615,7 +618,6 @@ public class GeyserImpl implements GeyserApi { return this.eventBus; } - @Override public RemoteServer defaultRemoteServer() { return this.remoteServer; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java index bc7736e9b..8e4a5323a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java @@ -40,7 +40,6 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import lombok.Getter; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.util.InteractionResult; @@ -114,7 +113,9 @@ public class ItemFrameEntity extends Entity { if (entityMetadata.getValue() != null) { this.heldItem = entityMetadata.getValue(); ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); - ItemMapping mapping = session.getItemMappings().getMapping(entityMetadata.getValue()); + + String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getId()); + NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); @@ -122,7 +123,7 @@ public class ItemFrameEntity extends Entity { builder.put("tag", itemData.getTag()); } builder.putShort("Damage", (short) itemData.getDamage()); - builder.putString("Name", mapping.getBedrockIdentifier()); + builder.putString("Name", customIdentifier != null ? customIdentifier : session.getItemMappings().getMapping(entityMetadata.getValue()).getBedrockIdentifier()); NbtMapBuilder tag = getDefaultTag().toBuilder(); tag.put("Item", builder.build()); tag.putFloat("ItemDropChance", 1.0f); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index 79cf1febc..7d80c2cf6 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.extension; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -32,7 +33,6 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.api.extension.ExtensionManager; -import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.text.GeyserLocale; import java.util.Collection; @@ -44,13 +44,15 @@ import java.util.stream.Collectors; public class GeyserExtensionManager extends ExtensionManager { private static final Key BASE_EXTENSION_LOADER_KEY = Key.key("geysermc", "base"); + private final Map extensionLoaderTypes = new Object2ObjectOpenHashMap<>(); + private final Map extensions = new LinkedHashMap<>(); private final Map extensionsLoaders = new LinkedHashMap<>(); public void init() { GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); - Registries.EXTENSION_LOADERS.register(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader()); + extensionLoaderTypes.put(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader()); for (ExtensionLoader loader : this.extensionLoaders().values()) { this.loadAllExtensions(loader); } @@ -98,6 +100,12 @@ public class GeyserExtensionManager extends ExtensionManager { } } + public void enableExtensions() { + for (Extension extension : this.extensions()) { + this.enable(extension); + } + } + private void disableExtension(@NonNull Extension extension) { if (extension.isEnabled()) { GeyserImpl.getInstance().eventBus().unregisterAll(extension); @@ -107,12 +115,6 @@ public class GeyserExtensionManager extends ExtensionManager { } } - public void enableExtensions() { - for (Extension extension : this.extensions()) { - this.enable(extension); - } - } - public void disableExtensions() { for (Extension extension : this.extensions()) { this.disable(extension); @@ -133,18 +135,18 @@ public class GeyserExtensionManager extends ExtensionManager { @Nullable @Override public ExtensionLoader extensionLoader(@NonNull String identifier) { - return Registries.EXTENSION_LOADERS.get(Key.key(identifier)); + return this.extensionLoaderTypes.get(Key.key(identifier)); } @Override public void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader) { - Registries.EXTENSION_LOADERS.register(Key.key(identifier), extensionLoader); + this.extensionLoaderTypes.put(Key.key(identifier), extensionLoader); } @NonNull @Override public Map extensionLoaders() { - return Registries.EXTENSION_LOADERS.get().entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue)); + return this.extensionLoaderTypes.entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue)); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java new file mode 100644 index 000000000..ddea9937c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.jetbrains.annotations.NotNull; + +@EqualsAndHashCode +@ToString +public class GeyserCustomItemData implements CustomItemData { + private final String name; + private final CustomItemOptions customItemOptions; + private final String displayName; + private final String icon; + private final boolean allowOffhand; + private final int textureSize; + private final CustomRenderOffsets renderOffsets; + + public GeyserCustomItemData(String name, + CustomItemOptions customItemOptions, + String displayName, + String icon, + boolean allowOffhand, + int textureSize, + CustomRenderOffsets renderOffsets) { + this.name = name; + this.customItemOptions = customItemOptions; + this.displayName = displayName; + this.icon = icon; + this.allowOffhand = allowOffhand; + this.textureSize = textureSize; + this.renderOffsets = renderOffsets; + } + + public @NotNull String name() { + return name; + } + + public CustomItemOptions customItemOptions() { + return customItemOptions; + } + + public @NotNull String displayName() { + return displayName; + } + + public @NotNull String icon() { + return icon; + } + + public boolean allowOffhand() { + return allowOffhand; + } + + public int textureSize() { + return textureSize; + } + + public CustomRenderOffsets renderOffsets() { + return renderOffsets; + } + + public static class CustomItemDataBuilder implements Builder { + protected String name = null; + protected CustomItemOptions customItemOptions = null; + + protected String displayName = null; + protected String icon = null; + protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java + protected int textureSize = 16; + protected CustomRenderOffsets renderOffsets = null; + + @Override + public Builder name(@NonNull String name) { + this.name = name; + return this; + } + + @Override + public Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) { + this.customItemOptions = customItemOptions; + return this; + } + + @Override + public Builder displayName(@NonNull String displayName) { + this.displayName = displayName; + return this; + } + + @Override + public Builder icon(@NonNull String icon) { + this.icon = icon; + return this; + } + + @Override + public Builder allowOffhand(boolean allowOffhand) { + this.allowOffhand = allowOffhand; + return this; + } + + @Override + public Builder textureSize(int textureSize) { + this.textureSize = textureSize; + return this; + } + + @Override + public Builder renderOffsets(CustomRenderOffsets renderOffsets) { + this.renderOffsets = renderOffsets; + return this; + } + + @Override + public CustomItemData build() { + if (this.name == null || this.customItemOptions == null) { + throw new IllegalArgumentException("Name and custom item options must be set"); + } + + if (this.displayName == null) { + this.displayName = this.name; + } + if (this.icon == null) { + this.icon = this.name; + } + return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.textureSize, this.renderOffsets); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java new file mode 100644 index 000000000..dd4ae01de --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item; + +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.util.TriState; + +import java.util.OptionalInt; + +public record GeyserCustomItemOptions(TriState unbreakable, + OptionalInt customModelData, + OptionalInt damagePredicate) implements CustomItemOptions { + + public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder { + private TriState unbreakable = TriState.NOT_SET; + private OptionalInt customModelData = OptionalInt.empty(); + private OptionalInt damagePredicate = OptionalInt.empty(); + + @Override + public Builder unbreakable(boolean unbreakable) { + if (unbreakable) { + this.unbreakable = TriState.TRUE; + } else { + this.unbreakable = TriState.FALSE; + } + return this; + } + + @Override + public Builder customModelData(int customModelData) { + this.customModelData = OptionalInt.of(customModelData); + return this; + } + + @Override + public Builder damagePredicate(int damagePredicate) { + this.damagePredicate = OptionalInt.of(damagePredicate); + return this; + } + + @Override + public CustomItemOptions build() { + return new GeyserCustomItemOptions(unbreakable, customModelData, damagePredicate); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java new file mode 100644 index 000000000..3829db3c3 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item; + +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; + +public record GeyserCustomMappingData(ComponentItemData componentItemData, StartGamePacket.ItemEntry startGamePacketItemEntry, String stringId, int integerId) { +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java new file mode 100644 index 000000000..efdc1fdcf --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.jetbrains.annotations.NotNull; + +import java.util.OptionalInt; +import java.util.Set; + +@EqualsAndHashCode(callSuper = true) +@ToString +public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData implements NonVanillaCustomItemData { + private final String identifier; + private final int javaId; + private final int stackSize; + private final int maxDamage; + private final String toolType; + private final String toolTier; + private final String armorType; + private final int protectionValue; + private final String translationString; + private final Set repairMaterials; + private final OptionalInt creativeCategory; + private final String creativeGroup; + private final boolean isHat; + private final boolean isTool; + + public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) { + super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, + builder.textureSize, builder.renderOffsets); + + this.identifier = builder.identifier; + this.javaId = builder.javaId; + this.stackSize = builder.stackSize; + this.maxDamage = builder.maxDamage; + this.toolType = builder.toolType; + this.toolTier = builder.toolTier; + this.armorType = builder.armorType; + this.protectionValue = builder.protectionValue; + this.translationString = builder.translationString; + this.repairMaterials = builder.repairMaterials; + this.creativeCategory = builder.creativeCategory; + this.creativeGroup = builder.creativeGroup; + this.isHat = builder.hat; + this.isTool = builder.tool; + } + + @Override + public @NotNull String identifier() { + return identifier; + } + + @Override + public int javaId() { + return javaId; + } + + @Override + public int stackSize() { + return stackSize; + } + + @Override + public int maxDamage() { + return maxDamage; + } + + @Override + public String toolType() { + return toolType; + } + + @Override + public String toolTier() { + return toolTier; + } + + @Override + public @Nullable String armorType() { + return armorType; + } + + @Override + public int protectionValue() { + return protectionValue; + } + + @Override + public String translationString() { + return translationString; + } + + @Override + public Set repairMaterials() { + return repairMaterials; + } + + @Override + public @NotNull OptionalInt creativeCategory() { + return creativeCategory; + } + + @Override + public String creativeGroup() { + return creativeGroup; + } + + @Override + public boolean isHat() { + return isHat; + } + + @Override + public boolean isTool() { + return isTool; + } + + public static class NonVanillaCustomItemDataBuilder extends GeyserCustomItemData.CustomItemDataBuilder implements NonVanillaCustomItemData.Builder { + private String identifier = null; + private int javaId = -1; + + private int stackSize = 64; + + private int maxDamage = 0; + + private String toolType = null; + private String toolTier = null; + + private String armorType = null; + private int protectionValue = 0; + + private String translationString; + + private Set repairMaterials; + + private OptionalInt creativeCategory = OptionalInt.empty(); + private String creativeGroup = null; + + private boolean hat = false; + private boolean tool = false; + + @Override + public NonVanillaCustomItemData.Builder name(@NonNull String name) { + return (NonVanillaCustomItemData.Builder) super.name(name); + } + + @Override + public NonVanillaCustomItemData.Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) { + //Do nothing, as that value won't be read + return this; + } + + @Override + public NonVanillaCustomItemData.Builder allowOffhand(boolean allowOffhand) { + return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand); + } + + @Override + public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) { + return (NonVanillaCustomItemData.Builder) super.displayName(displayName); + } + + @Override + public NonVanillaCustomItemData.Builder icon(@NonNull String icon) { + return (NonVanillaCustomItemData.Builder) super.icon(icon); + } + + @Override + public NonVanillaCustomItemData.Builder textureSize(int textureSize) { + return (NonVanillaCustomItemData.Builder) super.textureSize(textureSize); + } + + @Override + public NonVanillaCustomItemData.Builder renderOffsets(CustomRenderOffsets renderOffsets) { + return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets); + } + + @Override + public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) { + this.identifier = identifier; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder javaId(int javaId) { + this.javaId = javaId; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder stackSize(int stackSize) { + this.stackSize = stackSize; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder maxDamage(int maxDamage) { + this.maxDamage = maxDamage; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder toolType(@Nullable String toolType) { + this.toolType = toolType; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder toolTier(@Nullable String toolTier) { + this.toolTier = toolTier; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder armorType(@Nullable String armorType) { + this.armorType = armorType; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder protectionValue(int protectionValue) { + this.protectionValue = protectionValue; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder translationString(@Nullable String translationString) { + this.translationString = translationString; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder repairMaterials(@Nullable Set repairMaterials) { + this.repairMaterials = repairMaterials; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder creativeCategory(int creativeCategory) { + this.creativeCategory = OptionalInt.of(creativeCategory); + return this; + } + + @Override + public NonVanillaCustomItemData.Builder creativeGroup(@Nullable String creativeGroup) { + this.creativeGroup = creativeGroup; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder hat(boolean isHat) { + this.hat = isHat; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder tool(boolean isTool) { + this.tool = isTool; + return this; + } + + @Override + public NonVanillaCustomItemData build() { + if (identifier == null || javaId == -1) { + throw new IllegalArgumentException("Identifier and javaId must be set"); + } + + super.customItemOptions(CustomItemOptions.builder().build()); + super.build(); + return new GeyserNonVanillaCustomItemData(this); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java new file mode 100644 index 000000000..6330043e5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.components; + +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtType; + +import java.util.ArrayList; +import java.util.List; + +public class ToolBreakSpeedsUtils { + public static int toolTierToSpeed(String toolTier) { + ToolTier tier = ToolTier.getByName(toolTier); + if (tier != null) { + return tier.getSpeed(); + } + + return 0; + } + + private static NbtMap createTagBreakSpeed(int speed, String... tags) { + StringBuilder builder = new StringBuilder("query.any_tag('"); + builder.append(tags[0]); + for (int i = 1; i < tags.length; i++) { + builder.append("', '").append(tags[i]); + } + builder.append("')"); + + return NbtMap.builder() + .putCompound("block", NbtMap.builder() + .putString("tags", builder.toString()) + .build()) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putInt("speed", speed) + .build(); + } + + private static NbtMap createBreakSpeed(int speed, String block) { + return NbtMap.builder() + .putCompound("block", NbtMap.builder() + .putString("name", block).build()) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putInt("speed", speed) + .build(); + } + + private static NbtMap createDigger(List speeds) { + return NbtMap.builder() + .putList("destroy_speeds", NbtType.COMPOUND, speeds) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putBoolean("use_efficiency", true) + .build(); + } + + public static NbtMap getAxeDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createTagBreakSpeed(speed, "wood", "pumpkin", "plant")); + + return createDigger(speeds); + } + + public static NbtMap getPickaxeDigger(int speed, String toolTier) { + List speeds = new ArrayList<>(); + if (toolTier.equals(ToolTier.DIAMOND.toString()) || toolTier.equals(ToolTier.NETHERITE.toString())) { + speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable", "diamond_pick_diggable")); + } else { + speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable")); + } + speeds.add(createTagBreakSpeed(speed, "stone", "metal", "rail", "mob_spawner")); + + return createDigger(speeds); + } + + public static NbtMap getShovelDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createTagBreakSpeed(speed, "dirt", "sand", "gravel", "grass", "snow")); + + return createDigger(speeds); + } + + public static NbtMap getSwordDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:web")); + speeds.add(createBreakSpeed(speed, "minecraft:bamboo")); + + return createDigger(speeds); + } + + public static NbtMap getHoeDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); + + speeds.add(createBreakSpeed(speed, "minecraft:sculk")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_catalyst")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_sensor")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_shrieker")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_vein")); + + speeds.add(createBreakSpeed(speed, "minecraft:nether_wart_block")); + speeds.add(createBreakSpeed(speed, "minecraft:warped_wart_block")); + + speeds.add(createBreakSpeed(speed, "minecraft:hay_block")); + speeds.add(createBreakSpeed(speed, "minecraft:moss_block")); + speeds.add(createBreakSpeed(speed, "minecraft:shroomlight")); + speeds.add(createBreakSpeed(speed, "minecraft:sponge")); + speeds.add(createBreakSpeed(speed, "minecraft:target")); + + return createDigger(speeds); + } + + public static NbtMap getShearsDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:web")); + + speeds.add(createBreakSpeed(speed, "minecraft:leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); + + speeds.add(createBreakSpeed(speed, "minecraft:wool")); + + speeds.add(createBreakSpeed(speed, "minecraft:glow_lichen")); + speeds.add(createBreakSpeed(speed, "minecraft:vine")); + + return createDigger(speeds); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java new file mode 100644 index 000000000..37e581682 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.components; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Locale; + +public enum ToolTier { + WOODEN(2), + STONE(4), + IRON(6), + GOLDEN(12), + DIAMOND(8), + NETHERITE(9); + + public static final ToolTier[] VALUES = values(); + + private final int speed; + + ToolTier(int speed) { + this.speed = speed; + } + + public int getSpeed() { + return speed; + } + + @Override + public String toString() { + return this.name().toLowerCase(Locale.ROOT); + } + + public static ToolTier getByName(@NonNull String name) { + String upperCase = name.toUpperCase(Locale.ROOT); + for (ToolTier tier : VALUES) { + if (tier.name().equals(upperCase)) { + return tier; + } + } + return null; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java b/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java new file mode 100644 index 000000000..a4479f871 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.components; + +import com.nukkitx.nbt.NbtMap; + +import java.util.Locale; + +public enum WearableSlot { + HEAD, + CHEST, + LEGS, + FEET; + + private final NbtMap slotNbt; + + WearableSlot() { + this.slotNbt = NbtMap.builder().putString("slot", "slot.armor." + this.name().toLowerCase(Locale.ROOT)).build(); + } + + public NbtMap getSlotNbt() { + return slotNbt; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java b/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java new file mode 100644 index 000000000..5878f5cc7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.exception; + +public class InvalidCustomMappingsFileException extends Exception { + public InvalidCustomMappingsFileException(Throwable cause) { + super(cause); + } + + public InvalidCustomMappingsFileException(String message) { + super(message); + } + + public InvalidCustomMappingsFileException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java b/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java new file mode 100644 index 000000000..eaf07c382 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.mappings; + +import com.fasterxml.jackson.databind.JsonNode; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.item.mappings.versions.MappingsReader; +import org.geysermc.geyser.item.mappings.versions.MappingsReader_v1; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public class MappingsConfigReader { + private final Int2ObjectMap mappingReaders = new Int2ObjectOpenHashMap<>(); + private final Path customMappingsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("custom_mappings"); + + public MappingsConfigReader() { + this.mappingReaders.put(1, new MappingsReader_v1()); + } + + public Path[] getCustomMappingsFiles() { + try { + return Files.walk(this.customMappingsDirectory) + .filter(child -> child.toString().endsWith(".json")) + .toArray(Path[]::new); + } catch (IOException e) { + return new Path[0]; + } + } + + public void loadMappingsFromJson(BiConsumer consumer) { + Path customMappingsDirectory = this.customMappingsDirectory; + if (!Files.exists(customMappingsDirectory)) { + try { + Files.createDirectories(customMappingsDirectory); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Failed to create custom mappings directory", e); + return; + } + } + + Path[] mappingsFiles = this.getCustomMappingsFiles(); + for (Path mappingsFile : mappingsFiles) { + this.readMappingsFromJson(mappingsFile, consumer); + } + } + + public void readMappingsFromJson(Path file, BiConsumer consumer) { + JsonNode mappingsRoot; + try { + mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile()); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e); + return; + } + + if (!mappingsRoot.has("format_version")) { + GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " is missing the format version field!"); + return; + } + + int formatVersion = mappingsRoot.get("format_version").asInt(); + if (!this.mappingReaders.containsKey(formatVersion)) { + GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion); + return; + } + + this.mappingReaders.get(formatVersion).readMappings(file, mappingsRoot, consumer); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java new file mode 100644 index 000000000..ef553f488 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.mappings.versions; + +import com.fasterxml.jackson.databind.JsonNode; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException; + +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public abstract class MappingsReader { + public abstract void readMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); + + public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException; + + protected CustomRenderOffsets fromJsonNode(JsonNode node) { + if (node == null || !node.isObject()) { + return null; + } + + return new CustomRenderOffsets( + getHandOffsets(node, "main_hand"), + getHandOffsets(node, "off_hand") + ); + } + + protected CustomRenderOffsets.Hand getHandOffsets(JsonNode node, String hand) { + JsonNode tmpNode = node.get(hand); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + return new CustomRenderOffsets.Hand( + getPerspectiveOffsets(tmpNode, "first_person"), + getPerspectiveOffsets(tmpNode, "third_person") + ); + } + + protected CustomRenderOffsets.Offset getPerspectiveOffsets(JsonNode node, String perspective) { + JsonNode tmpNode = node.get(perspective); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + return new CustomRenderOffsets.Offset( + getOffsetXYZ(tmpNode, "position"), + getOffsetXYZ(tmpNode, "rotation"), + getOffsetXYZ(tmpNode, "scale") + ); + } + + protected CustomRenderOffsets.OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) { + JsonNode tmpNode = node.get(offsetType); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + if (!tmpNode.has("x") || !tmpNode.has("y") || !tmpNode.has("z")) { + return null; + } + + return new CustomRenderOffsets.OffsetXYZ( + tmpNode.get("x").floatValue(), + tmpNode.get("y").floatValue(), + tmpNode.get("z").floatValue() + ); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java new file mode 100644 index 000000000..217ff844e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.mappings.versions; + +import com.fasterxml.jackson.databind.JsonNode; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException; + +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public class MappingsReader_v1 extends MappingsReader { + @Override + public void readMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + this.readItemMappings(file, mappingsRoot, consumer); + } + + public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + JsonNode itemsNode = mappingsRoot.get("items"); + + if (itemsNode != null && itemsNode.isObject()) { + itemsNode.fields().forEachRemaining(entry -> { + if (entry.getValue().isArray()) { + entry.getValue().forEach(data -> { + try { + CustomItemData customItemData = this.readItemMappingEntry(data); + consumer.accept(entry.getKey(), customItemData); + } catch (InvalidCustomMappingsFileException e) { + GeyserImpl.getInstance().getLogger().error("Error in custom mapping file: " + file.toString(), e); + } + }); + } + }); + } + } + + private CustomItemOptions readItemCustomItemOptions(JsonNode node) { + CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder(); + + JsonNode customModelData = node.get("custom_model_data"); + if (customModelData != null && customModelData.isInt()) { + customItemOptions.customModelData(customModelData.asInt()); + } + + JsonNode damagePredicate = node.get("damage_predicate"); + if (damagePredicate != null && damagePredicate.isInt()) { + customItemOptions.damagePredicate(damagePredicate.asInt()); + } + + JsonNode unbreakable = node.get("unbreakable"); + if (unbreakable != null && unbreakable.isBoolean()) { + customItemOptions.unbreakable(unbreakable.asBoolean()); + } + + return customItemOptions.build(); + } + + @Override + public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException { + if (node == null || !node.isObject()) { + throw new InvalidCustomMappingsFileException("Invalid item mappings entry"); + } + + String name = node.get("name").asText(); + if (name == null || name.isEmpty()) { + throw new InvalidCustomMappingsFileException("An item entry has no name"); + } + + CustomItemData.Builder customItemData = CustomItemData.builder() + .name(name) + .customItemOptions(this.readItemCustomItemOptions(node)); + + //The next entries are optional + if (node.has("display_name")) { + customItemData.displayName(node.get("display_name").asText()); + } + + if (node.has("icon")) { + customItemData.icon(node.get("icon").asText()); + } + + if (node.has("allow_offhand")) { + customItemData.allowOffhand(node.get("allow_offhand").asBoolean()); + } + + if (node.has("texture_size")) { + customItemData.textureSize(node.get("texture_size").asInt()); + } + + if (node.has("render_offsets")) { + JsonNode tmpNode = node.get("render_offsets"); + + customItemData.renderOffsets(fromJsonNode(tmpNode)); + } + + return customItemData.build(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 6030b6ebf..33eca67a9 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.pack.ResourcePack; @@ -38,7 +39,6 @@ import org.geysermc.geyser.pack.ResourcePackManifest; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; @@ -160,7 +160,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), "")); } - if (session.getItemMappings().getFurnaceMinecartData() != null) { + if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { // Allow custom items to work stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true)); } diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java index 08d6b5738..bef5c7418 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java @@ -26,12 +26,16 @@ package org.geysermc.geyser.pack; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; import org.geysermc.geyser.util.FileUtils; +import org.geysermc.geyser.text.GeyserLocale; import java.io.File; -import java.util.HashMap; -import java.util.Map; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -59,16 +63,33 @@ public class ResourcePack { * Loop through the packs directory and locate valid resource pack files */ public static void loadPacks() { - File directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs").toFile(); + Path directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs"); - if (!directory.exists()) { - directory.mkdir(); + if (!Files.exists(directory)) { + try { + Files.createDirectory(directory); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e); + } // As we just created the directory it will be empty return; } - for (File file : directory.listFiles()) { + List resourcePacks; + try { + resourcePacks = Files.walk(directory).collect(Collectors.toList()); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e); + return; + } + + GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks); + GeyserImpl.getInstance().eventBus().fire(event); + + for (Path path : event.resourcePacks()) { + File file = path.toFile(); + if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) { ResourcePack pack = new ResourcePack(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 9c370bc1c..f1dd054f5 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -113,11 +113,6 @@ public final class Registries { */ public static final SimpleMappedRegistry> ENTITY_DEFINITIONS = SimpleMappedRegistry.create(RegistryLoaders.empty(() -> new EnumMap<>(EntityType.class))); - /** - * A map containing all the extension loaders. - */ - public static final SimpleMappedRegistry EXTENSION_LOADERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); - /** * A map containing all Java entity identifiers and their respective Geyser definitions */ diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java new file mode 100644 index 000000000..64543272e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.populator; + +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.geysermc.geyser.item.GeyserCustomMappingData; +import org.geysermc.geyser.item.components.ToolBreakSpeedsUtils; +import org.geysermc.geyser.item.components.WearableSlot; +import org.geysermc.geyser.registry.type.GeyserMappingItem; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.NonVanillaItemRegistration; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.OptionalInt; + +public class CustomItemRegistryPopulator { + public static GeyserCustomMappingData registerCustomItem(String customItemName, GeyserMappingItem javaItem, CustomItemData customItemData, int bedrockId) { + StartGamePacket.ItemEntry startGamePacketItemEntry = new StartGamePacket.ItemEntry(customItemName, (short) bedrockId, true); + + NbtMapBuilder builder = createComponentNbt(customItemData, javaItem, customItemName, bedrockId); + ComponentItemData componentItemData = new ComponentItemData(customItemName, builder.build()); + + return new GeyserCustomMappingData(componentItemData, startGamePacketItemEntry, customItemName, bedrockId); + } + + static boolean initialCheck(String identifier, CustomItemData item, Map mappings) { + if (!mappings.containsKey(identifier)) { + GeyserImpl.getInstance().getLogger().error("Could not find the Java item to add custom item properties to for " + item.name()); + return false; + } + if (!item.customItemOptions().hasCustomItemOptions()) { + GeyserImpl.getInstance().getLogger().error("The custom item " + item.name() + " has no registration types"); + } + return true; + } + + public static NonVanillaItemRegistration registerCustomItem(NonVanillaCustomItemData customItemData, int customItemId) { + String customIdentifier = customItemData.identifier(); + + ItemMapping customItemMapping = ItemMapping.builder() + .javaIdentifier(customIdentifier) + .bedrockIdentifier(customIdentifier) + .javaId(customItemData.javaId()) + .bedrockId(customItemId) + .bedrockData(0) + .bedrockBlockId(0) + .stackSize(customItemData.stackSize()) + .toolType(customItemData.toolType()) + .toolTier(customItemData.toolTier()) + .translationString(customItemData.translationString()) + .maxDamage(customItemData.maxDamage()) + .repairMaterials(customItemData.repairMaterials()) + .hasSuspiciousStewEffect(false) + .customItemOptions(Object2IntMaps.emptyMap()) + .build(); + + NbtMapBuilder builder = createComponentNbt(customItemData, customItemData.identifier(), customItemId, + customItemData.creativeCategory(), customItemData.creativeGroup(), customItemData.isHat(), customItemData.isTool()); + ComponentItemData componentItemData = new ComponentItemData(customIdentifier, builder.build()); + + return new NonVanillaItemRegistration(componentItemData, customItemMapping); + } + + private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, GeyserMappingItem mapping, + String customItemName, int customItemId) { + NbtMapBuilder builder = NbtMap.builder(); + builder.putString("name", customItemName) + .putInt("id", customItemId); + + NbtMapBuilder itemProperties = NbtMap.builder(); + NbtMapBuilder componentBuilder = NbtMap.builder(); + + setupBasicItemInfo(mapping.getMaxDamage(), mapping.getStackSize(), mapping.getToolType() != null, customItemData, itemProperties, componentBuilder); + + boolean canDestroyInCreative = true; + if (mapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here. + canDestroyInCreative = computeToolProperties(mapping.getToolTier(), mapping.getToolType(), itemProperties, componentBuilder); + } + itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative); + + if (mapping.getArmorType() != null) { + computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder); + } + + computeRenderOffsets(false, customItemData, componentBuilder); + + componentBuilder.putCompound("item_properties", itemProperties.build()); + builder.putCompound("components", componentBuilder.build()); + + return builder; + } + + private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customItemData, String customItemName, + int customItemId, OptionalInt creativeCategory, + String creativeGroup, boolean isHat, boolean isTool) { + NbtMapBuilder builder = NbtMap.builder(); + builder.putString("name", customItemName) + .putInt("id", customItemId); + + NbtMapBuilder itemProperties = NbtMap.builder(); + NbtMapBuilder componentBuilder = NbtMap.builder(); + + setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), isTool, customItemData, itemProperties, componentBuilder); + + boolean canDestroyInCreative = true; + if (customItemData.toolType() != null) { // This is not using the isTool boolean because it is not just a render type here. + canDestroyInCreative = computeToolProperties(customItemData.toolTier(), customItemData.toolType(), itemProperties, componentBuilder); + } + itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative); + + String armorType = customItemData.armorType(); + if (armorType != null) { + computeArmorProperties(armorType, customItemData.protectionValue(), componentBuilder); + } + + computeRenderOffsets(isHat, customItemData, componentBuilder); + + if (creativeGroup != null) { + itemProperties.putString("creative_group", creativeGroup); + } + if (creativeCategory.isPresent()) { + itemProperties.putInt("creative_category", creativeCategory.getAsInt()); + } + + componentBuilder.putCompound("item_properties", itemProperties.build()); + builder.putCompound("components", componentBuilder.build()); + + return builder; + } + + private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean isTool, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + itemProperties.putCompound("minecraft:icon", NbtMap.builder() + .putString("texture", customItemData.icon()) + .build()); + componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build()); + + itemProperties.putBoolean("allow_off_hand", customItemData.allowOffhand()); + itemProperties.putBoolean("hand_equipped", isTool); + itemProperties.putInt("max_stack_size", stackSize); + if (maxDamage > 0) { + componentBuilder.putCompound("minecraft:durability", NbtMap.builder() + .putCompound("damage_chance", NbtMap.builder() + .putInt("max", 1) + .putInt("min", 1) + .build()) + .putInt("max_durability", maxDamage) + .build()); + itemProperties.putBoolean("use_duration", true); + } + } + + /** + * @return can destroy in creative + */ + private static boolean computeToolProperties(String toolTier, String toolType, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + boolean canDestroyInCreative = true; + float miningSpeed = 1.0f; + + if (toolType.equals("shears")) { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShearsDigger(15)); + } else { + int toolSpeed = ToolBreakSpeedsUtils.toolTierToSpeed(toolTier); + switch (toolType) { + case "sword" -> { + miningSpeed = 1.5f; + canDestroyInCreative = false; + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getSwordDigger(toolSpeed)); + componentBuilder.putCompound("minecraft:weapon", NbtMap.EMPTY); + } + case "pickaxe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getPickaxeDigger(toolSpeed, toolTier)); + setItemTag(componentBuilder, "pickaxe"); + } + case "axe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getAxeDigger(toolSpeed)); + setItemTag(componentBuilder, "axe"); + } + case "shovel" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShovelDigger(toolSpeed)); + setItemTag(componentBuilder, "shovel"); + } + case "hoe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getHoeDigger(toolSpeed)); + setItemTag(componentBuilder, "hoe"); + } + } + } + + itemProperties.putBoolean("hand_equipped", true); + itemProperties.putFloat("mining_speed", miningSpeed); + + return canDestroyInCreative; + } + + private static void computeArmorProperties(String armorType, int protectionValue, NbtMapBuilder componentBuilder) { + switch (armorType) { + case "boots" -> { + componentBuilder.putString("minecraft:render_offsets", "boots"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "chestplate" -> { + componentBuilder.putString("minecraft:render_offsets", "chestplates"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "leggings" -> { + componentBuilder.putString("minecraft:render_offsets", "leggings"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "helmet" -> { + componentBuilder.putString("minecraft:render_offsets", "helmets"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + } + } + + private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) { + if (isHat) { + componentBuilder.remove("minecraft:render_offsets"); + componentBuilder.putString("minecraft:render_offsets", "helmets"); + + componentBuilder.remove("minecraft:wearable"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt()); + } + + CustomRenderOffsets renderOffsets = customItemData.renderOffsets(); + if (renderOffsets != null) { + componentBuilder.remove("minecraft:render_offsets"); + componentBuilder.putCompound("minecraft:render_offsets", toNbtMap(renderOffsets)); + } else if (customItemData.textureSize() != 16 && !componentBuilder.containsKey("minecraft:render_offsets")) { + float scale1 = (float) (0.075 / (customItemData.textureSize() / 16f)); + float scale2 = (float) (0.125 / (customItemData.textureSize() / 16f)); + float scale3 = (float) (0.075 / (customItemData.textureSize() / 16f * 2.4f)); + + componentBuilder.putCompound("minecraft:render_offsets", + NbtMap.builder().putCompound("main_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale3, scale3, scale3)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()) + .putCompound("off_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale1, scale2, scale1)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()).build()); + } + } + + private static NbtMap toNbtMap(CustomRenderOffsets renderOffsets) { + NbtMapBuilder builder = NbtMap.builder(); + + CustomRenderOffsets.Hand mainHand = renderOffsets.mainHand(); + if (mainHand != null) { + NbtMap nbt = toNbtMap(mainHand); + if (nbt != null) { + builder.putCompound("main_hand", nbt); + } + } + CustomRenderOffsets.Hand offhand = renderOffsets.offhand(); + if (offhand != null) { + NbtMap nbt = toNbtMap(offhand); + if (nbt != null) { + builder.putCompound("off_hand", nbt); + } + } + + return builder.build(); + } + + private static NbtMap toNbtMap(CustomRenderOffsets.Hand hand) { + NbtMap firstPerson = toNbtMap(hand.firstPerson()); + NbtMap thirdPerson = toNbtMap(hand.thirdPerson()); + + if (firstPerson == null && thirdPerson == null) { + return null; + } + + NbtMapBuilder builder = NbtMap.builder(); + if (firstPerson != null) { + builder.putCompound("first_person", firstPerson); + } + if (thirdPerson != null) { + builder.putCompound("third_person", thirdPerson); + } + + return builder.build(); + } + + private static NbtMap toNbtMap(@Nullable CustomRenderOffsets.Offset offset) { + if (offset == null) { + return null; + } + + CustomRenderOffsets.OffsetXYZ position = offset.position(); + CustomRenderOffsets.OffsetXYZ rotation = offset.rotation(); + CustomRenderOffsets.OffsetXYZ scale = offset.scale(); + + if (position == null && rotation == null && scale == null) { + return null; + } + + NbtMapBuilder builder = NbtMap.builder(); + if (position != null) { + builder.putList("position", NbtType.FLOAT, toList(position)); + } + if (rotation != null) { + builder.putList("rotation", NbtType.FLOAT, toList(rotation)); + } + if (scale != null) { + builder.putList("scale", NbtType.FLOAT, toList(scale)); + } + + return builder.build(); + } + + private static List toList(CustomRenderOffsets.OffsetXYZ xyz) { + return List.of(xyz.x(), xyz.y(), xyz.z()); + } + + private static void setItemTag(NbtMapBuilder builder, String tag) { + builder.putList("item_tags", NbtType.STRING, List.of("minecraft:is_" + tag)); + } + + private static NbtMap xyzToScaleList(float x, float y, float z) { + return NbtMap.builder().putList("scale", NbtType.FLOAT, List.of(x, y, z)).build(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index f3d936b2e..22669fd79 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -27,6 +27,8 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; @@ -35,14 +37,22 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.ints.*; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.*; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; import org.geysermc.geyser.inventory.item.StoredItemMappings; +import org.geysermc.geyser.item.GeyserCustomMappingData; +import org.geysermc.geyser.item.mappings.MappingsConfigReader; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.*; @@ -78,6 +88,58 @@ public class ItemRegistryPopulator { throw new AssertionError("Unable to load Java runtime item IDs", e); } + boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); + + Multimap customItems = MultimapBuilder.hashKeys().hashSetValues().build(); + List nonVanillaCustomItems; + + MappingsConfigReader mappingsConfigReader = new MappingsConfigReader(); + if (customItemsAllowed) { + // Load custom items from mappings files + mappingsConfigReader.loadMappingsFromJson((key, item) -> { + if (CustomItemRegistryPopulator.initialCheck(key, item, items)) { + customItems.get(key).add(item); + } + }); + + nonVanillaCustomItems = new ObjectArrayList<>(); + GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEvent(customItems, nonVanillaCustomItems) { + @Override + public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) { + if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) { + customItems.get(identifier).add(customItemData); + return true; + } + return false; + } + + @Override + public boolean register(@NonNull NonVanillaCustomItemData customItemData) { + if (customItemData.identifier().startsWith("minecraft:")) { + GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + + " is attempting to masquerade as a vanilla Minecraft item!"); + return false; + } + + if (customItemData.javaId() < items.size()) { + // Attempting to overwrite an item that already exists in the protocol + GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + + " is attempting to overwrite a vanilla Minecraft item!"); + return false; + } + nonVanillaCustomItems.add(customItemData); + return true; + } + }); + } else { + nonVanillaCustomItems = Collections.emptyList(); + } + + int customItemCount = customItems.size() + nonVanillaCustomItems.size(); + if (customItemCount > 0) { + GeyserImpl.getInstance().getLogger().info("Registered " + customItemCount + " custom items"); + } + // We can reduce some operations as Java information is the same across all palette versions boolean firstMappingsPass = true; Int2IntMap dyeColors = new FixedInt2IntMap(); @@ -99,11 +161,20 @@ public class ItemRegistryPopulator { throw new AssertionError("Unable to load Bedrock runtime item IDs", e); } + // Used for custom items + int nextFreeBedrockId = 0; + List componentItemData = new ObjectArrayList<>(); + Map entries = new Object2ObjectOpenHashMap<>(); for (PaletteItem entry : itemEntries) { - entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) entry.getId())); - bedrockIdentifierToId.put(entry.getName(), entry.getId()); + int id = entry.getId(); + if (id >= nextFreeBedrockId) { + nextFreeBedrockId = id + 1; + } + + entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) id)); + bedrockIdentifierToId.put(entry.getName(), id); } Object2IntMap bedrockBlockIdOverrides = new Object2IntOpenHashMap<>(); @@ -203,18 +274,20 @@ public class ItemRegistryPopulator { int itemIndex = 0; int javaFurnaceMinecartId = 0; - boolean usingFurnaceMinecart = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); Set javaOnlyItems = new ObjectOpenHashSet<>(); Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg", "minecraft:bundle"); - if (!usingFurnaceMinecart) { + if (!customItemsAllowed) { javaOnlyItems.add("minecraft:furnace_minecart"); } // Java-only items for this version javaOnlyItems.addAll(palette.getValue().additionalTranslatedItems().keySet()); + Int2ObjectMap customIdMappings = new Int2ObjectOpenHashMap<>(); + Set registeredItemNames = new ObjectOpenHashSet<>(); // This is used to check for duplicate item names + for (Map.Entry entry : items.entrySet()) { String javaIdentifier = entry.getKey().intern(); GeyserMappingItem mappingItem; @@ -226,7 +299,7 @@ public class ItemRegistryPopulator { mappingItem = entry.getValue(); } - if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { + if (customItemsAllowed && javaIdentifier.equals("minecraft:furnace_minecart")) { javaFurnaceMinecartId = itemIndex; itemIndex++; // Will be added later @@ -380,12 +453,46 @@ public class ItemRegistryPopulator { .toolTier(""); } } + if (javaOnlyItems.contains(javaIdentifier)) { // These items don't exist on Bedrock, so set up a variable that indicates they should have custom names mappingBuilder = mappingBuilder.translationString((bedrockBlockId != -1 ? "block." : "item.") + entry.getKey().replace(":", ".")); GeyserImpl.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated."); } + // Add the custom item properties, if applicable + Object2IntMap customItemOptions; + Collection customItemsToLoad = customItems.get(javaIdentifier); + if (customItemsAllowed && !customItemsToLoad.isEmpty()) { + customItemOptions = new Object2IntOpenHashMap<>(customItemsToLoad.size()); + + for (CustomItemData customItem : customItemsToLoad) { + int customProtocolId = nextFreeBedrockId++; + + String customItemName = "geyser_custom:" + customItem.name(); + if (!registeredItemNames.add(customItemName)) { + if (firstMappingsPass) { + GeyserImpl.getInstance().getLogger().error("Custom item name '" + customItem.name() + "' already exists and was registered again! Skipping..."); + } + continue; + } + + GeyserCustomMappingData customMapping = CustomItemRegistryPopulator.registerCustomItem( + customItemName, mappingItem, customItem, customProtocolId + ); + // StartGamePacket entry - needed for Bedrock to recognize the item through the protocol + entries.put(customMapping.stringId(), customMapping.startGamePacketItemEntry()); + // ComponentItemData - used to register some custom properties + componentItemData.add(customMapping.componentItemData()); + customItemOptions.put(customItem.customItemOptions(), customProtocolId); + + customIdMappings.put(customMapping.integerId(), customMapping.stringId()); + } + } else { + customItemOptions = Object2IntMaps.emptyMap(); + } + mappingBuilder.customItemOptions(customItemOptions); + ItemMapping mapping = mappingBuilder.build(); if (javaIdentifier.contains("boat")) { @@ -436,12 +543,12 @@ public class ItemRegistryPopulator { .bedrockData(0) .bedrockBlockId(-1) .stackSize(1) + .customItemOptions(Object2IntMaps.emptyMap()) .build(); - ComponentItemData furnaceMinecartData = null; - if (usingFurnaceMinecart) { + if (customItemsAllowed) { // Add the furnace minecart as a custom item - int furnaceMinecartId = mappings.size() + 1; + int furnaceMinecartId = nextFreeBedrockId++; entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); @@ -456,7 +563,7 @@ public class ItemRegistryPopulator { .build()); creativeItems.add(ItemData.builder() - .netId(netId) + .netId(netId++) .id(furnaceMinecartId) .count(1).build()); @@ -492,7 +599,36 @@ public class ItemRegistryPopulator { componentBuilder.putCompound("item_properties", itemProperties.build()); builder.putCompound("components", componentBuilder.build()); - furnaceMinecartData = new ComponentItemData("geysermc:furnace_minecart", builder.build()); + componentItemData.add(new ComponentItemData("geysermc:furnace_minecart", builder.build())); + + // Register any completely custom items given to us + IntSet registeredJavaIds = new IntOpenHashSet(); // Used to check for duplicate item java ids + for (NonVanillaCustomItemData customItem : nonVanillaCustomItems) { + if (!registeredJavaIds.add(customItem.javaId())) { + if (firstMappingsPass) { + GeyserImpl.getInstance().getLogger().error("Custom item java id " + customItem.javaId() + " already exists and was registered again! Skipping..."); + } + continue; + } + + int customItemId = nextFreeBedrockId++; + NonVanillaItemRegistration registration = CustomItemRegistryPopulator.registerCustomItem(customItem, customItemId); + + componentItemData.add(registration.componentItemData()); + ItemMapping mapping = registration.mapping(); + while (mapping.getJavaId() >= mappings.size()) { + // Fill with empty to get to the correct size + mappings.add(ItemMapping.AIR); + } + mappings.set(mapping.getJavaId(), mapping); + + if (customItem.creativeGroup() != null || customItem.creativeCategory().isPresent()) { + creativeItems.add(ItemData.builder() + .id(customItemId) + .netId(netId++) + .count(1).build()); + } + } } ItemMappings itemMappings = ItemMappings.builder() @@ -506,8 +642,9 @@ public class ItemRegistryPopulator { .boatIds(boats) .spawnEggIds(spawnEggs) .carpets(carpets) - .furnaceMinecartData(furnaceMinecartData) + .componentItemData(componentItemData) .lodestoneCompass(lodestoneEntry) + .customIdMappings(customIdMappings) .build(); Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings); diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java index e144fcd4f..af289bcda 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java @@ -29,8 +29,14 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; import org.geysermc.geyser.api.provider.BuilderProvider; import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.item.GeyserCustomItemData; +import org.geysermc.geyser.item.GeyserCustomItemOptions; +import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData; import org.geysermc.geyser.registry.ProviderRegistries; import org.geysermc.geyser.registry.SimpleMappedRegistry; @@ -46,6 +52,9 @@ public class GeyserBuilderProvider extends AbstractProvider implements BuilderPr @Override public void registerProviders(Map, ProviderSupplier> providers) { providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); + providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); + providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); + providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java index 9d06fd3a9..6c65f1c34 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java @@ -42,6 +42,8 @@ public class GeyserMappingItem { @JsonProperty("stack_size") int stackSize = 64; @JsonProperty("tool_type") String toolType; @JsonProperty("tool_tier") String toolTier; + @JsonProperty("armor_type") String armorType; + @JsonProperty("protection_value") int protectionValue; @JsonProperty("max_damage") int maxDamage = 0; @JsonProperty("repair_materials") List repairMaterials; @JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false; diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java index 332ab0167..12ba7d208 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java @@ -25,9 +25,12 @@ package org.geysermc.geyser.registry.type; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Value; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.BlockRegistries; @@ -39,7 +42,7 @@ import java.util.Set; public class ItemMapping { public static final ItemMapping AIR = new ItemMapping("minecraft:air", "minecraft:air", 0, 0, 0, BlockRegistries.BLOCKS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(), - 64, null, null, null, 0, null, false); + 64, null, null, null, Object2IntMaps.emptyMap(), 0, null, false); String javaIdentifier; String bedrockIdentifier; @@ -59,6 +62,8 @@ public class ItemMapping { String translationString; + Object2IntMap customItemOptions; + int maxDamage; Set repairMaterials; @@ -91,4 +96,4 @@ public class ItemMapping { public boolean isTool() { return this.toolType != null; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java index 3072568f3..c4e967dff 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Builder; import lombok.Value; @@ -36,7 +37,6 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.StoredItemMappings; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,7 +67,8 @@ public class ItemMappings { IntList spawnEggIds; List carpets; - @Nullable ComponentItemData furnaceMinecartData; + List componentItemData; + Int2ObjectMap customIdMappings; /** * Gets an {@link ItemMapping} from the given {@link ItemStack}. diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java b/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java new file mode 100644 index 000000000..e2063f41a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.type; + +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; + +/** + * The return data of a successful registration of a custom item. + */ +public record NonVanillaItemRegistration(ComponentItemData componentItemData, ItemMapping mapping) { +} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 49261500b..99e29dd21 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -594,9 +594,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Set the hardcoded shield ID to the ID we just defined in StartGamePacket upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId()); - if (this.itemMappings.getFurnaceMinecartData() != null) { + if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { ItemComponentPacket componentPacket = new ItemComponentPacket(); - componentPacket.getItems().add(this.itemMappings.getFurnaceMinecartData()); + componentPacket.getItems().addAll(itemMappings.getComponentItemData()); upstream.sendPacket(componentPacket); } @@ -1465,7 +1465,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); + startGamePacket.setItemEntries(this.itemMappings.getItemEntries()); + startGamePacket.setVanillaVersion("*"); startGamePacket.setInventoriesServerAuthoritative(true); startGamePacket.setServerEngine(""); // Do we want to fill this in? diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java index b5778e681..a0da82648 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java @@ -79,8 +79,7 @@ public class CompassTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index b9bfdd576..0a2ab57df 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -34,9 +34,12 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -122,7 +125,7 @@ public abstract class ItemTranslator { } } if (itemStack.getNbt().isEmpty()) { - // Otherwise, seems to causes issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy + // Otherwise, seems to cause issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null); } } @@ -170,6 +173,8 @@ public abstract class ItemTranslator { builder.blockRuntimeId(bedrockItem.getBedrockBlockId()); } + translateCustomItem(nbt, builder, bedrockItem); + if (nbt != null) { // Translate the canDestroy and canPlaceOn Java NBT ListTag canDestroy = nbt.get("CanDestroy"); @@ -292,6 +297,10 @@ public abstract class ItemTranslator { if (itemStack.getNbt() != null) { builder.tag(this.translateNbtToBedrock(itemStack.getNbt())); } + + CompoundTag nbt = itemStack.getNbt(); + translateCustomItem(nbt, builder, mapping); + return builder; } @@ -416,7 +425,7 @@ public abstract class ItemTranslator { if (object instanceof byte[]) { return new ByteArrayTag(name, (byte[]) object); } - + if (object instanceof Byte) { return new ByteTag(name, (byte) object); } @@ -524,6 +533,48 @@ public abstract class ItemTranslator { return tag; } + /** + * Translates the custom model data of an item + */ + private static void translateCustomItem(CompoundTag nbt, ItemData.Builder builder, ItemMapping mapping) { + int bedrockId = getCustomItem(nbt, mapping); + if (bedrockId != -1) { + builder.id(bedrockId); + } + } + + private static int getCustomItem(CompoundTag nbt, ItemMapping mapping) { + if (nbt == null) { + return -1; + } + Object2IntMap customMappings = mapping.getCustomItemOptions(); + if (customMappings.isEmpty()) { + return -1; + } + int customModelData = nbt.get("CustomModelData") instanceof IntTag customModelDataTag ? customModelDataTag.getValue() : 0; + TriState unbreakable = TriState.fromBoolean(nbt.get("Unbreakable") instanceof ByteTag unbreakableTag && unbreakableTag.getValue() == 1); + int damage = nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0; + for (Object2IntMap.Entry mappingTypes : customMappings.object2IntEntrySet()) { + CustomItemOptions options = mappingTypes.getKey(); + + TriState unbreakableOption = options.unbreakable(); + if (unbreakableOption == unbreakable) { // Implementation note: if the option is NOT_SET then this comparison will always be false because of how the item unbreaking TriState is created + return mappingTypes.getIntValue(); + } + + OptionalInt customModelDataOption = options.customModelData(); + if (customModelDataOption.isPresent() && customModelDataOption.getAsInt() == customModelData) { + return mappingTypes.getIntValue(); + } + + OptionalInt damagePredicate = options.damagePredicate(); + if (damagePredicate.isPresent() && damagePredicate.getAsInt() == damage) { + return mappingTypes.getIntValue(); + } + } + return -1; + } + /** * Checks if an {@link ItemStack} is equal to another item stack * diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index f12355ce6..3e814a098 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -74,8 +74,7 @@ public class PotionTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index 5dc525e56..bbf598ecd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -81,8 +81,7 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")) .collect(Collectors.toList()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java index 0d1538eb8..95dd07f22 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java @@ -76,8 +76,7 @@ public class BannerTranslator extends NbtItemStackTranslator { } public BannerTranslator() { - appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java index 1c9ded0c1..58b59d82e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java @@ -168,11 +168,12 @@ public class JavaMerchantOffersTranslator extends PacketTranslator Date: Sat, 2 Jul 2022 12:30:23 -0500 Subject: [PATCH 071/125] Use an immutable view in GeyserDefineCommandsEvent Methods to properly register/unregister commands are provided in the command manager --- .../geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java | 3 +-- .../java/org/geysermc/geyser/command/GeyserCommandManager.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java index 1a2c7b4d4..e506c0ca0 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -36,8 +36,7 @@ import java.util.Map; * Called when commands are defined within Geyser. * * @param commandManager the command manager - * @param commands a mutable list of the currently - * registered default commands + * @param commands an immutable view of the default commands */ public record GeyserDefineCommandsEvent(@NonNull CommandManager commandManager, @NonNull Map commands) implements Event { } diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index fea57efa1..c6b9cbdd2 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -70,7 +70,7 @@ public abstract class GeyserCommandManager extends CommandManager { register(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } - this.geyser.eventBus().fire(new GeyserDefineCommandsEvent(this, this.commands)); + this.geyser.eventBus().fire(new GeyserDefineCommandsEvent(this, this.commands())); } @Override From f9fd7cb831fc909ed4291a7ab9b652987952481f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 2 Jul 2022 12:42:31 -0500 Subject: [PATCH 072/125] Fix Geyser not working in IDE --- .../java/org/geysermc/api/GeyserApiBase.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 12 ++++++++-- .../command/defaults/VersionCommand.java | 2 +- .../extension/GeyserExtensionLoader.java | 22 ++++++++----------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index bf38f58b9..e5105b1be 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -78,7 +78,7 @@ public interface GeyserApiBase { * @return the major API version. Bumped whenever a significant breaking change or feature addition is added. */ default int majorApiVersion() { - return 0; + return 1; } /** diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 8f3156abe..4c3cf6fca 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -116,7 +116,7 @@ public class GeyserImpl implements GeyserApi { public static final String GIT_VERSION = "${gitVersion}"; // A fallback for running in IDEs public static final String VERSION = "${version}"; // A fallback for running in IDEs - public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}"); + public static final String BUILD_NUMBER = "${buildNumber}"; public static final String BRANCH = "${branch}"; /** @@ -318,7 +318,7 @@ public class GeyserImpl implements GeyserApi { pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); - this.newsHandler = new NewsHandler(BRANCH, BUILD_NUMBER); + this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether @@ -632,6 +632,14 @@ public class GeyserImpl implements GeyserApi { return this.getConfig().getMaxPlayers(); } + public int buildNumber() { + if (!this.isProductionEnvironment()) { + return 0; + } + + return Integer.parseInt(BUILD_NUMBER); + } + public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 9006ca959..fbe4fb4f6 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -79,7 +79,7 @@ public class VersionCommand extends GeyserCommand { URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); if (buildXML.startsWith("")) { int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim()); - int buildNum = GeyserImpl.BUILD_NUMBER; + int buildNum = this.geyser.buildNumber(); if (latestBuildNum == buildNum) { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); } else { diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 137a2005e..55f018ddb 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.api.Geyser; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.ExtensionEventBus; import org.geysermc.geyser.api.extension.*; @@ -125,14 +126,6 @@ public class GeyserExtensionLoader extends ExtensionLoader { @Override protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) { - // noinspection ConstantConditions - if (!GeyserImpl.VERSION.contains(".")) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_version_number")); - return; - } - - String[] apiVersion = GeyserImpl.VERSION.split("\\."); - try { if (Files.notExists(EXTENSION_DIRECTORY)) { Files.createDirectory(EXTENSION_DIRECTORY); @@ -166,27 +159,30 @@ public class GeyserExtensionLoader extends ExtensionLoader { return; } + int majorVersion = Geyser.api().majorApiVersion(); + int minorVersion = Geyser.api().minorApiVersion(); + try { // Check the format: majorVersion.minorVersion.patch if (!API_VERSION_PATTERN.matcher(description.apiVersion()).matches()) { throw new IllegalArgumentException(); } } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion[0] + "." + apiVersion[1])); + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, majorVersion + "." + minorVersion)); return; } String[] versionArray = description.apiVersion().split("\\."); // Completely different API version - if (!Objects.equals(Integer.valueOf(versionArray[0]), Integer.valueOf(apiVersion[0]))) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + if (Integer.parseInt(versionArray[0]) != majorVersion) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, majorVersion + "." + minorVersion)); return; } // If the extension requires new API features, being backwards compatible - if (Integer.parseInt(versionArray[1]) > Integer.parseInt(apiVersion[1])) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, apiVersion[0] + "." + apiVersion[1])); + if (Integer.parseInt(versionArray[1]) > minorVersion) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, majorVersion + "." + minorVersion)); return; } From 897c4dcfecda8a416028c8eda2a1f8997ef93a49 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 9 Jul 2022 18:39:02 -0400 Subject: [PATCH 073/125] Changes --- .../org/geysermc/geyser/api/GeyserApi.java | 38 ++------- .../geysermc/geyser/api/command/Command.java | 2 +- .../downstream/ServerDefineCommandsEvent.java | 6 +- .../api/item/custom/CustomItemData.java | 2 +- .../api/item/custom/CustomItemOptions.java | 2 +- .../item/custom/NonVanillaCustomItemData.java | 2 +- .../geysermc/geyser/api/network/AuthType.java | 18 ++-- .../geyser/api/network/BedrockListener.java | 4 +- .../geyser/api/provider/BuilderProvider.java | 47 ----------- .../geyser/api/provider/Provider.java | 29 ------- .../geyser/api/provider/ProviderManager.java | 41 --------- .../standalone/GeyserStandaloneBootstrap.java | 2 +- .../geysermc/geyser/FloodgateKeyLoader.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 84 ++++--------------- .../configuration/GeyserConfiguration.java | 32 +++---- .../GeyserJacksonConfiguration.java | 49 ++++++++++- .../geyser/network/BedrockListenerImpl.java | 31 ------- .../network/ConnectorServerEventHandler.java | 6 +- .../geyser/network/QueryPacketHandler.java | 6 +- .../geyser/network/RemoteServerImpl.java | 32 ------- .../geyser/network/UpstreamPacketHandler.java | 4 +- .../ping/GeyserLegacyPingPassthrough.java | 4 +- .../geyser/registry/ProviderRegistries.java | 42 ---------- .../geysermc/geyser/registry/Registries.java | 13 +-- .../loader/ProviderRegistryLoader.java | 23 +++-- .../registry/provider/AbstractProvider.java | 39 --------- .../provider/GeyserBuilderProvider.java | 70 ---------------- .../provider/GeyserProviderManager.java | 36 -------- .../geyser/session/GeyserSession.java | 4 +- .../org/geysermc/geyser/skin/SkinManager.java | 2 +- 30 files changed, 144 insertions(+), 528 deletions(-) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java delete mode 100644 core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java delete mode 100644 core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java delete mode 100644 core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java delete mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java delete mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java delete mode 100644 core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index d6cb3c25a..16bfe7070 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -35,7 +35,6 @@ import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; -import org.geysermc.geyser.api.provider.ProviderManager; import java.util.List; import java.util.UUID; @@ -44,24 +43,6 @@ import java.util.UUID; * Represents the API used in Geyser. */ public interface GeyserApi extends GeyserApiBase { - /** - * Shuts down the current Geyser instance. - */ - void shutdown(); - - /** - * Reloads the current Geyser instance. - */ - void reload(); - - /** - * Gets if this Geyser instance is running in an IDE. This only needs to be used in cases where files - * expected to be in a jarfile are not present. - * - * @return if we are in a production environment - */ - boolean isProductionEnvironment(); - /** * {@inheritDoc} */ @@ -101,11 +82,15 @@ public interface GeyserApi extends GeyserApiBase { CommandManager commandManager(); /** - * Gets the {@link ProviderManager}. + * Provides an implementation for the specified API type. * - * @return the provider manager + * @param apiClass the builder class + * @param the implementation type + * @param the API type + * @return the builder instance */ - ProviderManager providerManager(); + @NonNull + R provider(@NonNull Class apiClass, @Nullable Object... args); /** * Gets the {@link EventBus} for handling @@ -131,15 +116,6 @@ public interface GeyserApi extends GeyserApiBase { */ BedrockListener bedrockListener(); - /** - * Gets the maximum number of players that - * can join this Geyser instance. - * - * @return the maximum number of players that - * can join this Geyser instance - */ - int maxPlayers(); - /** * Gets the current {@link GeyserApiBase} instance. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java index f9ae68ab3..0ad296669 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -105,7 +105,7 @@ public interface Command { } static Command.Builder builder(Class sourceType) { - return GeyserApi.api().providerManager().builderProvider().provideBuilder(Builder.class, sourceType); + return GeyserApi.api().provider(Builder.class, sourceType); } interface Builder { diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java index ba7254c94..06412eb4c 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -45,9 +45,10 @@ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancel } /** - * A mutable collection of the commands sent over. + * A collection of commands sent from the server. Any element in this collection can be removed, but no element can + * be added. * - * @return a mutable collection of the commands sent over + * @return a collection of the commands sent over */ @NonNull public Set commands() { @@ -65,7 +66,6 @@ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancel } public interface CommandInfo { - /** * Gets the name of the command. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java index 7391c0680..17763fb77 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java @@ -83,7 +83,7 @@ public interface CustomItemData { @Nullable CustomRenderOffsets renderOffsets(); static CustomItemData.Builder builder() { - return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemData.Builder.class); + return GeyserApi.api().provider(CustomItemData.Builder.class); } interface Builder { diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java index 037f2f05e..ec26a6e37 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java @@ -68,7 +68,7 @@ public interface CustomItemOptions { } static CustomItemOptions.Builder builder() { - return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemOptions.Builder.class); + return GeyserApi.api().provider(CustomItemOptions.Builder.class); } interface Builder { diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java index 1df94f7ea..d2cef637a 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java @@ -137,7 +137,7 @@ public interface NonVanillaCustomItemData extends CustomItemData { boolean isTool(); static NonVanillaCustomItemData.Builder builder() { - return GeyserApi.api().providerManager().builderProvider().provideBuilder(NonVanillaCustomItemData.Builder.class); + return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class); } interface Builder extends CustomItemData.Builder { diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java index 5e1c2539d..3176f3384 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java @@ -25,16 +25,22 @@ package org.geysermc.geyser.api.network; +import java.util.Locale; + +/** + * The authentication types that a Java server can be on connection. + */ public enum AuthType { OFFLINE, ONLINE, + /** + * The internal name for connecting to an online mode server without needing a Java account. The presence of this + * authentication type does not necessarily mean the Floodgate plugin is installed; it only means that this + * authentication type will be attempted. + */ FLOODGATE; - public static final AuthType[] VALUES = values(); - - public static AuthType getById(int id) { - return id < VALUES.length ? VALUES[id] : OFFLINE; - } + private static final AuthType[] VALUES = values(); /** * Convert the AuthType string (from config) to the enum, ONLINE on fail @@ -44,7 +50,7 @@ public enum AuthType { * @return The converted AuthType */ public static AuthType getByName(String name) { - String upperCase = name.toUpperCase(); + String upperCase = name.toUpperCase(Locale.ROOT); for (AuthType type : VALUES) { if (type.name().equals(upperCase)) { return type; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java index 648f83e47..58a597eb6 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -48,7 +48,7 @@ public interface BedrockListener { int port(); /** - * Gets the primary MOTD shown to Bedrock players. + * Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. *

* This is the first line that will be displayed. * @@ -57,7 +57,7 @@ public interface BedrockListener { String primaryMotd(); /** - * Gets the secondary MOTD shown to Bedrock players. + * Gets the secondary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. *

* This is the second line that will be displayed. * diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java deleted file mode 100644 index b05a36f88..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/BuilderProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.provider; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Allows for obtaining instances of a builder that are - * used for constructing various data. - */ -public interface BuilderProvider extends Provider { - - /** - * Provides a builder for the specified builder type. - * - * @param builderClass the builder class - * @param the resulting type - * @param the builder type - * @return the builder instance - */ - @NonNull - B provideBuilder(@NonNull Class builderClass, @Nullable Object... args); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java deleted file mode 100644 index 4463efeed..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/Provider.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.provider; - -public interface Provider { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java deleted file mode 100644 index 48ec99a3f..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/provider/ProviderManager.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.provider; - -/** - * Holds a record of every {@link Provider} available - * that allows for accessing various information throughout - * the API. - */ -public interface ProviderManager { - - /** - * Returns the {@link BuilderProvider}. - * - * @return the builder provider - */ - BuilderProvider builderProvider(); -} diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index a89b18d1e..44194d75c 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -197,7 +197,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { handleArgsConfigOptions(); - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug geyserConfig.getRemote().setAddress("127.0.0.1"); } diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index aaf45ce35..8b51228c8 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -34,7 +34,7 @@ import java.nio.file.Path; public class FloodgateKeyLoader { public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { - if (config.getRemote().getAuthType() != AuthType.FLOODGATE) { + if (config.getRemote().authType() != AuthType.FLOODGATE) { return geyserDataFolder.resolve(config.getFloodgateKeyFile()); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 4c3cf6fca..146cb985f 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -64,14 +64,10 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; -import org.geysermc.geyser.network.BedrockListenerImpl; import org.geysermc.geyser.network.ConnectorServerEventHandler; -import org.geysermc.geyser.network.GameProtocol; -import org.geysermc.geyser.network.RemoteServerImpl; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.registry.provider.GeyserProviderManager; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; @@ -90,7 +86,6 @@ import javax.naming.directory.InitialDirContext; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -148,10 +143,6 @@ public class GeyserImpl implements GeyserApi { private final EventBus eventBus; private final GeyserExtensionManager extensionManager; - private final GeyserProviderManager providerManager = new GeyserProviderManager(); - - private final RemoteServer remoteServer; - private final BedrockListener bedrockListener; private Metrics metrics; @@ -215,22 +206,6 @@ public class GeyserImpl implements GeyserApi { } } - this.remoteServer = new RemoteServerImpl( - config.getRemote().getAddress(), - config.getRemote().getPort(), - GameProtocol.getJavaProtocolVersion(), - GameProtocol.getJavaMinecraftVersion(), - config.getRemote().getAuthType() - ); - - this.bedrockListener = new BedrockListenerImpl( - config.getBedrock().getAddress(), - config.getBedrock().getPort(), - config.getBedrock().getMotd1(), - config.getBedrock().getMotd2(), - config.getBedrock().getServerName() - ); - double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " "; if (isGui) { @@ -243,7 +218,7 @@ public class GeyserImpl implements GeyserApi { if (platformType == PlatformType.STANDALONE) { logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); - } else if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + } else if (config.getRemote().authType() == AuthType.FLOODGATE) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } } @@ -260,7 +235,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); - if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { + if (platformType != PlatformType.STANDALONE && config.getRemote().address().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); @@ -272,7 +247,7 @@ public class GeyserImpl implements GeyserApi { config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress()); } } - String remoteAddress = config.getRemote().getAddress(); + String remoteAddress = config.getRemote().address(); // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { int remotePort; @@ -298,24 +273,6 @@ public class GeyserImpl implements GeyserApi { // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; - String branch = "unknown"; - int buildNumber = -1; - if (this.isProductionEnvironment()) { - try (InputStream stream = bootstrap.getResource("git.properties")) { - Properties gitProperties = new Properties(); - gitProperties.load(stream); - branch = gitProperties.getProperty("git.branch"); - String build = gitProperties.getProperty("git.build.number"); - if (build != null) { - buildNumber = Integer.parseInt(build); - } - } catch (Throwable e) { - logger.error("Failed to read git.properties", e); - } - } else { - logger.debug("Not getting git properties for the news handler as we are in a development environment."); - } - pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); @@ -335,7 +292,7 @@ public class GeyserImpl implements GeyserApi { boolean enableProxyProtocol = config.getBedrock().isEnableProxyProtocol(); bedrockServer = new BedrockServer( - new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()), + new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port()), bedrockThreadCount, EventLoops.commonGroup(), enableProxyProtocol @@ -358,11 +315,11 @@ public class GeyserImpl implements GeyserApi { if (shouldStartListener) { bedrockServer.bind().whenComplete((avoid, throwable) -> { if (throwable == null) { - logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), - String.valueOf(config.getBedrock().getPort()))); + logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(), + String.valueOf(config.getBedrock().port()))); } else { - String address = config.getBedrock().getAddress(); - int port = config.getBedrock().getPort(); + String address = config.getBedrock().address(); + int port = config.getBedrock().port(); logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port))); if (!"0.0.0.0".equals(address)) { logger.info(ChatColor.GREEN + "Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0"); @@ -372,7 +329,7 @@ public class GeyserImpl implements GeyserApi { }).join(); } - if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + if (config.getRemote().authType() == AuthType.FLOODGATE) { try { Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); @@ -390,7 +347,7 @@ public class GeyserImpl implements GeyserApi { metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can - metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase(Locale.ROOT))); + metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); @@ -474,7 +431,7 @@ public class GeyserImpl implements GeyserApi { metrics = null; } - if (config.getRemote().getAuthType() == AuthType.ONLINE) { + if (config.getRemote().authType() == AuthType.ONLINE) { if (config.getUserAuths() != null && !config.getUserAuths().isEmpty()) { getLogger().warning("The 'userAuths' config section is now deprecated, and will be removed in the near future! " + "Please migrate to the new 'saved-user-logins' config option: " + @@ -552,7 +509,6 @@ public class GeyserImpl implements GeyserApi { return null; } - @Override public void shutdown() { bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown")); shuttingDown = true; @@ -579,7 +535,6 @@ public class GeyserImpl implements GeyserApi { bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } - @Override public void reload() { shutdown(); this.extensionManager.enableExtensions(); @@ -592,10 +547,10 @@ public class GeyserImpl implements GeyserApi { * * @return true if the version number is not 'DEV'. */ - @Override public boolean isProductionEnvironment() { + // First is if Blossom runs, second is if Blossom doesn't run // noinspection ConstantConditions - changes in production - return !"git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION); + return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION)); } @Override @@ -609,8 +564,8 @@ public class GeyserImpl implements GeyserApi { } @Override - public GeyserProviderManager providerManager() { - return this.providerManager; + public @NonNull R provider(@NonNull Class apiClass, @Nullable Object... args) { + return (R) Registries.PROVIDERS.get(apiClass).create(args); } @Override @@ -619,17 +574,12 @@ public class GeyserImpl implements GeyserApi { } public RemoteServer defaultRemoteServer() { - return this.remoteServer; + return getConfig().getRemote(); } @Override public BedrockListener bedrockListener() { - return this.bedrockListener; - } - - @Override - public int maxPlayers() { - return this.getConfig().getMaxPlayers(); + return getConfig().getBedrock(); } public int buildNumber() { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index d74d804df..7439b22f2 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -27,8 +27,10 @@ package org.geysermc.geyser.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.api.network.BedrockListener; +import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.network.CIDRMatcher; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; import java.nio.file.Path; @@ -109,20 +111,10 @@ public interface GeyserConfiguration { int getPendingAuthenticationTimeout(); - interface IBedrockConfiguration { - - String getAddress(); - - int getPort(); + interface IBedrockConfiguration extends BedrockListener { boolean isCloneRemotePort(); - String getMotd1(); - - String getMotd2(); - - String getServerName(); - int getCompressionLevel(); boolean isEnableProxyProtocol(); @@ -135,23 +127,25 @@ public interface GeyserConfiguration { List getWhitelistedIPsMatchers(); } - interface IRemoteConfiguration { - - String getAddress(); - - int getPort(); + interface IRemoteConfiguration extends RemoteServer { void setAddress(String address); void setPort(int port); - AuthType getAuthType(); - boolean isPasswordAuthentication(); boolean isUseProxyProtocol(); boolean isForwardHost(); + + default String minecraftVersion() { + return GameProtocol.getJavaMinecraftVersion(); + } + + default int protocolVersion() { + return GameProtocol.getJavaProtocolVersion(); + } } interface IUserAuthenticationInfo { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 77b351518..b80e60e49 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -153,24 +153,50 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("pending-authentication-timeout") private int pendingAuthenticationTimeout = 120; - @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class BedrockConfiguration implements IBedrockConfiguration { @AsteriskSerializer.Asterisk(isIp = true) private String address = "0.0.0.0"; + @Override + public String address() { + return address; + } + @Setter private int port = 19132; + @Override + public int port() { + return port; + } + + @Getter @JsonProperty("clone-remote-port") private boolean cloneRemotePort = false; private String motd1 = "GeyserMC"; + + @Override + public String primaryMotd() { + return motd1; + } + private String motd2 = "Geyser"; + @Override + public String secondaryMotd() { + return motd2; + } + @JsonProperty("server-name") private String serverName = GeyserImpl.NAME; + @Override + public String serverName() { + return serverName; + } + @JsonProperty("compression-level") private int compressionLevel = 6; @@ -178,9 +204,11 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration return Math.max(-1, Math.min(compressionLevel, 9)); } + @Getter @JsonProperty("enable-proxy-protocol") private boolean enableProxyProtocol = false; + @Getter @JsonProperty("proxy-protocol-whitelisted-ips") private List proxyProtocolWhitelistedIPs = Collections.emptyList(); @@ -202,28 +230,45 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration } } - @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class RemoteConfiguration implements IRemoteConfiguration { @Setter @AsteriskSerializer.Asterisk(isIp = true) private String address = "auto"; + @Override + public String address() { + return address; + } + @JsonDeserialize(using = PortDeserializer.class) @Setter private int port = 25565; + @Override + public int port() { + return port; + } + @Setter @JsonDeserialize(using = AuthTypeDeserializer.class) @JsonProperty("auth-type") private AuthType authType = AuthType.ONLINE; + @Override + public AuthType authType() { + return authType; + } + + @Getter @JsonProperty("allow-password-authentication") private boolean passwordAuthentication = true; + @Getter @JsonProperty("use-proxy-protocol") private boolean useProxyProtocol = false; + @Getter @JsonProperty("forward-hostname") private boolean forwardHost = false; } diff --git a/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java b/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java deleted file mode 100644 index e617be36a..000000000 --- a/core/src/main/java/org/geysermc/geyser/network/BedrockListenerImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.network; - -import org.geysermc.geyser.api.network.BedrockListener; - -public record BedrockListenerImpl(String address, int port, String primaryMotd, String secondaryMotd, String serverName) implements BedrockListener { -} diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index e231cd08a..3bfbf118d 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -108,7 +108,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setNintendoLimited(false); pong.setProtocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); pong.setVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. - pong.setIpv4Port(config.getBedrock().getPort()); + pong.setIpv4Port(config.getBedrock().port()); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); @@ -118,8 +118,8 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setMotd(mainMotd.trim()); pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. } else { - pong.setMotd(config.getBedrock().getMotd1()); - pong.setSubMotd(config.getBedrock().getMotd2()); + pong.setMotd(config.getBedrock().primaryMotd()); + pong.setSubMotd(config.getBedrock().secondaryMotd()); } if (config.isPassthroughPlayerCounts() && pingInfo != null) { diff --git a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java index f11851c1b..4a78d0bdb 100644 --- a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java @@ -151,7 +151,7 @@ public class QueryPacketHandler { String[] javaMotd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); motd = javaMotd[0].trim(); // First line of the motd. } else { - motd = geyser.getConfig().getBedrock().getMotd1(); + motd = geyser.getConfig().getBedrock().primaryMotd(); } // If passthrough player counts is enabled lets get players from the server @@ -180,8 +180,8 @@ public class QueryPacketHandler { gameData.put("map", map); gameData.put("numplayers", currentPlayerCount); gameData.put("maxplayers", maxPlayerCount); - gameData.put("hostport", String.valueOf(geyser.getConfig().getBedrock().getPort())); - gameData.put("hostip", geyser.getConfig().getBedrock().getAddress()); + gameData.put("hostport", String.valueOf(geyser.getConfig().getBedrock().port())); + gameData.put("hostip", geyser.getConfig().getBedrock().address()); try { writeString(query, "GeyserMC"); diff --git a/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java b/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java deleted file mode 100644 index a0d919c3a..000000000 --- a/core/src/main/java/org/geysermc/geyser/network/RemoteServerImpl.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.network; - -import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.api.network.RemoteServer; - -public record RemoteServerImpl(String address, int port, int protocolVersion, String minecraftVersion, AuthType authType) implements RemoteServer { -} diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 33eca67a9..5fc254363 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -31,7 +31,6 @@ import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.pack.ResourcePack; @@ -39,6 +38,7 @@ import org.geysermc.geyser.pack.ResourcePackManifest; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; @@ -119,7 +119,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { public boolean handle(ResourcePackClientResponsePacket packet) { switch (packet.getStatus()) { case COMPLETED: - if (geyser.getConfig().getRemote().getAuthType() != AuthType.ONLINE) { + if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) { session.authenticate(session.getAuthData().name()); } else if (!couldLoginUserByName(session.getAuthData().name())) { // We must spawn the white world diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index c3a242501..74501c8ae 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -76,8 +76,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn public void run() { try { Socket socket = new Socket(); - String address = geyser.getConfig().getRemote().getAddress(); - int port = geyser.getConfig().getRemote().getPort(); + String address = geyser.getConfig().getRemote().address(); + int port = geyser.getConfig().getRemote().port(); socket.connect(new InetSocketAddress(address, port), 5000); ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java b/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java deleted file mode 100644 index 46b44c8da..000000000 --- a/core/src/main/java/org/geysermc/geyser/registry/ProviderRegistries.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.registry; - -import org.geysermc.geyser.api.provider.Provider; -import org.geysermc.geyser.registry.loader.ProviderRegistryLoader; -import org.geysermc.geyser.registry.provider.GeyserBuilderProvider; -import org.geysermc.geyser.registry.provider.ProviderSupplier; - -/** - * Holds registries for the available {@link Provider}s - */ -public class ProviderRegistries { - - /** - * A registry containing all the providers for builders. - */ - public static final SimpleMappedRegistry, ProviderSupplier> BUILDERS = SimpleMappedRegistry.create(GeyserBuilderProvider.INSTANCE, ProviderRegistryLoader::new); -} diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index f1dd054f5..4b361ba4f 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -40,8 +40,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.kyori.adventure.key.Key; -import org.geysermc.geyser.api.extension.ExtensionLoader; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; @@ -49,6 +47,7 @@ import org.geysermc.geyser.registry.loader.*; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; import org.geysermc.geyser.registry.populator.RecipeRegistryPopulator; +import org.geysermc.geyser.registry.provider.ProviderSupplier; import org.geysermc.geyser.registry.type.EnchantmentData; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; @@ -59,10 +58,7 @@ import org.geysermc.geyser.translator.level.event.LevelEventTranslator; import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; import org.geysermc.geyser.translator.sound.SoundTranslator; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Holds all the common registries in Geyser. @@ -140,6 +136,11 @@ public final class Registries { */ public static final SimpleRegistry> POTION_MIXES; + /** + * A registry holding all the + */ + public static final SimpleMappedRegistry, ProviderSupplier> PROVIDERS = SimpleMappedRegistry.create(new IdentityHashMap<>(), ProviderRegistryLoader::new); + /** * A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value. */ diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java index 290f2991b..449f6574e 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java @@ -25,21 +25,32 @@ package org.geysermc.geyser.registry.loader; -import org.geysermc.geyser.registry.provider.AbstractProvider; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.item.GeyserCustomItemData; +import org.geysermc.geyser.item.GeyserCustomItemOptions; +import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData; import org.geysermc.geyser.registry.provider.ProviderSupplier; -import java.util.IdentityHashMap; import java.util.Map; /** * Registers the provider data from the provider. */ -public class ProviderRegistryLoader implements RegistryLoader, ProviderSupplier>> { +public class ProviderRegistryLoader implements RegistryLoader, ProviderSupplier>, Map, ProviderSupplier>> { + @SuppressWarnings("unchecked") @Override - public Map, ProviderSupplier> load(AbstractProvider input) { - Map, ProviderSupplier> providers = new IdentityHashMap<>(); - input.registerProviders(providers); + public Map, ProviderSupplier> load(Map, ProviderSupplier> providers) { + providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); + providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); + providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); + providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); + return providers; } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java deleted file mode 100644 index 8c5201c23..000000000 --- a/core/src/main/java/org/geysermc/geyser/registry/provider/AbstractProvider.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.registry.provider; - -import lombok.RequiredArgsConstructor; -import org.geysermc.geyser.api.provider.Provider; -import org.geysermc.geyser.registry.SimpleMappedRegistry; - -import java.util.Map; - -@RequiredArgsConstructor -public abstract class AbstractProvider implements Provider { - public abstract void registerProviders(Map, ProviderSupplier> providers); - - public abstract SimpleMappedRegistry, ProviderSupplier> providerRegistry(); -} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java deleted file mode 100644 index af289bcda..000000000 --- a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserBuilderProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.registry.provider; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.api.command.Command; -import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.api.item.custom.CustomItemData; -import org.geysermc.geyser.api.item.custom.CustomItemOptions; -import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; -import org.geysermc.geyser.api.provider.BuilderProvider; -import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.item.GeyserCustomItemData; -import org.geysermc.geyser.item.GeyserCustomItemOptions; -import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData; -import org.geysermc.geyser.registry.ProviderRegistries; -import org.geysermc.geyser.registry.SimpleMappedRegistry; - -import java.util.Map; - -public class GeyserBuilderProvider extends AbstractProvider implements BuilderProvider { - public static GeyserBuilderProvider INSTANCE = new GeyserBuilderProvider(); - - private GeyserBuilderProvider() { - } - - @SuppressWarnings("unchecked") - @Override - public void registerProviders(Map, ProviderSupplier> providers) { - providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); - providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); - providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); - providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); - } - - @Override - public SimpleMappedRegistry, ProviderSupplier> providerRegistry() { - return ProviderRegistries.BUILDERS; - } - - @SuppressWarnings("unchecked") - @Override - public @NonNull B provideBuilder(@NonNull Class builderClass, @Nullable Object... args) { - return (B) this.providerRegistry().get(builderClass).create(args); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java b/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java deleted file mode 100644 index 208981d7a..000000000 --- a/core/src/main/java/org/geysermc/geyser/registry/provider/GeyserProviderManager.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.registry.provider; - -import org.geysermc.geyser.api.provider.ProviderManager; - -public class GeyserProviderManager implements ProviderManager { - - @Override - public GeyserBuilderProvider builderProvider() { - return GeyserBuilderProvider.INSTANCE; - } -} diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 99e29dd21..53d22edbe 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -581,7 +581,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { disconnect(message); }); - this.remoteServer = geyser.getRemoteServer(); + this.remoteServer = geyser.defaultRemoteServer(); } /** @@ -1457,7 +1457,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setFromWorldTemplate(false); startGamePacket.setWorldTemplateOptionLocked(false); - String serverName = geyser.getConfig().getBedrock().getServerName(); + String serverName = geyser.getConfig().getBedrock().serverName(); startGamePacket.setLevelId(serverName); startGamePacket.setLevelName(serverName); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 38d57dc01..992835a2b 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -286,7 +286,7 @@ public class SkinManager { String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); - if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().getAuthType() != AuthType.ONLINE) { + if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); if (session != null) { From 83ba6b5ab53f869bd3e15739a33f61db8c5dc65f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 10 Jul 2022 20:58:48 -0400 Subject: [PATCH 074/125] Extensions have their own command --- .../downstream/ServerDefineCommandsEvent.java | 4 +- .../lifecycle/GeyserDefineCommandsEvent.java | 42 ----------- .../bungeecord/GeyserBungeePlugin.java | 7 +- .../command/GeyserBungeeCommandExecutor.java | 7 +- .../platform/spigot/GeyserSpigotInjector.java | 4 +- .../platform/spigot/GeyserSpigotPlugin.java | 13 ++-- .../command/GeyserSpigotCommandExecutor.java | 5 +- .../spigot/src/main/resources/plugin.yml | 5 +- .../platform/sponge/GeyserSpongePlugin.java | 7 +- .../command/GeyserSpongeCommandExecutor.java | 10 +-- .../velocity/GeyserVelocityPlugin.java | 7 +- .../GeyserVelocityCommandExecutor.java | 6 +- .../geyser/command/GeyserCommandExecutor.java | 6 +- .../geyser/command/GeyserCommandManager.java | 75 +++++++++++-------- .../geyser/command/defaults/HelpCommand.java | 12 ++- .../BedrockCommandRequestTranslator.java | 7 +- 16 files changed, 101 insertions(+), 116 deletions(-) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java index 06412eb4c..2ab1b9611 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -33,7 +33,9 @@ import org.geysermc.geyser.api.event.connection.ConnectionEvent; import java.util.Set; /** - * Called when the downstream server defines the commands available on the server. + * Called when the Java server defines the commands available on the server. + *
+ * This event is mapped to the existence of Brigadier on the server. */ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable { private final Set commands; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java deleted file mode 100644 index e506c0ca0..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.command.Command; -import org.geysermc.geyser.api.command.CommandManager; -import org.geysermc.geyser.api.event.Event; - -import java.util.Map; - -/** - * Called when commands are defined within Geyser. - * - * @param commandManager the command manager - * @param commands an immutable view of the default commands - */ -public record GeyserDefineCommandsEvent(@NonNull CommandManager commandManager, @NonNull Map commands) implements Event { -} diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index d98349eac..7b937ac6b 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -86,7 +86,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { InetSocketAddress javaAddr = listener.getHost(); // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { @@ -109,7 +109,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { @@ -134,7 +134,8 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); } - this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(geyser)); + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", geyser, geyserCommandManager.getCommands())); + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyserext", geyser, geyserCommandManager.commands())); } @Override diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java index d022074fd..6575f047c 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java @@ -37,14 +37,15 @@ import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; +import java.util.Map; public class GeyserBungeeCommandExecutor extends Command implements TabExecutor { private final GeyserCommandExecutor commandExecutor; - public GeyserBungeeCommandExecutor(GeyserImpl geyser) { - super("geyser"); + public GeyserBungeeCommandExecutor(String name, GeyserImpl geyser, Map commands) { + super(name); - this.commandExecutor = new GeyserCommandExecutor(geyser); + this.commandExecutor = new GeyserCommandExecutor(geyser, commands); } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index 0fd8d849b..c1d3b6871 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -170,8 +170,8 @@ public class GeyserSpigotInjector extends GeyserInjector { */ private void workAroundWeirdBug(GeyserBootstrap bootstrap) { MinecraftProtocol protocol = new MinecraftProtocol(); - LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().getAddress(), - bootstrap.getGeyserConfig().getRemote().getPort(), this.serverSocketAddress, + LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(), + bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper()); session.connect(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index fed5dd6b9..6e36b817d 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -43,7 +43,6 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -125,7 +124,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { @@ -148,7 +147,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; @@ -249,8 +248,10 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass()); } - PluginCommand pluginCommand = this.getCommand("geyser"); - pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser)); + PluginCommand geyserCommand = this.getCommand("geyser"); + geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); + PluginCommand geyserExtCommand = this.getCommand("geyserext"); + geyserExtCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); if (!INITIALIZED) { // Register permissions so they appear in, for example, LuckPerms' UI @@ -277,7 +278,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { boolean brigadierSupported = CommodoreProvider.isSupported(); geyserLogger.debug("Brigadier supported? " + brigadierSupported); if (brigadierSupported) { - GeyserBrigadierSupport.loadBrigadier(this, pluginCommand); + GeyserBrigadierSupport.loadBrigadier(this, geyserCommand); } // Check to ensure the current setup can support the protocol version Geyser uses diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java index b5a6ee887..52779db23 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java @@ -38,11 +38,12 @@ import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implements TabExecutor { - public GeyserSpigotCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserSpigotCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index e28b8981d..0f6398b49 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -8,4 +8,7 @@ api-version: 1.13 commands: geyser: description: The main command for Geyser. - usage: /geyser \ No newline at end of file + usage: /geyser + geyserext: + description: The command any extensions can register to. + usage: /geyserext \ No newline at end of file diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java index 4e088161d..312dfb087 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java @@ -99,14 +99,14 @@ public class GeyserSpongePlugin implements GeyserBootstrap { // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); geyserConfig.getRemote().setPort(javaAddr.getPort()); } } if (geyserConfig.getBedrock().isCloneRemotePort()) { - geyserConfig.getBedrock().setPort(geyserConfig.getRemote().getPort()); + geyserConfig.getBedrock().setPort(geyserConfig.getRemote().port()); } this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); @@ -121,7 +121,8 @@ public class GeyserSpongePlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser); this.geyserCommandManager.init(); - Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser), "geyser"); + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser"); + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.commands()), "geyserext"); } @Override diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java index 485c06c41..3598ea8c2 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.platform.sponge.command; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.command.GeyserCommandSource; @@ -40,15 +41,12 @@ import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; public class GeyserSpongeCommandExecutor extends GeyserCommandExecutor implements CommandCallable { - public GeyserSpongeCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserSpongeCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 0b228941b..739e99d43 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -102,7 +102,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { InetSocketAddress javaAddr = proxyServer.getBoundAddress(); // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { @@ -128,7 +128,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } catch (ClassNotFoundException ignored) { } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; @@ -148,7 +148,8 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserVelocityCommandManager(geyser); this.geyserCommandManager.init(); - this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser)); + this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands())); + this.commandManager.register("geyserext", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.commands())); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java index b3c4221df..c77a3daef 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.velocity.command; import com.velocitypowered.api.command.SimpleCommand; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.command.GeyserCommandSource; @@ -37,11 +38,12 @@ import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; public class GeyserVelocityCommandExecutor extends GeyserCommandExecutor implements SimpleCommand { - public GeyserVelocityCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserVelocityCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java index 3d08600d1..a9b1c734f 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java @@ -37,15 +37,16 @@ import java.util.List; import java.util.Map; /** - * Represents helper functions for listening to {@code /geyser} commands. + * Represents helper functions for listening to {@code /geyser} or {@code /geyserext} commands. */ @AllArgsConstructor public class GeyserCommandExecutor { protected final GeyserImpl geyser; + private final Map commands; public GeyserCommand getCommand(String label) { - return (GeyserCommand) geyser.commandManager().commands().get(label); + return (GeyserCommand) commands.get(label); } @Nullable @@ -78,7 +79,6 @@ public class GeyserCommandExecutor { } List availableCommands = new ArrayList<>(); - Map commands = geyser.commandManager().getCommands(); // Only show commands they have permission to use for (Map.Entry entry : commands.entrySet()) { diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index c6b9cbdd2..4fd5ba411 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.command; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; @@ -34,7 +35,6 @@ import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandExecutor; import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; import org.geysermc.geyser.command.defaults.*; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -42,91 +42,104 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; -import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; @RequiredArgsConstructor public abstract class GeyserCommandManager extends CommandManager { @Getter - private final Map commands = new HashMap<>(); + private final Map commands = new Object2ObjectOpenHashMap<>(12); + private final Map extensionCommands = new Object2ObjectOpenHashMap<>(0); private final GeyserImpl geyser; public void init() { - register(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help")); - register(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); - register(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); - register(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); - register(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); - register(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); - register(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); - register(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); - register(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); - register(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); - register(new ExtensionsCommand(geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions")); + registerBuiltInCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help", "geyser", commands)); + registerBuiltInCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); + registerBuiltInCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); + registerBuiltInCommand(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); + registerBuiltInCommand(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); + registerBuiltInCommand(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); + registerBuiltInCommand(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); + registerBuiltInCommand(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); + registerBuiltInCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); + registerBuiltInCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { - register(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); + registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } - this.geyser.eventBus().fire(new GeyserDefineCommandsEvent(this, this.commands())); + register(new HelpCommand(geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp", "geyserext", extensionCommands)); + } + + /** + * For internal Geyser commands + */ + public void registerBuiltInCommand(GeyserCommand command) { + register(command, this.commands); } @Override public void register(@NonNull Command command) { - this.commands.put(command.name(), command); - this.geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.name())); + register(command, this.extensionCommands); + } + + private void register(Command command, Map commands) { + commands.put(command.name(), command); + geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.name())); if (command.aliases().isEmpty()) { return; } for (String alias : command.aliases()) { - this.commands.put(alias, command); + commands.put(alias, command); } } @Override public void unregister(@NonNull Command command) { - this.commands.remove(command.name(), command); + this.extensionCommands.remove(command.name(), command); if (command.aliases().isEmpty()) { return; } for (String alias : command.aliases()) { - this.commands.remove(alias, command); + this.extensionCommands.remove(alias, command); } } @NotNull @Override public Map commands() { - return Collections.unmodifiableMap(this.commands); + return Collections.unmodifiableMap(this.extensionCommands); } - public void runCommand(GeyserCommandSource sender, String command) { - if (!command.startsWith("geyser ")) - return; + public boolean runCommand(GeyserCommandSource sender, String command) { + boolean extensionCommand = command.startsWith("geyserext "); + if (!command.startsWith("geyser ") && !extensionCommand) { + return false; + } - command = command.trim().replace("geyser ", ""); + command = command.trim().replace(extensionCommand ? "geyserext " : "geyser ", ""); String label; String[] args; if (!command.contains(" ")) { - label = command.toLowerCase(); + label = command.toLowerCase(Locale.ROOT); args = new String[0]; } else { - label = command.substring(0, command.indexOf(" ")).toLowerCase(); + label = command.substring(0, command.indexOf(" ")).toLowerCase(Locale.ROOT); String argLine = command.substring(command.indexOf(" ") + 1); args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; } - Command cmd = commands.get(label); + Command cmd = (extensionCommand ? this.extensionCommands : this.commands).get(label); if (cmd == null) { geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); - return; + return false; } if (cmd instanceof GeyserCommand) { @@ -140,6 +153,8 @@ public abstract class GeyserCommandManager extends CommandManager { } } } + + return true; } /** diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java index 84a0730b8..81f34b759 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java @@ -39,10 +39,15 @@ import java.util.Map; public class HelpCommand extends GeyserCommand { private final GeyserImpl geyser; + private final String baseCommand; + private final Map commands; - public HelpCommand(GeyserImpl geyser, String name, String description, String permission) { + public HelpCommand(GeyserImpl geyser, String name, String description, String permission, + String baseCommand, Map commands) { super(name, description, permission); this.geyser = geyser; + this.baseCommand = baseCommand; + this.commands = commands; this.setAliases(Collections.singletonList("?")); } @@ -61,8 +66,7 @@ public class HelpCommand extends GeyserCommand { String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage); sender.sendMessage(header); - Map cmds = geyser.commandManager().getCommands(); - for (Map.Entry entry : cmds.entrySet()) { + for (Map.Entry entry : commands.entrySet()) { Command cmd = entry.getValue(); // Standalone hack-in since it doesn't have a concept of permissions @@ -72,7 +76,7 @@ public class HelpCommand extends GeyserCommand { continue; } - sender.sendMessage(ChatColor.YELLOW + "/geyser " + entry.getKey() + ChatColor.WHITE + ": " + + sender.sendMessage(ChatColor.YELLOW + "/" + baseCommand + " " + entry.getKey() + ChatColor.WHITE + ": " + GeyserLocale.getPlayerLocaleString(cmd.description(), sender.locale())); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java index e5840ba0d..3301f7b9f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java @@ -28,7 +28,6 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -40,10 +39,8 @@ public class BedrockCommandRequestTranslator extends PacketTranslator Date: Tue, 12 Jul 2022 05:29:39 +0000 Subject: [PATCH 075/125] 1.19.10 fallout --- bootstrap/spigot/build.gradle.kts | 2 +- .../geyser/platform/spigot/GeyserPaperPingPassthrough.java | 4 ++-- build-logic/build.gradle.kts | 2 +- build-logic/src/main/kotlin/Versions.kt | 4 ++-- .../src/main/kotlin/geyser.base-conventions.gradle.kts | 4 ++-- core/build.gradle.kts | 2 +- .../geysermc/geyser/ping/GeyserLegacyPingPassthrough.java | 3 +-- .../java/org/geysermc/geyser/session/GeyserSession.java | 2 +- .../translator/inventory/item/GoatHornTranslator.java | 4 ++-- .../java/entity/player/JavaPlayerCombatKillTranslator.java | 6 +++--- 10 files changed, 16 insertions(+), 17 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 8e2b73cd1..d755f323c 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,4 +1,4 @@ -val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet +val paperVersion = "1.19-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet val viaVersion = "4.0.0" val adaptersVersion = "1.4-SNAPSHOT" val commodoreVersion = "1.13" diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java index 64c185585..36dd81d44 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java @@ -62,11 +62,11 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough // Approximately pre-1.19 event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), - Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } else { event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress), Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(), - Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 25cbfe9de..dc8d6d620 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -16,6 +16,6 @@ dependencies { tasks.withType { kotlinOptions { - jvmTarget = "16" + jvmTarget = "17" } } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 779065bc5..919935409 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -30,10 +30,10 @@ object Versions { const val guavaVersion = "29.0-jre" const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "977a9a1" + const val protocolVersion = "a78a64b" const val raknetVersion = "1.6.28-SNAPSHOT" const val mcauthlibVersion = "d9d773e" - const val mcprotocollibversion = "bb2b414" + const val mcprotocollibversion = "54fc9f0" const val packetlibVersion = "3.0" const val adventureVersion = "4.9.3" const val eventVersion = "3.0.0" diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 2ea5d88a4..b4cc63deb 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -26,8 +26,8 @@ tasks { } java { - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 withSourcesJar() } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e82af5687..561c2f554 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) - api("com.github.CloudburstMC.Protocol", "bedrock-v527", Versions.protocolVersion) { + api("com.github.CloudburstMC.Protocol", "bedrock-v534", Versions.protocolVersion) { exclude("com.nukkitx.network", "raknet") exclude("com.nukkitx", "nbt") } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index 817ee9e81..199e13918 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.ping; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; -import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.nukkitx.nbt.util.VarInts; import io.netty.handler.codec.haproxy.HAProxyCommand; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; @@ -85,7 +84,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); try (DataOutputStream handshake = new DataOutputStream(byteArrayStream)) { handshake.write(0x0); - VarInts.writeUnsignedInt(handshake, MinecraftProtocol.getJavaProtocolVersion()); + VarInts.writeUnsignedInt(handshake, GameProtocol.getJavaProtocolVersion()); VarInts.writeUnsignedInt(handshake, address.length()); handshake.writeBytes(address); handshake.writeShort(port); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 4639a952c..cf94d299d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1643,7 +1643,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { boolean spectator = gameMode == GameMode.SPECTATOR; boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator; - if (org.geysermc.geyser.network.MinecraftProtocol.supports1_19_10(this)) { + if (org.geysermc.geyser.network.GameProtocol.supports1_19_10(this)) { UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket(); adventureSettingsPacket.setNoMvP(false); adventureSettingsPacket.setNoPvM(false); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java index 08e8534af..2cb9d7ec7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -91,7 +91,7 @@ public class GoatHornTranslator extends ItemTranslator { @Override public List getAppliedItems() { return Collections.singletonList( - Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:goat_horn") ); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java index 0f1fd4d1b..89be26e4a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.translator.protocol.java.entity.player; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerCombatKillPacket; import com.nukkitx.protocol.bedrock.packet.DeathInfoPacket; import net.kyori.adventure.text.Component; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -39,11 +39,11 @@ public class JavaPlayerCombatKillTranslator extends PacketTranslator Date: Tue, 12 Jul 2022 14:24:53 +0000 Subject: [PATCH 076/125] Build on Java 17 --- .github/workflows/pullrequest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index f5bb4c042..9d925c4dc 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -9,11 +9,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: distribution: 'temurin' - java-version: 16 + java-version: 17 cache: 'gradle' - name: submodules-init uses: snickerbockers/submodules-init@v4 From 8b9f41d4a6dc409c3acd98f64ad2813d8529f93a Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Tue, 12 Jul 2022 07:38:57 -0700 Subject: [PATCH 077/125] Java 17 (Jenkinsfile) --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f92778318..b3df4bc95 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent any tools { gradle 'Gradle 7' - jdk 'Java 16' + jdk 'Java 17' } options { buildDiscarder(logRotator(artifactNumToKeepStr: '20')) From 2dbd39c5a437ee14c589acb1ab2576c9779725b9 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:33:57 +0000 Subject: [PATCH 078/125] Per review by @Camotoy --- bootstrap/spigot/build.gradle.kts | 2 +- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index d755f323c..123ef06f2 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,4 +1,4 @@ -val paperVersion = "1.19-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet +val paperVersion = "1.19-R0.1-SNAPSHOT" val viaVersion = "4.0.0" val adaptersVersion = "1.4-SNAPSHOT" val commodoreVersion = "1.13" diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index cf94d299d..edaed9893 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -116,6 +116,7 @@ import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; @@ -1643,7 +1644,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { boolean spectator = gameMode == GameMode.SPECTATOR; boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator; - if (org.geysermc.geyser.network.GameProtocol.supports1_19_10(this)) { + if (GameProtocol.supports1_19_10(this)) { UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket(); adventureSettingsPacket.setNoMvP(false); adventureSettingsPacket.setNoPvM(false); From d1fbb909a54ac4114bee7034b2d2df1300ac3d97 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:09:48 +0000 Subject: [PATCH 079/125] fixed java 16 thanks to @davchoo --- bootstrap/spigot/build.gradle.kts | 14 ++++++++++++-- build-logic/build.gradle.kts | 2 +- .../main/kotlin/geyser.base-conventions.gradle.kts | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 123ef06f2..5a12ded01 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -9,6 +9,18 @@ dependencies { implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) implementation("me.lucko", "commodore", commodoreVersion) + + // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19 + compileOnly("io.papermc.paper", "paper-api", paperVersion) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) + } + } + compileOnly("io.papermc.paper", "paper-mojangapi", paperVersion) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) + } + } } platformRelocate("it.unimi.dsi.fastutil") @@ -19,8 +31,6 @@ platformRelocate("me.lucko.commodore") platformRelocate("io.netty.channel.kqueue") // These dependencies are already present on the platform -provided("io.papermc.paper", "paper-api", paperVersion) -provided("io.papermc.paper", "paper-mojangapi", paperVersion) provided("com.viaversion", "viaversion", viaVersion) application { diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index dc8d6d620..25cbfe9de 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -16,6 +16,6 @@ dependencies { tasks.withType { kotlinOptions { - jvmTarget = "17" + jvmTarget = "16" } } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index b4cc63deb..2ea5d88a4 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -26,8 +26,8 @@ tasks { } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_16 + targetCompatibility = JavaVersion.VERSION_16 withSourcesJar() } \ No newline at end of file From 0335c8263c21e30a28baa0f34a9eb97dbe808de3 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:20:42 +0000 Subject: [PATCH 080/125] Address @Camotoy's review --- bootstrap/spigot/build.gradle.kts | 2 +- build-logic/src/main/kotlin/Versions.kt | 2 ++ settings.gradle.kts | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 5a12ded01..02883999d 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,6 +1,6 @@ val paperVersion = "1.19-R0.1-SNAPSHOT" val viaVersion = "4.0.0" -val adaptersVersion = "1.4-SNAPSHOT" +val adaptersVersion = "1.5-SNAPSHOT" val commodoreVersion = "1.13" dependencies { diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 919935409..27f7bcaf5 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -31,6 +31,8 @@ object Versions { const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" const val protocolVersion = "a78a64b" + // Not pinned to specific version due to possible gradle bug + // See comment in settings.gradle.kts const val raknetVersion = "1.6.28-SNAPSHOT" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "54fc9f0" diff --git a/settings.gradle.kts b/settings.gradle.kts index 0bc694b43..dd08f3922 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,12 @@ dependencyResolutionManagement { mavenContent { releasesOnly() } } maven("https://repo.opencollab.dev/maven-snapshots") { - mavenContent { snapshotsOnly() } + mavenContent { + // This has the unintended side effect of not allowing snapshot version pinning. + // Likely a bug in Gradle's implementation of snapshot pinning + // See https://github.com/gradle/gradle/pull/406 + snapshotsOnly() + } } // Paper, Velocity From 616c088b66830c8640a2b6e9f39f36af9ef14eb5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 20 Jul 2022 18:35:40 -0400 Subject: [PATCH 081/125] Fix custom items with ItemTranslator#getBedrockItemMapping --- .../geysermc/geyser/inventory/Inventory.java | 7 +++---- .../inventory/item/ItemTranslator.java | 17 ++++++++++++----- .../BedrockInventoryTransactionTranslator.java | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index ca7e90a25..58d551489 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -34,7 +34,6 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.jetbrains.annotations.Range; @@ -136,9 +135,9 @@ public abstract class Inventory { protected void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) { if (!newItem.isEmpty()) { - ItemMapping oldMapping = ItemTranslator.getBedrockItemMapping(session, oldItem); - ItemMapping newMapping = ItemTranslator.getBedrockItemMapping(session, newItem); - if (oldMapping.getBedrockId() == newMapping.getBedrockId()) { + int oldMapping = ItemTranslator.getBedrockItemId(session, oldItem); + int newMapping = ItemTranslator.getBedrockItemId(session, newItem); + if (oldMapping == newMapping) { newItem.setNetId(oldItem.getNetId()); } else { newItem.setNetId(session.getNextItemNetId()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index 0a2ab57df..b36833cb1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -266,16 +266,23 @@ public abstract class ItemTranslator { } /** - * Given an item stack, determine the item mapping that should be applied to Bedrock players. + * Given an item stack, determine the Bedrock item ID that should be applied to Bedrock players. */ - @Nonnull - public static ItemMapping getBedrockItemMapping(GeyserSession session, @Nonnull GeyserItemStack itemStack) { + public static int getBedrockItemId(GeyserSession session, @Nonnull GeyserItemStack itemStack) { if (itemStack.isEmpty()) { - return ItemMapping.AIR; + return ItemMapping.AIR.getJavaId(); } int javaId = itemStack.getJavaId(); - return ITEM_STACK_TRANSLATORS.getOrDefault(javaId, DEFAULT_TRANSLATOR) + ItemMapping mapping = ITEM_STACK_TRANSLATORS.getOrDefault(javaId, DEFAULT_TRANSLATOR) .getItemMapping(javaId, itemStack.getNbt(), session.getItemMappings()); + + int customItemId = getCustomItem(itemStack.getNbt(), mapping); + if (customItemId == -1) { + // No custom item + return mapping.getBedrockId(); + } else { + return customItemId; + } } private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 24c046ef2..815456132 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -542,7 +542,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Tue, 2 Aug 2022 00:25:07 -0400 Subject: [PATCH 082/125] Merge mistake fixes --- .../standalone/GeyserStandaloneBootstrap.java | 2 -- build-logic/src/main/kotlin/Versions.kt | 2 +- .../geysermc/geyser/command/CommandManager.java | 0 .../geyser/command/GeyserCommandManager.java | 1 + .../command/defaults/ConnectionTestCommand.java | 14 +++++++------- .../protocol/java/JavaLoginTranslator.java | 2 -- pom.xml | 0 7 files changed, 9 insertions(+), 12 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/command/CommandManager.java delete mode 100644 pom.xml diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 523917ab3..052a41439 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -52,8 +52,6 @@ import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.LoopbackUtil; -import org.geysermc.geyser.platform.standalone.command.GeyserCommandManager; -import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import java.io.File; import java.io.IOException; diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 27f7bcaf5..b02f3b02c 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -35,7 +35,7 @@ object Versions { // See comment in settings.gradle.kts const val raknetVersion = "1.6.28-SNAPSHOT" const val mcauthlibVersion = "d9d773e" - const val mcprotocollibversion = "54fc9f0" + const val mcprotocollibversion = "9f78bd5" const val packetlibVersion = "3.0" const val adventureVersion = "4.9.3" const val eventVersion = "3.0.0" diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandManager.java b/core/src/main/java/org/geysermc/geyser/command/CommandManager.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index 4fd5ba411..cb3cf6eee 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -66,6 +66,7 @@ public abstract class GeyserCommandManager extends CommandManager { registerBuiltInCommand(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); registerBuiltInCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); registerBuiltInCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); + registerBuiltInCommand(new ConnectionTestCommand(geyser, "connectiontest", "geyser.commands.connectiontest.desc", "geyser.command.connectiontest")); if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index 576d17128..95c115769 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.command.defaults; import com.fasterxml.jackson.databind.JsonNode; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoopbackUtil; @@ -47,10 +47,10 @@ public class ConnectionTestCommand extends GeyserCommand { } @Override - public void execute(@Nullable GeyserSession session, CommandSender sender, String[] args) { + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { // Only allow the console to create dumps on Geyser Standalone if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } @@ -69,13 +69,13 @@ public class ConnectionTestCommand extends GeyserCommand { } // Issue: do the ports not line up? - if (port != geyser.getConfig().getBedrock().getPort()) { + if (port != geyser.getConfig().getBedrock().port()) { sender.sendMessage("The port you supplied (" + port + ") does not match the port supplied in Geyser's configuration (" - + geyser.getConfig().getBedrock().getPort() + "). You can change it under `bedrock` `port`."); + + geyser.getConfig().getBedrock().port() + "). You can change it under `bedrock` `port`."); } // Issue: is the `bedrock` `address` in the config different? - if (!geyser.getConfig().getBedrock().getAddress().equals("0.0.0.0")) { + if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) { sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional."); } @@ -129,7 +129,7 @@ public class ConnectionTestCommand extends GeyserCommand { }); } - private void sendLinks(CommandSender sender) { + private void sendLinks(GeyserCommandSource sender) { sender.sendMessage("If you still have issues, check to see if your hosting provider has a specific setup: " + "https://wiki.geysermc.org/geyser/supported-hosting-providers/" + ", see this page: " + "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/" + ", or contact us on our Discord: " + "https://discord.gg/geysermc"); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 0adb46c45..6aa613b24 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -40,8 +40,6 @@ import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.ChatTypeEntry; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.TextDecoration; import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; diff --git a/pom.xml b/pom.xml deleted file mode 100644 index e69de29bb..000000000 From 17f3deb8df94f4e22403e176096c112791f06874 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 2 Aug 2022 01:11:17 -0400 Subject: [PATCH 083/125] try to reset languages --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 615694055..d92904027 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 615694055b1bc7ffba5771dd931ef76b453d16a2 +Subproject commit d92904027061856248ece8382face369e9cc5d67 From a5dc70a3b56bd84974f6bb0cca0fcca438d089c9 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 2 Aug 2022 23:22:08 -0400 Subject: [PATCH 084/125] Refactor extension description --- .../api/extension/ExtensionDescription.java | 26 ++++++- .../extension/GeyserExtensionDescription.java | 76 ++++++++++++------- .../extension/GeyserExtensionLoader.java | 35 +++------ 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index e77411144..3969bb65f 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -50,13 +50,35 @@ public interface ExtensionDescription { @NonNull String main(); + /** + * Gets the extension's major api version + * + * @return the extension's major api version + */ + int majorApiVersion(); + + /** + * Gets the extension's minor api version + * + * @return the extension's minor api version + */ + int minorApiVersion(); + + /** + * Gets the extension's patch api version + * + * @return the extension's patch api version + */ + int patchApiVersion(); + /** * Gets the extension's api version * * @return the extension's api version */ - @NonNull - String apiVersion(); + default String apiVersion() { + return majorApiVersion() + "." + minorApiVersion() + "." + patchApiVersion(); + } /** * Gets the extension's description diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index eaf29a819..797b43a81 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -25,53 +25,75 @@ package org.geysermc.geyser.extension; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; -import org.yaml.snakeyaml.DumperOptions; +import org.geysermc.geyser.text.GeyserLocale; import org.yaml.snakeyaml.Yaml; import java.io.Reader; import java.util.*; +import java.util.regex.Pattern; -public record GeyserExtensionDescription(String name, String main, String apiVersion, String version, List authors) implements ExtensionDescription { +public record GeyserExtensionDescription(@NonNull String name, + @NonNull String main, + int majorApiVersion, + int minorApiVersion, + int patchApiVersion, + @NonNull String version, + @NonNull List authors) implements ExtensionDescription { + + private static final Yaml YAML = new Yaml(); + public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]*$"); + public static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); + + @NonNull @SuppressWarnings("unchecked") public static GeyserExtensionDescription fromYaml(Reader reader) throws InvalidDescriptionException { - DumperOptions dumperOptions = new DumperOptions(); - dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - - Yaml yaml = new Yaml(dumperOptions); - Map yamlMap = yaml.loadAs(reader, LinkedHashMap.class); - - String name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); - if (name.isBlank()) { - throw new InvalidDescriptionException("Invalid extension name, cannot be empty"); + Map map; + try { + map = YAML.loadAs(reader, HashMap.class); + } catch (Exception e) { + throw new InvalidDescriptionException(e); } - name = name.replace(" ", "_"); - String version = String.valueOf(yamlMap.get("version")); - String main = (String) yamlMap.get("main"); - String apiVersion; - - Object api = yamlMap.get("api"); - if (api instanceof String) { - apiVersion = (String) api; - } else { - throw new InvalidDescriptionException("Invalid api version format, should be a string: major.minor.patch"); + String name = require(map, "name"); + if (!NAME_PATTERN.matcher(name).matches()) { + throw new InvalidDescriptionException("Invalid extension name, must match: " + NAME_PATTERN.pattern()); } + String version = String.valueOf(map.get("version")); + String main = require(map, "main"); + + String apiVersion = require(map, "api"); + if (!API_VERSION_PATTERN.matcher(apiVersion).matches()) { + throw new InvalidDescriptionException(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion)); + } + String[] api = apiVersion.split("\\."); + int majorApi = Integer.parseUnsignedInt(api[0]); + int minorApi = Integer.parseUnsignedInt(api[1]); + int patchApi = Integer.parseUnsignedInt(api[2]); List authors = new ArrayList<>(); - if (yamlMap.containsKey("author")) { - authors.add((String) yamlMap.get("author")); + if (map.containsKey("author")) { + authors.add(String.valueOf(map.get("author"))); } - - if (yamlMap.containsKey("authors")) { + if (map.containsKey("authors")) { try { - authors.addAll((Collection) yamlMap.get("authors")); + authors.addAll((Collection) map.get("authors")); } catch (Exception e) { throw new InvalidDescriptionException("Invalid authors format, should be a list of strings", e); } } - return new GeyserExtensionDescription(name, main, apiVersion, version, authors); + return new GeyserExtensionDescription(name, main, majorApi, minorApi, patchApi, version, authors); + } + + @NonNull + private static String require(Map desc, String key) throws InvalidDescriptionException { + Object value = desc.get(key); + if (value instanceof String) { + return (String) value; + } + throw new InvalidDescriptionException("Extension description is missing string property '" + key + "'"); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 55f018ddb..fcb718dec 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -39,25 +39,24 @@ import org.geysermc.geyser.extension.event.GeyserExtensionEventBus; import org.geysermc.geyser.text.GeyserLocale; import java.io.IOException; +import java.io.Reader; import java.nio.file.*; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Stream; @RequiredArgsConstructor public class GeyserExtensionLoader extends ExtensionLoader { private static final Path EXTENSION_DIRECTORY = Paths.get("extensions"); - private static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); private static final Pattern[] EXTENSION_FILTERS = new Pattern[] { Pattern.compile("^.+\\.jar$") }; private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); private final Map classLoaders = new HashMap<>(); private final Map extensionContainers = new HashMap<>(); - public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException, InvalidDescriptionException { + public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException { if (path == null) { throw new InvalidExtensionException("Path is null"); } @@ -94,7 +93,9 @@ public class GeyserExtensionLoader extends ExtensionLoader { Map environment = new HashMap<>(); try (FileSystem fileSystem = FileSystems.newFileSystem(path, environment, null)) { Path extensionYml = fileSystem.getPath("extension.yml"); - return GeyserExtensionDescription.fromYaml(Files.newBufferedReader(extensionYml)); + try (Reader reader = Files.newBufferedReader(extensionYml)) { + return GeyserExtensionDescription.fromYaml(reader); + } } catch (IOException ex) { throw new InvalidDescriptionException("Failed to load extension description for " + path, ex); } @@ -149,9 +150,6 @@ public class GeyserExtensionLoader extends ExtensionLoader { try { GeyserExtensionDescription description = this.extensionDescription(path); - if (description == null) { - return; - } String name = description.name(); if (extensions.containsKey(name) || extensionManager.extension(name) != null) { @@ -159,30 +157,15 @@ public class GeyserExtensionLoader extends ExtensionLoader { return; } - int majorVersion = Geyser.api().majorApiVersion(); - int minorVersion = Geyser.api().minorApiVersion(); - - try { - // Check the format: majorVersion.minorVersion.patch - if (!API_VERSION_PATTERN.matcher(description.apiVersion()).matches()) { - throw new IllegalArgumentException(); - } - } catch (NullPointerException | IllegalArgumentException e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, majorVersion + "." + minorVersion)); - return; - } - - String[] versionArray = description.apiVersion().split("\\."); - // Completely different API version - if (Integer.parseInt(versionArray[0]) != majorVersion) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, majorVersion + "." + minorVersion)); + if (description.majorApiVersion() != Geyser.api().majorApiVersion()) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion())); return; } // If the extension requires new API features, being backwards compatible - if (Integer.parseInt(versionArray[1]) > minorVersion) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, majorVersion + "." + minorVersion)); + if (description.minorApiVersion() > Geyser.api().minorApiVersion()) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion())); return; } From aa7d0f4a57b2b93acc69aab0e2f1704c2e0e188f Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 3 Aug 2022 00:20:27 -0400 Subject: [PATCH 085/125] Use class for reading extension.yml --- .../extension/GeyserExtensionDescription.java | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index 797b43a81..8fab4e338 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -25,14 +25,18 @@ package org.geysermc.geyser.extension; +import lombok.Getter; +import lombok.Setter; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; import org.geysermc.geyser.text.GeyserLocale; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor; import java.io.Reader; import java.util.*; +import java.util.function.Supplier; import java.util.regex.Pattern; public record GeyserExtensionDescription(@NonNull String name, @@ -43,28 +47,27 @@ public record GeyserExtensionDescription(@NonNull String name, @NonNull String version, @NonNull List authors) implements ExtensionDescription { - private static final Yaml YAML = new Yaml(); + private static final Yaml YAML = new Yaml(new CustomClassLoaderConstructor(Source.class.getClassLoader())); public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]*$"); public static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); @NonNull - @SuppressWarnings("unchecked") public static GeyserExtensionDescription fromYaml(Reader reader) throws InvalidDescriptionException { - Map map; + Source source; try { - map = YAML.loadAs(reader, HashMap.class); + source = YAML.loadAs(reader, Source.class); } catch (Exception e) { throw new InvalidDescriptionException(e); } - String name = require(map, "name"); + String name = require(source::getName, "name"); if (!NAME_PATTERN.matcher(name).matches()) { throw new InvalidDescriptionException("Invalid extension name, must match: " + NAME_PATTERN.pattern()); } - String version = String.valueOf(map.get("version")); - String main = require(map, "main"); + String version = String.valueOf(source.version); + String main = require(source::getMain, "main"); - String apiVersion = require(map, "api"); + String apiVersion = require(source::getApi, "api"); if (!API_VERSION_PATTERN.matcher(apiVersion).matches()) { throw new InvalidDescriptionException(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion)); } @@ -74,26 +77,33 @@ public record GeyserExtensionDescription(@NonNull String name, int patchApi = Integer.parseUnsignedInt(api[2]); List authors = new ArrayList<>(); - if (map.containsKey("author")) { - authors.add(String.valueOf(map.get("author"))); + if (source.author != null) { + authors.add(source.author); } - if (map.containsKey("authors")) { - try { - authors.addAll((Collection) map.get("authors")); - } catch (Exception e) { - throw new InvalidDescriptionException("Invalid authors format, should be a list of strings", e); - } + if (source.authors != null) { + authors.addAll(source.authors); } return new GeyserExtensionDescription(name, main, majorApi, minorApi, patchApi, version, authors); } @NonNull - private static String require(Map desc, String key) throws InvalidDescriptionException { - Object value = desc.get(key); - if (value instanceof String) { - return (String) value; + private static String require(Supplier supplier, String name) throws InvalidDescriptionException { + String value = supplier.get(); + if (value == null) { + throw new InvalidDescriptionException("Extension description is missing string property '" + name + "'"); } - throw new InvalidDescriptionException("Extension description is missing string property '" + key + "'"); + return value; + } + + @Getter + @Setter + public static class Source { + String name; + String main; + String api; + String version; + String author; + List authors; } } From 36ef23b24e50590fef2b63b52478a86ea2008763 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 3 Aug 2022 00:30:22 -0400 Subject: [PATCH 086/125] Don't allow empty extension name --- .../geysermc/geyser/extension/GeyserExtensionDescription.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index 8fab4e338..010c1430c 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -48,7 +48,7 @@ public record GeyserExtensionDescription(@NonNull String name, @NonNull List authors) implements ExtensionDescription { private static final Yaml YAML = new Yaml(new CustomClassLoaderConstructor(Source.class.getClassLoader())); - public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]*$"); + public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]+$"); public static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); @NonNull From 67e3bf1f8dd864fa68c0cd672212145e03a030ba Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 4 Aug 2022 17:55:12 -0400 Subject: [PATCH 087/125] Move extensions folder to Geyser's config folder (#3202) * Move extensions folder to Geyser's config folder * Move directory field --- .../geysermc/geyser/extension/GeyserExtensionLoader.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 55f018ddb..d317bc013 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -43,19 +43,18 @@ import java.nio.file.*; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Stream; @RequiredArgsConstructor public class GeyserExtensionLoader extends ExtensionLoader { - private static final Path EXTENSION_DIRECTORY = Paths.get("extensions"); private static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); private static final Pattern[] EXTENSION_FILTERS = new Pattern[] { Pattern.compile("^.+\\.jar$") }; private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); private final Map classLoaders = new HashMap<>(); private final Map extensionContainers = new HashMap<>(); + private final Path extensionsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("extensions"); public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException, InvalidDescriptionException { if (path == null) { @@ -127,15 +126,15 @@ public class GeyserExtensionLoader extends ExtensionLoader { @Override protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) { try { - if (Files.notExists(EXTENSION_DIRECTORY)) { - Files.createDirectory(EXTENSION_DIRECTORY); + if (Files.notExists(extensionsDirectory)) { + Files.createDirectory(extensionsDirectory); } Map extensions = new LinkedHashMap<>(); Map loadedExtensions = new LinkedHashMap<>(); Pattern[] extensionFilters = this.extensionFilters(); - try (Stream entries = Files.walk(EXTENSION_DIRECTORY)) { + try (Stream entries = Files.walk(extensionsDirectory)) { entries.forEach(path -> { if (Files.isDirectory(path)) { return; From 2a2e63e51961b82a066fcd70d157fe84b3b46c0a Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:39:48 -0400 Subject: [PATCH 088/125] Explicitly set gson dependency in common --- build-logic/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 1 + 2 files changed, 2 insertions(+) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 38ef82d26..d9af831f0 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -28,6 +28,7 @@ object Versions { const val fastutilVersion = "8.5.2" const val nettyVersion = "4.1.66.Final" const val guavaVersion = "29.0-jre" + const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8 const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" const val protocolVersion = "0bd459f" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 205b20c0e..6c1414105 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,4 @@ dependencies { api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) + api("com.google.code.gson", "gson", Versions.gsonVersion) } \ No newline at end of file From 80588a07bd39314cafab6cf84a779ab022ef6ae0 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Tue, 9 Aug 2022 20:06:53 +0200 Subject: [PATCH 089/125] Initial API changes --- api/base/build.gradle.kts | 6 ++- .../java/org/geysermc/api/GeyserApiBase.java | 50 ++++++++++++------- .../geysermc/api/connection/Connection.java | 21 ++++++-- .../org/geysermc/geyser/api/GeyserApi.java | 2 +- .../network/session/GeyserSession.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 12 +++-- .../geyser/command/defaults/ListCommand.java | 2 +- .../geyser/scoreboard/ScoreboardUpdater.java | 2 +- .../geyser/session/GeyserSession.java | 16 +++--- .../geyser/session/cache/TagCache.java | 4 +- .../geyser/skin/FloodgateSkinUploader.java | 2 +- .../inventory/InventoryTranslator.java | 4 +- .../inventory/PlayerInventoryTranslator.java | 2 +- ...BedrockInventoryTransactionTranslator.java | 2 +- ...SetLocalPlayerAsInitializedTranslator.java | 4 +- .../player/BedrockMovePlayerTranslator.java | 2 +- .../JavaContainerSetContentTranslator.java | 2 +- .../JavaContainerSetSlotTranslator.java | 2 +- 18 files changed, 87 insertions(+), 50 deletions(-) diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts index d7500fdaa..c9ddf4489 100644 --- a/api/base/build.gradle.kts +++ b/api/base/build.gradle.kts @@ -1 +1,5 @@ -provided("net.kyori", "event-api", Versions.eventVersion) \ No newline at end of file +dependencies { + api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) +} + +provided("net.kyori", "event-api", Versions.eventVersion) diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index e5105b1be..e9957e21c 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -25,9 +25,12 @@ package org.geysermc.api; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.connection.Connection; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import java.util.List; import java.util.UUID; @@ -37,52 +40,65 @@ import java.util.UUID; */ public interface GeyserApiBase { /** - * Gets the session from the given UUID, if applicable. The player must be logged in to the Java server + * Gets the connection from the given UUID, if applicable. The player must be logged in to the Java server * for this to return a non-null value. * - * @param uuid the UUID of the session - * @return the session from the given UUID, if applicable + * @param uuid the UUID of the connection + * @return the connection from the given UUID, if applicable */ @Nullable Connection connectionByUuid(@NonNull UUID uuid); /** - * Gets the session from the given - * XUID, if applicable. + * Gets the connection from the given XUID, if applicable. This method only works for online connections. * * @param xuid the XUID of the session - * @return the session from the given UUID, if applicable + * @return the connection from the given UUID, if applicable */ @Nullable Connection connectionByXuid(@NonNull String xuid); /** - * Gets the session from the given - * name, if applicable. + * Method to determine if the given online player is a Bedrock player. * - * @param name the uuid of the session - * @return the session from the given name, if applicable + * @param uuid the uuid of the online player + * @return true if the given online player is a Bedrock player */ - @Nullable - Connection connectionByName(@NonNull String name); + boolean isBedrockPlayer(@NonNull UUID uuid); + + boolean sendForm(UUID uuid, Form form); + + boolean sendForm(UUID uuid, FormBuilder formBuilder); + + boolean transfer(UUID uuid, String address, int port); + /** - * Gets all the online sessions. - * - * @return all the online sessions + * Returns all the online connections. */ @NonNull List onlineConnections(); /** - * @return the major API version. Bumped whenever a significant breaking change or feature addition is added. + * Returns the amount of online connections. + */ + int onlineConnectionsCount(); + + /** + * Returns the prefix used by Floodgate. Will be null when the auth-type isn't Floodgate. + */ + @MonotonicNonNull + String usernamePrefix(); + + /** + * Returns the major API version. Bumped whenever a significant breaking change or feature addition is added. */ default int majorApiVersion() { return 1; } /** - * @return the minor API version. May be bumped for new API additions. + * Returns the minor API version. May be bumped for new API additions. */ default int minorApiVersion() { return 0; diff --git a/api/base/src/main/java/org/geysermc/api/connection/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java index fc6cdae20..db09149e2 100644 --- a/api/base/src/main/java/org/geysermc/api/connection/Connection.java +++ b/api/base/src/main/java/org/geysermc/api/connection/Connection.java @@ -25,6 +25,7 @@ package org.geysermc.api.connection; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.value.qual.IntRange; @@ -33,27 +34,37 @@ import java.util.UUID; /** * Represents a player connection. */ -@NonNull public interface Connection { /** - * Gets the name of the connection. + * Gets the bedrock name of the connection. * - * @return the name of the connection + * @return the bedrock name of the connection */ - String name(); + @NonNull + String bedrockUsername(); + + /** + * Gets the java name of the connection. + * + * @return the java name of the connection + */ + @MonotonicNonNull + String javaUsername(); /** * Gets the {@link UUID} of the connection. * * @return the UUID of the connection */ - UUID uuid(); + @MonotonicNonNull + UUID javaUuid(); /** * Gets the XUID of the connection. * * @return the XUID of the connection */ + @NonNull String xuid(); /** diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 16bfe7070..9b584b985 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -59,7 +59,7 @@ public interface GeyserApi extends GeyserApiBase { * {@inheritDoc} */ @Override - @Nullable GeyserConnection connectionByName(@NonNull String name); + @Nullable GeyserConnection connectionByUsername(@NonNull String username); /** * {@inheritDoc} diff --git a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 21aa35efc..258787e78 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -128,7 +128,7 @@ public class GeyserSession { } public String getName() { - return this.handle.name(); + return this.handle.bedrockUsername(); } public boolean isConsole() { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 276c5328d..67a144f36 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -465,9 +465,10 @@ public class GeyserImpl implements GeyserApi { } @Override - public @Nullable GeyserSession connectionByName(@NonNull String name) { + public @Nullable GeyserSession connectionByUsername(@NonNull String username) { for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.name().equals(name) || session.getProtocol().getProfile().getName().equals(name)) { + if (session.bedrockUsername().equals(username) || session.getProtocol().getProfile().getName().equals( + username)) { return session; } } @@ -477,7 +478,12 @@ public class GeyserImpl implements GeyserApi { @Override public @NonNull List onlineConnections() { - return this.sessionManager.getAllSessions(); + return sessionManager.getAllSessions(); + } + + @Override + public int onlineConnectionsCount() { + return sessionManager.size(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java index f911e431e..90446fbb6 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java @@ -47,7 +47,7 @@ public class ListCommand extends GeyserCommand { public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.locale(), geyser.getSessionManager().size(), - geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::name).collect(Collectors.joining(" "))); + geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::bedrockUsername).collect(Collectors.joining(" "))); sender.sendMessage(message); } diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java index 45ae7eff2..fed3054b4 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java @@ -118,7 +118,7 @@ public final class ScoreboardUpdater extends Thread { FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD; geyser.getLogger().info( - GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.name(), threshold, pps) + + GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.bedrockUsername(), threshold, pps) + GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached", (millisBetweenUpdates / 1000.0)) ); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 031d8f7f5..c8ae4792b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -735,7 +735,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { try { service.login(); } catch (RequestException e) { - geyser.getLogger().error("Error while attempting to use refresh token for " + name() + "!", e); + geyser.getLogger().error("Error while attempting to use refresh token for " + bedrockUsername() + "!", e); return Boolean.FALSE; } @@ -747,7 +747,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } protocol = new MinecraftProtocol(profile, service.getAccessToken()); - geyser.saveRefreshToken(name(), service.getRefreshToken()); + geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); return Boolean.TRUE; }).whenComplete((successful, ex) -> { if (this.closed) { @@ -838,7 +838,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { connectDownstream(); // Save our refresh token for later use - geyser.saveRefreshToken(name(), service.getRefreshToken()); + geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); return true; } } @@ -1071,7 +1071,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { try { runnable.run(); } catch (Throwable e) { - geyser.getLogger().error("Error thrown in " + this.name() + "'s event loop!", e); + geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } }); } @@ -1084,7 +1084,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { try { runnable.run(); } catch (Throwable e) { - geyser.getLogger().error("Error thrown in " + this.name() + "'s event loop!", e); + geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } }, duration, timeUnit); } @@ -1326,13 +1326,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } @Override - public String name() { + public String bedrockUsername() { return authData.name(); } @Override - public UUID uuid() { - return authData.uuid(); + public UUID javaUuid() { + } @Override diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java index ac0c93204..9cd5b2ef6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java @@ -89,7 +89,7 @@ public class TagCache { boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0; session.setEmulatePost1_18Logic(emulatePost1_18Logic); if (logger.isDebug()) { - logger.debug("Emulating post 1.18 block predication logic for " + session.name() + "? " + emulatePost1_18Logic); + logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic); } Map itemTags = packet.getTags().get("minecraft:item"); @@ -104,7 +104,7 @@ public class TagCache { boolean emulatePost1_13Logic = itemTags.get("minecraft:signs").length > 1; session.setEmulatePost1_13Logic(emulatePost1_13Logic); if (logger.isDebug()) { - logger.debug("Emulating post 1.13 villager logic for " + session.name() + "? " + emulatePost1_13Logic); + logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic); } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java index 4d0e98444..7b6dacd16 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java @@ -114,7 +114,7 @@ public final class FloodgateSkinUploader { if (session != null) { if (!node.get("success").asBoolean()) { - logger.info("Failed to upload skin for " + session.name()); + logger.info("Failed to upload skin for " + session.bedrockUsername()); return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 6f4ca7ee4..fc442c329 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -201,7 +201,7 @@ public abstract class InventoryTranslator { TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { if (session.getGeyser().getConfig().isDebugMode()) { - session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.name()); + session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); } return rejectRequest(request); @@ -292,7 +292,7 @@ public abstract class InventoryTranslator { if (!(checkNetId(session, inventory, source) && checkNetId(session, inventory, destination))) { if (session.getGeyser().getConfig().isDebugMode()) { - session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.name()); + session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, source, destination); } return rejectRequest(request); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java index e2349e5a5..ee7d6a7c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java @@ -371,7 +371,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } } default -> { - session.getGeyser().getLogger().error("Unknown crafting state induced by " + session.name()); + session.getGeyser().getLogger().error("Unknown crafting state induced by " + session.bedrockUsername()); return rejectRequest(request); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 815456132..521adb687 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -546,7 +546,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator 300) { - session.getGeyser().getLogger().debug(ChatColor.RED + session.name() + " moved too quickly." + + session.getGeyser().getLogger().debug(ChatColor.RED + session.bedrockUsername() + " moved too quickly." + " current position: " + currentPosition + ", new position: " + newPosition); return false; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java index 0775028fe..619825338 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java @@ -48,7 +48,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator inventorySize) { GeyserImpl geyser = session.getGeyser(); - geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.name() + geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername() + " that exceeds inventory size!"); if (geyser.getConfig().isDebugMode()) { geyser.getLogger().debug(packet); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java index aef8cf8b2..9a5569392 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java @@ -76,7 +76,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator= inventory.getSize()) { GeyserImpl geyser = session.getGeyser(); - geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.name() + geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.bedrockUsername() + " that exceeds inventory size!"); if (geyser.getConfig().isDebugMode()) { geyser.getLogger().debug(packet); From ab6e0d1e168d46e8883477998b1acc2cd17cb5d9 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 12 Aug 2022 01:01:26 +0200 Subject: [PATCH 090/125] Some more API changes --- .../java/org/geysermc/api/GeyserApiBase.java | 30 ++++- .../geysermc/api/connection/Connection.java | 78 +++++++++--- .../geysermc/api/util/BedrockPlatform.java | 70 +++++++++++ .../java/org/geysermc/api/util/InputMode.java | 46 +++++++ .../java/org/geysermc/api/util/UiProfile.java | 42 +++++++ .../java/org/geysermc/geyser/GeyserImpl.java | 45 ++++++- .../type/player/SessionPlayerEntity.java | 2 +- .../geyser/session/GeyserSession.java | 112 ++++++++++++------ .../geyser/session/SessionManager.java | 11 ++ 9 files changed, 374 insertions(+), 62 deletions(-) create mode 100644 api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java create mode 100644 api/base/src/main/java/org/geysermc/api/util/InputMode.java create mode 100644 api/base/src/main/java/org/geysermc/api/util/UiProfile.java diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index e9957e21c..a845e37fd 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -28,6 +28,7 @@ package org.geysermc.api; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.common.value.qual.IntRange; import org.geysermc.api.connection.Connection; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -66,11 +67,34 @@ public interface GeyserApiBase { */ boolean isBedrockPlayer(@NonNull UUID uuid); - boolean sendForm(UUID uuid, Form form); + /** + * Sends a form to the given connection and opens it. + * + * @param uuid the uuid of the connection to open it on + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull Form form); - boolean sendForm(UUID uuid, FormBuilder formBuilder); + /** + * Sends a form to the given connection and opens it. + * + * @param uuid the uuid of the connection to open it on + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder); - boolean transfer(UUID uuid, String address, int port); + /** + * Transfer the given connection to a server. A Bedrock player can successfully transfer to the same server they are + * currently playing on. + * + * @param uuid the uuid of the connection + * @param address the address of the server + * @param port the port of the server + * @return true if the transfer was a success + */ + boolean transfer(@NonNull UUID uuid, @NonNull String address, @IntRange(from = 0, to = 65535) int port); /** diff --git a/api/base/src/main/java/org/geysermc/api/connection/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java index db09149e2..1cd7a9d13 100644 --- a/api/base/src/main/java/org/geysermc/api/connection/Connection.java +++ b/api/base/src/main/java/org/geysermc/api/connection/Connection.java @@ -28,6 +28,11 @@ package org.geysermc.api.connection; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import java.util.UUID; @@ -36,43 +41,80 @@ import java.util.UUID; */ public interface Connection { /** - * Gets the bedrock name of the connection. - * - * @return the bedrock name of the connection + * Returns the bedrock name of the connection. */ - @NonNull - String bedrockUsername(); + @NonNull String bedrockUsername(); /** - * Gets the java name of the connection. - * - * @return the java name of the connection + * Returns the java name of the connection. */ @MonotonicNonNull String javaUsername(); /** - * Gets the {@link UUID} of the connection. - * - * @return the UUID of the connection + * Returns the UUID of the connection. */ @MonotonicNonNull UUID javaUuid(); /** - * Gets the XUID of the connection. - * - * @return the XUID of the connection + * Returns the XUID of the connection. */ - @NonNull - String xuid(); + @NonNull String xuid(); + + /** + * Returns the version of the Bedrock client. + */ + @NonNull String version(); + + /** + * Returns the platform that the connection is playing on. + */ + @NonNull BedrockPlatform platform(); + + /** + * Returns the language code of the connection. + */ + @NonNull String languageCode(); + + /** + * Returns the User Interface Profile of the connection. + */ + @NonNull UiProfile uiProfile(); + + /** + * Returns the Input Mode of the Bedrock client. + */ + @NonNull InputMode inputMode(); + + /** + * Returns whether the connection is linked. + * This will always return false when the auth-type isn't Floodgate. + */ + boolean isLinked(); + + /** + * Sends a form to the connection and opens it. + * + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull Form form); + + /** + * Sends a form to the connection and opens it. + * + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull FormBuilder formBuilder); /** * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are * currently playing on. * - * @param address The address of the server - * @param port The port of the server + * @param address the address of the server + * @param port the port of the server * @return true if the transfer was a success */ boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); diff --git a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java new file mode 100644 index 000000000..d66077a87 --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.api.util; + +public enum BedrockPlatform { + UNKNOWN("Unknown"), + GOOGLE("Android"), + IOS("iOS"), + OSX("macOS"), + AMAZON("Amazon"), + GEARVR("Gear VR"), + HOLOLENS("Hololens"), + UWP("Windows 10"), + WIN32("Windows x86"), + DEDICATED("Dedicated"), + TVOS("Apple TV"), + PS4("PS4"), + NX("Switch"), + XBOX("Xbox One"), + WINDOWS_PHONE("Windows Phone"); + + private static final BedrockPlatform[] VALUES = values(); + + private final String displayName; + + BedrockPlatform(String displayName) { + this.displayName = displayName; + } + + /** + * Get the BedrockPlatform from the identifier. + * + * @param id the BedrockPlatform identifier + * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found + */ + public static BedrockPlatform fromId(int id) { + return id < VALUES.length ? VALUES[id] : VALUES[0]; + } + + /** + * @return friendly display name of platform. + */ + @Override + public String toString() { + return displayName; + } +} diff --git a/api/base/src/main/java/org/geysermc/api/util/InputMode.java b/api/base/src/main/java/org/geysermc/api/util/InputMode.java new file mode 100644 index 000000000..eadb457ab --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/InputMode.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.api.util; + +public enum InputMode { + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, + CONTROLLER, + VR; + + private static final InputMode[] VALUES = values(); + + /** + * Get the InputMode from the identifier. + * + * @param id the InputMode identifier + * @return The InputMode or {@link #UNKNOWN} if the mode wasn't found + */ + public static InputMode fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java new file mode 100644 index 000000000..c28ff869c --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.api.util; + +public enum UiProfile { + CLASSIC, POCKET; + + private static final UiProfile[] VALUES = values(); + + /** + * Get the UiProfile from the identifier. + * + * @param id the UiProfile identifier + * @return The UiProfile or {@link #CLASSIC} if the profile wasn't found + */ + public static UiProfile fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 67a144f36..94a2b4304 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -41,10 +41,13 @@ import io.netty.util.internal.SystemPropertyUtil; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.common.PlatformType; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.Base64Topping; @@ -486,6 +489,11 @@ public class GeyserImpl implements GeyserApi { return sessionManager.size(); } + @Override + public @MonotonicNonNull String usernamePrefix() { + return null; + } + @Override public @Nullable GeyserSession connectionByUuid(@NonNull UUID uuid) { return this.sessionManager.getSessions().get(uuid); @@ -493,13 +501,38 @@ public class GeyserImpl implements GeyserApi { @Override public @Nullable GeyserSession connectionByXuid(@NonNull String xuid) { - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.xuid().equals(xuid)) { - return session; - } - } + return sessionManager.sessionByXuid(xuid); + } - return null; + @Override + public boolean isBedrockPlayer(@NonNull UUID uuid) { + return connectionByUuid(uuid) != null; + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull Form form) { + Objects.requireNonNull(uuid); + Objects.requireNonNull(form); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.sendForm(form); + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder) { + return sendForm(uuid, formBuilder.build()); + } + + @Override + public boolean transfer(@NonNull UUID uuid, @NonNull String address, int port) { + Objects.requireNonNull(uuid); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.transfer(address, port); } public void shutdown() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index f16f46e2e..7a5d34973 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -73,7 +73,7 @@ public class SessionPlayerEntity extends PlayerEntity { private int fakeTradeXp; public SessionPlayerEntity(GeyserSession session) { - super(session, -1, 1, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "unknown", null); + super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); valid = true; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index c8ae4792b..f67d8d92d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -91,6 +91,9 @@ import lombok.Setter; import lombok.experimental.Accessors; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; import org.geysermc.common.PlatformType; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -136,7 +139,6 @@ import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; -import javax.annotation.Nonnull; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -151,13 +153,13 @@ import java.util.concurrent.atomic.AtomicInteger; @Getter public class GeyserSession implements GeyserConnection, GeyserCommandSource { - private final @Nonnull GeyserImpl geyser; - private final @Nonnull UpstreamSession upstream; + private final @NonNull GeyserImpl geyser; + private final @NonNull UpstreamSession upstream; /** * The loop where all packets and ticking is processed to prevent concurrency issues. * If this is manually called, ensure that any exceptions are properly handled. */ - private final @Nonnull EventLoop eventLoop; + private final @NonNull EventLoop eventLoop; private TcpSession downstream; @Setter private AuthData authData; @@ -1326,33 +1328,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } @Override - public String bedrockUsername() { - return authData.name(); - } - - @Override - public UUID javaUuid() { - - } - - @Override - public String xuid() { - return authData.xuid(); - } - - @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations - @Override - public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { - if (address == null || address.isBlank()) { - throw new IllegalArgumentException("Server address cannot be null or blank"); - } else if (port < 0 || port > 65535) { - throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); - } - TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress(address); - transferPacket.setPort(port); - sendUpstreamPacket(transferPacket); - return true; + public String name() { + return null; } @Override @@ -1405,12 +1382,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return this.upstream.getAddress(); } - public void sendForm(Form form) { + public boolean sendForm(@NonNull Form form) { formCache.showForm(form); + return true; } - public void sendForm(FormBuilder formBuilder) { + public boolean sendForm(@NonNull FormBuilder formBuilder) { formCache.showForm(formBuilder.build()); + return true; } /** @@ -1765,7 +1744,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * * @param statistics Updated statistics values */ - public void updateStatistics(@Nonnull Object2IntMap statistics) { + public void updateStatistics(@NonNull Object2IntMap statistics) { if (this.statistics.isEmpty()) { // Initialize custom statistics to 0, so that they appear in the form for (CustomStatistic customStatistic : CustomStatistic.values()) { @@ -1851,4 +1830,69 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { public MinecraftCodecHelper getCodecHelper() { return (MinecraftCodecHelper) this.downstream.getCodecHelper(); } + + @Override + public String bedrockUsername() { + return authData.name(); + } + + @Override + public @MonotonicNonNull String javaUsername() { + return playerEntity.getUsername(); + } + + @Override + public UUID javaUuid() { + return playerEntity.getUuid(); + } + + @Override + public String xuid() { + return authData.xuid(); + } + + @Override + public @NonNull String version() { + return clientData.getGameVersion(); + } + + @Override + public @NonNull BedrockPlatform platform() { + return BedrockPlatform.values()[clientData.getDeviceOs().ordinal()]; //todo + } + + @Override + public @NonNull String languageCode() { + return locale(); + } + + @Override + public @NonNull UiProfile uiProfile() { + return UiProfile.values()[clientData.getUiProfile().ordinal()]; //todo + } + + @Override + public @NonNull InputMode inputMode() { + return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo + } + + @Override + public boolean isLinked() { + return false; //todo + } + + @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations + @Override + public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { + if (address == null || address.isBlank()) { + throw new IllegalArgumentException("Server address cannot be null or blank"); + } else if (port < 0 || port > 65535) { + throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); + } + TransferPacket transferPacket = new TransferPacket(); + transferPacket.setAddress(address); + transferPacket.setPort(port); + sendUpstreamPacket(transferPacket); + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java index fc6c37356..2660f54b1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.session; import com.google.common.collect.ImmutableList; import lombok.AccessLevel; import lombok.Getter; +import lombok.NonNull; import org.geysermc.geyser.text.GeyserLocale; import java.util.*; @@ -67,6 +68,16 @@ public final class SessionManager { } } + public GeyserSession sessionByXuid(@NonNull String xuid) { + Objects.requireNonNull(xuid); + for (GeyserSession session : sessions.values()) { + if (session.xuid().equals(xuid)) { + return session; + } + } + return null; + } + /** * Creates a new, immutable list containing all pending and active sessions. */ From 13046a8602a1f9499dd2618e10c94fe1fb316849 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 13 Aug 2022 22:48:12 +0200 Subject: [PATCH 091/125] Fixed building and switched event library --- api/base/build.gradle.kts | 3 +- .../org/geysermc/geyser/api/GeyserApi.java | 6 - .../geysermc/geyser/api/event/EventBus.java | 63 +---------- ...{Cancellable.java => EventSubscriber.java} | 26 ++--- .../geyser/api/event/EventSubscription.java | 82 -------------- .../geyser/api/event/ExtensionEventBus.java | 31 +----- ...ent.java => ExtensionEventSubscriber.java} | 15 +-- .../geysermc/geyser/api/event/Subscribe.java | 103 ----------------- .../api/event/connection/ConnectionEvent.java | 2 +- .../downstream/ServerDefineCommandsEvent.java | 2 +- .../GeyserDefineCustomItemsEvent.java | 29 ++--- .../GeyserLoadResourcePacksEvent.java | 2 +- .../lifecycle/GeyserPostInitializeEvent.java | 2 +- .../lifecycle/GeyserPreInitializeEvent.java | 2 +- .../event/lifecycle/GeyserShutdownEvent.java | 2 +- build-logic/src/main/kotlin/Versions.kt | 2 +- core/build.gradle.kts | 3 - .../java/org/geysermc/geyser/GeyserImpl.java | 12 -- .../event/AbstractEventSubscription.java | 68 ------------ .../event/GeneratedEventSubscription.java | 60 ---------- .../geysermc/geyser/event/GeyserEventBus.java | 105 ++++-------------- ...iption.java => GeyserEventSubscriber.java} | 35 ++---- .../event/type/DefineCustomItemsEvent.java | 85 ++++++++++++++ .../event/GeyserExtensionEventBus.java | 72 +++++------- .../populator/ItemRegistryPopulator.java | 3 +- 25 files changed, 193 insertions(+), 622 deletions(-) rename api/geyser/src/main/java/org/geysermc/geyser/api/event/{Cancellable.java => EventSubscriber.java} (74%) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java rename api/geyser/src/main/java/org/geysermc/geyser/api/event/{Event.java => ExtensionEventSubscriber.java} (85%) delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java delete mode 100644 core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java delete mode 100644 core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java rename core/src/main/java/org/geysermc/geyser/event/{BaseEventSubscription.java => GeyserEventSubscriber.java} (55%) create mode 100644 core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts index c9ddf4489..03f08c6d2 100644 --- a/api/base/build.gradle.kts +++ b/api/base/build.gradle.kts @@ -1,5 +1,4 @@ dependencies { api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) + api("org.geysermc.event", "events", Versions.eventsVersion) } - -provided("net.kyori", "event-api", Versions.eventVersion) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 9b584b985..82f0566fe 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -55,12 +55,6 @@ public interface GeyserApi extends GeyserApiBase { @Override @Nullable GeyserConnection connectionByXuid(@NonNull String xuid); - /** - * {@inheritDoc} - */ - @Override - @Nullable GeyserConnection connectionByUsername(@NonNull String username); - /** * {@inheritDoc} */ diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java index b13f12300..c42698d47 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -26,71 +26,18 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.event.bus.OwnedEventBus; import org.geysermc.geyser.api.extension.Extension; import java.util.Set; -import java.util.function.Consumer; /** * Represents a bus capable of subscribing * or "listening" to events and firing them. */ -public interface EventBus { - - /** - * Subscribes to the given event see {@link EventSubscription}. - * - * The difference between this method and {@link ExtensionEventBus#subscribe(Class, Consumer)} - * is that this method takes in an extension parameter which allows for - * the event to be unsubscribed upon extension disable and reloads. - * - * @param extension the extension to subscribe the event to - * @param eventClass the class of the event - * @param consumer the consumer for handling the event - * @param the event class - * @return the event subscription - */ +public interface EventBus extends OwnedEventBus> { + @Override @NonNull - EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer); - - /** - * Unsubscribes the given {@link EventSubscription}. - * - * @param subscription the event subscription - */ - void unsubscribe(@NonNull EventSubscription subscription); - - /** - * Registers events for the given listener. - * - * @param extension the extension registering the event - * @param eventHolder the listener - */ - void register(@NonNull Extension extension, @NonNull Object eventHolder); - - /** - * Unregisters all events from a given {@link Extension}. - * - * @param extension the extension - */ - void unregisterAll(@NonNull Extension extension); - - /** - * Fires the given {@link Event} and returns the result. - * - * @param event the event to fire - * - * @return true if the event successfully fired - */ - boolean fire(@NonNull Event event); - - /** - * Gets the subscriptions for the given event class. - * - * @param eventClass the event class - * @param the value - * @return the subscriptions for the event class - */ - @NonNull - Set> subscriptions(@NonNull Class eventClass); + Set> subscribers(@NonNull Class eventClass); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java similarity index 74% rename from api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java index 94d0b832d..7ce5b7883 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Cancellable.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java @@ -25,22 +25,16 @@ package org.geysermc.geyser.api.event; +import org.geysermc.event.Event; +import org.geysermc.event.subscribe.OwnedSubscriber; +import org.geysermc.geyser.api.extension.Extension; + /** - * Represents a cancellable event. + * Represents a subscribed listener to a {@link Event}. Wraps around + * the event and is capable of unsubscribing from the event or give + * information about it. + * + * @param the class of the event */ -public interface Cancellable { - - /** - * Gets if the event is cancelled. - * - * @return if the event is cancelled - */ - boolean isCancelled(); - - /** - * Cancels the event. - * - * @param cancelled if the event is cancelled - */ - void setCancelled(boolean cancelled); +public interface EventSubscriber extends OwnedSubscriber { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java deleted file mode 100644 index 9a04b697c..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscription.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.event; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.extension.Extension; - -/** - * Represents a subscribed listener to a {@link Event}. Wraps around - * the event and is capable of unsubscribing from the event or give - * information about it. - * - * @param the class of the event - */ -public interface EventSubscription { - - /** - * Gets the event class. - * - * @return the event class - */ - @NonNull - Class eventClass(); - - /** - * Gets the {@link Extension} that owns this - * event subscription. - * - * @return the extension that owns this subscription - */ - @NonNull - Extension owner(); - - /** - * Gets the post order of this event subscription. - * - * @return the post order of this event subscription - */ - Subscribe.PostOrder order(); - - /** - * Gets if this event subscription is active. - * - * @return if this event subscription is active - */ - boolean isActive(); - - /** - * Unsubscribes from this event listener - */ - void unsubscribe(); - - /** - * Invokes the given event - * - * @param event the event - */ - void invoke(@NonNull T event) throws Throwable; -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java index db0209e5f..172c0f9de 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java @@ -26,36 +26,15 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; -import java.util.function.Consumer; +import java.util.Set; /** * An {@link EventBus} with additional methods that implicitly * set the extension instance. - * */ -public interface ExtensionEventBus extends EventBus { - - /** - * Subscribes to the given event see {@link EventSubscription}. - * - * @param eventClass the class of the event - * @param consumer the consumer for handling the event - * @param the event class - * @return the event subscription - */ - @NonNull - EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer); - - /** - * Registers events for the given listener. - * - * @param eventHolder the listener - */ - void register(@NonNull Object eventHolder); - - /** - * Unregisters all events for this extension. - */ - void unregisterAll(); +public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { + @Override + @NonNull Set> subscribers(@NonNull Class eventClass); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java similarity index 85% rename from api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java index c32e1701e..9c5fffa2f 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Event.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java @@ -25,17 +25,8 @@ package org.geysermc.geyser.api.event; -/** - * Represents an event. - */ -public interface Event { +import org.geysermc.event.Event; +import org.geysermc.event.subscribe.Subscriber; - /** - * Gets if the event is async. - * - * @return if the event is async - */ - default boolean isAsync() { - return false; - } +public interface ExtensionEventSubscriber extends Subscriber { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java deleted file mode 100644 index 488fa0ea3..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/Subscribe.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.api.event; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation used to signify the given method is - * an {@link Event}. Only should be applied to methods - * where the class containing them is designated for - * events specifically. - * - * When using {@link EventBus#subscribe}, this annotation should - * not be applied whatsoever as it will have no use and potentially - * throw errors due to it being used wrongly. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Subscribe { - - /** - * The {@link PostOrder} of the event - * - * @return the post order of the event - */ - Subscribe.PostOrder postOrder() default PostOrder.NORMAL; - - /** - * Represents the post order of an event. - */ - enum PostOrder { - - /** - * The lowest priority. Called first to - * allow for other events to customize - * the outcome - */ - FIRST(net.kyori.event.PostOrders.FIRST), - - /** - * The second lowest priority. - */ - EARLY(net.kyori.event.PostOrders.EARLY), - - /** - * Normal priority. Event is neither - * important nor unimportant - */ - NORMAL(net.kyori.event.PostOrders.NORMAL), - - /** - * The second highest priority - */ - LATE(net.kyori.event.PostOrders.LATE), - - /** - * The highest of importance! Event is called - * last and has the final say in the outcome - */ - LAST(net.kyori.event.PostOrders.LAST); - - private final int postOrder; - - PostOrder(int postOrder) { - this.postOrder = postOrder; - } - - /** - * The numerical post order value. - * - * @return numerical post order value - */ - public int postOrder() { - return this.postOrder; - } - } -} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java index 48f3acdb7..158f14d53 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.api.event.connection; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.event.Event; /** * An event that contains a {@link GeyserConnection}. diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java index 2ab1b9611..e46492b36 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.api.event.downstream; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Cancellable; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.event.Cancellable; import org.geysermc.geyser.api.event.connection.ConnectionEvent; import java.util.Set; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java index 308b39d22..bfed5d534 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java @@ -25,45 +25,34 @@ package org.geysermc.geyser.api.event.lifecycle; -import com.google.common.collect.Multimap; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * Called on Geyser's startup when looking for custom items. Custom items must be registered through this event. * * This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config. */ -public abstract class GeyserDefineCustomItemsEvent implements Event { - private final Multimap customItems; - private final List nonVanillaCustomItems; - - public GeyserDefineCustomItemsEvent(Multimap customItems, List nonVanillaCustomItems) { - this.customItems = customItems; - this.nonVanillaCustomItems = nonVanillaCustomItems; - } - +public interface GeyserDefineCustomItemsEvent extends Event { /** * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. * * @return a multimap of all the already registered custom items */ - public Map> getExistingCustomItems() { - return Collections.unmodifiableMap(this.customItems.asMap()); - } + Map> getExistingCustomItems(); /** * Gets the list of the already registered non-vanilla custom items. * * @return the list of the already registered non-vanilla custom items */ - public List getExistingNonVanillaCustomItems() { - return Collections.unmodifiableList(this.nonVanillaCustomItems); - } + List getExistingNonVanillaCustomItems(); /** * Registers a custom item with a base Java item. This is used to register items with custom textures and properties @@ -73,7 +62,7 @@ public abstract class GeyserDefineCustomItemsEvent implements Event { * @param customItemData the custom item data to register * @return if the item was registered */ - public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); /** * Registers a custom item with no base item. This is used for mods. @@ -81,5 +70,5 @@ public abstract class GeyserDefineCustomItemsEvent implements Event { * @param customItemData the custom item data to register * @return if the item was registered */ - public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData); + boolean register(@NonNull NonVanillaCustomItemData customItemData); } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java index 0f181aedf..e9b283ecb 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; import java.nio.file.Path; import java.util.List; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java index 94e42e075..9e88c017b 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java index fa130c883..2be0272dc 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java index a0fc2294b..f2fab901d 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; import org.geysermc.geyser.api.command.CommandManager; -import org.geysermc.geyser.api.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index d9af831f0..f7b20cca1 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -39,9 +39,9 @@ object Versions { const val mcprotocollibversion = "9f78bd5" const val packetlibVersion = "3.0" const val adventureVersion = "4.9.3" - const val eventVersion = "3.0.0" const val junitVersion = "4.13.1" const val checkerQualVersion = "3.19.0" const val cumulusVersion = "1.1.1" + const val eventsVersion = "1.0-SNAPSHOT" const val log4jVersion = "2.17.1" } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 602e0e8e3..83591e7ad 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -67,9 +67,6 @@ dependencies { implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) implementation("net.kyori", "adventure-text-serializer-plain", Versions.adventureVersion) - // Kyori Misc - implementation("net.kyori", "event-api", Versions.eventVersion) - // Test testImplementation("junit", "junit", Versions.junitVersion) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 94a2b4304..87a40c828 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -467,18 +467,6 @@ public class GeyserImpl implements GeyserApi { this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); } - @Override - public @Nullable GeyserSession connectionByUsername(@NonNull String username) { - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.bedrockUsername().equals(username) || session.getProtocol().getProfile().getName().equals( - username)) { - return session; - } - } - - return null; - } - @Override public @NonNull List onlineConnections() { return sessionManager.getAllSessions(); diff --git a/core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java deleted file mode 100644 index 69dc74935..000000000 --- a/core/src/main/java/org/geysermc/geyser/event/AbstractEventSubscription.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.event; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import net.kyori.event.EventSubscriber; -import org.geysermc.geyser.api.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventSubscription; -import org.geysermc.geyser.api.event.Subscribe; -import org.geysermc.geyser.api.extension.Extension; - -@Getter -@Accessors(fluent = true) -@RequiredArgsConstructor -public abstract class AbstractEventSubscription implements EventSubscription, EventSubscriber { - protected final EventBus eventBus; - protected final Class eventClass; - protected final Extension owner; - protected final Subscribe.PostOrder order; - @Getter(AccessLevel.NONE) private boolean active; - - @Override - public boolean isActive() { - return this.active; - } - - @Override - public void unsubscribe() { - if (!this.active) { - return; - } - - this.active = false; - this.eventBus.unsubscribe(this); - } - - @Override - public int postOrder() { - return this.order.postOrder(); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java deleted file mode 100644 index b1ba7bf8b..000000000 --- a/core/src/main/java/org/geysermc/geyser/event/GeneratedEventSubscription.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.event; - -import lombok.Getter; -import lombok.experimental.Accessors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.Subscribe; -import org.geysermc.geyser.api.extension.Extension; - -import java.util.function.BiConsumer; - -@Getter -@Accessors(fluent = true) -public class GeneratedEventSubscription extends AbstractEventSubscription { - private final Object eventHolder; - private final BiConsumer eventConsumer; - - public GeneratedEventSubscription(EventBus eventBus, Class eventClass, Extension owner, Subscribe.PostOrder order, Object eventHolder, BiConsumer eventConsumer) { - super(eventBus, eventClass, owner, order); - - this.eventHolder = eventHolder; - this.eventConsumer = eventConsumer; - } - - @Override - public void invoke(@NonNull T event) throws Throwable { - try { - this.eventConsumer.accept(this.eventHolder, event); - } catch (Throwable ex) { - this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); - ex.printStackTrace(); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index 60e354ac9..7d0d6336d 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -25,97 +25,40 @@ package org.geysermc.geyser.event; -import net.kyori.event.EventSubscriber; -import net.kyori.event.SimpleEventBus; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; +import org.geysermc.event.bus.impl.OwnedEventBusImpl; +import org.geysermc.event.subscribe.OwnedSubscriber; +import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventSubscription; -import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.geyser.api.event.EventSubscriber; import org.geysermc.geyser.api.extension.Extension; -import org.lanternpowered.lmbda.LambdaFactory; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -public class GeyserEventBus implements EventBus { - private static final MethodHandles.Lookup CALLER = MethodHandles.lookup(); +@SuppressWarnings("unchecked") +public final class GeyserEventBus extends OwnedEventBusImpl> + implements EventBus { + @Override + protected > B makeSubscription( + Extension owner, Class eventClass, Subscribe subscribe, + L listener, BiConsumer handler) { + return (B) new GeyserEventSubscriber<>( + owner, eventClass, subscribe.postOrder(), subscribe.ignoreCancelled(), listener, handler + ); + } - private final SimpleEventBus bus = new SimpleEventBus<>(Event.class); + @Override + protected > B makeSubscription( + Extension owner, Class eventClass, Consumer handler) { + return (B) new GeyserEventSubscriber<>(owner, eventClass, handler); + } + @Override @NonNull - @Override - public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { - return this.subscribe(eventClass, consumer, extension, Subscribe.PostOrder.NORMAL); - } - - @Override - public void unsubscribe(@NonNull EventSubscription subscription) { - this.bus.unregister((AbstractEventSubscription) subscription); - } - - @SuppressWarnings("unchecked") - @Override - public void register(@NonNull Extension extension, @NonNull Object eventHolder) { - for (Method method : eventHolder.getClass().getMethods()) { - if (!method.isAnnotationPresent(Subscribe.class)) { - continue; - } - - if (method.getParameterCount() > 1) { - continue; - } - - if (!Event.class.isAssignableFrom(method.getParameters()[0].getType())) { - continue; - } - - Subscribe subscribe = method.getAnnotation(Subscribe.class); - - try { - Class type = (Class) method.getParameters()[0].getType(); - this.subscribe(type, eventHolder, LambdaFactory.createBiConsumer(CALLER.unreflect(method)), extension, subscribe.postOrder()); - } catch (IllegalAccessException ex) { - ex.printStackTrace(); - } - } - } - - @Override - public void unregisterAll(@NonNull Extension extension) { - this.bus.unregister((Predicate>) subscriber -> extension.equals(((AbstractEventSubscription) subscriber).owner())); - } - - @Override - public boolean fire(@NonNull Event event) { - return this.bus.post(event).wasSuccessful(); - } - - @SuppressWarnings("unchecked") - @NonNull - @Override - public Set> subscriptions(@NonNull Class eventClass) { - return bus.subscribers().values() - .stream() - .filter(sub -> sub instanceof EventSubscription && ((EventSubscription) sub).eventClass().isAssignableFrom(eventClass)) - .map(sub -> ((EventSubscription) sub)) - .collect(Collectors.toSet()); - } - - private EventSubscription subscribe(Class eventClass, Consumer handler, Extension extension, Subscribe.PostOrder postOrder) { - BaseEventSubscription eventSubscription = new BaseEventSubscription<>(this, eventClass, extension, postOrder, handler); - this.bus.register(eventClass, eventSubscription); - return eventSubscription; - } - - private EventSubscription subscribe(Class eventClass, Object eventHolder, BiConsumer handler, Extension extension, Subscribe.PostOrder postOrder) { - GeneratedEventSubscription eventSubscription = new GeneratedEventSubscription<>(this, eventClass, extension, postOrder, eventHolder, handler); - this.bus.register(eventClass, eventSubscription); - return eventSubscription; + public Set> subscribers(@NonNull Class eventClass) { + return castGenericSet(super.subscribers(eventClass)); } } diff --git a/core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java similarity index 55% rename from core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java rename to core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java index 79df94e5d..6fac82b96 100644 --- a/core/src/main/java/org/geysermc/geyser/event/BaseEventSubscription.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java @@ -25,34 +25,23 @@ package org.geysermc.geyser.event; -import lombok.Getter; -import lombok.experimental.Accessors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.Subscribe; +import org.geysermc.event.Event; +import org.geysermc.event.PostOrder; +import org.geysermc.event.subscribe.impl.OwnedSubscriberImpl; +import org.geysermc.geyser.api.event.ExtensionEventSubscriber; import org.geysermc.geyser.api.extension.Extension; +import java.util.function.BiConsumer; import java.util.function.Consumer; -@Getter -@Accessors(fluent = true) -public class BaseEventSubscription extends AbstractEventSubscription { - private final Consumer eventConsumer; - - public BaseEventSubscription(EventBus eventBus, Class eventClass, Extension owner, Subscribe.PostOrder order, Consumer eventConsumer) { - super(eventBus, eventClass, owner, order); - - this.eventConsumer = eventConsumer; +public final class GeyserEventSubscriber extends OwnedSubscriberImpl + implements ExtensionEventSubscriber { + GeyserEventSubscriber(Extension owner, Class eventClass, Consumer handler) { + super(owner, eventClass, handler); } - @Override - public void invoke(@NonNull T event) throws Throwable { - try { - this.eventConsumer.accept(event); - } catch (Throwable ex) { - this.owner.logger().warning("Unable to fire event " + event.getClass().getSimpleName() + " with subscription " + this.eventConsumer.getClass().getSimpleName()); - ex.printStackTrace(); - } + GeyserEventSubscriber(Extension owner, Class eventClass, PostOrder postOrder, boolean ignoreCancelled, + H handlerInstance, BiConsumer handler) { + super(owner, eventClass, postOrder, ignoreCancelled, handlerInstance, handler); } } diff --git a/core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java b/core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java new file mode 100644 index 000000000..9011bfb3a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event.type; + +import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public abstract class DefineCustomItemsEvent implements GeyserDefineCustomItemsEvent { + private final Multimap customItems; + private final List nonVanillaCustomItems; + + public DefineCustomItemsEvent(Multimap customItems, List nonVanillaCustomItems) { + this.customItems = customItems; + this.nonVanillaCustomItems = nonVanillaCustomItems; + } + + /** + * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. + * + * @return a multimap of all the already registered custom items + */ + @Override + public Map> getExistingCustomItems() { + return Collections.unmodifiableMap(this.customItems.asMap()); + } + + /** + * Gets the list of the already registered non-vanilla custom items. + * + * @return the list of the already registered non-vanilla custom items + */ + @Override + public List getExistingNonVanillaCustomItems() { + return Collections.unmodifiableList(this.nonVanillaCustomItems); + } + + /** + * Registers a custom item with a base Java item. This is used to register items with custom textures and properties + * based on NBT data. + * + * @param identifier the base (java) item + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + + /** + * Registers a custom item with no base item. This is used for mods. + * + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData); +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java index 4104871fa..d1a647be7 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java @@ -26,62 +26,50 @@ package org.geysermc.geyser.extension.event; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.Event; +import org.geysermc.event.Event; +import org.geysermc.event.bus.impl.EventBusImpl; +import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.event.subscribe.Subscriber; import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventSubscription; +import org.geysermc.geyser.api.event.EventSubscriber; import org.geysermc.geyser.api.event.ExtensionEventBus; +import org.geysermc.geyser.api.event.ExtensionEventSubscriber; import org.geysermc.geyser.api.extension.Extension; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; -public record GeyserExtensionEventBus(EventBus eventBus, - Extension extension) implements ExtensionEventBus { - @NonNull +public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) implements ExtensionEventBus { @Override - public EventSubscription subscribe(@NonNull Class eventClass, @NonNull Consumer consumer) { - return this.eventBus.subscribe(this.extension, eventClass, consumer); - } - - @Override - public void register(@NonNull Object eventHolder) { - this.eventBus.register(this.extension, eventHolder); - } - - @Override - public void unregisterAll() { - this.eventBus.unregisterAll(this.extension); - } - - @NonNull - @Override - public EventSubscription subscribe(@NonNull Extension extension, @NonNull Class eventClass, @NonNull Consumer consumer) { - return this.eventBus.subscribe(extension, eventClass, consumer); - } - - @Override - public void unsubscribe(@NonNull EventSubscription subscription) { - this.eventBus.unsubscribe(subscription); - } - - @Override - public void register(@NonNull Extension extension, @NonNull Object eventHolder) { - this.eventBus.register(extension, eventHolder); - } - - @Override - public void unregisterAll(@NonNull Extension extension) { - this.eventBus.unregisterAll(extension); + public void unsubscribe(@NonNull EventSubscriber subscription) { + eventBus.unsubscribe(subscription); } @Override public boolean fire(@NonNull Event event) { - return this.eventBus.fire(event); + return eventBus.fire(event); } - @NonNull @Override - public Set> subscriptions(@NonNull Class eventClass) { - return this.eventBus.subscriptions(eventClass); + public @NonNull Set> subscribers(@NonNull Class eventClass) { + return eventBus.subscribers(eventClass); + } + + @Override + public void register(@NonNull Object listener) { + eventBus.register(extension, listener); + } + + @Override + @SuppressWarnings("unchecked") + public > @NonNull U subscribe( + @NonNull Class eventClass, @NonNull Consumer consumer) { + return eventBus.subscribe(extension, eventClass, consumer); + } + + @Override + public void unregisterAll() { + eventBus.unregisterAll(extension); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 72ec4af1e..e6d1cb3e4 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -52,6 +52,7 @@ import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.geysermc.geyser.event.type.DefineCustomItemsEvent; import org.geysermc.geyser.inventory.item.StoredItemMappings; import org.geysermc.geyser.item.GeyserCustomMappingData; import org.geysermc.geyser.item.mappings.MappingsConfigReader; @@ -108,7 +109,7 @@ public class ItemRegistryPopulator { }); nonVanillaCustomItems = new ObjectArrayList<>(); - GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEvent(customItems, nonVanillaCustomItems) { + GeyserImpl.getInstance().eventBus().fire(new DefineCustomItemsEvent(customItems, nonVanillaCustomItems) { @Override public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) { if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) { From e35f3785b2566b3f8ca78ff7fea271afe71ac8c2 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 24 Aug 2022 04:53:13 +0000 Subject: [PATCH 092/125] Resolve fallout --- api/geyser/build.gradle.kts | 2 ++ bootstrap/bungeecord/build.gradle.kts | 2 ++ .../bungeecord/GeyserBungeeUpdateListener.java | 4 ++-- bootstrap/spigot/build.gradle.kts | 7 ++++++- .../platform/spigot/GeyserSpigotUpdateListener.java | 4 ++-- .../velocity/GeyserVelocityUpdateListener.java | 4 ++-- build-logic/src/main/kotlin/Versions.kt | 1 + .../main/java/org/geysermc/geyser/GeyserLogger.java | 4 ++-- .../BedrockInventoryTransactionTranslator.java | 4 ++-- .../org/geysermc/geyser/util/VersionCheckUtils.java | 12 ++++++------ 10 files changed, 27 insertions(+), 17 deletions(-) diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts index dcde85337..60bd4a431 100644 --- a/api/geyser/build.gradle.kts +++ b/api/geyser/build.gradle.kts @@ -4,6 +4,8 @@ plugins { dependencies { api(projects.api) + + implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) } publishing { diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts index 873df692a..9f3b49b67 100644 --- a/bootstrap/bungeecord/build.gradle.kts +++ b/bootstrap/bungeecord/build.gradle.kts @@ -2,6 +2,8 @@ val bungeeVersion = "a7c6ede"; dependencies { api(projects.core) + + implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion) } platformRelocate("net.md_5.bungee.jni") diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java index bbde8771e..c68839b20 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java @@ -31,7 +31,7 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSender; +import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserBungeeUpdateListener implements Listener { @@ -41,7 +41,7 @@ public final class GeyserBungeeUpdateListener implements Listener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final ProxiedPlayer player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player)); } } } diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 02883999d..2fe3d6e2b 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,7 +1,7 @@ val paperVersion = "1.19-R0.1-SNAPSHOT" val viaVersion = "4.0.0" val adaptersVersion = "1.5-SNAPSHOT" -val commodoreVersion = "1.13" +val commodoreVersion = "2.2" dependencies { api(projects.core) @@ -9,6 +9,8 @@ dependencies { implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) implementation("me.lucko", "commodore", commodoreVersion) + + implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion) // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19 compileOnly("io.papermc.paper", "paper-api", paperVersion) { @@ -61,5 +63,8 @@ tasks.withType { // Commodore includes Brigadier exclude(dependency("com.mojang:.*")) + + // Adventure slf4j + exclude(dependency("net.kyori.adventure.text.logger.slf4j:ComponentLogger")) } } \ No newline at end of file diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java index 02f5367b3..5e3c4def8 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java @@ -31,7 +31,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; +import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserSpigotUpdateListener implements Listener { @@ -41,7 +41,7 @@ public final class GeyserSpigotUpdateListener implements Listener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player)); } } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java index 506dfff71..31e584612 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java @@ -30,7 +30,7 @@ import com.velocitypowered.api.event.connection.PostLoginEvent; import com.velocitypowered.api.proxy.Player; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.velocity.command.VelocityCommandSender; +import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserVelocityUpdateListener { @@ -40,7 +40,7 @@ public final class GeyserVelocityUpdateListener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player)); } } } diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index f7b20cca1..d4a0a80e3 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -39,6 +39,7 @@ object Versions { const val mcprotocollibversion = "9f78bd5" const val packetlibVersion = "3.0" const val adventureVersion = "4.9.3" + const val adventurePlatformVersion = "4.1.2" const val junitVersion = "4.13.1" const val checkerQualVersion = "3.19.0" const val cumulusVersion = "1.1.1" diff --git a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java index 197a031dd..88220eec9 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java @@ -26,11 +26,11 @@ package org.geysermc.geyser; import net.kyori.adventure.text.Component; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import javax.annotation.Nullable; -public interface GeyserLogger extends CommandSender { +public interface GeyserLogger extends GeyserCommandSource { /** * Logs a severe message to console diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 02a7ddace..e72f7d786 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -54,7 +54,7 @@ import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -467,7 +467,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator recipient) { + public static void checkForGeyserUpdate(Supplier recipient) { CompletableFuture.runAsync(() -> { try { JsonNode json = WebUtils.getJson("https://api.geysermc.org/v2/versions/geyser"); JsonNode bedrock = json.get("bedrock").get("protocol"); int protocolVersion = bedrock.get("id").asInt(); - if (MinecraftProtocol.getBedrockCodec(protocolVersion) != null) { + if (GameProtocol.getBedrockCodec(protocolVersion) != null) { // We support the latest version! No need to print a message. return; } @@ -68,11 +68,11 @@ public final class VersionCheckUtils { final String newBedrockVersion = bedrock.get("name").asText(); // Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join. - CommandSender sender = recipient.get(); + GeyserCommandSource sender = recipient.get(); // Overarching component is green - geyser.version.new component cannot be green or else the link blue is overshadowed Component message = Component.text().color(NamedTextColor.GREEN) - .append(Component.text(GeyserLocale.getPlayerLocaleString("geyser.version.new", sender.getLocale(), newBedrockVersion)) + .append(Component.text(GeyserLocale.getPlayerLocaleString("geyser.version.new", sender.locale(), newBedrockVersion)) .replaceText(TextReplacementConfig.builder() .match("\\{1\\}") // Replace "Download here: {1}" so we can use fancy text component yesyes .replacement(Component.text() From dc29d997fd7e7ea81ca7bfe4534b6ea946ec24cd Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 24 Aug 2022 05:18:12 +0000 Subject: [PATCH 093/125] Exclude from relocation, not inclusion --- bootstrap/spigot/build.gradle.kts | 4 ++-- build-logic/src/main/kotlin/extensions.kt | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 2fe3d6e2b..1b954e1d4 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -27,7 +27,7 @@ dependencies { platformRelocate("it.unimi.dsi.fastutil") platformRelocate("com.fasterxml.jackson") -platformRelocate("net.kyori") +platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger") platformRelocate("org.objectweb.asm") platformRelocate("me.lucko.commodore") platformRelocate("io.netty.channel.kqueue") @@ -65,6 +65,6 @@ tasks.withType { exclude(dependency("com.mojang:.*")) // Adventure slf4j - exclude(dependency("net.kyori.adventure.text.logger.slf4j:ComponentLogger")) + //exclude(dependency("net.kyori.adventure.text.logger.slf4j:ComponentLogger")) } } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 1f9793ee4..43cdafdcc 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -43,9 +43,11 @@ fun Project.exclude(group: String) { } } -fun Project.platformRelocate(pattern: String) { +fun Project.platformRelocate(pattern: String, exclusion: String = "") { tasks.named("shadowJar") { - relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") + relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") { + exclude(exclusion) + } } } From f1642d81dc2e2c03ae9bb78c300c305aa6a9dbc3 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 24 Aug 2022 13:41:24 +0000 Subject: [PATCH 094/125] Fix comment --- bootstrap/spigot/build.gradle.kts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 1b954e1d4..5a459a09b 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { platformRelocate("it.unimi.dsi.fastutil") platformRelocate("com.fasterxml.jackson") +// Relocate net.kyori but exclude the component logger platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger") platformRelocate("org.objectweb.asm") platformRelocate("me.lucko.commodore") @@ -63,8 +64,5 @@ tasks.withType { // Commodore includes Brigadier exclude(dependency("com.mojang:.*")) - - // Adventure slf4j - //exclude(dependency("net.kyori.adventure.text.logger.slf4j:ComponentLogger")) } } \ No newline at end of file From 936fed1ded9e8c778e296433a2221c693a9462ba Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 24 Aug 2022 15:38:54 +0000 Subject: [PATCH 095/125] Move sendMessage(Component) to GeyserCommandSource --- api/geyser/build.gradle.kts | 2 -- .../org/geysermc/geyser/api/command/CommandSource.java | 7 ------- .../org/geysermc/geyser/command/GeyserCommandSource.java | 6 ++++++ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts index 60bd4a431..dcde85337 100644 --- a/api/geyser/build.gradle.kts +++ b/api/geyser/build.gradle.kts @@ -4,8 +4,6 @@ plugins { dependencies { api(projects.api) - - implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) } publishing { diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java index 4465e79b2..aabf0c4e8 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java @@ -25,9 +25,6 @@ package org.geysermc.geyser.api.command; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; - /** * Represents an instance capable of sending commands. */ @@ -58,10 +55,6 @@ public interface CommandSource { } } - default void sendMessage(Component message) { - sendMessage(LegacyComponentSerializer.legacySection().serialize(message)); - } - /** * If this source is the console. * diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java index eabccc243..88d148b11 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java @@ -27,6 +27,8 @@ package org.geysermc.geyser.command; import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.text.GeyserLocale; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; /** * Implemented on top of any class that can send a command. @@ -40,4 +42,8 @@ public interface GeyserCommandSource extends CommandSource { default String locale() { return GeyserLocale.getDefaultLocale(); } + + default void sendMessage(Component message) { + sendMessage(LegacyComponentSerializer.legacySection().serialize(message)); + } } From 1db77ad2bd22d941750908a02bde1441838e29f0 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Sun, 28 Aug 2022 17:42:31 -0700 Subject: [PATCH 096/125] Fix address, port, & motd being ignored in config (#3259) --- .../geyser/configuration/GeyserJacksonConfiguration.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 9f2eaa898..acb78a8a3 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -159,6 +159,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonIgnoreProperties(ignoreUnknown = true) public static class BedrockConfiguration implements IBedrockConfiguration { @AsteriskSerializer.Asterisk(isIp = true) + @JsonProperty("address") private String address = "0.0.0.0"; @Override @@ -167,6 +168,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration } @Setter + @JsonProperty("port") private int port = 19132; @Override @@ -178,6 +180,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("clone-remote-port") private boolean cloneRemotePort = false; + @JsonProperty("motd1") private String motd1 = "GeyserMC"; @Override @@ -185,6 +188,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration return motd1; } + @JsonProperty("motd2") private String motd2 = "Geyser"; @Override @@ -237,6 +241,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration public static class RemoteConfiguration implements IRemoteConfiguration { @Setter @AsteriskSerializer.Asterisk(isIp = true) + @JsonProperty("address") private String address = "auto"; @Override @@ -246,6 +251,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonDeserialize(using = PortDeserializer.class) @Setter + @JsonProperty("port") private int port = 25565; @Override From c07c7b933749630eb3512ea59ae0cbd659eb4f76 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 1 Sep 2022 00:50:03 +0200 Subject: [PATCH 097/125] Added support for latest events version --- .../geysermc/geyser/event/GeyserEventBus.java | 15 +++++++++++---- .../geyser/event/GeyserEventSubscriber.java | 18 ++++++++++++++---- .../event/GeyserExtensionEventBus.java | 15 +++++++++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index 7d0d6336d..f634931f4 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.event; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; +import org.geysermc.event.PostOrder; import org.geysermc.event.bus.impl.OwnedEventBusImpl; import org.geysermc.event.subscribe.OwnedSubscriber; import org.geysermc.event.subscribe.Subscribe; @@ -43,8 +44,11 @@ public final class GeyserEventBus extends OwnedEventBusImpl> B makeSubscription( - Extension owner, Class eventClass, Subscribe subscribe, - L listener, BiConsumer handler) { + @NonNull Extension owner, + @NonNull Class eventClass, + @NonNull Subscribe subscribe, + @NonNull L listener, + @NonNull BiConsumer handler) { return (B) new GeyserEventSubscriber<>( owner, eventClass, subscribe.postOrder(), subscribe.ignoreCancelled(), listener, handler ); @@ -52,8 +56,11 @@ public final class GeyserEventBus extends OwnedEventBusImpl> B makeSubscription( - Extension owner, Class eventClass, Consumer handler) { - return (B) new GeyserEventSubscriber<>(owner, eventClass, handler); + @NonNull Extension owner, + @NonNull Class eventClass, + @NonNull Consumer handler, + @NonNull PostOrder postOrder) { + return (B) new GeyserEventSubscriber<>(owner, eventClass, handler, postOrder); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java index 6fac82b96..5012037bb 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.event; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.impl.OwnedSubscriberImpl; @@ -36,12 +37,21 @@ import java.util.function.Consumer; public final class GeyserEventSubscriber extends OwnedSubscriberImpl implements ExtensionEventSubscriber { - GeyserEventSubscriber(Extension owner, Class eventClass, Consumer handler) { - super(owner, eventClass, handler); + GeyserEventSubscriber( + @NonNull Extension owner, + @NonNull Class eventClass, + @NonNull Consumer handler, + @NonNull PostOrder postOrder) { + super(owner, eventClass, handler, postOrder); } - GeyserEventSubscriber(Extension owner, Class eventClass, PostOrder postOrder, boolean ignoreCancelled, - H handlerInstance, BiConsumer handler) { + GeyserEventSubscriber( + @NonNull Extension owner, + @NonNull Class eventClass, + @NonNull PostOrder postOrder, + boolean ignoreCancelled, + @NonNull H handlerInstance, + @NonNull BiConsumer handler) { super(owner, eventClass, postOrder, ignoreCancelled, handlerInstance, handler); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java index d1a647be7..7294d4345 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java @@ -27,17 +27,14 @@ package org.geysermc.geyser.extension.event; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; -import org.geysermc.event.bus.impl.EventBusImpl; -import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscriber; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventSubscriber; import org.geysermc.geyser.api.event.ExtensionEventBus; -import org.geysermc.geyser.api.event.ExtensionEventSubscriber; import org.geysermc.geyser.api.extension.Extension; import java.util.Set; -import java.util.function.BiConsumer; import java.util.function.Consumer; public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) implements ExtensionEventBus { @@ -68,6 +65,16 @@ public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) im return eventBus.subscribe(extension, eventClass, consumer); } + @Override + @SuppressWarnings("unchecked") + public > @NonNull U subscribe( + @NonNull Class eventClass, + @NonNull Consumer consumer, + @NonNull PostOrder postOrder + ) { + return eventBus.subscribe(extension, eventClass, consumer, postOrder); + } + @Override public void unregisterAll() { eventBus.unregisterAll(extension); From e5337b629828f8531868f2abbe6eeada9653eba8 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 4 Sep 2022 13:08:17 -0500 Subject: [PATCH 098/125] Reintroduce GeyserDefineCommandsEvent and cleanup a few things --- api/base/build.gradle.kts | 7 +- .../main/java/org/geysermc/api/Geyser.java | 4 +- .../org/geysermc/geyser/api/GeyserApi.java | 13 +- .../geysermc/geyser/api/command/Command.java | 39 ++++-- .../lifecycle/GeyserDefineCommandsEvent.java} | 29 ++--- .../event/lifecycle/GeyserShutdownEvent.java | 3 +- .../api/extension/ExtensionDescription.java | 20 ++- .../bungeecord/GeyserBungeePlugin.java | 14 +- .../platform/spigot/GeyserSpigotPlugin.java | 50 ++++++- .../command/GeyserSpigotCommandManager.java | 4 + .../spigot/src/main/resources/plugin.yml | 5 +- .../platform/sponge/GeyserSpongePlugin.java | 13 +- .../velocity/GeyserVelocityPlugin.java | 13 +- .../java/org/geysermc/geyser/GeyserImpl.java | 8 +- .../geyser/command/GeyserCommandManager.java | 122 ++++++++++++------ .../geyser/command/defaults/HelpCommand.java | 6 +- .../type/GeyserDefineCommandsEventImpl.java | 46 +++++++ ... => GeyserDefineCustomItemsEventImpl.java} | 4 +- .../extension/GeyserExtensionDescription.java | 9 +- .../command/GeyserExtensionCommand.java | 43 ++++++ .../loader/ProviderRegistryLoader.java | 5 +- .../populator/ItemRegistryPopulator.java | 5 +- 22 files changed, 355 insertions(+), 107 deletions(-) rename api/geyser/src/main/java/org/geysermc/geyser/api/{command/CommandManager.java => event/lifecycle/GeyserDefineCommandsEvent.java} (68%) create mode 100644 core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java rename core/src/main/java/org/geysermc/geyser/event/type/{DefineCustomItemsEvent.java => GeyserDefineCustomItemsEventImpl.java} (93%) create mode 100644 core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts index 03f08c6d2..a6fa608cc 100644 --- a/api/base/build.gradle.kts +++ b/api/base/build.gradle.kts @@ -1,4 +1,7 @@ dependencies { api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) - api("org.geysermc.event", "events", Versions.eventsVersion) -} + api("org.geysermc.event", "events", Versions.eventsVersion) { + exclude(group = "com.google.guava", module = "guava") + exclude(group = "org.lanternpowered", module = "lmbda") + } +} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/Geyser.java b/api/base/src/main/java/org/geysermc/api/Geyser.java index 9f315faf4..d0bdf3b5e 100644 --- a/api/base/src/main/java/org/geysermc/api/Geyser.java +++ b/api/base/src/main/java/org/geysermc/api/Geyser.java @@ -69,7 +69,7 @@ public class Geyser { /** * Registers the given api type. The api cannot be - * registered if {@link #registered()} is true as + * registered if {@link #isRegistered()} is true as * an api has already been specified. * * @param api the api @@ -88,7 +88,7 @@ public class Geyser { * * @return if the api has been registered */ - public static boolean registered() { + public static boolean isRegistered() { return api != null; } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 82f0566fe..e73727104 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -29,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; -import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; @@ -66,15 +65,9 @@ public interface GeyserApi extends GeyserApiBase { * * @return the extension manager */ + @NonNull ExtensionManager extensionManager(); - /** - * Gets the {@link CommandManager}. - * - * @return the command manager - */ - CommandManager commandManager(); - /** * Provides an implementation for the specified API type. * @@ -92,6 +85,7 @@ public interface GeyserApi extends GeyserApiBase { * * @return the event bus */ + @NonNull EventBus eventBus(); /** @@ -100,6 +94,7 @@ public interface GeyserApi extends GeyserApiBase { * * @return the default remote server used within Geyser */ + @NonNull RemoteServer defaultRemoteServer(); /** @@ -108,6 +103,7 @@ public interface GeyserApi extends GeyserApiBase { * * @return the listener used for Bedrock client connectins */ + @NonNull BedrockListener bedrockListener(); /** @@ -115,6 +111,7 @@ public interface GeyserApi extends GeyserApiBase { * * @return the current geyser api instance */ + @NonNull static GeyserApi api() { return Geyser.api(GeyserApi.class); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java index 0ad296669..2f1f2b24d 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -27,6 +27,8 @@ package org.geysermc.geyser.api.command; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.extension.Extension; import java.util.Collections; import java.util.List; @@ -104,19 +106,39 @@ public interface Command { return false; } - static Command.Builder builder(Class sourceType) { - return GeyserApi.api().provider(Builder.class, sourceType); + /** + * Creates a new {@link Command.Builder} used to construct commands. + * + * @param extension the extension + * @param the source type + * @return a new command builder used to construct commands + */ + static Command.Builder builder(@NonNull Extension extension) { + return GeyserApi.api().provider(Builder.class, extension); } interface Builder { + /** + * Defines the source type to use for this command. + *

+ * Command source types can be anything that extend + * {@link CommandSource}, such as {@link GeyserConnection}. + * This will guarantee that the source used in the executor + * is an instance of this source. + * + * @param sourceType the source type + * @return the builder + */ + Builder source(@NonNull Class sourceType); + /** * Sets the command name. * * @param name the command name * @return the builder */ - Builder name(String name); + Builder name(@NonNull String name); /** * Sets the command description. @@ -124,7 +146,7 @@ public interface Command { * @param description the command description * @return the builder */ - Builder description(String description); + Builder description(@NonNull String description); /** * Sets the permission node. @@ -132,7 +154,7 @@ public interface Command { * @param permission the permission node * @return the builder */ - Builder permission(String permission); + Builder permission(@NonNull String permission); /** * Sets the aliases. @@ -140,7 +162,7 @@ public interface Command { * @param aliases the aliases * @return the builder */ - Builder aliases(List aliases); + Builder aliases(@NonNull List aliases); /** * Sets if this command is designed to be used only by server operators. @@ -164,7 +186,7 @@ public interface Command { * @param subCommands the subcommands * @return the builder */ - Builder subCommands(List subCommands); + Builder subCommands(@NonNull List subCommands); /** * Sets if this command is bedrock only. @@ -180,13 +202,14 @@ public interface Command { * @param executor the command executor * @return the builder */ - Builder executor(CommandExecutor executor); + Builder executor(@NonNull CommandExecutor executor); /** * Builds the command. * * @return the command */ + @NonNull Command build(); } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java similarity index 68% rename from api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java index 9f29651ba..77d5efa65 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandManager.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -23,36 +23,35 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.api.command; +package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.command.Command; import java.util.Map; /** - * Manages Bedrock commands within Geyser. + * Called when commands are defined within Geyser. + * + * This event allows you to register new commands using the {@link #register(Command)} + * method and retrieve the default commands defined. */ -public abstract class CommandManager { +public interface GeyserDefineCommandsEvent extends Event { /** - * Registers the given {@link Command}. + * Registers the given {@link Command} into the Geyser + * command manager. * * @param command the command to register */ - public abstract void register(@NonNull Command command); + void register(@NonNull Command command); /** - * Unregisters the given {@link Command}. + * Gets all the registered built-in {@link Command}s. * - * @param command the command to unregister - */ - public abstract void unregister(@NonNull Command command); - - /** - * Gets all the registered {@link Command}s. - * - * @return all the registered commands + * @return all the registered built-in commands */ @NonNull - public abstract Map commands(); + Map commands(); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java index f2fab901d..a1c68d876 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -27,12 +27,11 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; -import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.extension.ExtensionManager; /** * Called when Geyser is shutting down. */ -public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull CommandManager commandManager, @NonNull EventBus eventBus) implements Event { +public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index e77411144..8136bd761 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -30,12 +30,20 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.util.List; /** - * This is the Geyser extension description + * Represents the description of an {@link Extension}. */ public interface ExtensionDescription { /** - * Gets the extension's name + * Gets the extension's id. + * + * @return the extension's id + */ + @NonNull + String id(); + + /** + * Gets the extension's name. * * @return the extension's name */ @@ -43,7 +51,7 @@ public interface ExtensionDescription { String name(); /** - * Gets the extension's main class + * Gets the extension's main class. * * @return the extension's main class */ @@ -51,7 +59,7 @@ public interface ExtensionDescription { String main(); /** - * Gets the extension's api version + * Gets the extension's api version. * * @return the extension's api version */ @@ -59,7 +67,7 @@ public interface ExtensionDescription { String apiVersion(); /** - * Gets the extension's description + * Gets the extension's description. * * @return the extension's description */ @@ -67,7 +75,7 @@ public interface ExtensionDescription { String version(); /** - * Gets the extension's authors + * Gets the extension's authors. * * @return the extension's authors */ diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index c72503450..ed2c340db 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -32,6 +32,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -49,6 +51,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; import java.util.UUID; import java.util.logging.Level; @@ -149,8 +152,15 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); } - this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", geyser, geyserCommandManager.getCommands())); - this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyserext", geyser, geyserCommandManager.commands())); + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands())); + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands)); + } } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 4a371cfe8..e3b794578 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -32,9 +32,11 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import io.netty.buffer.ByteBuf; import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; import org.bukkit.command.PluginCommand; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; import org.geysermc.geyser.Constants; @@ -42,6 +44,7 @@ import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -62,6 +65,8 @@ import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.SocketAddress; import java.nio.file.Path; import java.util.List; @@ -269,13 +274,32 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { PluginCommand geyserCommand = this.getCommand("geyser"); geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); - PluginCommand geyserExtCommand = this.getCommand("geyserext"); - geyserExtCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); + + CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap(); + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + // Thanks again, Bukkit + try { + Constructor constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); + constructor.setAccessible(true); + + PluginCommand pluginCommand = constructor.newInstance(entry.getKey().description().id(), this); + pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands)); + pluginCommand.setDescription("The main command for the " + entry.getKey().name() + " Geyser extension!"); + commandMap.register(entry.getKey().description().id(), "geyserext", pluginCommand); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { + this.geyserLogger.error("Failed to construct PluginCommand for extension " + entry.getKey().description().name(), ex); + } + } if (!INITIALIZED) { // Register permissions so they appear in, for example, LuckPerms' UI // Re-registering permissions throws an error - for (Map.Entry entry : geyserCommandManager.getCommands().entrySet()) { + for (Map.Entry entry : geyserCommandManager.commands().entrySet()) { Command command = entry.getValue(); if (command.aliases().contains(entry.getKey())) { // Don't register aliases @@ -286,6 +310,26 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { GeyserLocale.getLocaleStringLog(command.description()), command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE)); } + + // Register permissions for extension commands + for (Map.Entry> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) { + for (Map.Entry entry : commandEntry.getValue().entrySet()) { + Command command = entry.getValue(); + if (command.aliases().contains(entry.getKey())) { + // Don't register aliases + continue; + } + + if (command.permission().isBlank()) { + continue; + } + + Bukkit.getPluginManager().addPermission(new Permission(command.permission(), + GeyserLocale.getLocaleStringLog(command.description()), + command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE)); + } + } + Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION, "Whether update notifications can be seen", PermissionDefault.OP)); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java index 9fb19f0da..655d3be23 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java @@ -65,4 +65,8 @@ public class GeyserSpigotCommandManager extends GeyserCommandManager { Command cmd = COMMAND_MAP.getCommand(command.replace("/", "")); return cmd != null ? cmd.getDescription() : ""; } + + public static CommandMap getCommandMap() { + return COMMAND_MAP; + } } diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index 0f6398b49..e28b8981d 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -8,7 +8,4 @@ api-version: 1.13 commands: geyser: description: The main command for Geyser. - usage: /geyser - geyserext: - description: The command any extensions can register to. - usage: /geyserext \ No newline at end of file + usage: /geyser \ No newline at end of file diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java index 312dfb087..d912d28d8 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java @@ -29,6 +29,8 @@ import com.google.inject.Inject; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -50,6 +52,7 @@ import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Path; +import java.util.Map; import java.util.UUID; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Sponge", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @@ -122,7 +125,15 @@ public class GeyserSpongePlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser); this.geyserCommandManager.init(); Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser"); - Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.commands()), "geyserext"); + + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(this.geyser, commands), entry.getKey().description().id()); + } } @Override diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index e2f84a2a7..30d8deccd 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -39,6 +39,8 @@ import net.kyori.adventure.util.Codec; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -58,6 +60,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; import java.util.UUID; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @@ -159,7 +162,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserCommandManager.init(); this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands())); - this.commandManager.register("geyserext", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.commands())); + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands)); + } + if (geyserConfig.isLegacyPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index ed65ffbe9..24a5f5d2c 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -547,7 +547,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.PACKS.clear(); - this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.commandManager(), this.eventBus)); + this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus)); this.extensionManager.disableExtensions(); bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); @@ -572,11 +572,12 @@ public class GeyserImpl implements GeyserApi { } @Override + @NonNull public GeyserExtensionManager extensionManager() { return this.extensionManager; } - @Override + @NonNull public GeyserCommandManager commandManager() { return this.bootstrap.getGeyserCommandManager(); } @@ -587,15 +588,18 @@ public class GeyserImpl implements GeyserApi { } @Override + @NonNull public EventBus eventBus() { return this.eventBus; } + @NonNull public RemoteServer defaultRemoteServer() { return getConfig().getRemote(); } @Override + @NonNull public BedrockListener bedrockListener() { return getConfig().getBedrock(); } diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java index cb3cf6eee..7c5bd6580 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -33,30 +33,46 @@ import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandExecutor; -import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.command.defaults.*; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.defaults.AdvancedTooltipsCommand; +import org.geysermc.geyser.command.defaults.AdvancementsCommand; +import org.geysermc.geyser.command.defaults.ConnectionTestCommand; +import org.geysermc.geyser.command.defaults.DumpCommand; +import org.geysermc.geyser.command.defaults.ExtensionsCommand; +import org.geysermc.geyser.command.defaults.HelpCommand; +import org.geysermc.geyser.command.defaults.ListCommand; +import org.geysermc.geyser.command.defaults.OffhandCommand; +import org.geysermc.geyser.command.defaults.ReloadCommand; +import org.geysermc.geyser.command.defaults.SettingsCommand; +import org.geysermc.geyser.command.defaults.StatisticsCommand; +import org.geysermc.geyser.command.defaults.StopCommand; +import org.geysermc.geyser.command.defaults.VersionCommand; +import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl; +import org.geysermc.geyser.extension.command.GeyserExtensionCommand; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @RequiredArgsConstructor -public abstract class GeyserCommandManager extends CommandManager { +public abstract class GeyserCommandManager { @Getter private final Map commands = new Object2ObjectOpenHashMap<>(12); - private final Map extensionCommands = new Object2ObjectOpenHashMap<>(0); + private final Map> extensionCommands = new Object2ObjectOpenHashMap<>(0); private final GeyserImpl geyser; public void init() { - registerBuiltInCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help", "geyser", commands)); + registerBuiltInCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help", "geyser", this.commands)); registerBuiltInCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); registerBuiltInCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); registerBuiltInCommand(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); @@ -67,11 +83,32 @@ public abstract class GeyserCommandManager extends CommandManager { registerBuiltInCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); registerBuiltInCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); registerBuiltInCommand(new ConnectionTestCommand(geyser, "connectiontest", "geyser.commands.connectiontest.desc", "geyser.command.connectiontest")); - if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { + if (this.geyser.getPlatformType() == PlatformType.STANDALONE) { registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } - register(new HelpCommand(geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp", "geyserext", extensionCommands)); + if (this.geyser.extensionManager().extensions().size() > 0) { + registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions")); + } + + GeyserDefineCommandsEvent defineCommandsEvent = new GeyserDefineCommandsEventImpl(this.commands) { + + @Override + public void register(@NonNull Command command) { + if (!(command instanceof GeyserExtensionCommand extensionCommand)) { + throw new IllegalArgumentException("Expected GeyserExtensionCommand as part of command registration but got " + command + "! Did you use the Command builder properly?"); + } + + registerExtensionCommand(extensionCommand.extension(), extensionCommand); + } + }; + + this.geyser.eventBus().fire(defineCommandsEvent); + + // Register help commands for all extensions with commands + for (Map.Entry> entry : this.extensionCommands.entrySet()) { + registerExtensionCommand(entry.getKey(), new HelpCommand(this.geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp", entry.getKey().description().id(), entry.getValue())); + } } /** @@ -81,9 +118,8 @@ public abstract class GeyserCommandManager extends CommandManager { register(command, this.commands); } - @Override - public void register(@NonNull Command command) { - register(command, this.extensionCommands); + public void registerExtensionCommand(@NonNull Extension extension, @NonNull Command command) { + register(command, this.extensionCommands.computeIfAbsent(extension, e -> new HashMap<>())); } private void register(Command command, Map commands) { @@ -99,32 +135,30 @@ public abstract class GeyserCommandManager extends CommandManager { } } - @Override - public void unregister(@NonNull Command command) { - this.extensionCommands.remove(command.name(), command); - - if (command.aliases().isEmpty()) { - return; - } - - for (String alias : command.aliases()) { - this.extensionCommands.remove(alias, command); - } + @NotNull + public Map commands() { + return Collections.unmodifiableMap(this.commands); } @NotNull - @Override - public Map commands() { + public Map> extensionCommands() { return Collections.unmodifiableMap(this.extensionCommands); } public boolean runCommand(GeyserCommandSource sender, String command) { - boolean extensionCommand = command.startsWith("geyserext "); - if (!command.startsWith("geyser ") && !extensionCommand) { + Extension extension = null; + for (Extension loopedExtension : this.extensionCommands.keySet()) { + if (command.startsWith(loopedExtension.description().id() + " ")) { + extension = loopedExtension; + break; + } + } + + if (!command.startsWith("geyser ") && extension == null) { return false; } - command = command.trim().replace(extensionCommand ? "geyserext " : "geyser ", ""); + command = command.trim().replace(extension != null ? extension.description().id() + " " : "geyser ", ""); String label; String[] args; @@ -137,9 +171,9 @@ public abstract class GeyserCommandManager extends CommandManager { args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; } - Command cmd = (extensionCommand ? this.extensionCommands : this.commands).get(label); + Command cmd = (extension != null ? this.extensionCommands.getOrDefault(extension, Collections.emptyMap()) : this.commands).get(label); if (cmd == null) { - geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); + sender.sendMessage(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); return false; } @@ -168,7 +202,8 @@ public abstract class GeyserCommandManager extends CommandManager { @RequiredArgsConstructor public static class CommandBuilder implements Command.Builder { - private final Class sourceType; + private final Extension extension; + private Class sourceType; private String name; private String description = ""; private String permission = ""; @@ -179,22 +214,28 @@ public abstract class GeyserCommandManager extends CommandManager { private boolean bedrockOnly; private CommandExecutor executor; - public CommandBuilder name(String name) { + @Override + public Command.Builder source(@NonNull Class sourceType) { + this.sourceType = sourceType; + return this; + } + + public CommandBuilder name(@NonNull String name) { this.name = name; return this; } - public CommandBuilder description(String description) { + public CommandBuilder description(@NonNull String description) { this.description = description; return this; } - public CommandBuilder permission(String permission) { + public CommandBuilder permission(@NonNull String permission) { this.permission = permission; return this; } - public CommandBuilder aliases(List aliases) { + public CommandBuilder aliases(@NonNull List aliases) { this.aliases = aliases; return this; } @@ -210,7 +251,7 @@ public abstract class GeyserCommandManager extends CommandManager { return this; } - public CommandBuilder subCommands(List subCommands) { + public CommandBuilder subCommands(@NonNull List subCommands) { this.subCommands = subCommands; return this; } @@ -220,22 +261,27 @@ public abstract class GeyserCommandManager extends CommandManager { return this; } - public CommandBuilder executor(CommandExecutor executor) { + public CommandBuilder executor(@NonNull CommandExecutor executor) { this.executor = executor; return this; } - public GeyserCommand build() { + @NonNull + public GeyserExtensionCommand build() { if (this.name == null || this.name.isBlank()) { throw new IllegalArgumentException("Command cannot be null or blank!"); } - return new GeyserCommand(this.name, this.description, this.permission) { + if (this.sourceType == null) { + throw new IllegalArgumentException("Source type was not defined for command " + this.name + " in extension " + this.extension.name()); + } + + return new GeyserExtensionCommand(this.extension, this.name, this.description, this.permission) { @SuppressWarnings("unchecked") @Override public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { - Class sourceType = CommandBuilder.this.sourceType; + Class sourceType = CommandBuilder.this.sourceType; CommandExecutor executor = CommandBuilder.this.executor; if (sourceType.isInstance(session)) { executor.execute((T) session, this, args); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java index 81f34b759..6e7ad2f04 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java @@ -66,19 +66,19 @@ public class HelpCommand extends GeyserCommand { String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage); sender.sendMessage(header); - for (Map.Entry entry : commands.entrySet()) { + this.commands.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> { Command cmd = entry.getValue(); // Standalone hack-in since it doesn't have a concept of permissions if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.permission())) { // Only list commands the player can actually run if (cmd.isBedrockOnly() && session == null) { - continue; + return; } sender.sendMessage(ChatColor.YELLOW + "/" + baseCommand + " " + entry.getKey() + ChatColor.WHITE + ": " + GeyserLocale.getPlayerLocaleString(cmd.description(), sender.locale())); } - } + }); } } diff --git a/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java new file mode 100644 index 000000000..e07a62d8a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event.type; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; + +import java.util.Collections; +import java.util.Map; + +public abstract class GeyserDefineCommandsEventImpl implements GeyserDefineCommandsEvent { + private final Map commands; + + public GeyserDefineCommandsEventImpl(Map commands) { + this.commands = commands; + } + + @Override + public @NonNull Map commands() { + return Collections.unmodifiableMap(this.commands); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java similarity index 93% rename from core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java rename to core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java index 9011bfb3a..65fd7ea0d 100644 --- a/core/src/main/java/org/geysermc/geyser/event/type/DefineCustomItemsEvent.java +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java @@ -36,11 +36,11 @@ import java.util.Collections; import java.util.List; import java.util.Map; -public abstract class DefineCustomItemsEvent implements GeyserDefineCustomItemsEvent { +public abstract class GeyserDefineCustomItemsEventImpl implements GeyserDefineCustomItemsEvent { private final Multimap customItems; private final List nonVanillaCustomItems; - public DefineCustomItemsEvent(Multimap customItems, List nonVanillaCustomItems) { + public GeyserDefineCustomItemsEventImpl(Multimap customItems, List nonVanillaCustomItems) { this.customItems = customItems; this.nonVanillaCustomItems = nonVanillaCustomItems; } diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java index eaf29a819..04f80e19d 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -33,7 +33,7 @@ import org.yaml.snakeyaml.Yaml; import java.io.Reader; import java.util.*; -public record GeyserExtensionDescription(String name, String main, String apiVersion, String version, List authors) implements ExtensionDescription { +public record GeyserExtensionDescription(String id, String name, String main, String apiVersion, String version, List authors) implements ExtensionDescription { @SuppressWarnings("unchecked") public static GeyserExtensionDescription fromYaml(Reader reader) throws InvalidDescriptionException { DumperOptions dumperOptions = new DumperOptions(); @@ -42,6 +42,11 @@ public record GeyserExtensionDescription(String name, String main, String apiVer Yaml yaml = new Yaml(dumperOptions); Map yamlMap = yaml.loadAs(reader, LinkedHashMap.class); + String id = ((String) yamlMap.get("id")).replaceAll("[^A-Za-z0-9 _.-]", ""); + if (id.isBlank()) { + throw new InvalidDescriptionException("Invalid extension id, cannot be empty"); + } + String name = ((String) yamlMap.get("name")).replaceAll("[^A-Za-z0-9 _.-]", ""); if (name.isBlank()) { throw new InvalidDescriptionException("Invalid extension name, cannot be empty"); @@ -72,6 +77,6 @@ public record GeyserExtensionDescription(String name, String main, String apiVer } } - return new GeyserExtensionDescription(name, main, apiVersion, version, authors); + return new GeyserExtensionDescription(id, name, main, apiVersion, version, Collections.unmodifiableList(authors)); } } diff --git a/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java new file mode 100644 index 000000000..4a7830c90 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.extension.command; + +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.GeyserCommand; + +public abstract class GeyserExtensionCommand extends GeyserCommand { + private final Extension extension; + + public GeyserExtensionCommand(Extension extension, String name, String description, String permission) { + super(name, description, permission); + + this.extension = extension; + } + + public Extension extension() { + return this.extension; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java index 449f6574e..e7837688c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.registry.loader; import org.geysermc.geyser.api.command.Command; -import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; @@ -43,10 +43,9 @@ import java.util.Map; */ public class ProviderRegistryLoader implements RegistryLoader, ProviderSupplier>, Map, ProviderSupplier>> { - @SuppressWarnings("unchecked") @Override public Map, ProviderSupplier> load(Map, ProviderSupplier> providers) { - providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class) args[0])); + providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0])); providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index e6d1cb3e4..60a16245c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -48,11 +48,10 @@ import it.unimi.dsi.fastutil.objects.*; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; -import org.geysermc.geyser.event.type.DefineCustomItemsEvent; +import org.geysermc.geyser.event.type.GeyserDefineCustomItemsEventImpl; import org.geysermc.geyser.inventory.item.StoredItemMappings; import org.geysermc.geyser.item.GeyserCustomMappingData; import org.geysermc.geyser.item.mappings.MappingsConfigReader; @@ -109,7 +108,7 @@ public class ItemRegistryPopulator { }); nonVanillaCustomItems = new ObjectArrayList<>(); - GeyserImpl.getInstance().eventBus().fire(new DefineCustomItemsEvent(customItems, nonVanillaCustomItems) { + GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEventImpl(customItems, nonVanillaCustomItems) { @Override public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) { if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) { From f1da9d70728ad50cbff5c345052f8b18fabb06f2 Mon Sep 17 00:00:00 2001 From: ImDaBigBoss <67973871+imdabigboss@users.noreply.github.com> Date: Sun, 4 Sep 2022 16:11:08 -0500 Subject: [PATCH 099/125] Allow events to be registered by any class Supersedes & closes #3073 Co-authored-by: Redned --- .../main/java/org/geysermc/api/Geyser.java | 1 + .../geysermc/api/util/BedrockPlatform.java | 3 + .../java/org/geysermc/api/util/InputMode.java | 3 + .../java/org/geysermc/api/util/UiProfile.java | 3 + .../org/geysermc/geyser/api/GeyserApi.java | 3 +- .../geysermc/geyser/api/event/EventBus.java | 4 +- .../geyser/api/event/EventRegistrar.java | 45 ++++++++ .../geyser/api/event/EventSubscriber.java | 2 +- .../geyser/api/event/ExtensionEventBus.java | 5 +- .../GeyserDefineCustomItemsEvent.java | 2 + .../lifecycle/GeyserPostInitializeEvent.java | 3 +- .../lifecycle/GeyserPreInitializeEvent.java | 3 +- .../event/lifecycle/GeyserShutdownEvent.java | 3 +- .../geyser/api/extension/Extension.java | 15 ++- .../bungeecord/GeyserBungeePlugin.java | 88 ++++++++++---- .../platform/spigot/GeyserSpigotPlugin.java | 108 ++++++++++++------ .../platform/sponge/GeyserSpongePlugin.java | 17 ++- .../standalone/GeyserStandaloneBootstrap.java | 4 +- .../velocity/GeyserVelocityPlugin.java | 36 +++--- .../java/org/geysermc/geyser/GeyserImpl.java | 46 +++++--- .../geysermc/geyser/event/GeyserEventBus.java | 15 +-- .../geyser/event/GeyserEventRegistrar.java | 38 ++++++ .../geyser/event/GeyserEventSubscriber.java | 7 +- .../event/GeyserExtensionEventBus.java | 11 +- .../loader/ProviderRegistryLoader.java | 3 + 25 files changed, 353 insertions(+), 115 deletions(-) create mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java diff --git a/api/base/src/main/java/org/geysermc/api/Geyser.java b/api/base/src/main/java/org/geysermc/api/Geyser.java index d0bdf3b5e..7543d1661 100644 --- a/api/base/src/main/java/org/geysermc/api/Geyser.java +++ b/api/base/src/main/java/org/geysermc/api/Geyser.java @@ -39,6 +39,7 @@ public class Geyser { * * @return the base api */ + @NonNull public static GeyserApiBase api() { if (api == null) { throw new RuntimeException("Api has not been registered yet!"); diff --git a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java index d66077a87..e486f73bc 100644 --- a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java +++ b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java @@ -25,6 +25,8 @@ package org.geysermc.api.util; +import org.checkerframework.checker.nullness.qual.NonNull; + public enum BedrockPlatform { UNKNOWN("Unknown"), GOOGLE("Android"), @@ -56,6 +58,7 @@ public enum BedrockPlatform { * @param id the BedrockPlatform identifier * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found */ + @NonNull public static BedrockPlatform fromId(int id) { return id < VALUES.length ? VALUES[id] : VALUES[0]; } diff --git a/api/base/src/main/java/org/geysermc/api/util/InputMode.java b/api/base/src/main/java/org/geysermc/api/util/InputMode.java index eadb457ab..70346ffa5 100644 --- a/api/base/src/main/java/org/geysermc/api/util/InputMode.java +++ b/api/base/src/main/java/org/geysermc/api/util/InputMode.java @@ -25,6 +25,8 @@ package org.geysermc.api.util; +import org.checkerframework.checker.nullness.qual.NonNull; + public enum InputMode { UNKNOWN, KEYBOARD_MOUSE, @@ -40,6 +42,7 @@ public enum InputMode { * @param id the InputMode identifier * @return The InputMode or {@link #UNKNOWN} if the mode wasn't found */ + @NonNull public static InputMode fromId(int id) { return VALUES.length > id ? VALUES[id] : VALUES[0]; } diff --git a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java index c28ff869c..cddb97260 100644 --- a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java +++ b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java @@ -25,6 +25,8 @@ package org.geysermc.api.util; +import org.checkerframework.checker.nullness.qual.NonNull; + public enum UiProfile { CLASSIC, POCKET; @@ -36,6 +38,7 @@ public enum UiProfile { * @param id the UiProfile identifier * @return The UiProfile or {@link #CLASSIC} if the profile wasn't found */ + @NonNull public static UiProfile fromId(int id) { return VALUES.length > id ? VALUES[id] : VALUES[0]; } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index e73727104..f86206d36 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -31,6 +31,7 @@ import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; @@ -86,7 +87,7 @@ public interface GeyserApi extends GeyserApiBase { * @return the event bus */ @NonNull - EventBus eventBus(); + EventBus eventBus(); /** * Gets the default {@link RemoteServer} configured diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java index c42698d47..801bfa45f 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -36,8 +36,8 @@ import java.util.Set; * Represents a bus capable of subscribing * or "listening" to events and firing them. */ -public interface EventBus extends OwnedEventBus> { +public interface EventBus extends OwnedEventBus> { @Override @NonNull - Set> subscribers(@NonNull Class eventClass); + Set> subscribers(@NonNull Class eventClass); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java new file mode 100644 index 000000000..7a2cc0071 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.geysermc.geyser.api.GeyserApi; + +/** + * Represents an owner for an event that allows it + * to be registered through an {@link EventBus}. + */ +public interface EventRegistrar { + + /** + * Creates an {@link EventRegistrar} instance. + * + * @param object the object to wrap around + * @return an event registrar instance + */ + static EventRegistrar of(Object object) { + return GeyserApi.api().provider(EventRegistrar.class, object); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java index 7ce5b7883..7f91d09a3 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java @@ -36,5 +36,5 @@ import org.geysermc.geyser.api.extension.Extension; * * @param the class of the event */ -public interface EventSubscriber extends OwnedSubscriber { +public interface EventSubscriber extends OwnedSubscriber { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java index 172c0f9de..a58d35891 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.api.event; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; +import org.geysermc.geyser.api.extension.Extension; import java.util.Set; @@ -34,7 +35,7 @@ import java.util.Set; * An {@link EventBus} with additional methods that implicitly * set the extension instance. */ -public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { +public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { @Override - @NonNull Set> subscribers(@NonNull Class eventClass); + @NonNull Set> subscribers(@NonNull Class eventClass); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java index bfed5d534..0957b8551 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java @@ -45,6 +45,7 @@ public interface GeyserDefineCustomItemsEvent extends Event { * * @return a multimap of all the already registered custom items */ + @NonNull Map> getExistingCustomItems(); /** @@ -52,6 +53,7 @@ public interface GeyserDefineCustomItemsEvent extends Event { * * @return the list of the already registered non-vanilla custom items */ + @NonNull List getExistingNonVanillaCustomItems(); /** diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java index 9e88c017b..8d145f615 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.extension.ExtensionManager; /** @@ -36,5 +37,5 @@ import org.geysermc.geyser.api.extension.ExtensionManager; * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java index 2be0272dc..8be89dafd 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.extension.ExtensionManager; /** @@ -36,5 +37,5 @@ import org.geysermc.geyser.api.extension.ExtensionManager; * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java index a1c68d876..7793ef997 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -28,10 +28,11 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.extension.ExtensionManager; /** * Called when Geyser is shutting down. */ -public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java index 2982a76fb..8d4e4da64 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -25,16 +25,19 @@ package org.geysermc.geyser.api.extension; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.ExtensionEventBus; import java.nio.file.Path; +import java.util.Objects; /** * Represents an extension within Geyser. */ -public interface Extension { +public interface Extension extends EventRegistrar { /** * Gets if the extension is enabled @@ -59,6 +62,7 @@ public interface Extension { * * @return the extension's data folder */ + @NonNull default Path dataFolder() { return this.extensionLoader().dataFolder(this); } @@ -68,6 +72,7 @@ public interface Extension { * * @return the extension event bus */ + @NonNull default ExtensionEventBus eventBus() { return this.extensionLoader().eventBus(this); } @@ -77,6 +82,7 @@ public interface Extension { * * @return the extension manager */ + @NonNull default ExtensionManager extensionManager() { return this.geyserApi().extensionManager(); } @@ -86,6 +92,7 @@ public interface Extension { * * @return the extension's name */ + @NonNull default String name() { return this.description().name(); } @@ -95,6 +102,7 @@ public interface Extension { * * @return the extension's description */ + @NonNull default ExtensionDescription description() { return this.extensionLoader().description(this); } @@ -104,6 +112,7 @@ public interface Extension { * * @return the extension's logger */ + @NonNull default ExtensionLogger logger() { return this.extensionLoader().logger(this); } @@ -113,8 +122,9 @@ public interface Extension { * * @return the extension loader */ + @NonNull default ExtensionLoader extensionLoader() { - return this.extensionManager().extensionLoader(this); + return Objects.requireNonNull(this.extensionManager().extensionLoader(this)); } /** @@ -122,6 +132,7 @@ public interface Extension { * * @return the geyser api instance */ + @NonNull default GeyserApi geyserApi() { return GeyserApi.api(); } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index ed2c340db..eb2a3b9c4 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.platform.bungeecord; +import io.netty.channel.Channel; +import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.protocol.ProtocolConstants; @@ -47,12 +49,15 @@ import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { @@ -66,23 +71,9 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { private GeyserImpl geyser; @Override - public void onEnable() { + public void onLoad() { GeyserLocale.init(this); - // Copied from ViaVersion. - // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 - try { - ProtocolConstants.class.getField("MINECRAFT_1_19_1"); - } catch (NoSuchFieldException e) { - getLogger().warning(" / \\"); - getLogger().warning(" / \\"); - getLogger().warning(" / | \\"); - getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName())); - getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); - getLogger().warning(" / o \\"); - getLogger().warning("/_____________\\"); - } - if (!getDataFolder().exists()) getDataFolder().mkdir(); @@ -121,6 +112,25 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); + } + + @Override + public void onEnable() { + // Copied from ViaVersion. + // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 + try { + ProtocolConstants.class.getField("MINECRAFT_1_19_1"); + } catch (NoSuchFieldException e) { + getLogger().warning(" / \\"); + getLogger().warning(" / \\"); + getLogger().warning(" / | \\"); + getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName())); + getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); + getLogger().warning(" / o \\"); + getLogger().warning("/_____________\\"); + } + // Remove this in like a year if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); @@ -138,7 +148,41 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { geyserConfig.loadFloodgate(this); - this.geyser = GeyserImpl.start(PlatformType.BUNGEECORD, this); + // Big hack - Bungee does not provide us an event to listen to, so schedule a repeating + // task that waits for a field to be filled which is set after the plugin enable + // process is complete + this.awaitStartupCompletion(0); + } + + @SuppressWarnings("unchecked") + private void awaitStartupCompletion(int tries) { + // After 20 tries give up waiting. This will happen + // just after 3 minutes approximately + if (tries >= 20) { + this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " + + "If all your plugins are loaded properly, this is a bug! " + + "If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times."); + this.postStartup(); + return; + } + + try { + Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners"); + listenersField.setAccessible(true); + + Collection listeners = (Collection) listenersField.get(BungeeCord.getInstance()); + if (!listeners.isEmpty()) { + this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS); + } else { + this.awaitStartupCompletion(++tries); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + } + } + + private void postStartup() { + GeyserImpl.start(); this.geyserInjector = new GeyserBungeeInjector(this); this.geyserInjector.initializeLocalChannel(this); @@ -146,12 +190,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserBungeeCommandManager(geyser); this.geyserCommandManager.init(); - if (geyserConfig.isLegacyPingPassthrough()) { - this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); - } else { - this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); - } - this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands())); for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { Map commands = entry.getValue(); @@ -161,6 +199,12 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands)); } + + if (geyserConfig.isLegacyPingPassthrough()) { + this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); + } else { + this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); + } } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index e3b794578..2000dc114 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -34,6 +34,9 @@ import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; import org.bukkit.command.CommandMap; import org.bukkit.command.PluginCommand; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; import org.bukkit.plugin.Plugin; @@ -59,7 +62,12 @@ import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; -import org.geysermc.geyser.platform.spigot.world.manager.*; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigot1_12NativeWorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigot1_12WorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotFallbackWorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotLegacyNativeWorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; @@ -95,7 +103,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { private String minecraftVersion; @Override - public void onEnable() { + public void onLoad() { GeyserLocale.init(this); // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed @@ -113,6 +121,30 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { return; } + // By default this should be localhost but may need to be changed in some circumstances + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { + geyserConfig.setAutoconfiguredRemote(true); + // Don't use localhost if not listening on all interfaces + if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { + geyserConfig.getRemote().setAddress(Bukkit.getIp()); + } + geyserConfig.getRemote().setPort(Bukkit.getPort()); + } + + if (geyserConfig.getBedrock().isCloneRemotePort()) { + geyserConfig.getBedrock().setPort(Bukkit.getPort()); + } + + this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) + : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); + + GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + + this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this); + } + + @Override + public void onEnable() { try { // AvailableCommandsSerializer_v291 complains otherwise ByteBuf.class.getMethod("writeShortLE", int.class); @@ -144,24 +176,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } } - // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { - geyserConfig.setAutoconfiguredRemote(true); - // Don't use localhost if not listening on all interfaces - if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { - geyserConfig.getRemote().setAddress(Bukkit.getIp()); - } - geyserConfig.getRemote().setPort(Bukkit.getPort()); - } - - if (geyserConfig.getBedrock().isCloneRemotePort()) { - geyserConfig.getBedrock().setPort(Bukkit.getPort()); - } - - this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) - : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - // Remove this in like a year if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION)); @@ -172,7 +186,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); - return; } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); @@ -181,11 +194,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserConfig.loadFloodgate(this); + // Needs to be an anonymous inner class otherwise Bukkit complains about missing classes + Bukkit.getPluginManager().registerEvents(new Listener() { + + @EventHandler + public void onServerLoaded(ServerLoadEvent event) { + // Wait until all plugins have loaded so Geyser can start + postStartup(); + } + }, this); + + // Because Bukkit locks its command map upon startup, we need to + // add our plugin commands in onEnable, but populating the executor + // can happen at any time + CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap(); + for (Extension extension : this.geyser.extensionManager().extensions()) { + + // Thanks again, Bukkit + try { + Constructor constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); + constructor.setAccessible(true); + + PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this); + pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!"); + + commandMap.register(extension.description().id(), "geyserext", pluginCommand); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { + this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.description().name(), ex); + } + } + } + + private void postStartup() { + GeyserImpl.start(); + // Turn "(MC: 1.16.4)" into 1.16.4. this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0]; - this.geyser = GeyserImpl.start(PlatformType.SPIGOT, this); - if (geyserConfig.isLegacyPingPassthrough()) { this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { @@ -275,25 +320,18 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { PluginCommand geyserCommand = this.getCommand("geyser"); geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); - CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap(); for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { Map commands = entry.getValue(); if (commands.isEmpty()) { continue; } - // Thanks again, Bukkit - try { - Constructor constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); - constructor.setAccessible(true); - - PluginCommand pluginCommand = constructor.newInstance(entry.getKey().description().id(), this); - pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands)); - pluginCommand.setDescription("The main command for the " + entry.getKey().name() + " Geyser extension!"); - commandMap.register(entry.getKey().description().id(), "geyserext", pluginCommand); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { - this.geyserLogger.error("Failed to construct PluginCommand for extension " + entry.getKey().description().name(), ex); + PluginCommand command = this.getCommand(entry.getKey().description().id()); + if (command == null) { + continue; } + + command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands)); } if (!INITIALIZED) { diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java index d912d28d8..42040f6ab 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java @@ -72,8 +72,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { private GeyserImpl geyser; - @Override - public void onEnable() { + public void onLoad() { GeyserLocale.init(this); if (!configDir.exists()) @@ -114,7 +113,13 @@ public class GeyserSpongePlugin implements GeyserBootstrap { this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - this.geyser = GeyserImpl.start(PlatformType.SPONGE, this); + + this.geyser = GeyserImpl.load(PlatformType.SPONGE, this); + } + + @Override + public void onEnable() { + GeyserImpl.start(); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); @@ -124,6 +129,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser); this.geyserCommandManager.init(); + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser"); for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { @@ -166,6 +172,11 @@ public class GeyserSpongePlugin implements GeyserBootstrap { return configDir.toPath(); } + @Listener + public void onServerStarting() { + onLoad(); + } + @Listener public void onServerStart(GameStartedServerEvent event) { onEnable(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 052a41439..80d17f6a7 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -217,7 +217,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { // Allow libraries like Protocol to have their debug information passthrough logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); - geyser = GeyserImpl.start(PlatformType.STANDALONE, this); + geyser = GeyserImpl.load(PlatformType.STANDALONE, this); + GeyserImpl.start(); + geyserCommandManager = new GeyserStandaloneCommandManager(geyser); geyserCommandManager.init(); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 30d8deccd..dc31b3fdd 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -88,15 +88,6 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { @Override public void onEnable() { - try { - Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class); - } catch (NoSuchMethodException e) { - // velocitypowered.com has a build that is very outdated - logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " + - "that has likely been downloaded is very outdated and does not support 1.19."); - return; - } - GeyserLocale.init(this); try { @@ -131,6 +122,17 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); + + try { + Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class); + } catch (NoSuchMethodException e) { + // velocitypowered.com has a build that is very outdated + logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " + + "that has likely been downloaded is very outdated and does not support 1.19."); + return; + } + // Remove this in like a year try { // Should only exist on 1.0 @@ -153,7 +155,10 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); - this.geyser = GeyserImpl.start(PlatformType.VELOCITY, this); + } + + private void postStartup() { + GeyserImpl.start(); this.geyserInjector = new GeyserVelocityInjector(proxyServer); // Will be initialized after the proxy has been bound @@ -222,9 +227,14 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { @Subscribe public void onProxyBound(ListenerBoundEvent event) { - if (event.getListenerType() == ListenerType.MINECRAFT && geyserInjector != null) { - // After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too - geyserInjector.initializeLocalChannel(this); + if (event.getListenerType() == ListenerType.MINECRAFT) { + // Once listener is bound, do our startup process + this.postStartup(); + + if (geyserInjector != null) { + // After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too + geyserInjector.initializeLocalChannel(this); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 24a5f5d2c..115a7245e 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -57,6 +57,7 @@ import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; @@ -143,8 +144,8 @@ public class GeyserImpl implements GeyserApi { private final PlatformType platformType; private final GeyserBootstrap bootstrap; - private final EventBus eventBus; - private final GeyserExtensionManager extensionManager; + private final EventBus eventBus; + private GeyserExtensionManager extensionManager; private Metrics metrics; @@ -162,9 +163,19 @@ public class GeyserImpl implements GeyserApi { this.platformType = platformType; this.bootstrap = bootstrap; + GeyserLocale.finalizeDefaultLocale(this); + + /* Initialize event bus */ + this.eventBus = new GeyserEventBus(); + + /* Load Extensions */ + this.extensionManager = new GeyserExtensionManager(); + this.extensionManager.init(); + } + + public void initialize() { long startupTime = System.currentTimeMillis(); - GeyserLocale.finalizeDefaultLocale(this); GeyserLogger logger = bootstrap.getGeyserLogger(); logger.info("******************************************"); @@ -173,13 +184,8 @@ public class GeyserImpl implements GeyserApi { logger.info(""); logger.info("******************************************"); - /* Initialize event bus */ - this.eventBus = new GeyserEventBus(); - - /* Load Extensions */ - this.extensionManager = new GeyserExtensionManager(); - this.extensionManager.init(); + /* Enable extensions */ this.extensionManager.enableExtensions(); this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); @@ -193,7 +199,7 @@ public class GeyserImpl implements GeyserApi { MessageTranslator.init(); MinecraftLocale.init(); - start(); + startInstance(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -225,7 +231,7 @@ public class GeyserImpl implements GeyserApi { } } - private void start() { + private void startInstance() { this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread")); GeyserLogger logger = bootstrap.getGeyserLogger(); @@ -589,7 +595,7 @@ public class GeyserImpl implements GeyserApi { @Override @NonNull - public EventBus eventBus() { + public EventBus eventBus() { return this.eventBus; } @@ -612,18 +618,26 @@ public class GeyserImpl implements GeyserApi { return Integer.parseInt(BUILD_NUMBER); } - public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { + public static GeyserImpl load(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); } + return instance; + } + + public static void start() { + if (instance == null) { + throw new RuntimeException("Geyser has not been loaded yet!"); + } + // We've been reloaded if (instance.isShuttingDown()) { instance.shuttingDown = false; - instance.start(); + instance.startInstance(); + } else { + instance.initialize(); } - - return instance; } public GeyserLogger getLogger() { diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java index f634931f4..9593e327e 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -32,6 +32,7 @@ import org.geysermc.event.bus.impl.OwnedEventBusImpl; import org.geysermc.event.subscribe.OwnedSubscriber; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.EventSubscriber; import org.geysermc.geyser.api.extension.Extension; @@ -40,11 +41,11 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; @SuppressWarnings("unchecked") -public final class GeyserEventBus extends OwnedEventBusImpl> - implements EventBus { +public final class GeyserEventBus extends OwnedEventBusImpl> + implements EventBus { @Override - protected > B makeSubscription( - @NonNull Extension owner, + protected > B makeSubscription( + @NonNull EventRegistrar owner, @NonNull Class eventClass, @NonNull Subscribe subscribe, @NonNull L listener, @@ -55,8 +56,8 @@ public final class GeyserEventBus extends OwnedEventBusImpl> B makeSubscription( - @NonNull Extension owner, + protected > B makeSubscription( + @NonNull EventRegistrar owner, @NonNull Class eventClass, @NonNull Consumer handler, @NonNull PostOrder postOrder) { @@ -65,7 +66,7 @@ public final class GeyserEventBus extends OwnedEventBusImpl Set> subscribers(@NonNull Class eventClass) { + public Set> subscribers(@NonNull Class eventClass) { return castGenericSet(super.subscribers(eventClass)); } } diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java new file mode 100644 index 000000000..85c36a132 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.event; + +import org.geysermc.geyser.api.event.EventRegistrar; + +public record GeyserEventRegistrar(Object owner) implements EventRegistrar { + + @Override + public String toString() { + return "GeyserEventRegistrar{" + + "owner=" + this.owner + + '}'; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java index 5012037bb..d33de8cdd 100644 --- a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java @@ -29,16 +29,17 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.impl.OwnedSubscriberImpl; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.ExtensionEventSubscriber; import org.geysermc.geyser.api.extension.Extension; import java.util.function.BiConsumer; import java.util.function.Consumer; -public final class GeyserEventSubscriber extends OwnedSubscriberImpl +public final class GeyserEventSubscriber extends OwnedSubscriberImpl implements ExtensionEventSubscriber { GeyserEventSubscriber( - @NonNull Extension owner, + @NonNull R owner, @NonNull Class eventClass, @NonNull Consumer handler, @NonNull PostOrder postOrder) { @@ -46,7 +47,7 @@ public final class GeyserEventSubscriber extends OwnedSubscribe } GeyserEventSubscriber( - @NonNull Extension owner, + @NonNull R owner, @NonNull Class eventClass, @NonNull PostOrder postOrder, boolean ignoreCancelled, diff --git a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java index 7294d4345..f56b254a6 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java +++ b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java @@ -30,6 +30,7 @@ import org.geysermc.event.Event; import org.geysermc.event.PostOrder; import org.geysermc.event.subscribe.Subscriber; import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.EventSubscriber; import org.geysermc.geyser.api.event.ExtensionEventBus; import org.geysermc.geyser.api.extension.Extension; @@ -37,10 +38,12 @@ import org.geysermc.geyser.api.extension.Extension; import java.util.Set; import java.util.function.Consumer; -public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) implements ExtensionEventBus { +public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) implements ExtensionEventBus { + + @SuppressWarnings({"rawtypes", "unchecked"}) @Override - public void unsubscribe(@NonNull EventSubscriber subscription) { - eventBus.unsubscribe(subscription); + public void unsubscribe(@NonNull EventSubscriber subscription) { + eventBus.unsubscribe((EventSubscriber) subscription); } @Override @@ -49,7 +52,7 @@ public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) im } @Override - public @NonNull Set> subscribers(@NonNull Class eventClass) { + public @NonNull Set> subscribers(@NonNull Class eventClass) { return eventBus.subscribers(eventClass); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java index e7837688c..99a9213fe 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java @@ -26,11 +26,13 @@ package org.geysermc.geyser.registry.loader; import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.event.GeyserEventRegistrar; import org.geysermc.geyser.item.GeyserCustomItemData; import org.geysermc.geyser.item.GeyserCustomItemOptions; import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData; @@ -49,6 +51,7 @@ public class ProviderRegistryLoader implements RegistryLoader, Prov providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); + providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0])); return providers; } From 896bf7c218919f42c1096fcb695dd7dc6d4facbf Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 4 Sep 2022 16:19:56 -0500 Subject: [PATCH 100/125] Fix bungeecord startup and move version checks to onLoad --- .../bungeecord/GeyserBungeePlugin.java | 80 +++++++++--------- .../platform/spigot/GeyserSpigotPlugin.java | 82 +++++++++---------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index eb2a3b9c4..13604a3d4 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -72,6 +72,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { @Override public void onLoad() { + // Copied from ViaVersion. + // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 + try { + ProtocolConstants.class.getField("MINECRAFT_1_19_1"); + } catch (NoSuchFieldException e) { + getLogger().warning(" / \\"); + getLogger().warning(" / \\"); + getLogger().warning(" / | \\"); + getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName())); + getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); + getLogger().warning(" / o \\"); + getLogger().warning("/_____________\\"); + } + GeyserLocale.init(this); if (!getDataFolder().exists()) @@ -89,6 +103,31 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { return; } + this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); + GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + + this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); + } + + @Override + public void onEnable() { + // Remove this in like a year + if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + return; + } + + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); + return; + } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + } + + geyserConfig.loadFloodgate(this); + if (getProxy().getConfig().getListeners().size() == 1) { ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0]; @@ -109,45 +148,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { } } - this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - - this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); - } - - @Override - public void onEnable() { - // Copied from ViaVersion. - // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 - try { - ProtocolConstants.class.getField("MINECRAFT_1_19_1"); - } catch (NoSuchFieldException e) { - getLogger().warning(" / \\"); - getLogger().warning(" / \\"); - getLogger().warning(" / | \\"); - getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName())); - getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); - getLogger().warning(" / o \\"); - getLogger().warning("/_____________\\"); - } - - // Remove this in like a year - if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); - return; - } - - if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); - return; - } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { - // Floodgate installed means that the user wants Floodgate authentication - geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); - } - - geyserConfig.loadFloodgate(this); - // Big hack - Bungee does not provide us an event to listen to, so schedule a repeating // task that waits for a field to be filled which is set after the plugin enable // process is complete @@ -171,7 +171,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { listenersField.setAccessible(true); Collection listeners = (Collection) listenersField.get(BungeeCord.getInstance()); - if (!listeners.isEmpty()) { + if (listeners.isEmpty()) { this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS); } else { this.awaitStartupCompletion(++tries); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 2000dc114..4407af6c7 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -104,47 +104,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { @Override public void onLoad() { - GeyserLocale.init(this); - - // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed - try { - if (!getDataFolder().exists()) { - getDataFolder().mkdir(); - } - File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); - } catch (IOException ex) { - getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); - Bukkit.getPluginManager().disablePlugin(this); - return; - } - - // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { - geyserConfig.setAutoconfiguredRemote(true); - // Don't use localhost if not listening on all interfaces - if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { - geyserConfig.getRemote().setAddress(Bukkit.getIp()); - } - geyserConfig.getRemote().setPort(Bukkit.getPort()); - } - - if (geyserConfig.getBedrock().isCloneRemotePort()) { - geyserConfig.getBedrock().setPort(Bukkit.getPort()); - } - - this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) - : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); - - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - - this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this); - } - - @Override - public void onEnable() { try { // AvailableCommandsSerializer_v291 complains otherwise ByteBuf.class.getMethod("writeShortLE", int.class); @@ -176,6 +135,33 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } } + GeyserLocale.init(this); + + // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed + try { + if (!getDataFolder().exists()) { + getDataFolder().mkdir(); + } + File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", + (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); + this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); + } catch (IOException ex) { + getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); + ex.printStackTrace(); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + + this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) + : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); + + GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + + this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this); + } + + @Override + public void onEnable() { // Remove this in like a year if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION)); @@ -192,6 +178,20 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); } + // By default this should be localhost but may need to be changed in some circumstances + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { + geyserConfig.setAutoconfiguredRemote(true); + // Don't use localhost if not listening on all interfaces + if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { + geyserConfig.getRemote().setAddress(Bukkit.getIp()); + } + geyserConfig.getRemote().setPort(Bukkit.getPort()); + } + + if (geyserConfig.getBedrock().isCloneRemotePort()) { + geyserConfig.getBedrock().setPort(Bukkit.getPort()); + } + geyserConfig.loadFloodgate(this); // Needs to be an anonymous inner class otherwise Bukkit complains about missing classes From 770dfca32889b7d8e9c4fbc511966b3f0020a65f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 4 Sep 2022 16:35:50 -0500 Subject: [PATCH 101/125] Fix dumps --- .../java/org/geysermc/geyser/dump/DumpInfo.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index da52d064c..4c2d24dd4 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -70,7 +70,7 @@ public class DumpInfo { private final String cpuName; private final Locale systemLocale; private final String systemEncoding; - private Properties gitInfo; + private final GitInfo gitInfo; private final GeyserConfiguration config; private final Floodgate floodgate; private final Object2IntMap userPlatforms; @@ -89,11 +89,7 @@ public class DumpInfo { this.systemLocale = Locale.getDefault(); this.systemEncoding = System.getProperty("file.encoding"); - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("git.properties")) { - this.gitInfo = new Properties(); - this.gitInfo.load(stream); - } catch (IOException ignored) { - } + this.gitInfo = new GitInfo(GeyserImpl.VERSION, GeyserImpl.BUILD_NUMBER, GeyserImpl.GIT_VERSION, GeyserImpl.BRANCH); this.config = GeyserImpl.getInstance().getConfig(); this.floodgate = new Floodgate(); @@ -300,4 +296,13 @@ public class DumpInfo { public String main; public List authors; } + + @Getter + @AllArgsConstructor + public static class GitInfo { + private final String version; + private final String buildNumber; + private final String commitHash; + private final String branchName; + } } From 25f43f3152a2166abbb64936616299d9f0c9e3ed Mon Sep 17 00:00:00 2001 From: Typical <53472591+TypicalShavonne@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:35:47 +0700 Subject: [PATCH 102/125] Updating README (#3268) --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 464e67d76..d02d50d2b 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge ## What's Left to be Added/Fixed - Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you) -- Resource pack conversion/CustomModelData - Some Entity Flags - Structure block UI @@ -43,9 +42,8 @@ There are a few things Geyser is unable to support due to various differences be ## Compiling 1. Clone the repo to your computer -2. [Install Maven](https://maven.apache.org/install.html) -3. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process. -4. Run `mvn clean install` and locate to the `target` folder. +2. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process. +3. Run `gradlew build` and locate to `bootstrap/build` folder. ## Contributing Any contributions are appreciated. Please feel free to reach out to us on [Discord](http://discord.geysermc.org/) if From 2d7a463089698a8ed33d4261bf64ae3e1a892ee5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 11 Sep 2022 19:26:22 -0400 Subject: [PATCH 103/125] Make Geyser dumps backwards compatible --- core/build.gradle.kts | 6 +++++- .../src/main/java/org/geysermc/geyser/GeyserImpl.java | 2 ++ .../main/java/org/geysermc/geyser/dump/DumpInfo.java | 11 ++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 83591e7ad..41cf83538 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -80,12 +80,16 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val gitVersion = "git-${branchName()}-${indra.commit()?.name?.substring(0, 7) ?: "0000000"}" + val commit = indra.commit() + val git = indra.git() + val gitVersion = "git-${branchName()}-${commit?.name?.substring(0, 7) ?: "0000000"}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) replaceToken("\${buildNumber}", buildNumber(), mainFile) replaceToken("\${branch}", branchName(), mainFile) + if (commit != null && commit.name != null) replaceToken("\${commit}", commit.name, mainFile) + if (git != null) replaceToken("\${repository}", git.repository.config.getString("remote", "origin", "url")) } fun Project.branchName(): String = diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 115a7245e..ce2474d50 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -116,6 +116,8 @@ public class GeyserImpl implements GeyserApi { public static final String BUILD_NUMBER = "${buildNumber}"; public static final String BRANCH = "${branch}"; + public static final String COMMIT = "${commit}"; + public static final String REPOSITORY = "${repository}"; /** * Oauth client ID for Microsoft authentication diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 4c2d24dd4..5197f2107 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.dump; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; @@ -50,7 +51,6 @@ import org.geysermc.geyser.util.WebUtils; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -89,7 +89,7 @@ public class DumpInfo { this.systemLocale = Locale.getDefault(); this.systemEncoding = System.getProperty("file.encoding"); - this.gitInfo = new GitInfo(GeyserImpl.VERSION, GeyserImpl.BUILD_NUMBER, GeyserImpl.GIT_VERSION, GeyserImpl.BRANCH); + this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); this.config = GeyserImpl.getInstance().getConfig(); this.floodgate = new Floodgate(); @@ -300,9 +300,14 @@ public class DumpInfo { @Getter @AllArgsConstructor public static class GitInfo { - private final String version; private final String buildNumber; + @JsonProperty("git.commit.id.abbrev") + private final String commitHashAbbrev; + @JsonProperty("git.commit.id") private final String commitHash; + @JsonProperty("git.branch") private final String branchName; + @JsonProperty("git.remote.origin.url") + private final String originUrl; } } From a99afe44183b89b54b67993ffe0c23150a649d62 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:24:08 -0400 Subject: [PATCH 104/125] Remove usage of Fastutil Object2Reference maps These are only beneficial for containsValue checks. --- core/build.gradle.kts | 1 - .../geyser/extension/GeyserExtensionClassLoader.java | 6 +++--- .../geysermc/geyser/extension/GeyserExtensionLoader.java | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 41cf83538..5049c93ab 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -26,7 +26,6 @@ dependencies { implementation("com.nukkitx.fastutil", "fastutil-int-boolean-maps", Versions.fastutilVersion) implementation("com.nukkitx.fastutil", "fastutil-object-int-maps", Versions.fastutilVersion) implementation("com.nukkitx.fastutil", "fastutil-object-object-maps", Versions.fastutilVersion) - implementation("com.nukkitx.fastutil", "fastutil-object-reference-maps", Versions.fastutilVersion) // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java index b220ab576..b94e70ed0 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.extension; -import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; -import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.ExtensionDescription; import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; @@ -39,7 +39,7 @@ import java.nio.file.Path; public class GeyserExtensionClassLoader extends URLClassLoader { private final GeyserExtensionLoader loader; - private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); + private final Object2ObjectMap> classes = new Object2ObjectOpenHashMap<>(); public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path) throws MalformedURLException { super(new URL[] { path.toUri().toURL() }, parent); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java index 44558e798..7e998e413 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.extension; -import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; -import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.api.Geyser; @@ -51,7 +51,7 @@ import java.util.stream.Stream; public class GeyserExtensionLoader extends ExtensionLoader { private static final Pattern[] EXTENSION_FILTERS = new Pattern[] { Pattern.compile("^.+\\.jar$") }; - private final Object2ReferenceMap> classes = new Object2ReferenceOpenHashMap<>(); + private final Object2ObjectMap> classes = new Object2ObjectOpenHashMap<>(); private final Map classLoaders = new HashMap<>(); private final Map extensionContainers = new HashMap<>(); private final Path extensionsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("extensions"); From 6df8740955f0060821e7e2c7b7fbcb0a25496cbc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:19:56 -0400 Subject: [PATCH 105/125] Only register commands on Spigot if the extension has commands --- .../geyser/api/extension/Extension.java | 2 +- .../geyser/api/extension/ExtensionLoader.java | 3 +- .../api/extension/ExtensionManager.java | 36 ++------------- .../platform/spigot/GeyserSpigotPlugin.java | 16 +++---- .../java/org/geysermc/geyser/GeyserImpl.java | 8 +--- .../geyser/command/GeyserCommand.java | 3 +- .../extension/GeyserExtensionManager.java | 45 +++---------------- 7 files changed, 21 insertions(+), 92 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java index 8d4e4da64..33fc159de 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -124,7 +124,7 @@ public interface Extension extends EventRegistrar { */ @NonNull default ExtensionLoader extensionLoader() { - return Objects.requireNonNull(this.extensionManager().extensionLoader(this)); + return Objects.requireNonNull(this.extensionManager().extensionLoader()); } /** diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index c84c37919..30414d500 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -34,7 +34,6 @@ import java.nio.file.Path; * The extension loader is responsible for loading, unloading, enabling and disabling extensions */ public abstract class ExtensionLoader { - /** * Gets if the given {@link Extension} is enabled. * @@ -101,6 +100,6 @@ public abstract class ExtensionLoader { * @param extensionManager the extension manager */ protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { - extensionManager.register(extension, this); + extensionManager.register(extension); } } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java index 65d6c66da..a9d0d7376 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java @@ -29,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; -import java.util.Map; /** * Manages Geyser {@link Extension}s @@ -59,15 +58,6 @@ public abstract class ExtensionManager { */ public abstract void disable(@NonNull Extension extension); - /** - * Gets the {@link ExtensionLoader} responsible for loading - * the given {@link Extension}. - * - * @return the extension loader for loading the given extension - */ - @Nullable - public abstract ExtensionLoader extensionLoader(@NonNull Extension extension); - /** * Gets all the {@link Extension}s currently loaded. * @@ -77,37 +67,19 @@ public abstract class ExtensionManager { public abstract Collection extensions(); /** - * Gets the {@link ExtensionLoader} with the given identifier. + * Gets the {@link ExtensionLoader}. * - * @param identifier the identifier - * @return the extension loader at the given identifier + * @return the extension loader */ @Nullable - public abstract ExtensionLoader extensionLoader(@NonNull String identifier); - - /** - * Registers an {@link ExtensionLoader} with the given identifier. - * - * @param identifier the identifier - * @param extensionLoader the extension loader - */ - public abstract void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader); - - /** - * Gets all the currently registered {@link ExtensionLoader}s. - * - * @return all the currently registered extension loaders - */ - @NonNull - public abstract Map extensionLoaders(); + public abstract ExtensionLoader extensionLoader(); /** * Registers an {@link Extension} with the given {@link ExtensionLoader}. * * @param extension the extension - * @param loader the loader */ - public abstract void register(@NonNull Extension extension, @NonNull ExtensionLoader loader); + public abstract void register(@NonNull Extension extension); /** * Loads all extensions from the given {@link ExtensionLoader}. diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 4407af6c7..60b1cfa21 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -62,12 +62,7 @@ import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigot1_12NativeWorldManager; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigot1_12WorldManager; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotFallbackWorldManager; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotLegacyNativeWorldManager; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager; -import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; +import org.geysermc.geyser.platform.spigot.world.manager.*; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; @@ -204,12 +199,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } }, this); + this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); + this.geyserCommandManager.init(); + // Because Bukkit locks its command map upon startup, we need to // add our plugin commands in onEnable, but populating the executor // can happen at any time CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap(); - for (Extension extension : this.geyser.extensionManager().extensions()) { - + for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) { // Thanks again, Bukkit try { Constructor constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); @@ -245,9 +242,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass())); - this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); - this.geyserCommandManager.init(); - boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; if (isViaVersion) { try { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index ce2474d50..a10e54f90 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -147,7 +147,7 @@ public class GeyserImpl implements GeyserApi { private final GeyserBootstrap bootstrap; private final EventBus eventBus; - private GeyserExtensionManager extensionManager; + private final GeyserExtensionManager extensionManager; private Metrics metrics; @@ -173,6 +173,7 @@ public class GeyserImpl implements GeyserApi { /* Load Extensions */ this.extensionManager = new GeyserExtensionManager(); this.extensionManager.init(); + this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); } public void initialize() { @@ -186,11 +187,6 @@ public class GeyserImpl implements GeyserApi { logger.info(""); logger.info("******************************************"); - - /* Enable extensions */ - this.extensionManager.enableExtensions(); - this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); - /* Initialize registries */ Registries.init(); BlockRegistries.init(); diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java index 0d020ad08..5808dbc2c 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java @@ -33,7 +33,6 @@ import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -49,7 +48,7 @@ public abstract class GeyserCommand implements Command { protected final String description; protected final String permission; - private List aliases = new ArrayList<>(); + private List aliases = Collections.emptyList(); public abstract void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args); diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java index 7d80c2cf6..5dd924301 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.extension; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; @@ -39,34 +37,23 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Collectors; public class GeyserExtensionManager extends ExtensionManager { - private static final Key BASE_EXTENSION_LOADER_KEY = Key.key("geysermc", "base"); - - private final Map extensionLoaderTypes = new Object2ObjectOpenHashMap<>(); - + private final GeyserExtensionLoader extensionLoader = new GeyserExtensionLoader(); private final Map extensions = new LinkedHashMap<>(); - private final Map extensionsLoaders = new LinkedHashMap<>(); public void init() { GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); - extensionLoaderTypes.put(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader()); - for (ExtensionLoader loader : this.extensionLoaders().values()) { - this.loadAllExtensions(loader); - } + loadAllExtensions(this.extensionLoader); + enableExtensions(); GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.done", this.extensions.size())); } @Override public Extension extension(@NonNull String name) { - if (this.extensions.containsKey(name)) { - return this.extensions.get(name); - } - - return null; + return this.extensions.get(name); } @Override @@ -121,37 +108,19 @@ public class GeyserExtensionManager extends ExtensionManager { } } - @Override - public ExtensionLoader extensionLoader(@NonNull Extension extension) { - return this.extensionsLoaders.get(extension); - } - @NonNull @Override public Collection extensions() { return Collections.unmodifiableCollection(this.extensions.values()); } - @Nullable @Override - public ExtensionLoader extensionLoader(@NonNull String identifier) { - return this.extensionLoaderTypes.get(Key.key(identifier)); + public @Nullable ExtensionLoader extensionLoader() { + return this.extensionLoader; } @Override - public void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader) { - this.extensionLoaderTypes.put(Key.key(identifier), extensionLoader); - } - - @NonNull - @Override - public Map extensionLoaders() { - return this.extensionLoaderTypes.entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue)); - } - - @Override - public void register(@NonNull Extension extension, @NonNull ExtensionLoader loader) { - this.extensionsLoaders.put(extension, loader); + public void register(@NonNull Extension extension) { this.extensions.put(extension.name(), extension); } } From 77827d5cf5b81457f912570d1c4673e83c872813 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 14 Sep 2022 18:09:08 -0400 Subject: [PATCH 106/125] Return to using snapshot pinned Network version --- build-logic/src/main/kotlin/Versions.kt | 4 +--- settings.gradle.kts | 12 +----------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 44996cd3d..2dac0f4c6 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -32,9 +32,7 @@ object Versions { const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" const val protocolVersion = "0bd459f" - // Not pinned to specific version due to possible gradle bug - // See comment in settings.gradle.kts - const val raknetVersion = "1.6.28-SNAPSHOT" + const val raknetVersion = "1.6.28-20220125.214016-6" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "9f78bd5" const val packetlibVersion = "3.0" diff --git a/settings.gradle.kts b/settings.gradle.kts index dd08f3922..3aa66d493 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,17 +4,7 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { // Floodgate, Cumulus etc. - maven("https://repo.opencollab.dev/maven-releases") { - mavenContent { releasesOnly() } - } - maven("https://repo.opencollab.dev/maven-snapshots") { - mavenContent { - // This has the unintended side effect of not allowing snapshot version pinning. - // Likely a bug in Gradle's implementation of snapshot pinning - // See https://github.com/gradle/gradle/pull/406 - snapshotsOnly() - } - } + maven("https://repo.opencollab.dev/main") // Paper, Velocity maven("https://repo.papermc.io/repository/maven-public") From e64e12ff98886a338a3beee76f49a86010cc388f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 14 Sep 2022 21:17:08 -0400 Subject: [PATCH 107/125] Initial support for 1.19.30 Bedrock --- build-logic/src/main/kotlin/Versions.kt | 2 +- core/build.gradle.kts | 2 +- .../network/ConnectorServerEventHandler.java | 3 +- .../geysermc/geyser/network/GameProtocol.java | 2 + .../geyser/network/LoggingPacketHandler.java | 7 +++ .../geyser/network/UpstreamPacketHandler.java | 15 +++++ .../populator/RecipeRegistryPopulator.java | 7 ++- .../geyser/session/SessionManager.java | 3 +- .../java/JavaUpdateRecipesTranslator.java | 63 +++++++++---------- .../JavaContainerSetSlotTranslator.java | 3 +- 10 files changed, 66 insertions(+), 41 deletions(-) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 2dac0f4c6..8d0185cc8 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -31,7 +31,7 @@ object Versions { const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8 const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "0bd459f" + const val protocolVersion = "f0feacd" const val raknetVersion = "1.6.28-20220125.214016-6" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "9f78bd5" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5049c93ab..b0325e33c 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) - api("com.github.CloudburstMC.Protocol", "bedrock-v544", Versions.protocolVersion) { + api("com.github.CloudburstMC.Protocol", "bedrock-v553", Versions.protocolVersion) { exclude("com.nukkitx.network", "raknet") exclude("com.nukkitx", "nbt") } diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index 4ed077a7b..bf655740f 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.network; import com.nukkitx.protocol.bedrock.BedrockPong; import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; import com.nukkitx.protocol.bedrock.BedrockServerSession; +import com.nukkitx.protocol.bedrock.v553.Bedrock_v553; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.DefaultEventLoopGroup; @@ -171,7 +172,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public void onSessionCreation(@Nonnull BedrockServerSession bedrockServerSession) { try { - bedrockServerSession.setPacketCodec(GameProtocol.DEFAULT_BEDROCK_CODEC); + bedrockServerSession.setPacketCodec(Bedrock_v553.V553_CODEC); // Has the RequestNetworkSettingsPacket bedrockServerSession.setLogging(true); bedrockServerSession.setCompressionLevel(geyser.getConfig().getBedrock().getCompressionLevel()); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(geyser, new GeyserSession(geyser, bedrockServerSession, eventLoopGroup.next()))); diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 006f40ace..dde0b0c04 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; +import com.nukkitx.protocol.bedrock.v553.Bedrock_v553; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -71,6 +72,7 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() .minecraftVersion("1.19.21/1.19.22") .build()); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v553.V553_CODEC); } /** diff --git a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java index b0b707ee0..7edf560e8 100644 --- a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java @@ -856,4 +856,11 @@ public class LoggingPacketHandler implements BedrockPacketHandler { public boolean handle(FilterTextPacket packet) { return defaultHandler(packet); } + + // 1.19.30 new packet + + @Override + public boolean handle(RequestNetworkSettingsPacket packet) { + return defaultHandler(packet); + } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 5a8fa3013..286a2eb9b 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.network; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.data.ExperimentData; +import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.geyser.GeyserImpl; @@ -61,6 +62,20 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return translateAndDefault(packet); } + @Override + public boolean handle(RequestNetworkSettingsPacket packet) { + // New since 1.19.30 - sent before login packet + PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB; + + NetworkSettingsPacket responsePacket = new NetworkSettingsPacket(); + responsePacket.setCompressionAlgorithm(algorithm); + responsePacket.setCompressionThreshold(512); + session.sendUpstreamPacketImmediately(responsePacket); + + session.getUpstream().getSession().setCompression(algorithm); + return true; + } + @Override public boolean handle(LoginPacket loginPacket) { if (geyser.isShuttingDown()) { diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java index f0a215f2a..920ada5fb 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java @@ -33,6 +33,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -171,7 +172,7 @@ public class RecipeRegistryPopulator { /* Convert end */ return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } List inputs = new ObjectArrayList<>(); for (JsonNode entry : node.get("inputs")) { @@ -191,10 +192,10 @@ public class RecipeRegistryPopulator { if (type == 5) { // Shulker box return CraftingData.fromShulkerBox(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } return CraftingData.fromShapeless(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } private static ItemData getBedrockItemFromIdentifierJson(ItemMapping mapping, JsonNode itemNode) { diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java index 2660f54b1..02940e00c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java @@ -62,7 +62,8 @@ public final class SessionManager { } public void removeSession(GeyserSession session) { - if (sessions.remove(session.getPlayerEntity().getUuid()) == null) { + UUID uuid = session.getPlayerEntity().getUuid(); + if (uuid == null || sessions.remove(uuid) == null) { // Connection was likely pending pendingSessions.remove(session); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index 4c72a359a..d11caf601 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -34,9 +34,10 @@ import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeDa import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket; -import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.data.inventory.descriptor.DefaultDescriptor; +import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -99,8 +100,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator, IntSet> squashedOptions = new HashMap<>(); + private ItemDescriptorWithCount[][] combinations(GeyserSession session, Ingredient[] ingredients) { + Map, IntSet> squashedOptions = new HashMap<>(); for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getOptions().length == 0) { - squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i); + squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i); continue; } Ingredient ingredient = ingredients[i]; - Map> groupedByIds = Arrays.stream(ingredient.getOptions()) - .map(item -> ItemTranslator.translateToBedrock(session, item)) - .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag()))); - Set optionSet = new HashSet<>(groupedByIds.size()); - for (Map.Entry> entry : groupedByIds.entrySet()) { + Map> groupedByIds = Arrays.stream(ingredient.getOptions()) + .map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item))) + .collect(Collectors.groupingBy(item -> new GroupedItem(((DefaultDescriptor) item.getDescriptor()).getItemId(), item.getCount()))); + Set optionSet = new HashSet<>(groupedByIds.size()); + for (Map.Entry> entry : groupedByIds.entrySet()) { if (entry.getValue().size() > 1) { GroupedItem groupedItem = entry.getKey(); int idCount = 0; @@ -234,42 +236,38 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator new IntOpenHashSet()).add(i); } int totalCombinations = 1; - for (Set optionSet : squashedOptions.keySet()) { + for (Set optionSet : squashedOptions.keySet()) { totalCombinations *= optionSet.size(); } if (totalCombinations > 500) { - ItemData[] translatedItems = new ItemData[ingredients.length]; + ItemDescriptorWithCount[] translatedItems = new ItemDescriptorWithCount[ingredients.length]; for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getOptions().length > 0) { - translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]); + translatedItems[i] = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0])); } else { - translatedItems[i] = ItemData.AIR; + translatedItems[i] = ItemDescriptorWithCount.EMPTY; } } - return new ItemData[][]{translatedItems}; + return new ItemDescriptorWithCount[][]{translatedItems}; } - List> sortedSets = new ArrayList<>(squashedOptions.keySet()); + List> sortedSets = new ArrayList<>(squashedOptions.keySet()); sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder())); - ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length]; + ItemDescriptorWithCount[][] combinations = new ItemDescriptorWithCount[totalCombinations][ingredients.length]; int x = 1; - for (Set set : sortedSets) { + for (Set set : sortedSets) { IntSet slotSet = squashedOptions.get(set); int i = 0; - for (ItemData item : set) { + for (ItemDescriptorWithCount item : set) { for (int j = 0; j < totalCombinations / set.size(); j++) { final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x); for (int slot : slotSet) { @@ -288,6 +286,5 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator Date: Wed, 14 Sep 2022 21:22:34 -0400 Subject: [PATCH 108/125] Use Indra to get branch name --- core/build.gradle.kts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b0325e33c..f2225dbb8 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -79,20 +79,18 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" + val branchName = indra.branchName() ?: "DEV" val commit = indra.commit() val git = indra.git() - val gitVersion = "git-${branchName()}-${commit?.name?.substring(0, 7) ?: "0000000"}" + val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) replaceToken("\${gitVersion}", gitVersion, mainFile) replaceToken("\${buildNumber}", buildNumber(), mainFile) - replaceToken("\${branch}", branchName(), mainFile) + replaceToken("\${branch}", branchName, mainFile) if (commit != null && commit.name != null) replaceToken("\${commit}", commit.name, mainFile) if (git != null) replaceToken("\${repository}", git.repository.config.getString("remote", "origin", "url")) } -fun Project.branchName(): String = - System.getenv("GIT_BRANCH") ?: "local/dev" - fun Project.buildNumber(): Int = Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") \ No newline at end of file From 83be01958f577ddeca9d9934a3deafea9e2bc020 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 15 Sep 2022 15:53:03 -0400 Subject: [PATCH 109/125] Yeet logging into Microsoft with password This has been broken for ages; we need to finally remove it. --- .../geyser/util/LoginEncryptionUtils.java | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index c8d6e42d7..8d832f8fa 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -245,13 +245,7 @@ public class LoginEncryptionUtils { } if (response.clickedButtonId() == 1) { - if (isPasswordAuthEnabled) { - session.setMicrosoftAccount(true); - buildAndShowMicrosoftAuthenticationWindow(session); - } else { - // Just show the OAuth code - session.authenticateWithMicrosoftCode(); - } + session.authenticateWithMicrosoftCode(); return; } @@ -315,39 +309,10 @@ public class LoginEncryptionUtils { .input("geyser.auth.login.form.details.email", "account@geysermc.org", "") .input("geyser.auth.login.form.details.pass", "123456", "") .invalidResultHandler(() -> buildAndShowLoginDetailsWindow(session)) - .closedResultHandler(() -> { - if (session.isMicrosoftAccount()) { - buildAndShowMicrosoftAuthenticationWindow(session); - } else { - buildAndShowLoginWindow(session); - } - }) + .closedResultHandler(() -> buildAndShowLoginWindow(session)) .validResultHandler((response) -> session.authenticate(response.next(), response.next()))); } - /** - * Prompts the user between either OAuth code login or manual password authentication - */ - public static void buildAndShowMicrosoftAuthenticationWindow(GeyserSession session) { - session.sendForm( - SimpleForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.locale()) - .title("geyser.auth.login.form.notice.btn_login.microsoft") - .button("geyser.auth.login.method.browser") - .button("geyser.auth.login.method.password") - .button("geyser.auth.login.form.notice.btn_disconnect") - .closedOrInvalidResultHandler(() -> buildAndShowLoginWindow(session)) - .validResultHandler((response) -> { - if (response.clickedButtonId() == 0) { - session.authenticateWithMicrosoftCode(); - } else if (response.clickedButtonId() == 1) { - buildAndShowLoginDetailsWindow(session); - } else { - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.locale())); - } - })); - } - /** * Shows the code that a user must input into their browser */ @@ -374,7 +339,7 @@ public class LoginEncryptionUtils { .content(message.toString()) .button1("%gui.done") .button2("%menu.disconnect") - .closedOrInvalidResultHandler(() -> buildAndShowMicrosoftAuthenticationWindow(session)) + .closedOrInvalidResultHandler(() -> buildAndShowLoginWindow(session)) .validResultHandler((response) -> { if (response.clickedButtonId() == 1) { session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", locale)); From 0aa7411d02380223046a9b55ad38eee8c64b84f6 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:32:38 -0400 Subject: [PATCH 110/125] Fix git branch on Jenkins (#3286) --- core/build.gradle.kts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index f2225dbb8..0d1c16825 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -79,7 +79,8 @@ configure { val indra = the() val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - val branchName = indra.branchName() ?: "DEV" + // On Jenkins, a detached head is checked out, so indra cannot determine the branch. Fortunately, this environment variable is available. + val branchName = indra.branchName() ?: System.getenv("GIT_BRANCH") ?: "DEV" val commit = indra.commit() val git = indra.git() val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" @@ -93,4 +94,4 @@ configure { } fun Project.buildNumber(): Int = - Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") \ No newline at end of file + Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") From c8a51d783468d18f6d61d8f219277c598020420d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Sep 2022 16:37:30 -0400 Subject: [PATCH 111/125] Update to release protocol 1.19.30 --- build-logic/src/main/kotlin/Versions.kt | 2 +- core/build.gradle.kts | 2 +- .../geysermc/geyser/network/ConnectorServerEventHandler.java | 4 ++-- .../main/java/org/geysermc/geyser/network/GameProtocol.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 8d0185cc8..1aa91aee0 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -31,7 +31,7 @@ object Versions { const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8 const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "f0feacd" + const val protocolVersion = "96a4daf" const val raknetVersion = "1.6.28-20220125.214016-6" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "9f78bd5" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 0d1c16825..62e8f1c17 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) - api("com.github.CloudburstMC.Protocol", "bedrock-v553", Versions.protocolVersion) { + api("com.github.CloudburstMC.Protocol", "bedrock-v554", Versions.protocolVersion) { exclude("com.nukkitx.network", "raknet") exclude("com.nukkitx", "nbt") } diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index bf655740f..c9a3201c1 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.network; import com.nukkitx.protocol.bedrock.BedrockPong; import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; import com.nukkitx.protocol.bedrock.BedrockServerSession; -import com.nukkitx.protocol.bedrock.v553.Bedrock_v553; +import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.DefaultEventLoopGroup; @@ -172,7 +172,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public void onSessionCreation(@Nonnull BedrockServerSession bedrockServerSession) { try { - bedrockServerSession.setPacketCodec(Bedrock_v553.V553_CODEC); // Has the RequestNetworkSettingsPacket + bedrockServerSession.setPacketCodec(Bedrock_v554.V554_CODEC); // Has the RequestNetworkSettingsPacket bedrockServerSession.setLogging(true); bedrockServerSession.setCompressionLevel(geyser.getConfig().getBedrock().getCompressionLevel()); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(geyser, new GeyserSession(geyser, bedrockServerSession, eventLoopGroup.next()))); diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index dde0b0c04..ab931e901 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; -import com.nukkitx.protocol.bedrock.v553.Bedrock_v553; +import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -72,7 +72,7 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() .minecraftVersion("1.19.21/1.19.22") .build()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v553.V553_CODEC); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.V554_CODEC); } /** From 9fdbfdb0ab249f1763800cc9c3c88814831be10a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Sep 2022 21:55:30 -0400 Subject: [PATCH 112/125] Fix Adventure version --- build-logic/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 1aa91aee0..edb9f8213 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -36,7 +36,7 @@ object Versions { const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "9f78bd5" const val packetlibVersion = "3.0" - const val adventureVersion = "4.9.3" + const val adventureVersion = "4.12.0-20220629.025215-9" const val adventurePlatformVersion = "4.1.2" const val junitVersion = "4.13.1" const val checkerQualVersion = "3.19.0" From 64c03b9610e74d2c1912572b29cabbd48e148232 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:18:32 -0400 Subject: [PATCH 113/125] Correctly detect flying in 1.19.30 --- .../geyser/network/LoggingPacketHandler.java | 7 ++ .../BedrockAdventureSettingsTranslator.java | 18 +---- .../BedrockRequestAbilityTranslator.java | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java index 7edf560e8..8d2db081a 100644 --- a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java @@ -857,6 +857,13 @@ public class LoggingPacketHandler implements BedrockPacketHandler { return defaultHandler(packet); } + // 1.19.0 new packet + + @Override + public boolean handle(RequestAbilityPacket packet) { + return defaultHandler(packet); + } + // 1.19.30 new packet @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java index 641161127..aabc39e12 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java @@ -25,10 +25,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import com.nukkitx.protocol.bedrock.data.AdventureSetting; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -40,19 +37,6 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, RequestAbilityPacket packet) { + if (packet.getAbility() == Ability.FLYING) { + handle(session, packet.isBoolValue()); + } + } + + //FIXME remove after pre-1.19.30 support is dropped and merge into main method + static void handle(GeyserSession session, boolean isFlying) { + if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) { + // We should always be flying in spectator mode + session.sendAdventureSettings(); + return; + } else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) { + // As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling + // If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE + session.sendAdventureSettings(); + return; + } + + session.setFlying(isFlying); + ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(isFlying); + session.sendDownstreamPacket(abilitiesPacket); + } +} From 4e2d750791a4a892c9850c92de41f8386beba726 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:20:54 -0400 Subject: [PATCH 114/125] Gatekeep RequestAbilityPackets to 1.19.30+ --- .../main/java/org/geysermc/geyser/network/GameProtocol.java | 4 ++++ .../protocol/bedrock/BedrockRequestAbilityTranslator.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index ab931e901..f31d800c7 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -95,6 +95,10 @@ public final class GameProtocol { return session.getUpstream().getProtocolVersion() >= Bedrock_v534.V534_CODEC.getProtocolVersion(); } + public static boolean supports1_19_30(GeyserSession session) { + return session.getUpstream().getProtocolVersion() >= Bedrock_v554.V554_CODEC.getProtocolVersion(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java index 1ac75079c..fe8150d40 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.nukkitx.protocol.bedrock.data.Ability; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.RequestAbilityPacket; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -42,6 +43,11 @@ public class BedrockRequestAbilityTranslator extends PacketTranslator Date: Sun, 18 Sep 2022 13:27:16 -0400 Subject: [PATCH 115/125] Bump Protocol to fix 1.19.2x crafting --- build-logic/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index edb9f8213..47f35726d 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -31,7 +31,7 @@ object Versions { const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8 const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "96a4daf" + const val protocolVersion = "fed46166" const val raknetVersion = "1.6.28-20220125.214016-6" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "9f78bd5" From bb2f4644beadd701a370c8bc80866707e233e4f2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:11:18 -0400 Subject: [PATCH 116/125] Fix IO_Uring being included in builds --- core/build.gradle.kts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 62e8f1c17..d5f1c7a72 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -43,8 +43,6 @@ dependencies { api("com.github.steveice10", "packetlib", Versions.packetlibVersion) { exclude("io.netty", "netty-all") - // This is still experimental - additionally, it could only really benefit standalone - exclude("io.netty.incubator", "netty-incubator-transport-native-io_uring") } implementation("com.nukkitx.network", "raknet", Versions.raknetVersion) { @@ -63,6 +61,7 @@ dependencies { implementation("io.netty", "netty-transport-native-kqueue", Versions.nettyVersion, null, "osx-x86_64") // Adventure text serialization + implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureVersion) // Remove when we remove our Adventure bump implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) implementation("net.kyori", "adventure-text-serializer-plain", Versions.adventureVersion) @@ -75,6 +74,11 @@ dependencies { annotationProcessor(projects.ap) } +configurations.api { + // This is still experimental - additionally, it could only really benefit standalone + exclude(group = "io.netty.incubator", module = "netty-incubator-transport-native-io_uring") +} + configure { val indra = the() From f71fa9ccacec8b56e49fa2037877b704898111c6 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:18:48 -0400 Subject: [PATCH 117/125] Only check for correct protocol version once --- .../geyser/network/UpstreamPacketHandler.java | 39 +++++++++++++------ settings.gradle.kts | 3 ++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 286a2eb9b..c2a91fd75 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -62,8 +62,34 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return translateAndDefault(packet); } + private boolean newProtocol = false; // TEMPORARY + + private boolean setCorrectCodec(int protocolVersion) { + BedrockPacketCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion); + if (packetCodec == null) { + String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); + if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + // Too early to determine session locale + session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); + return false; + } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); + return false; + } + } + + session.getUpstream().getSession().setPacketCodec(packetCodec); + return true; + } + @Override public boolean handle(RequestNetworkSettingsPacket packet) { + if (setCorrectCodec(packet.getProtocolVersion())) { + newProtocol = true; + } else { + return true; + } + // New since 1.19.30 - sent before login packet PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB; @@ -84,21 +110,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } - BedrockPacketCodec packetCodec = GameProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); - if (packetCodec == null) { - String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); - if (loginPacket.getProtocolVersion() > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { - // Too early to determine session locale - session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); - return true; - } else if (loginPacket.getProtocolVersion() < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { - session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); + if (!newProtocol) { + if (!setCorrectCodec(loginPacket.getProtocolVersion())) { // REMOVE WHEN ONLY 1.19.30 IS SUPPORTED OR 1.20 return true; } } - session.getUpstream().getSession().setPacketCodec(packetCodec); - // Set the block translation based off of version session.setBlockMappings(BlockRegistries.BLOCKS.forVersion(loginPacket.getProtocolVersion())); session.setItemMappings(Registries.ITEMS.forVersion(loginPacket.getProtocolVersion())); diff --git a/settings.gradle.kts b/settings.gradle.kts index 3aa66d493..f6d13ad0d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,9 @@ dependencyResolutionManagement { maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } } + + // For Adventure snapshots + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") } } From 44e60b7ad83ae36777af7d4b0920230cf74d465d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:40:44 -0400 Subject: [PATCH 118/125] An educated guess to fix Git branch --- api/base/build.gradle.kts | 1 + .../org/geysermc/geyser/api/command/CommandExecutor.java | 5 +++-- .../java/org/geysermc/geyser/api/command/CommandSource.java | 6 ++++-- .../java/org/geysermc/geyser/api/event/EventRegistrar.java | 4 +++- .../org/geysermc/geyser/api/network/BedrockListener.java | 3 +++ .../java/org/geysermc/geyser/api/network/RemoteServer.java | 3 +++ core/build.gradle.kts | 2 +- 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts index a6fa608cc..c6cebe0ba 100644 --- a/api/base/build.gradle.kts +++ b/api/base/build.gradle.kts @@ -4,4 +4,5 @@ dependencies { exclude(group = "com.google.guava", module = "guava") exclude(group = "org.lanternpowered", module = "lmbda") } + compileOnlyApi("org.jetbrains", "annotations", "23.0.0") } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java index d384d097c..12a54ee90 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java @@ -25,13 +25,14 @@ package org.geysermc.geyser.api.command; +import org.checkerframework.checker.nullness.qual.NonNull; + /** * Handles executing a command. * * @param the command source */ public interface CommandExecutor { - /** * Executes the given {@link Command} with the given * {@link CommandSource}. @@ -40,5 +41,5 @@ public interface CommandExecutor { * @param command the command * @param args the arguments */ - void execute(T source, Command command, String[] args); + void execute(@NonNull T source, @NonNull Command command, @NonNull String[] args); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java index aabf0c4e8..45276e2c4 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.api.command; +import org.checkerframework.checker.nullness.qual.NonNull; + /** * Represents an instance capable of sending commands. */ @@ -42,7 +44,7 @@ public interface CommandSource { * * @param message the message to send */ - void sendMessage(String message); + void sendMessage(@NonNull String message); /** * Sends the given messages to the command source @@ -58,7 +60,7 @@ public interface CommandSource { /** * If this source is the console. * - * @return true if this source is the console + * @return true if this source is the console */ boolean isConsole(); diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java index 7a2cc0071..064dd55f6 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.api.event; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; /** @@ -39,7 +40,8 @@ public interface EventRegistrar { * @param object the object to wrap around * @return an event registrar instance */ - static EventRegistrar of(Object object) { + @NonNull + static EventRegistrar of(@NonNull Object object) { return GeyserApi.api().provider(EventRegistrar.class, object); } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java index 58a597eb6..61fe286aa 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.api.network; +import org.checkerframework.checker.nullness.qual.NonNull; + /** * The listener that handles connections from Minecraft: * Bedrock Edition. @@ -37,6 +39,7 @@ public interface BedrockListener { * * @return the listening address */ + @NonNull String address(); /** diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java index b13ae5930..d0ac6ee7e 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.api.network; +import org.jetbrains.annotations.NotNull; + /** * Represents the Java server that Geyser is connecting to. */ @@ -63,5 +65,6 @@ public interface RemoteServer { * * @return the auth type required by the remote server */ + @NotNull AuthType authType(); } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index d5f1c7a72..98ddd99b8 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -84,7 +84,7 @@ configure { val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" // On Jenkins, a detached head is checked out, so indra cannot determine the branch. Fortunately, this environment variable is available. - val branchName = indra.branchName() ?: System.getenv("GIT_BRANCH") ?: "DEV" + val branchName = indra.branchName() ?: System.getenv("BRANCH") ?: "DEV" val commit = indra.commit() val git = indra.git() val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" From 8605f0a91cdd46b5b29bebe4f807d8b04be690a5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:56:30 -0400 Subject: [PATCH 119/125] Use master languages branch --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index d92904027..51d6f5ba7 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit d92904027061856248ece8382face369e9cc5d67 +Subproject commit 51d6f5ba7d85bfda318879dad34481d9ef4d488d From f11dc6d03d6234d111b27983b3003137f2f74f1d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:56:46 -0400 Subject: [PATCH 120/125] A better educated guess to fix the git branch --- core/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 98ddd99b8..89d292bed 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -84,7 +84,7 @@ configure { val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" // On Jenkins, a detached head is checked out, so indra cannot determine the branch. Fortunately, this environment variable is available. - val branchName = indra.branchName() ?: System.getenv("BRANCH") ?: "DEV" + val branchName = indra.branchName() ?: System.getenv("GIT_LOCAL_BRANCH") ?: "DEV" val commit = indra.commit() val git = indra.git() val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" From d4ab388258cb220e9fc7d534a31312bd53493fc6 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:56:59 -0400 Subject: [PATCH 121/125] Remove unused annotation dependency --- api/base/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts index c6cebe0ba..a6fa608cc 100644 --- a/api/base/build.gradle.kts +++ b/api/base/build.gradle.kts @@ -4,5 +4,4 @@ dependencies { exclude(group = "com.google.guava", module = "guava") exclude(group = "org.lanternpowered", module = "lmbda") } - compileOnlyApi("org.jetbrains", "annotations", "23.0.0") } \ No newline at end of file From fd2c24223078190d52176ec92cc637f73cd9b1cb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:59:49 -0400 Subject: [PATCH 122/125] oops --- .../java/org/geysermc/geyser/api/network/RemoteServer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java index d0ac6ee7e..8ac5d8a03 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.api.network; -import org.jetbrains.annotations.NotNull; +import org.checkerframework.checker.nullness.qual.NonNull; /** * Represents the Java server that Geyser is connecting to. @@ -65,6 +65,6 @@ public interface RemoteServer { * * @return the auth type required by the remote server */ - @NotNull + @NonNull AuthType authType(); } From 9791e7b544ddbc6d1236b2b62a8f64bfab462ec5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Sep 2022 16:29:44 -0400 Subject: [PATCH 123/125] One more try on branch name --- core/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 89d292bed..f5c41ed12 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -84,7 +84,7 @@ configure { val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" // On Jenkins, a detached head is checked out, so indra cannot determine the branch. Fortunately, this environment variable is available. - val branchName = indra.branchName() ?: System.getenv("GIT_LOCAL_BRANCH") ?: "DEV" + val branchName = indra.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV" val commit = indra.commit() val git = indra.git() val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" From c84d53c827914e1985b319bcc59a167730d9f246 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Mon, 19 Sep 2022 11:22:09 -0400 Subject: [PATCH 124/125] Re-add git.properties (#3287) Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- core/build.gradle.kts | 68 +++++++++++++++++++++----- core/src/main/resources/git.properties | 7 +++ 2 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 core/src/main/resources/git.properties diff --git a/core/build.gradle.kts b/core/build.gradle.kts index f5c41ed12..2825ead1f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -79,23 +79,65 @@ configurations.api { exclude(group = "io.netty.incubator", module = "netty-incubator-transport-native-io_uring") } +tasks.processResources { + // This is solely for backwards compatibility for other programs that used this file before the switch to gradle. + // It used to be generated by the maven Git-Commit-Id-Plugin + filesMatching("git.properties") { + val info = GitInfo() + expand( + "branch" to info.branch, + "buildNumber" to info.buildNumber, + "projectVersion" to project.version, + "commit" to info.commit, + "commitAbbrev" to info.commitAbbrev, + "commitMessage" to info.commitMessage, + "repository" to info.repository + ) + } +} + configure { - val indra = the() - val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" - // On Jenkins, a detached head is checked out, so indra cannot determine the branch. Fortunately, this environment variable is available. - val branchName = indra.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV" - val commit = indra.commit() - val git = indra.git() - val gitVersion = "git-${branchName}-${commit?.name?.substring(0, 7) ?: "0000000"}" + val info = GitInfo() - replaceToken("\${version}", "${project.version} ($gitVersion)", mainFile) - replaceToken("\${gitVersion}", gitVersion, mainFile) - replaceToken("\${buildNumber}", buildNumber(), mainFile) - replaceToken("\${branch}", branchName, mainFile) - if (commit != null && commit.name != null) replaceToken("\${commit}", commit.name, mainFile) - if (git != null) replaceToken("\${repository}", git.repository.config.getString("remote", "origin", "url")) + replaceToken("\${version}", "${project.version} (${info.gitVersion})", mainFile) + replaceToken("\${gitVersion}", info.gitVersion, mainFile) + replaceToken("\${buildNumber}", info.buildNumber, mainFile) + replaceToken("\${branch}", info.branch, mainFile) + replaceToken("\${commit}", info.commit, mainFile) + replaceToken("\${repository}", info.repository, mainFile) } fun Project.buildNumber(): Int = Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") + +inner class GitInfo { + val branch: String + val commit: String + val commitAbbrev: String + + val gitVersion: String + val version: String + val buildNumber: Int + + val commitMessage: String + val repository: String + + init { + // On Jenkins, a detached head is checked out, so indra cannot determine the branch. + // Fortunately, this environment variable is available. + branch = indraGit.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV" + + val commit = indraGit.commit() + this.commit = commit?.name ?: "0".repeat(40) + commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7) + + gitVersion = "git-${branch}-${commitAbbrev}" + version = "${project.version} ($gitVersion)" + buildNumber = buildNumber() + + val git = indraGit.git() + commitMessage = git?.commit()?.message ?: "" + repository = git?.repository?.config?.getString("remote", "origin", "url") ?: "" + } +} diff --git a/core/src/main/resources/git.properties b/core/src/main/resources/git.properties new file mode 100644 index 000000000..f14e55623 --- /dev/null +++ b/core/src/main/resources/git.properties @@ -0,0 +1,7 @@ +git.branch=${branch} +git.build.number=${buildNumber} +git.build.version=${projectVersion} +git.commit.id=${commit} +git.commit.id.abbrev=${commitAbbrev} +git.commit.message.full=${commitMessage} +git.remote.origin.url=${repository} From f31b183a3363792cda5f6cb476beadeb9b319f23 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 19 Sep 2022 11:26:47 -0400 Subject: [PATCH 125/125] Rename Geyser standalone jar to Geyser-Standalone.jar --- bootstrap/standalone/build.gradle.kts | 2 +- core/build.gradle.kts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts index d49c7c490..3c1a10b09 100644 --- a/bootstrap/standalone/build.gradle.kts +++ b/bootstrap/standalone/build.gradle.kts @@ -27,7 +27,7 @@ application { } tasks.withType { - archiveBaseName.set("Geyser") + archiveBaseName.set("Geyser-Standalone") transform(Log4j2PluginsCacheFileTransformer()) } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 2825ead1f..49ce2fbff 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,4 @@ import net.kyori.blossom.BlossomExtension -import net.kyori.indra.git.IndraGitExtension plugins { id("net.kyori.blossom") @@ -109,7 +108,7 @@ configure { } fun Project.buildNumber(): Int = - Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") + System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1 inner class GitInfo { val branch: String