diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index f4a0e21ff..036d838ef 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -55,9 +55,9 @@ body:
required: true
- type: input
attributes:
- label: "Minecraft: Bedrock Edition Version"
- description: "What version of Minecraft: Bedrock Edition are you using? Leave empty if the bug happens before you can connect with Minecraft: Bedrock Edition."
- placeholder: "For example: 1.16.201"
+ label: "Minecraft: Bedrock Edition Device/Version"
+ description: "What version of Minecraft: Bedrock Edition are you using, and what device(s) does the bug occur on? Leave empty if the bug happens before you can connect with Minecraft: Bedrock Edition."
+ placeholder: "For example: 1.16.201, Nintendo Switch"
- type: textarea
attributes:
label: Additional Context
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 000000000..598cab46a
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,36 @@
+name: SonarCloud
+on:
+ push:
+ branches:
+ - master
+jobs:
+ build:
+ name: SonarCloud
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ submodules: true
+ - name: Set up JDK 17
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: 17
+ - name: Cache SonarCloud packages
+ uses: actions/cache@v1
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Maven packages
+ uses: actions/cache@v1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=GeyserMC_Geyser
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 85f8a6e9e..f1baa3abb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -239,8 +239,10 @@ nbdist/
run/
config.yml
logs/
-public-key.pem
+key.pem
locales/
/cache/
/packs/
-/dump.json
\ No newline at end of file
+/dump.json
+/saved-refresh-tokens.json
+/languages/
\ No newline at end of file
diff --git a/README.md b/README.md
index d4b375a5c..3e247f4b5 100644
--- a/README.md
+++ b/README.md
@@ -17,16 +17,16 @@ 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.30 - 1.17.41 + 1.18.0 - 1.18.2 and Minecraft Java 1.18/1.18.1.
+### Currently supporting Minecraft Bedrock 1.18.0 - 1.18.31 and Minecraft Java 1.18.2.
## Setting Up
-Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set up Geyser.
+Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
[![YouTube Video](https://img.youtube.com/vi/U7dZZ8w7Gi4/0.jpg)](https://www.youtube.com/watch?v=U7dZZ8w7Gi4)
## Links:
- Website: https://geysermc.org
-- Docs: https://github.com/GeyserMC/Geyser/wiki
+- Docs: https://wiki.geysermc.org/geyser/
- Download: https://ci.geysermc.org
- Discord: https://discord.gg/geysermc
- Donate: https://opencollective.com/geysermc
@@ -39,7 +39,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set
- Structure block UI
## What can't be fixed
-There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page.
+There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
## Compiling
1. Clone the repo to your computer
diff --git a/ap/pom.xml b/ap/pom.xml
index e14829ab1..0644044a1 100644
--- a/ap/pom.xml
+++ b/ap/pom.xml
@@ -6,9 +6,9 @@
org.geysermc
geyser-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
ap
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
\ No newline at end of file
diff --git a/api/base/pom.xml b/api/base/pom.xml
index c584885ce..1d051eaa3 100644
--- a/api/base/pom.xml
+++ b/api/base/pom.xml
@@ -5,7 +5,7 @@
org.geysermc
api-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
4.0.0
diff --git a/api/base/src/main/java/org/geysermc/api/session/Connection.java b/api/base/src/main/java/org/geysermc/api/session/Connection.java
index ccf3f7122..3e997912b 100644
--- a/api/base/src/main/java/org/geysermc/api/session/Connection.java
+++ b/api/base/src/main/java/org/geysermc/api/session/Connection.java
@@ -26,6 +26,7 @@
package org.geysermc.api.session;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.common.value.qual.IntRange;
import java.util.UUID;
@@ -55,5 +56,13 @@ public interface Connection {
*/
String xuid();
-
+ /**
+ * 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
+ * @return true if the transfer was a success
+ */
+ boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port);
}
diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml
index 7cee80208..2a933a2e0 100644
--- a/api/geyser/pom.xml
+++ b/api/geyser/pom.xml
@@ -5,7 +5,7 @@
org.geysermc
api-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
4.0.0
@@ -26,7 +26,7 @@
org.geysermc
base-api
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
diff --git a/api/pom.xml b/api/pom.xml
index d79922690..5d078fba5 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
geyser-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
api-parent
diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml
index 73cea9ec4..b2c661447 100644
--- a/bootstrap/bungeecord/pom.xml
+++ b/bootstrap/bungeecord/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
bootstrap-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-bungeecord
@@ -14,7 +14,7 @@
org.geysermc
core
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -49,7 +49,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.3.0-SNAPSHOT
+ 3.3.0
package
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index e4710e085..7d6ac8f98 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
geyser-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-parent
pom
@@ -34,7 +34,7 @@
org.geysermc
ap
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
provided
diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml
index 7aca462c9..e9e7687f4 100644
--- a/bootstrap/spigot/pom.xml
+++ b/bootstrap/spigot/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
bootstrap-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-spigot
@@ -19,13 +19,18 @@
viaversion-repo
https://repo.viaversion.com
+
+
+ minecraft-repo
+ https://libraries.minecraft.net/
+
org.geysermc
core
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -34,6 +39,12 @@
1.18.1-R0.1-SNAPSHOT
provided
+
+ io.papermc.paper
+ paper-mojangapi
+ 1.18.1-R0.1-SNAPSHOT
+ provided
+
com.viaversion
viaversion
@@ -43,7 +54,13 @@
org.geysermc.geyser.adapters
spigot-all
- 1.3-SNAPSHOT
+ 1.4-SNAPSHOT
+
+
+ me.lucko
+ commodore
+ 1.13
+ compile
@@ -70,7 +87,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.3.0-SNAPSHOT
+ 3.3.0
package
@@ -95,6 +112,10 @@
org.objectweb.asm
org.geysermc.geyser.platform.spigot.shaded.asm
+
+ me.lucko.commodore
+ org.geysermc.geyser.platform.spigot.shaded.commodore
+
@@ -118,6 +139,7 @@
io.netty:netty-codec-dns:*
io.netty:netty-resolver-dns:*
io.netty:netty-resolver-dns-native-macos:*
+ com.mojang:*
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..4ece501c4 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
@@ -29,40 +29,51 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
+import me.lucko.commodore.CommodoreProvider;
import org.bukkit.Bukkit;
+import org.bukkit.command.PluginCommand;
+import org.bukkit.permissions.Permission;
+import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType;
-import org.geysermc.geyser.GeyserImpl;
+import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserBootstrap;
+import org.geysermc.geyser.GeyserImpl;
+import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.command.CommandManager;
-import org.geysermc.geyser.session.auth.AuthType;
+import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
-import org.geysermc.geyser.network.MinecraftProtocol;
import org.geysermc.geyser.level.WorldManager;
+import org.geysermc.geyser.network.MinecraftProtocol;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
-import org.geysermc.geyser.Constants;
-import org.geysermc.geyser.util.FileUtils;
-import org.geysermc.geyser.text.GeyserLocale;
-import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
+import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport;
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.world.GeyserPistonListener;
-import org.geysermc.geyser.platform.spigot.world.GeyserSpigot1_11CraftingListener;
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.text.GeyserLocale;
+import org.geysermc.geyser.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.file.Path;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
+ /**
+ * Determines if the plugin has been ran once before, including before /geyser reload.
+ */
+ private static boolean INITIALIZED = false;
+
private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotInjector geyserInjector;
@@ -230,20 +241,42 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
}
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
}
- GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
- Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
- Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
+ PluginCommand pluginCommand = this.getCommand("geyser");
+ pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser));
- if (isPre1_12) {
- // Register events needed to send all recipes to the client
- Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigot1_11CraftingListener(geyser), this);
+ 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()) {
+ GeyserCommand command = entry.getValue();
+ if (command.getAliases().contains(entry.getKey())) {
+ // Don't register aliases
+ continue;
+ }
+
+ Bukkit.getPluginManager().addPermission(new Permission(command.getPermission(),
+ GeyserLocale.getLocaleStringLog(command.getDescription()),
+ command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
+ }
+
+ // Events cannot be unregistered - re-registering results in duplicate firings
+ GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
+ Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
+
+ Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
}
- this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(geyser));
+ boolean brigadierSupported = CommodoreProvider.isSupported();
+ geyserLogger.debug("Brigadier supported? " + brigadierSupported);
+ if (brigadierSupported) {
+ GeyserBrigadierSupport.loadBrigadier(this, pluginCommand);
+ }
// Check to ensure the current setup can support the protocol version Geyser uses
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
+
+ INITIALIZED = true;
}
@Override
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java
new file mode 100644
index 000000000..61900174c
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.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.platform.spigot.command;
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import me.lucko.commodore.Commodore;
+import me.lucko.commodore.CommodoreProvider;
+import org.bukkit.Bukkit;
+import org.bukkit.command.PluginCommand;
+import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin;
+
+/**
+ * Needs to be a separate class so pre-1.13 loads correctly.
+ */
+public final class GeyserBrigadierSupport {
+
+ public static void loadBrigadier(GeyserSpigotPlugin plugin, PluginCommand pluginCommand) {
+ // Enable command completions if supported
+ // This is beneficial because this is sent over the network and Bedrock can see it
+ Commodore commodore = CommodoreProvider.getCommodore(plugin);
+ LiteralArgumentBuilder> builder = LiteralArgumentBuilder.literal("geyser");
+ for (String command : plugin.getGeyserCommandManager().getCommands().keySet()) {
+ builder.then(LiteralArgumentBuilder.literal(command));
+ }
+ commodore.register(pluginCommand, builder);
+
+ try {
+ Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent");
+ Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), plugin);
+ plugin.getGeyserLogger().debug("Successfully registered AsyncPlayerSendCommandsEvent listener.");
+ } catch (ClassNotFoundException e) {
+ plugin.getGeyserLogger().debug("Not registering AsyncPlayerSendCommandsEvent listener.");
+ }
+ }
+
+ private GeyserBrigadierSupport() {
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java
new file mode 100644
index 000000000..00c1ba58d
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.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.platform.spigot.command;
+
+import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent;
+import com.mojang.brigadier.tree.CommandNode;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.geysermc.geyser.GeyserImpl;
+import org.geysermc.geyser.command.GeyserCommand;
+
+import java.net.InetSocketAddress;
+import java.util.Iterator;
+import java.util.Map;
+
+public final class GeyserPaperCommandListener implements Listener {
+
+ @EventHandler
+ @SuppressWarnings("deprecation") // Used to indicate an unstable event
+ public void onCommandSend(AsyncPlayerSendCommandsEvent> event) {
+ // Documentation says to check (event.isAsynchronous() || !event.hasFiredAsync()), but as of Paper 1.18.2
+ // event.hasFiredAsync is never true
+ if (event.isAsynchronous()) {
+ CommandNode> geyserBrigadier = event.getCommandNode().getChild("geyser");
+ if (geyserBrigadier != null) {
+ Player player = event.getPlayer();
+ boolean isJavaPlayer = isProbablyJavaPlayer(player);
+ Map commands = GeyserImpl.getInstance().getCommandManager().getCommands();
+ Iterator extends CommandNode>> it = geyserBrigadier.getChildren().iterator();
+
+ while (it.hasNext()) {
+ CommandNode> subnode = it.next();
+ GeyserCommand command = commands.get(subnode.getName());
+ if (command != null) {
+ if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.getPermission())) {
+ // Remove this from the node as we don't have permission to use it
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This early on, there is a rare chance that Geyser has yet to process the connection. We'll try to minimize that
+ * chance, though.
+ */
+ private boolean isProbablyJavaPlayer(Player player) {
+ if (GeyserImpl.getInstance().connectionByUuid(player.getUniqueId()) != null) {
+ // For sure this is a Bedrock player
+ return false;
+ }
+
+ if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) {
+ InetSocketAddress address = player.getAddress();
+ if (address != null) {
+ return address.getPort() != 0;
+ }
+ }
+ return true;
+ }
+}
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..6107d5b47 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
@@ -26,6 +26,7 @@
package org.geysermc.geyser.platform.spigot.command;
import org.bukkit.Bukkit;
+import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.geysermc.geyser.GeyserImpl;
@@ -35,16 +36,24 @@ import java.lang.reflect.Field;
public class GeyserSpigotCommandManager extends CommandManager {
- private static CommandMap COMMAND_MAP;
+ private static final CommandMap COMMAND_MAP;
static {
+ CommandMap commandMap = null;
try {
- Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
- cmdMapField.setAccessible(true);
- COMMAND_MAP = (CommandMap) cmdMapField.get(Bukkit.getServer());
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- ex.printStackTrace();
+ // Paper-only
+ Server.class.getMethod("getCommandMap");
+ commandMap = Bukkit.getServer().getCommandMap();
+ } catch (NoSuchMethodException e) {
+ try {
+ Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
+ cmdMapField.setAccessible(true);
+ commandMap = (CommandMap) cmdMapField.get(Bukkit.getServer());
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ ex.printStackTrace();
+ }
}
+ COMMAND_MAP = commandMap;
}
public GeyserSpigotCommandManager(GeyserImpl geyser) {
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java
deleted file mode 100644
index 78a64e47b..000000000
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java
+++ /dev/null
@@ -1,203 +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.platform.spigot.world;
-
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
-import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
-import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType;
-import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
-import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
-import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
-import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
-import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
-import com.viaversion.viaversion.api.Via;
-import com.viaversion.viaversion.api.data.MappingData;
-import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
-import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
-import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
-import com.viaversion.viaversion.util.Pair;
-import org.bukkit.Bukkit;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerJoinEvent;
-import org.bukkit.inventory.Recipe;
-import org.bukkit.inventory.ShapedRecipe;
-import org.bukkit.inventory.ShapelessRecipe;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.network.MinecraftProtocol;
-import org.geysermc.geyser.session.GeyserSession;
-import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
-import org.geysermc.geyser.util.InventoryUtils;
-
-import java.util.*;
-
-/**
- * Used to send all available recipes from the server to the client, as a valid recipe book packet won't be sent by the server.
- * Requires ViaVersion.
- */
-public class GeyserSpigot1_11CraftingListener implements Listener {
-
- private final GeyserImpl geyser;
- /**
- * Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 item into 1.13.
- */
- private final MappingData mappingData1_12to1_13;
- /**
- * The list of all protocols from the client's version to 1.13.
- */
- private final List protocolList;
-
- public GeyserSpigot1_11CraftingListener(GeyserImpl geyser) {
- this.geyser = geyser;
- this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData();
- this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(),
- ProtocolVersion.v1_13.getVersion());
- }
-
- @EventHandler
- public void onPlayerJoin(PlayerJoinEvent event) {
- GeyserSession session = null;
- for (GeyserSession otherSession : geyser.getSessionManager().getSessions().values()) {
- if (otherSession.name().equals(event.getPlayer().getName())) {
- session = otherSession;
- break;
- }
- }
- if (session == null) {
- return;
- }
-
- sendServerRecipes(session);
- }
-
- public void sendServerRecipes(GeyserSession session) {
- int netId = InventoryUtils.LAST_RECIPE_NET_ID;
-
- CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
- craftingDataPacket.setCleanRecipes(true);
-
- Iterator recipeIterator = Bukkit.getServer().recipeIterator();
- while (recipeIterator.hasNext()) {
- Recipe recipe = recipeIterator.next();
-
- Pair outputs = translateToBedrock(session, recipe.getResult());
- ItemStack javaOutput = outputs.getKey();
- ItemData output = outputs.getValue();
- if (output == null || output.getId() == 0) continue; // If items make air we don't want that
-
- boolean isNotAllAir = false; // Check for all-air recipes
- if (recipe instanceof ShapedRecipe shapedRecipe) {
- int size = shapedRecipe.getShape().length * shapedRecipe.getShape()[0].length();
- Ingredient[] ingredients = new Ingredient[size];
- ItemData[] input = new ItemData[size];
- for (int i = 0; i < input.length; i++) {
- // Index is converting char to integer, adding i then converting back to char based on ASCII code
- Pair result = translateToBedrock(session, shapedRecipe.getIngredientMap().get((char) ('a' + i)));
- ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
- input[i] = result.getValue();
- isNotAllAir |= input[i].getId() != 0;
- }
-
- if (!isNotAllAir) continue;
- UUID uuid = UUID.randomUUID();
- // Add recipe to our internal cache
- ShapedRecipeData data = new ShapedRecipeData(shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length,
- "", ingredients, javaOutput);
- session.getCraftingRecipes().put(netId,
- new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data));
-
- // Add recipe for Bedrock
- craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
- shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length, Arrays.asList(input),
- Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
- } else if (recipe instanceof ShapelessRecipe shapelessRecipe) {
- Ingredient[] ingredients = new Ingredient[shapelessRecipe.getIngredientList().size()];
- ItemData[] input = new ItemData[shapelessRecipe.getIngredientList().size()];
-
- for (int i = 0; i < input.length; i++) {
- Pair result = translateToBedrock(session, shapelessRecipe.getIngredientList().get(i));
- ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
- input[i] = result.getValue();
- isNotAllAir |= input[i].getId() != 0;
- }
-
- if (!isNotAllAir) continue;
- UUID uuid = UUID.randomUUID();
- // Add recipe to our internal cache
- ShapelessRecipeData data = new ShapelessRecipeData("", ingredients, javaOutput);
- session.getCraftingRecipes().put(netId,
- new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPELESS, uuid.toString(), data));
-
- // Add recipe for Bedrock
- craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
- Arrays.asList(input), Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
- }
- }
-
- session.sendUpstreamPacket(craftingDataPacket);
- }
-
- @SuppressWarnings("deprecation")
- private Pair translateToBedrock(GeyserSession session, org.bukkit.inventory.ItemStack itemStack) {
- if (itemStack != null && itemStack.getData() != null) {
- if (itemStack.getType().getId() == 0) {
- return new Pair<>(null, ItemData.AIR);
- }
-
- int legacyId = (itemStack.getType().getId() << 4) | (itemStack.getData().getData() & 0xFFFF);
-
- if (itemStack.getType().getId() == 355 && itemStack.getData().getData() == (byte) 0) { // Handle bed color since the server will always be pre-1.12
- legacyId = (itemStack.getType().getId() << 4) | ((byte) 14 & 0xFFFF);
- }
-
- // old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 and so on
- int itemId;
- if (mappingData1_12to1_13.getItemMappings().containsKey(legacyId)) {
- itemId = mappingData1_12to1_13.getNewItemId(legacyId);
- } else if (mappingData1_12to1_13.getItemMappings().containsKey((itemStack.getType().getId() << 4) | (0))) {
- itemId = mappingData1_12to1_13.getNewItemId((itemStack.getType().getId() << 4) | (0));
- } else {
- // No ID found, just send back air
- return new Pair<>(null, ItemData.AIR);
- }
-
- for (int i = protocolList.size() - 1; i >= 0; i--) {
- MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
- if (mappingData != null) {
- itemId = mappingData.getNewItemId(itemId);
- }
- }
-
- ItemStack mcItemStack = new ItemStack(itemId, itemStack.getAmount());
- ItemData finalData = ItemTranslator.translateToBedrock(session, mcItemStack);
- return new Pair<>(mcItemStack, finalData);
- }
-
- // Empty slot, most likely
- return new Pair<>(null, ItemData.AIR);
- }
-
-}
diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml
index 18773402e..aa2747979 100644
--- a/bootstrap/spigot/src/main/resources/plugin.yml
+++ b/bootstrap/spigot/src/main/resources/plugin.yml
@@ -9,34 +9,3 @@ commands:
geyser:
description: The main command for Geyser.
usage: /geyser
-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
diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml
index 0587e811f..8eba4d73d 100644
--- a/bootstrap/sponge/pom.xml
+++ b/bootstrap/sponge/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
bootstrap-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-sponge
@@ -14,7 +14,7 @@
org.geysermc
core
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -48,7 +48,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.3.0-SNAPSHOT
+ 3.3.0
package
diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml
index cb6b01b6a..8ee94b793 100644
--- a/bootstrap/standalone/pom.xml
+++ b/bootstrap/standalone/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
bootstrap-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-standalone
@@ -18,7 +18,7 @@
org.geysermc
core
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -47,17 +47,17 @@
org.jline
jline-terminal
- 3.20.0
+ 3.21.0
org.jline
jline-terminal-jna
- 3.20.0
+ 3.21.0
org.jline
jline-reader
- 3.20.0
+ 3.21.0
org.apache.logging.log4j
@@ -93,7 +93,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.3.0-SNAPSHOT
+ 3.3.0
com.github.edwgiz
@@ -132,7 +132,6 @@
implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer">
- ${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 43ab4b3fe..7c49585d5 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
@@ -275,6 +275,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return Paths.get(System.getProperty("user.dir"));
}
+ @Override
+ public Path getSavedUserLoginsFolder() {
+ // Return the location of the config
+ return new File(configFilename).getAbsoluteFile().getParentFile().toPath();
+ }
+
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserStandaloneDumpInfo(this);
diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml
index 87fa95637..36882a19e 100644
--- a/bootstrap/velocity/pom.xml
+++ b/bootstrap/velocity/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
bootstrap-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
bootstrap-velocity
@@ -14,7 +14,7 @@
org.geysermc
core
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -48,7 +48,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.3.0-SNAPSHOT
+ 3.3.0
package
diff --git a/common/pom.xml b/common/pom.xml
index 6c204aa9b..0786f3f4d 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -6,7 +6,7 @@
org.geysermc
geyser-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
common
@@ -20,12 +20,12 @@
org.geysermc.cumulus
cumulus
- 1.1-SNAPSHOT
+ 1.0-SNAPSHOT
com.google.code.gson
gson
- 2.8.6
+ 2.8.9
\ No newline at end of file
diff --git a/common/src/main/java/org/geysermc/floodgate/pluginmessage/PluginMessageChannels.java b/common/src/main/java/org/geysermc/floodgate/pluginmessage/PluginMessageChannels.java
new file mode 100644
index 000000000..37c5d4015
--- /dev/null
+++ b/common/src/main/java/org/geysermc/floodgate/pluginmessage/PluginMessageChannels.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.floodgate.pluginmessage;
+
+import com.google.common.base.Charsets;
+
+public final class PluginMessageChannels {
+ public static final String SKIN = "floodgate:skin";
+ public static final String FORM = "floodgate:form";
+ public static final String TRANSFER = "floodgate:transfer";
+
+ private static final byte[] FLOODGATE_REGISTER_DATA = String.join("\0", SKIN, FORM, TRANSFER).getBytes(Charsets.UTF_8);
+
+ /**
+ * Get the prebuilt register data as a byte array
+ *
+ * @return the register data of the Floodgate channels
+ */
+ public static byte[] getFloodgateRegisterData() {
+ return FLOODGATE_REGISTER_DATA;
+ }
+}
diff --git a/core/pom.xml b/core/pom.xml
index 121b58548..8af2aa907 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,14 +6,14 @@
org.geysermc
geyser-parent
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
core
4.9.3
8.5.2
- 2.12.4
+ 2.13.2
4.1.66.Final
@@ -21,19 +21,19 @@
org.geysermc
ap
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
provided
org.geysermc
geyser-api
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
org.geysermc
common
- 2.0.1-cumulus-SNAPSHOT
+ 2.0.3-SNAPSHOT
compile
@@ -52,7 +52,7 @@
com.fasterxml.jackson.core
jackson-databind
- ${jackson.version}
+ ${jackson.version}.1
compile
@@ -120,8 +120,8 @@
com.github.CloudburstMC.Protocol
- bedrock-v475
- c22aa595
+ bedrock-v503
+ 297567d
compile
@@ -147,23 +147,23 @@
- com.github.RednedEpic
+ com.github.GeyserMC
MCAuthLib
- 6c99331
+ d9d773e
compile
com.github.GeyserMC
MCProtocolLib
- 6a23a780
+ 0771504
compile
- com.github.steveice10
+ com.github.GeyserMC
packetlib
- com.github.steveice10
+ com.github.GeyserMC
mcauthlib
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 85bfd583d..890290a01 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
@@ -100,7 +100,7 @@ public class GeyserSession {
}
public void login() {
- this.handle.login();
+ throw new UnsupportedOperationException();
}
public void authenticate(String username) {
@@ -120,7 +120,7 @@ public class GeyserSession {
}
public void close() {
- this.handle.close();
+ throw new UnsupportedOperationException();
}
public void executeInEventLoop(Runnable runnable) {
diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java
index 49f8fa676..23fb76d16 100644
--- a/core/src/main/java/org/geysermc/geyser/Constants.java
+++ b/core/src/main/java/org/geysermc/geyser/Constants.java
@@ -37,6 +37,8 @@ public final class Constants {
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/";
+ static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
+
static {
URI wsUri = null;
try {
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
index 54ca36787..d40060310 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java
@@ -97,6 +97,13 @@ public interface GeyserBootstrap {
*/
Path getConfigFolder();
+ /**
+ * @return the folder where user tokens are saved. This should always point to the location of the config.
+ */
+ default Path getSavedUserLoginsFolder() {
+ return getConfigFolder();
+ }
+
/**
* Information used for the bootstrap section of the debug dump
*
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
index f63e222cc..f3ebfa4a3 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
@@ -26,6 +26,7 @@
package org.geysermc.geyser;
import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.packetlib.tcp.TcpSession;
@@ -37,6 +38,7 @@ import io.netty.channel.kqueue.KQueue;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.internal.SystemPropertyUtil;
+import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -59,9 +61,11 @@ import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
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.skin.FloodgateSkinUploader;
+import org.geysermc.geyser.skin.SkinProvider;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
@@ -70,6 +74,9 @@ import org.geysermc.geyser.util.*;
import javax.naming.directory.Attribute;
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;
@@ -77,6 +84,7 @@ import java.net.UnknownHostException;
import java.security.Key;
import java.text.DecimalFormat;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
@@ -124,6 +132,10 @@ public class GeyserImpl implements GeyserApi {
private Metrics metrics;
+ private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
+ @Getter(AccessLevel.NONE)
+ private Map savedRefreshTokens;
+
private static GeyserImpl instance;
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
@@ -195,6 +207,8 @@ public class GeyserImpl implements GeyserApi {
ScoreboardUpdater.init();
+ SkinProvider.registerCacheImageTask(this);
+
ResourcePack.loadPacks();
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
@@ -265,6 +279,8 @@ public class GeyserImpl implements GeyserApi {
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, buildNumber);
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
@@ -317,7 +333,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()));
+ metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().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));
@@ -401,6 +417,47 @@ public class GeyserImpl implements GeyserApi {
metrics = null;
}
+ if (config.getRemote().getAuthType() == 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: " +
+ "https://wiki.geysermc.org/geyser/understanding-the-config/");
+ }
+
+ // May be written/read to on multiple threads from each GeyserSession as well as writing the config
+ savedRefreshTokens = new ConcurrentHashMap<>();
+
+ File tokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile();
+ if (tokensFile.exists()) {
+ TypeReference