From 99880c7d46d99d2cb7e047c7e41b55add7cab334 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Thu, 30 Apr 2020 07:40:45 +0100 Subject: [PATCH 1/4] Adds a message if someone runs one of the plugins directly (#457) * Added Bukkit run message * Added the rest of the plugins run messages * Renamed plugin run classes to main * Added gui message if the jar isnt run from command line * Updated help message * Changed wording for bukkit --- bootstrap/bukkit/pom.xml | 20 +++-- .../platform/bukkit/GeyserBukkitMain.java | 44 +++++++++++ bootstrap/bungeecord/pom.xml | 20 +++-- .../platform/bungeecord/GeyserBungeeMain.java | 44 +++++++++++ bootstrap/sponge/pom.xml | 20 +++-- .../platform/sponge/GeyserSpongeMain.java | 44 +++++++++++ bootstrap/velocity/pom.xml | 20 +++-- .../platform/velocity/GeyserVelocityMain.java | 44 +++++++++++ .../org/geysermc/common/main/IGeyserMain.java | 74 +++++++++++++++++++ common/src/main/resources/help.txt | 18 +++++ 10 files changed, 316 insertions(+), 32 deletions(-) create mode 100644 bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitMain.java create mode 100644 bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeMain.java create mode 100644 bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeMain.java create mode 100644 bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityMain.java create mode 100644 common/src/main/java/org/geysermc/common/main/IGeyserMain.java create mode 100644 common/src/main/resources/help.txt diff --git a/bootstrap/bukkit/pom.xml b/bootstrap/bukkit/pom.xml index fd2ecbf0..94fab83b 100644 --- a/bootstrap/bukkit/pom.xml +++ b/bootstrap/bukkit/pom.xml @@ -33,6 +33,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + org.geysermc.platform.bukkit.GeyserBukkitMain + + + + org.apache.maven.plugins maven-shade-plugin @@ -58,14 +70,6 @@ - - - *:* - - META-INF/* - - - com.google.code.gson:* diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitMain.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitMain.java new file mode 100644 index 00000000..b6da66c1 --- /dev/null +++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitMain.java @@ -0,0 +1,44 @@ +/* + * 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.bukkit; + +import org.geysermc.common.main.IGeyserMain; + +public class GeyserBukkitMain extends IGeyserMain { + + public static void main(String[] args) { + new GeyserBukkitMain().displayMessage(); + } + + public String getPluginType() { + return "Spigot or Paper (recommended)"; + } + + public String getPluginFolder() { + return "plugins"; + } +} diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 0f6de3fa..b9f06916 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -33,6 +33,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + org.geysermc.platform.bungeecord.GeyserBungeeMain + + + + org.apache.maven.plugins maven-shade-plugin @@ -58,14 +70,6 @@ - - - *:* - - META-INF/* - - - com.google.code.gson:* diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeMain.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeMain.java new file mode 100644 index 00000000..eabbcc69 --- /dev/null +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeMain.java @@ -0,0 +1,44 @@ +/* + * 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.bungeecord; + +import org.geysermc.common.main.IGeyserMain; + +public class GeyserBungeeMain extends IGeyserMain { + + public static void main(String[] args) { + new GeyserBungeeMain().displayMessage(); + } + + public String getPluginType() { + return "BungeeCord"; + } + + public String getPluginFolder() { + return "plugins"; + } +} diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index 696721d2..c9abbe3e 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -33,6 +33,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + org.geysermc.platform.sponge.GeyserSpongeMain + + + + org.apache.maven.plugins maven-shade-plugin @@ -62,14 +74,6 @@ - - - *:* - - META-INF/* - - - com.google.code.gson:* diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeMain.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeMain.java new file mode 100644 index 00000000..11b9583f --- /dev/null +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeMain.java @@ -0,0 +1,44 @@ +/* + * 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.sponge; + +import org.geysermc.common.main.IGeyserMain; + +public class GeyserSpongeMain extends IGeyserMain { + + public static void main(String[] args) { + new GeyserSpongeMain().displayMessage(); + } + + public String getPluginType() { + return "Sponge"; + } + + public String getPluginFolder() { + return "mods"; + } +} diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index 075aedc3..fb06767e 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -33,6 +33,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + org.geysermc.platform.velocity.GeyserVelocityMain + + + + org.apache.maven.plugins maven-shade-plugin @@ -54,14 +66,6 @@ - - - *:* - - META-INF/* - - - com.google.code.gson:* diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityMain.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityMain.java new file mode 100644 index 00000000..73eaddf0 --- /dev/null +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityMain.java @@ -0,0 +1,44 @@ +/* + * 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.velocity; + +import org.geysermc.common.main.IGeyserMain; + +public class GeyserVelocityMain extends IGeyserMain { + + public static void main(String[] args) { + new GeyserVelocityMain().displayMessage(); + } + + public String getPluginType() { + return "Velocity"; + } + + public String getPluginFolder() { + return "plugins"; + } +} diff --git a/common/src/main/java/org/geysermc/common/main/IGeyserMain.java b/common/src/main/java/org/geysermc/common/main/IGeyserMain.java new file mode 100644 index 00000000..75da4e6b --- /dev/null +++ b/common/src/main/java/org/geysermc/common/main/IGeyserMain.java @@ -0,0 +1,74 @@ +/* + * 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.common.main; + +import javax.swing.*; +import java.io.InputStream; +import java.util.Scanner; + +public class IGeyserMain { + + public void displayMessage() { + String message = createMessage(); + + if (System.console() == null) { + JOptionPane.showMessageDialog(null, message, "GeyserMC Plugin: " + this.getPluginType(), JOptionPane.ERROR_MESSAGE); + } + + printMessage(message); + } + + private String createMessage() { + String message = ""; + + InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("help.txt"); + Scanner help = new Scanner(helpStream).useDelimiter("\\Z"); + String line = ""; + while (help.hasNext()) { + line = help.next(); + + line = line.replace("${plugin_type}", this.getPluginType()); + line = line.replace("${plugin_folder}", this.getPluginFolder()); + + message += line + "\n"; + } + + return message; + } + + private void printMessage(String message) { + System.out.print(message); + } + + public String getPluginType() { + return "unknown"; + } + + public String getPluginFolder() { + return "unknown"; + } +} diff --git a/common/src/main/resources/help.txt b/common/src/main/resources/help.txt new file mode 100644 index 00000000..3512ed83 --- /dev/null +++ b/common/src/main/resources/help.txt @@ -0,0 +1,18 @@ + +-------------------------------------------------------------------------------- + + Oops! You attempted to run a plugin version of Geyser directly! + + This jar file is a plugin for ${plugin_type}. You can run this file as a + plugin by dropping the jar file into the "${plugin_folder}" directory. + + There is also a standalone version available that doesn't need to + be installed as a plugin, you can find it on our build server: + + http://ci.geysermc.org/ + + If you need more help, you should check out our discord: + + http://discord.geysermc.org/ + +-------------------------------------------------------------------------------- \ No newline at end of file From 2e3f32d76913eeb30f2d4fcedf5adf84ae529bed Mon Sep 17 00:00:00 2001 From: rtm516 Date: Fri, 1 May 2020 06:49:29 +0100 Subject: [PATCH 2/4] Only replace the first '.' in a message (#463) --- .../translators/bedrock/BedrockTextTranslator.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java index 4dfe4c76..8a912142 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java @@ -38,19 +38,7 @@ public class BedrockTextTranslator extends PacketTranslator { @Override public void translate(TextPacket packet, GeyserSession session) { - if (packet.getMessage().charAt(0) == '.') { - String message = packet.getMessage().replace(".", "/").trim(); - - if (MessageUtils.isTooLong(message, session)) { - return; - } - - ClientChatPacket chatPacket = new ClientChatPacket(message); - session.getDownstream().getSession().send(chatPacket); - return; - } - - String message = packet.getMessage().trim(); + String message = packet.getMessage().replaceAll("^\\.", "/").trim(); if (MessageUtils.isTooLong(message, session)) { return; From b07161b0a911d6ebb91dd9d5db3522ab8048f527 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 1 May 2020 01:51:23 -0400 Subject: [PATCH 3/4] Add block values + note block note graphics (#455) * Add note block visual without note pitch * Add rest of block value code * Add rest of block value code * Fix pistons, somewhat * Remove note block attempt * Re-add whitespace * Simplify sendPacket of BlockEventPacket * Add note block visual without note pitch * Add rest of block value code * Fix pistons, somewhat * Remove note block attempt * Re-add whitespace * Add mappings for noteblock pitch * Change noteblock pitch code * Noteblock Pitch Attempt * Commit with PistonBlockEntityTranslator * Cleanup for PR * Improve pistons Co-authored-by: blazewalker462 --- .../network/translators/Translators.java | 16 +++ .../translators/block/BlockStateValues.java | 28 +++++- .../NoteblockBlockEntityTranslator.java | 58 +++++++++++ .../java/world/JavaBlockValueTranslator.java | 99 ++++++++++++++++++- .../java/world/JavaChunkDataTranslator.java | 2 - .../geysermc/connector/utils/ChunkUtils.java | 15 +-- 6 files changed, 200 insertions(+), 18 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java index f0a3fd28..f7c7c28a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java @@ -32,6 +32,7 @@ import java.util.Map; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.protocol.bedrock.data.ContainerType; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.network.translators.block.entity.*; @@ -61,6 +62,9 @@ public class Translators { @Getter private static Map blockEntityTranslators = new HashMap<>(); + @Getter + private static ObjectArrayList requiresBlockStateMap = new ObjectArrayList<>(); + private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag(); public static final byte[] EMPTY_LEVEL_CHUNK_DATA; @@ -129,6 +133,18 @@ public class Translators { GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity " + clazz.getCanonicalName() + "."); } } + + for (Class clazz : ref.getSubTypesOf(RequiresBlockState.class)) { + + GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName()); + + try { + requiresBlockStateMap.add((RequiresBlockState) clazz.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + clazz.getCanonicalName() + "."); + } + + } } private static void registerInventoryTranslators() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java index 76ee5238..87966fd1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java @@ -41,6 +41,7 @@ public class BlockStateValues { private static final Object2IntMap BANNER_COLORS = new Object2IntOpenHashMap<>(); private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>(); + private static final Object2IntMap NOTEBLOCK_PITCHES = new Object2IntOpenHashMap<>(); private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>(); private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>(); private static final Object2ByteMap SHULKERBOX_DIRECTIONS = new Object2ByteOpenHashMap<>(); @@ -53,24 +54,30 @@ public class BlockStateValues { public static void storeBlockStateValues(Map.Entry entry, BlockState javaBlockState) { JsonNode bannerColor = entry.getValue().get("banner_color"); if (bannerColor != null) { - BlockStateValues.BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); + BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); return; // There will never be a banner color and a skull variant } JsonNode bedColor = entry.getValue().get("bed_color"); if (bedColor != null) { - BlockStateValues.BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); + BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); + return; + } + + JsonNode notePitch = entry.getValue().get("note_pitch"); + if (notePitch != null) { + NOTEBLOCK_PITCHES.put(javaBlockState, entry.getValue().get("note_pitch").intValue()); return; } JsonNode skullVariation = entry.getValue().get("variation"); if(skullVariation != null) { - BlockStateValues.SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); + SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); } JsonNode skullRotation = entry.getValue().get("skull_rotation"); if (skullRotation != null) { - BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); + SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); } JsonNode shulkerDirection = entry.getValue().get("shulker_direction"); @@ -107,6 +114,19 @@ public class BlockStateValues { return -1; } + /** + * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. + * This gives an integer pitch that Bedrock can use. + * @param state BlockState of the block + * @return note block note integer or -1 if not present + */ + public static int getNoteblockPitch(BlockState state) { + if (NOTEBLOCK_PITCHES.containsKey(state)) { + return NOTEBLOCK_PITCHES.getInt(state); + } + return -1; + } + /** * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. * This gives a byte variant ID that Bedrock can use. diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java new file mode 100644 index 00000000..ba9ac0de --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java @@ -0,0 +1,58 @@ +/* + * 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.block.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.block.BlockStateValues; +import org.geysermc.connector.utils.ChunkUtils; + +/** + * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock + */ +public class NoteblockBlockEntityTranslator implements RequiresBlockState { + + @Override + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getNoteblockPitch(blockState) != -1; + } + + public static void translate(GeyserSession session, Position position) { + BlockState blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.get(position); + BlockEventPacket blockEventPacket = new BlockEventPacket(); + blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); + blockEventPacket.setEventType(0); + blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); + session.getUpstream().sendPacket(blockEventPacket); + + ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position); + } + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java index f4a4d9ef..36ccf655 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java @@ -25,15 +25,21 @@ package org.geysermc.connector.network.translators.java.world; -import com.github.steveice10.mc.protocol.data.game.world.block.value.ChestValue; -import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValue; +import com.github.steveice10.mc.protocol.data.game.world.block.value.*; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockValuePacket; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; 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.network.translators.block.entity.NoteblockBlockEntityTranslator; + +import java.util.concurrent.TimeUnit; + @Translator(packet = ServerBlockValuePacket.class) public class JavaBlockValueTranslator extends PacketTranslator { @@ -53,5 +59,94 @@ public class JavaBlockValueTranslator extends PacketTranslator + extendPiston(session, position, (progress >= 1.0f) ? 1.0f : progress + 0.5f, progress), + 20, TimeUnit.MILLISECONDS); + } + } + + /** + * Emulate a piston retracting. + * @param session GeyserSession + * @param position Block position + * @param progress Current progress of piston + * @param lastProgress Last progress of piston + */ + private void retractPiston(GeyserSession session, Vector3i position, float progress, float lastProgress) { + BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket(); + blockEntityDataPacket.setBlockPosition(position); + byte state = (byte) ((progress == 0.0f && lastProgress == 0.0f) ? 0 : 3); + blockEntityDataPacket.setData(buildPistonTag(position, progress, lastProgress, state)); + session.getUpstream().sendPacket(blockEntityDataPacket); + if (lastProgress != 0.0f) { + session.getConnector().getGeneralThreadPool().schedule(() -> + retractPiston(session, position, (progress <= 0.0f) ? 0.0f : progress - 0.5f, progress), + 20, TimeUnit.MILLISECONDS); + } + } + + /** + * Build a piston tag + * @param position Piston position + * @param progress Current progress of piston + * @param lastProgress Last progress of piston + * @param state + * @return Bedrock CompoundTag of piston + */ + private CompoundTag buildPistonTag(Vector3i position, float progress, float lastProgress, byte state) { + CompoundTagBuilder builder = CompoundTag.EMPTY.toBuilder(); + builder.intTag("x", position.getX()) + .intTag("y", position.getY()) + .intTag("z", position.getZ()) + .floatTag("Progress", progress) + .floatTag("LastProgress", lastProgress) + .stringTag("id", "PistonArm") + .byteTag("NewState", state) + .byteTag("State", state); + return builder.buildRootTag(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index e72038c5..4117add7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -46,8 +46,6 @@ import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.world.chunk.ChunkSection; -import java.util.Map; - @Translator(packet = ServerChunkDataPacket.class) public class JavaChunkDataTranslator extends PacketTranslator { diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index d496215a..c1ddb60a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -32,9 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; -import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; -import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -172,13 +170,10 @@ public class ChunkUtils { // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag // This is the only place I could find that interacts with the Java block state and block updates // Iterates through all block entity translators and determines if the block state needs to be saved - for (Map.Entry entry : Translators.getBlockEntityTranslators().entrySet()) { - if (entry.getValue() instanceof RequiresBlockState) { - RequiresBlockState requiresBlockState = (RequiresBlockState) entry.getValue(); - if (requiresBlockState.isBlock(blockState)) { - CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); - break; //No block will be a part of two classes - } + for (RequiresBlockState requiresBlockState : Translators.getRequiresBlockStateMap()) { + if (requiresBlockState.isBlock(blockState)) { + CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + break; //No block will be a part of two classes } } } From 9846058377e12456e47c47c8cfb19e8a0910c57b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 2 May 2020 16:44:05 -0400 Subject: [PATCH 4/4] Add item frames (#415) * Initial attempt * Item frames 'work' * Blocks in the item frames work * Remove commented code * Small changes * More progress * Whittling down * Fix swords, etc * NBT data implemented * Remove unused import * Add item frame item removing; add checks for removing item frames * Add requested changes; clean up logic * Add license * Always delay item frame updates by 500 milliseconds * Switch to per-session item frame cache * Revert item translator refactoring --- .../connector/entity/ItemFrameEntity.java | 226 ++++++++++++++++++ .../connector/entity/type/EntityType.java | 7 +- .../network/session/GeyserSession.java | 8 + ...BedrockInventoryTransactionTranslator.java | 25 ++ .../BedrockItemFrameDropItemTranslator.java | 57 +++++ .../translators/block/BlockTranslator.java | 34 ++- .../network/translators/item/ItemEntry.java | 1 - .../spawn/JavaSpawnObjectTranslator.java | 8 +- .../geysermc/connector/utils/ChunkUtils.java | 17 ++ .../connector/utils/DimensionUtils.java | 1 + 10 files changed, 370 insertions(+), 14 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemFrameDropItemTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java new file mode 100644 index 00000000..08831a45 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -0,0 +1,226 @@ +/* + * 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.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.entity.type.object.HangingDirection; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.Translators; +import org.geysermc.connector.network.translators.block.BlockTranslator; +import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.utils.Toolbox; + +import java.util.concurrent.TimeUnit; + +/** + * Item frames are an entity in Java but a block entity in Bedrock. + */ +public class ItemFrameEntity extends Entity { + + /** + * Used for getting the Bedrock block position. + * Blocks deal with integers whereas entities deal with floats. + */ + private final Vector3i bedrockPosition; + /** + * Specific block 'state' we are emulating in Bedrock. + */ + private final int bedrockRuntimeId; + /** + * Rotation of item in frame. + */ + private float rotation = 0.0f; + /** + * Cached item frame's Bedrock compound tag. + */ + private CompoundTag cachedTag; + + public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) { + super(entityId, geyserId, entityType, position, motion, rotation); + CompoundTagBuilder builder = CompoundTag.builder(); + builder.tag(CompoundTag.builder() + .stringTag("name", "minecraft:frame") + .intTag("version", BlockTranslator.getBlockStateVersion()) + .tag(CompoundTag.builder() + .intTag("facing_direction", direction.ordinal()) + .byteTag("item_frame_map_bit", (byte) 0) + .build("states")) + .build("block")); + builder.shortTag("id", (short) 199); + bedrockRuntimeId = BlockTranslator.getItemFrame(builder.buildRootTag()); + bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); + } + + @Override + public void spawnEntity(GeyserSession session) { + session.getItemFrameCache().put(bedrockPosition, entityId); + updateBlock(session); + valid = true; + session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { + ItemData itemData = Translators.getItemTranslator().translateToBedrock(session, (ItemStack) entityMetadata.getValue()); + ItemEntry itemEntry = Translators.getItemTranslator().getItem((ItemStack) entityMetadata.getValue()); + CompoundTagBuilder builder = CompoundTag.builder(); + + String blockName = ""; + for (StartGamePacket.ItemEntry startGamePacketItemEntry: Toolbox.ITEMS) { + if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) { + blockName = startGamePacketItemEntry.getIdentifier(); + break; + } + } + + builder.byteTag("Count", (byte) itemData.getCount()); + if (itemData.getTag() != null) { + builder.tag(itemData.getTag().toBuilder().build("tag")); + } + builder.shortTag("Damage", itemData.getDamage()); + builder.stringTag("Name", blockName); + CompoundTagBuilder tag = getDefaultTag().toBuilder(); + tag.tag(builder.build("Item")); + tag.floatTag("ItemDropChance", 1.0f); + tag.floatTag("ItemRotation", rotation); + cachedTag = tag.buildRootTag(); + updateBlock(session); + } + else if (entityMetadata.getId() == 7 && entityMetadata.getValue() == null && cachedTag != null) { + cachedTag = getDefaultTag(); + updateBlock(session); + } + else if (entityMetadata.getId() == 8) { + rotation = ((int) entityMetadata.getValue()) * 45; + if (cachedTag == null) { + updateBlock(session); + return; + } + CompoundTagBuilder builder = cachedTag.toBuilder(); + builder.floatTag("ItemRotation", rotation); + cachedTag = builder.buildRootTag(); + updateBlock(session); + } + else { + updateBlock(session); + } + } + + @Override + public boolean despawnEntity(GeyserSession session) { + UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); + updateBlockPacket.setDataLayer(0); + updateBlockPacket.setBlockPosition(bedrockPosition); + updateBlockPacket.setRuntimeId(0); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); + session.getUpstream().sendPacket(updateBlockPacket); + session.getItemFrameCache().remove(position, entityId); + valid = false; + return true; + } + + private CompoundTag getDefaultTag() { + CompoundTagBuilder builder = CompoundTag.builder(); + builder.intTag("x", bedrockPosition.getX()); + builder.intTag("y", bedrockPosition.getY()); + builder.intTag("z", bedrockPosition.getZ()); + builder.byteTag("isMovable", (byte) 1); + builder.stringTag("id", "ItemFrame"); + return builder.buildRootTag(); + } + + /** + * Updates the item frame as a block + * @param session GeyserSession. + */ + public void updateBlock(GeyserSession session) { + // Delay is required, or else loading in frames on chunk load is sketchy at best + session.getConnector().getGeneralThreadPool().schedule(() -> { + UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); + updateBlockPacket.setDataLayer(0); + updateBlockPacket.setBlockPosition(bedrockPosition); + updateBlockPacket.setRuntimeId(bedrockRuntimeId); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); + session.getUpstream().sendPacket(updateBlockPacket); + + BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket(); + blockEntityDataPacket.setBlockPosition(bedrockPosition); + if (cachedTag != null) { + blockEntityDataPacket.setData(cachedTag); + } else { + blockEntityDataPacket.setData(getDefaultTag()); + } + + session.getUpstream().sendPacket(blockEntityDataPacket); + }, 500, TimeUnit.MILLISECONDS); + } + + /** + * Finds the Java entity ID of an item frame from its Bedrock position. + * @param position position of item frame in Bedrock. + * @param session GeyserSession. + * @return Java entity ID or -1 if not found. + */ + public static long getItemFrameEntityId(GeyserSession session, Vector3i position) { + return session.getItemFrameCache().getOrDefault(position, -1); + } + + /** + * Determines if the position contains an item frame. + * Does largely the same thing as getItemFrameEntityId, but for speed purposes is implemented separately, + * since every block destroy packet has to check for an item frame. + * @param position position of block. + * @param session GeyserSession. + * @return true if position contains item frame, false if not. + */ + public static boolean positionContainsItemFrame(GeyserSession session, Vector3i position) { + return session.getItemFrameCache().containsKey(position); + } + + /** + * Force-remove from the position-to-ID map so it doesn't cause conflicts. + * @param session GeyserSession. + * @param position position of the removed item frame. + */ + public static void removePosition(GeyserSession session, Vector3i position) { + session.getItemFrameCache().remove(position); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 7a7e13c0..263d0041 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -147,7 +147,12 @@ public enum EntityType { COD(AbstractFishEntity.class, 112, 0.25f, 0.5f), PANDA(PandaEntity.class, 113, 1.25f, 1.125f, 1.825f), FOX(FoxEntity.class, 121, 0.5f, 1.25f), - BEE(BeeEntity.class, 122, 0.6f, 0.6f); + BEE(BeeEntity.class, 122, 0.6f, 0.6f), + + /** + * Item frames are handled differently since they are a block in Bedrock. + */ + ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0); private Class entityClass; private final int type; 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 186576e1..1ea3a1c0 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 @@ -49,6 +49,8 @@ import com.nukkitx.protocol.bedrock.data.GamePublishSetting; import com.nukkitx.protocol.bedrock.data.GameRuleData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.packet.*; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import lombok.Getter; import lombok.Setter; import org.geysermc.common.AuthType; @@ -100,6 +102,12 @@ public class GeyserSession implements CommandSender { @Setter private TeleportCache teleportCache; + /** + * A map of Vector3i positions to Java entity IDs. + * Used for translating Bedrock block actions to Java entity actions. + */ + private final Object2LongMap itemFrameCache = new Object2LongOpenHashMap<>(); + private DataCache javaPacketCache; @Setter 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 3f6eba55..7890ead7 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 @@ -27,11 +27,13 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.inventory.Inventory; 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.network.translators.Translators; +import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; @@ -67,6 +69,20 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + + @Override + public void translate(ItemFrameDropItemPacket packet, GeyserSession session) { + // I hope that, when we die, God (or whoever is waiting for us) tells us exactly why this code exists + // The packet sends the Y coordinate (and just the Y coordinate) divided by two, and it's negative if it needs to be subtracted by one + int y; + if (packet.getBlockPosition().getY() > 0) { + y = packet.getBlockPosition().getY() * 2; + } else { + y = (packet.getBlockPosition().getY() * -2) - 1; + } + Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), y, packet.getBlockPosition().getZ()); + ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position), + InteractAction.ATTACK, Hand.MAIN_HAND); + session.getDownstream().getSession().send(interactPacket); + } + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index 5b5d5c9c..650a494b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -32,16 +32,7 @@ import com.nukkitx.nbt.NbtUtils; import com.nukkitx.nbt.stream.NBTInputStream; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.ListTag; -import it.unimi.dsi.fastutil.ints.Int2BooleanMap; -import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2DoubleMap; -import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.connector.GeyserConnector; @@ -61,6 +52,7 @@ public class BlockTranslator { private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>(); private static final Map JAVA_ID_BLOCK_MAP = new HashMap<>(); private static final IntSet WATERLOGGED = new IntOpenHashSet(); + private static final Object2IntMap ITEM_FRAMES = new Object2IntOpenHashMap<>(); // Bedrock carpet ID, used in LlamaEntity.java for decoration public static final int CARPET = 171; @@ -202,6 +194,16 @@ public class BlockTranslator { paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client + // Loop around again to find all item frame runtime IDs + int frameRuntimeId = 0; + for (CompoundTag tag : paletteList) { + CompoundTag blockTag = tag.getCompound("block"); + if (blockTag.getString("name").equals("minecraft:frame")) { + ITEM_FRAMES.put(tag, frameRuntimeId); + } + frameRuntimeId++; + } + BLOCKS = new ListTag<>("", CompoundTag.class, paletteList); } @@ -253,6 +255,18 @@ public class BlockTranslator { return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId); } + public static int getItemFrame(CompoundTag tag) { + return ITEM_FRAMES.getOrDefault(tag, -1); + } + + public static boolean isItemFrame(int bedrockBlockRuntimeId) { + return ITEM_FRAMES.values().contains(bedrockBlockRuntimeId); + } + + public static int getBlockStateVersion() { + return BLOCK_STATE_VERSION; + } + public static BlockState getJavaBlockState(String javaId) { return JAVA_ID_BLOCK_MAP.get(javaId); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java index e579c20e..e9815ba6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java @@ -36,7 +36,6 @@ public class ItemEntry { private final String javaIdentifier; private final int javaId; - private final int bedrockId; private final int bedrockData; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java index c3998f87..d544e24b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java @@ -29,8 +29,10 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import com.github.steveice10.mc.protocol.data.game.entity.type.object.FallingBlockData; +import com.github.steveice10.mc.protocol.data.game.entity.type.object.HangingDirection; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.FallingBlockEntity; +import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -46,8 +48,6 @@ public class JavaSpawnObjectTranslator extends PacketTranslator entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class, Vector3f.class, Vector3f.class, Vector3f.class); diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index c1ddb60a..1347aed4 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -39,6 +39,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.block.entity.*; import org.geysermc.connector.network.translators.Translators; @@ -49,6 +51,7 @@ import org.geysermc.connector.world.chunk.ChunkSection; import java.util.HashMap; import java.util.Map; +import static org.geysermc.connector.network.translators.block.BlockTranslator.AIR; import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID; public class ChunkUtils { @@ -148,6 +151,20 @@ public class ChunkUtils { } public static void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) { + + // Checks for item frames so they aren't tripped up and removed + if (ItemFrameEntity.positionContainsItemFrame(session, position) && blockState.equals(AIR)) { + ((ItemFrameEntity) session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position))).updateBlock(session); + return; + } else if (ItemFrameEntity.positionContainsItemFrame(session, position)) { + Entity entity = session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position)); + if (entity != null) { + session.getEntityCache().removeEntity(entity, false); + } else { + ItemFrameEntity.removePosition(session, position); + } + } + int blockId = BlockTranslator.getBedrockBlockId(blockState); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index 9d874f13..6dd182a7 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -38,6 +38,7 @@ public class DimensionUtils { return; session.getEntityCache().removeAllEntities(); + session.getItemFrameCache().clear(); if (session.getPendingDimSwitches().getAndIncrement() > 0) { ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true); }