diff --git a/README.md b/README.md
index ba3e6fc2d..e0404a934 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![forthebadge made-with-java](http://ForTheBadge.com/images/badges/made-with-java.svg)](https://java.com/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
-[![Build Status](https://ci.nukkitx.com/job/Geyser/job/master/badge/icon)](https://ci.nukkitx.com/job/Geyser/job/master/)
+[![Build Status](https://ci.nukkitx.com/job/Geyser/job/master/badge/icon)](https://ci.nukkitx.com/job/GeyserMC/job/Geyser/job/master/)
[![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](http://discord.geysermc.org/)
[![HitCount](http://hits.dwyl.io/Geyser/GeyserMC.svg)](http://hits.dwyl.io/Geyser/GeyserMC)
[![Crowdin](https://badges.crowdin.net/geyser/localized.svg)](https://translate.geysermc.org/)
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
index 9d8e05ac7..e06640896 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
@@ -97,7 +97,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;
- } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) {
+ } 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("floodgate");
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
index a8ae50d69..12794d1c8 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
@@ -101,7 +101,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
this.getPluginLoader().disablePlugin(this);
return;
- } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) {
+ } 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("floodgate");
diff --git a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java
index ec5dd349a..7bf0ac67f 100644
--- a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java
+++ b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java
@@ -37,7 +37,7 @@ public class FloodgateKeyLoader {
if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) {
if (floodgate != null) {
- Path autoKey = floodgateDataFolder.resolve("public-key.pem");
+ Path autoKey = floodgateDataFolder.resolve("key.pem");
if (Files.exists(autoKey)) {
logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded"));
floodgateKey = autoKey;
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index ec8c7f0ee..abb50a560 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -152,8 +152,13 @@ public class GeyserSession implements CommandSender {
@Setter
private boolean interacting;
+ /**
+ * Stores the last position of the block the player interacted with. This can either be a block that the client
+ * placed or an existing block the player interacted with (for example, a chest).
+ * Initialized as (0, 0, 0) so it is always not-null.
+ */
@Setter
- private Vector3i lastInteractionPosition;
+ private Vector3i lastInteractionPosition = Vector3i.ZERO;
private boolean manyDimPackets = false;
private ServerRespawnPacket lastDimPacket = null;
@@ -189,6 +194,13 @@ public class GeyserSession implements CommandSender {
@Setter
private long lastHitTime;
+ /**
+ * Store the last time the player interacted. Used to fix a right-click spam bug.
+ * See https://github.com/GeyserMC/Geyser/issues/503 for context.
+ */
+ @Setter
+ private long lastInteractionTime;
+
private boolean reducedDebugInfo = false;
/**
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
index 95e669572..8e891d925 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -39,8 +39,10 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
@@ -78,6 +80,17 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator {
RespawnPacket respawnPacket = new RespawnPacket();
respawnPacket.setRuntimeEntityId(0);
respawnPacket.setPosition(Vector3f.ZERO);
- respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING);
+ respawnPacket.setState(RespawnPacket.State.SERVER_READY);
session.sendUpstreamPacket(respawnPacket);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java
new file mode 100644
index 000000000..d61b37863
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.bedrock.entity.player;
+
+import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+/**
+ * In vanilla Bedrock, if you have operator status, this sets the player's gamemode without confirmation from the server.
+ * Since we have a custom server option to request the gamemode, we just reset the gamemode and ignore this.
+ */
+@Translator(packet = SetPlayerGameTypePacket.class)
+public class BedrockSetPlayerGameTypeTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(SetPlayerGameTypePacket packet, GeyserSession session) {
+ // no
+ SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
+ playerGameTypePacket.setGamemode(session.getGameMode().ordinal());
+ session.sendUpstreamPacket(playerGameTypePacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
index f5ebe62df..b260565b8 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
@@ -30,12 +30,9 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
-import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
-import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
-import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.List;
@@ -48,7 +45,7 @@ public class CraftingInventoryTranslator extends BlockInventoryTranslator {
@Override
public int bedrockSlotToJava(InventoryActionData action) {
if (action.getSlot() == 50) {
- GeyserConnector.getInstance().getLogger().warning("Slot 50 found, please report: " + action);
+ // Slot 50 is used for crafting with a controller.
return 0;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
index a8fc122bd..c479b23fe 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java;
import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference;
import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility;
import com.github.steveice10.mc.protocol.data.game.setting.SkinPart;
+import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientSettingsPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
@@ -38,6 +39,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.DimensionUtils;
+import org.geysermc.connector.utils.PluginMessageUtils;
import java.util.Arrays;
import java.util.List;
@@ -92,6 +94,8 @@ public class JavaJoinGameTranslator extends PacketTranslator {
- private static final byte[] brandData;
-
- static {
- byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8);
- byte[] varInt = writeVarInt(data.length);
- brandData = new byte[varInt.length + data.length];
- System.arraycopy(varInt, 0, brandData, 0, varInt.length);
- System.arraycopy(data, 0, brandData, varInt.length, data.length);
- }
-
- private static byte[] writeVarInt(int value) {
- byte[] data = new byte[getVarIntLength(value)];
- int index = 0;
- do {
- byte temp = (byte) (value & 0b01111111);
- value >>>= 7;
- if (value != 0) {
- temp |= 0b10000000;
- }
- data[index] = temp;
- index++;
- } while (value != 0);
- return data;
- }
-
- private static int getVarIntLength(int number) {
- if ((number & 0xFFFFFF80) == 0) {
- return 1;
- } else if ((number & 0xFFFFC000) == 0) {
- return 2;
- } else if ((number & 0xFFE00000) == 0) {
- return 3;
- } else if ((number & 0xF0000000) == 0) {
- return 4;
- }
- return 5;
- }
-
- @Override
- public void translate(ServerPluginMessagePacket packet, GeyserSession session) {
- String channel = packet.getChannel();
-
- if (channel.equals("minecraft:brand")) {
- session.sendDownstreamPacket(
- new ClientPluginMessagePacket(channel, brandData)
- );
- }
-
- // Floodgate plugin messages
- if (session.getConnector().getAuthType() != AuthType.FLOODGATE) {
- return;
- }
-
- if (channel.equals("floodgate:form")) {
- byte[] data = packet.getData();
-
- // receive: first byte is form type, second and third are the id, remaining is the form data
- // respond: first and second byte id, remaining is form response data
-
- Form.Type type = Form.Type.getByOrdinal(data[0]);
- if (type == null) {
- throw new NullPointerException(
- "Got type " + data[0] + " which isn't a valid form type!");
- }
-
- String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8);
-
- Form form = new GsonBuilder().registerTypeAdapter(ModalForm.class, new FormAdaptor()).create().fromJson(dataString, type.getTypeClass());
- form.setResponseHandler(response -> {
- byte[] raw = response.getBytes(StandardCharsets.UTF_8);
- byte[] finalData = new byte[raw.length + 2];
-
- finalData[0] = data[1];
- finalData[1] = data[2];
- System.arraycopy(raw, 0, finalData, 2, raw.length);
-
- session.sendDownstreamPacket(new ClientPluginMessagePacket(channel, finalData));
- });
- session.sendForm(form);
- }
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java
similarity index 61%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java
index a90c7016b..270c33a7a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java
@@ -23,26 +23,32 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.java.world;
+package org.geysermc.connector.network.translators.java.entity;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityCollectItemPacket;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.TakeItemEntityPacket;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.ExpOrbEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
+/**
+ * This packet is called whenever a player picks up an item.
+ * In Java, this is called for item entities, experience orbs and arrows
+ * Bedrock uses it for arrows and item entities, but not experience orbs.
+ */
@Translator(packet = ServerEntityCollectItemPacket.class)
-public class JavaCollectItemTranslator extends PacketTranslator {
+public class JavaEntityCollectItemTranslator extends PacketTranslator {
@Override
public void translate(ServerEntityCollectItemPacket packet, GeyserSession session) {
- // This is the definition of translating - both packets take the same values
- TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket();
- // Collected entity is the item
+ // Collected entity is the other entity
Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId());
if (collectedEntity == null) return;
- // Collector is the entity picking up the item
+ // Collector is the entity 'picking up' the item
Entity collectorEntity;
if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) {
collectorEntity = session.getPlayerEntity();
@@ -50,8 +56,19 @@ public class JavaCollectItemTranslator extends PacketTranslator>>= 7;
+ if (value != 0) {
+ temp |= 0b10000000;
+ }
+ data[index] = temp;
+ index++;
+ } while (value != 0);
+ return data;
+ }
+
+ private static int getVarIntLength(int number) {
+ if ((number & 0xFFFFFF80) == 0) {
+ return 1;
+ } else if ((number & 0xFFFFC000) == 0) {
+ return 2;
+ } else if ((number & 0xFFE00000) == 0) {
+ return 3;
+ } else if ((number & 0xF0000000) == 0) {
+ return 4;
+ }
+ return 5;
+ }
+}
diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml
index 0602bb546..b9e6700fc 100644
--- a/connector/src/main/resources/config.yml
+++ b/connector/src/main/resources/config.yml
@@ -34,9 +34,9 @@ remote:
auth-type: online
# Floodgate uses encryption to ensure use from authorised sources.
-# This should point to the public key generated by Floodgate (Bungee or CraftBukkit)
+# This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity)
# You can ignore this when not using Floodgate.
-floodgate-key-file: public-key.pem
+floodgate-key-file: key.pem
## the Xbox/MCPE username is the key for the Java server auth-info
## this allows automatic configuration/login to the remote Java server