diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml
index f3b9cf88d..adaa7557f 100644
--- a/bootstrap/spigot/pom.xml
+++ b/bootstrap/spigot/pom.xml
@@ -29,6 +29,11 @@
3.2.0
provided
+
+ org.geysermc.adapters
+ spigot-all
+ 1.0-SNAPSHOT
+
${outputName}-Spigot
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 741763ec1..39d4f993b 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
@@ -25,8 +25,10 @@
package org.geysermc.platform.spigot;
+import com.github.steveice10.mc.protocol.MinecraftConstants;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
+import org.geysermc.adapters.spigot.SpigotAdapters;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
@@ -42,18 +44,23 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
import org.geysermc.platform.spigot.command.SpigotCommandSender;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
-import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
+import org.geysermc.platform.spigot.world.manager.*;
+import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
-
private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotLogger geyserLogger;
@@ -142,8 +149,48 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
// Set if we need to use a different method for getting a player's locale
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
- this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion);
- GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion);
+ if (connector.getConfig().isUseAdapters()) {
+ try {
+ String name = Bukkit.getServer().getClass().getPackage().getName();
+ String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
+ SpigotAdapters.registerWorldAdapter(nmsVersion);
+ if (isViaVersion && isViaVersionNeeded()) {
+ if (isLegacy) {
+ // Pre-1.13
+ this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
+ } else {
+ // Post-1.13
+ this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
+ }
+ } else {
+ // No ViaVersion
+ this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
+ }
+ geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
+ } catch (Exception e) {
+ if (geyserConfig.isDebugMode()) {
+ geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
+ e.printStackTrace();
+ }
+ }
+ } else {
+ geyserLogger.debug("Not using NMS adapter as it is disabled in the config.");
+ }
+ if (this.geyserWorldManager == null) {
+ // No NMS adapter
+ if (isLegacy && isViaVersion) {
+ // Use ViaVersion for converting pre-1.13 block states
+ this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
+ } else if (isLegacy) {
+ // Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
+ this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
+ } else {
+ // Post-1.13
+ this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
+ }
+ geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
+ }
+ GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
@@ -152,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public void onDisable() {
- if (connector != null)
+ if (connector != null) {
connector.shutdown();
+ }
}
@Override
@@ -186,6 +234,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
return getDataFolder().toPath();
}
+ @Override
+ public BootstrapDumpInfo getDumpInfo() {
+ return new GeyserSpigotDumpInfo();
+ }
+
public boolean isCompatible(String version, String whichVersion) {
int[] currentVersion = parseVersion(version);
int[] otherVersion = parseVersion(whichVersion);
@@ -213,15 +266,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
String t = stringArray[index].replaceAll("\\D", "");
try {
temp[index] = Integer.parseInt(t);
- } catch(NumberFormatException ex) {
+ } catch (NumberFormatException ex) {
temp[index] = 0;
}
}
return temp;
}
- @Override
- public BootstrapDumpInfo getDumpInfo() {
- return new GeyserSpigotDumpInfo();
+ /**
+ * @return the server version before ViaVersion finishes initializing
+ */
+ public ProtocolVersion getServerProtocolVersion() {
+ String bukkitVersion = Bukkit.getServer().getVersion();
+ // Turn "(MC: 1.16.4)" into 1.16.4.
+ String version = bukkitVersion.split("\\(MC: ")[1].split("\\)")[0];
+ return ProtocolVersion.getClosest(version);
+ }
+
+ /**
+ * This function should not run unless ViaVersion is installed on the server.
+ *
+ * @return true if there is any block mappings difference between the server and client.
+ */
+ private boolean isViaVersionNeeded() {
+ ProtocolVersion serverVersion = getServerProtocolVersion();
+ List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
+ serverVersion.getVersion());
+ if (protocolList == null) {
+ // No translation needed!
+ return false;
+ }
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ return true;
+ }
+ }
+ // All mapping data is null, which means client and server block states are the same
+ return false;
}
}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
index cb59e202b..55a368be4 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
@@ -36,13 +36,13 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager;
@AllArgsConstructor
public class GeyserSpigotBlockPlaceListener implements Listener {
private final GeyserConnector connector;
- private final boolean isLegacy;
- private final boolean isViaVersion;
+ private final GeyserSpigotWorldManager worldManager;
@EventHandler
public void place(final BlockPlaceEvent event) {
@@ -52,14 +52,13 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
placeBlockSoundPacket.setBabySound(false);
- String javaBlockId;
- if (isLegacy) {
- javaBlockId = BlockTranslator.getJavaIdBlockMap().inverse().get(GeyserSpigotWorldManager.getLegacyBlock(session,
- event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
+ if (worldManager.isLegacy()) {
+ placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
+ event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
} else {
- javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
+ String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
+ placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
}
- placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, 0)));
placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null);
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java
new file mode 100644
index 000000000..f58b75cdd
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java
@@ -0,0 +1,59 @@
+/*
+ * 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.platform.spigot.world.manager;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.geysermc.adapters.spigot.SpigotAdapters;
+import org.geysermc.adapters.spigot.SpigotWorldAdapter;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
+
+/**
+ * Used with ViaVersion and pre-1.13.
+ */
+public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
+ private final SpigotWorldAdapter adapter;
+
+ public GeyserSpigot1_12NativeWorldManager() {
+ this.adapter = SpigotAdapters.getWorldAdapter();
+ // Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
+ return getLegacyBlock(storage, blockId, x, y, z);
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java
new file mode 100644
index 000000000..b00ddafaa
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java
@@ -0,0 +1,141 @@
+/*
+ * 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.platform.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import us.myles.ViaVersion.api.Pair;
+import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.minecraft.Position;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
+
+import java.util.List;
+
+/**
+ * Should be used when ViaVersion is present, no NMS adapter is being used, and we are pre-1.13.
+ *
+ * You need ViaVersion to connect to an older server with the Geyser-Spigot plugin.
+ */
+public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
+ /**
+ * Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 block into the 1.13 block state.
+ * (Block IDs did not change between server versions until 1.13 and after)
+ */
+ 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_12WorldManager() {
+ super(false);
+ this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
+ this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
+ ProtocolVersion.v1_13.getVersion());
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ Block block = player.getWorld().getBlockAt(x, y, z);
+ // Black magic that gets the old block state ID
+ int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
+ return getLegacyBlock(storage, blockId, x, y, z);
+ }
+
+ /**
+ *
+ * @param storage ViaVersion's block entity storage (used to fix block entity state differences)
+ * @param blockId the pre-1.13 block id
+ * @param x X coordinate of block
+ * @param y Y coordinate of block
+ * @param z Z coordinate of block
+ * @return the block state updated to the latest Minecraft version
+ */
+ @SuppressWarnings("deprecation")
+ public int getLegacyBlock(BlockStorage storage, int blockId, int x, int y, int z) {
+ // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
+ blockId = mappingData1_12to1_13.getNewBlockId(blockId);
+ // Translate block entity differences - some information was stored in block tags and not block states
+ if (storage.isWelcome(blockId)) { // No getOrDefault method
+ BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
+ if (data != null && data.getReplacement() != -1) {
+ blockId = data.getReplacement();
+ }
+ }
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ blockId = mappingData.getNewBlockStateId(blockId);
+ }
+ }
+ return blockId;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return;
+ }
+ World world = player.getWorld();
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
+ for (int blockZ = 0; blockZ < 16; blockZ++) {
+ for (int blockX = 0; blockX < 16; blockX++) {
+ Block block = world.getBlockAt(x, y, z);
+ // Black magic that gets the old block state ID
+ int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
+ chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java
new file mode 100644
index 000000000..49c675a1d
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java
@@ -0,0 +1,62 @@
+/*
+ * 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.platform.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+/**
+ * Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)}
+ * cannot be accurate. Typically, this is when ViaVersion is not installed but a client still manages to connect.
+ * If this occurs to you somehow, please let us know!!
+ */
+public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
+ public GeyserSpigotFallbackWorldManager() {
+ // Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
+ super(false);
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+
+ @Override
+ public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
+ // Do nothing, since we can't do anything with the chunk
+ }
+
+ @Override
+ public boolean hasMoreBlockDataThanChunkCache() {
+ return false;
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java
new file mode 100644
index 000000000..dec9b4141
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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.platform.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.MinecraftConstants;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntList;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.platform.spigot.GeyserSpigotPlugin;
+import us.myles.ViaVersion.api.Pair;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
+
+import java.util.List;
+
+/**
+ * Used when block IDs need to be translated to the latest version
+ */
+public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorldManager {
+
+ private final Int2IntMap oldToNewBlockId;
+
+ public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
+ super(use3dBiomes);
+ IntList allBlockStates = adapter.getAllBlockStates();
+ oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
+ ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
+ List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
+ serverVersion.getVersion());
+ for (int oldBlockId : allBlockStates) {
+ int newBlockId = oldBlockId;
+ // protocolList should *not* be null; we checked for that before initializing this class
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ newBlockId = mappingData.getNewBlockStateId(newBlockId);
+ }
+ }
+ oldToNewBlockId.put(oldBlockId, newBlockId);
+ }
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ int nativeBlockId = super.getBlockAt(session, x, y, z);
+ return oldToNewBlockId.getOrDefault(nativeBlockId, nativeBlockId);
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java
new file mode 100644
index 000000000..f703ecdb5
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java
@@ -0,0 +1,51 @@
+/*
+ * 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.platform.spigot.world.manager;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.geysermc.adapters.spigot.SpigotAdapters;
+import org.geysermc.adapters.spigot.SpigotWorldAdapter;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
+ protected final SpigotWorldAdapter adapter;
+
+ public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
+ super(use3dBiomes);
+ adapter = SpigotAdapters.getWorldAdapter();
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ return adapter.getBlockAt(player.getWorld(), x, y, z);
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
similarity index 59%
rename from bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
rename to bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
index 3493fc25a..cd1774baf 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.platform.spigot.world;
+package org.geysermc.platform.spigot.world.manager;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.MinecraftConstants;
@@ -34,6 +34,7 @@ import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
@@ -42,38 +43,22 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.GameRule;
import org.geysermc.connector.utils.LanguageUtils;
-import us.myles.ViaVersion.api.Pair;
-import us.myles.ViaVersion.api.Via;
-import us.myles.ViaVersion.api.data.MappingData;
-import us.myles.ViaVersion.api.minecraft.Position;
-import us.myles.ViaVersion.api.protocol.Protocol;
-import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
-import us.myles.ViaVersion.api.protocol.ProtocolVersion;
-import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
-import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import java.io.InputStream;
-import java.util.List;
+/**
+ * The base world manager to use when there is no supported NMS revision
+ */
public class GeyserSpigotWorldManager extends GeyserWorldManager {
/**
* The current client protocol version for ViaVersion usage.
*/
- private static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
+ protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
- /**
- * Whether the server is pre-1.13.
- */
- private final boolean isLegacy;
/**
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
*/
private final boolean use3dBiomes;
- /**
- * You need ViaVersion to connect to an older server with Geyser.
- * However, we still check for ViaVersion in case there's some other way that gets Geyser on a pre-1.13 Bukkit server
- */
- private final boolean isViaVersion;
/**
* Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs.
*
@@ -87,10 +72,8 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
*/
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
- public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) {
- this.isLegacy = isLegacy;
+ public GeyserSpigotWorldManager(boolean use3dBiomes) {
this.use3dBiomes = use3dBiomes;
- this.isViaVersion = isViaVersion;
// Load the values into the biome-to-ID map
InputStream biomeStream = FileUtils.getResource("biomes.json");
@@ -116,83 +99,26 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player bukkitPlayer;
- if ((this.isLegacy && !this.isViaVersion)
- || session.getPlayerEntity() == null
- || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return BlockTranslator.JAVA_AIR_ID;
}
World world = bukkitPlayer.getWorld();
- if (isLegacy) {
- return getLegacyBlock(session, x, y, z, true);
- }
- //TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below
- return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), 0);
- }
-
- public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
- if (isViaVersion) {
- Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
- // Get block entity storage
- BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
- return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z);
- } else {
- return BlockTranslator.JAVA_AIR_ID;
- }
- }
-
- @SuppressWarnings("deprecation")
- public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) {
- Block block = world.getBlockAt(x, y, z);
- // Black magic that gets the old block state ID
- int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
- // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
- blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId);
- List> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
- ProtocolVersion.v1_13.getId());
- // Translate block entity differences - some information was stored in block tags and not block states
- if (storage.isWelcome(blockId)) { // No getOrDefault method
- BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
- if (data != null && data.getReplacement() != -1) {
- blockId = data.getReplacement();
- }
- }
- for (int i = protocolList.size() - 1; i >= 0; i--) {
- MappingData mappingData = protocolList.get(i).getValue().getMappingData();
- if (mappingData != null) {
- blockId = mappingData.getNewBlockStateId(blockId);
- }
- }
- return blockId;
+ return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player bukkitPlayer;
- if ((this.isLegacy && !this.isViaVersion)
- || session.getPlayerEntity() == null
- || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
World world = bukkitPlayer.getWorld();
- if (this.isLegacy) {
- // Get block entity storage
- BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
- for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
- for (int blockZ = 0; blockZ < 16; blockZ++) {
- for (int blockX = 0; blockX < 16; blockX++) {
- chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
- }
- }
- }
- } else {
- //TODO: see above TODO in getBlockAt
- for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
- for (int blockZ = 0; blockZ < 16; blockZ++) {
- for (int blockX = 0; blockX < 16; blockX++) {
- Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
- int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
- chunk.set(blockX, blockY, blockZ, id);
- }
+ for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
+ for (int blockZ = 0; blockZ < 16; blockZ++) {
+ for (int blockX = 0; blockX < 16; blockX++) {
+ Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
+ int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
+ chunk.set(blockX, blockY, blockZ, id);
}
}
}
@@ -254,4 +180,16 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
public boolean hasPermission(GeyserSession session, String permission) {
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
}
+
+ /**
+ * This must be set to true if we are pre-1.13, and {@link BlockData#getAsString() does not exist}.
+ *
+ * This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
+ * to the current one.
+ *
+ * @return whether there is a difference between client block state and server block state that requires extra processing
+ */
+ public boolean isLegacy() {
+ return false;
+ }
}
diff --git a/connector/pom.xml b/connector/pom.xml
index 95794ff25..d7017b0d8 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -257,6 +257,8 @@