From 516d8e573e6319d942a2758c30f9c1aa8d5e0965 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 15 Jan 2022 16:28:52 -0500 Subject: [PATCH 01/26] Config option to disable Bedrock scaffolding/godbridging --- .../configuration/GeyserConfiguration.java | 2 ++ .../GeyserJacksonConfiguration.java | 3 +++ .../BedrockInventoryTransactionTranslator.java | 18 +++++++++++++++++- core/src/main/resources/config.yml | 3 +++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index 3fc7971b0..06d6bdbc5 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -76,6 +76,8 @@ public interface GeyserConfiguration { boolean isShowCoordinates(); + boolean isDisableBedrockScaffolding(); + EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); String getDefaultLocale(); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 55721f894..825edf43e 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -105,6 +105,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("show-coordinates") private boolean showCoordinates = true; + @JsonProperty("disable-bedrock-scaffolding") + private boolean disableBedrockScaffolding = false; + @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) @JsonProperty("emote-offhand-workaround") private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index c730b12ad..29308f9ec 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -109,6 +109,23 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace()); + + if (session.getGeyser().getConfig().isDisableBedrockScaffolding()) { + float yaw = session.getPlayerEntity().getYaw(); + boolean isGodBridging = switch (packet.getBlockFace()) { + case 2 -> yaw <= -135f || yaw > 135f; + case 3 -> yaw <= 45f && yaw > -45f; + case 4 -> yaw > 45f && yaw <= 135f; + case 5 -> yaw <= -45f && yaw > -135f; + default -> false; + }; + if (isGodBridging) { + restoreCorrectBlock(session, blockPos, packet); + return; + } + } + // Check to make sure the client isn't spamming interaction // Based on Nukkit 1.0, with changes to ensure holding down still works boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 && @@ -138,7 +155,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Sat, 15 Jan 2022 20:29:00 -0500 Subject: [PATCH 02/26] Send the client render distance to the server Previously we've had discussions on if the render distance math should be tweaked like we do server -> client, but for now this is better than nothing and can be tweaked further in the future. --- .../network/session/GeyserSession.java | 2 +- .../geyser/session/GeyserSession.java | 33 +++++++++++-- .../BedrockRequestChunkRadiusTranslator.java | 47 +++++++++++++++++++ .../protocol/java/JavaLoginTranslator.java | 23 +++------ .../JavaSetChunkCacheRadiusTranslator.java | 2 +- .../org/geysermc/geyser/util/ChunkUtils.java | 2 +- 6 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestChunkRadiusTranslator.java diff --git a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 932761d4b..85bfd583d 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -64,7 +64,7 @@ public class GeyserSession { } public int getRenderDistance() { - return this.handle.getRenderDistance(); + return this.handle.getServerRenderDistance(); } public boolean isSentSpawnPacket() { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 7ea65e49e..742a2e4a9 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -38,10 +38,14 @@ import com.github.steveice10.mc.protocol.data.ProtocolState; import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +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.data.game.statistic.CustomStatistic; import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientInformationPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; @@ -245,7 +249,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { @Setter private Vector2i lastChunkPosition = null; - private int renderDistance; + @Setter + private int clientRenderDistance = -1; + private int serverRenderDistance; // Exposed for GeyserConnect usage protected boolean sentSpawnPacket; @@ -1160,9 +1166,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { return clientData.getLanguageCode(); } - public void setRenderDistance(int renderDistance) { + public void setServerRenderDistance(int renderDistance) { renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle - this.renderDistance = renderDistance; + this.serverRenderDistance = renderDistance; ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); chunkRadiusUpdatedPacket.setRadius(renderDistance); @@ -1420,6 +1426,27 @@ public class GeyserSession implements GeyserConnection, CommandSender { sendUpstreamPacket(adventureSettingsPacket); } + private int getRenderDistance() { + if (clientRenderDistance != -1) { + // The client has sent a render distance + return clientRenderDistance; + } + return serverRenderDistance; + } + + // We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc + private static final List SKIN_PARTS = Arrays.asList(SkinPart.values()); + + /** + * Send a packet to the server to indicate client render distance, locale, skin parts, and hand preference. + */ + public void sendJavaClientSettings() { + ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(getLocale(), + getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, + HandPreference.RIGHT_HAND, false, true); + sendDownstreamPacket(clientSettingsPacket); + } + /** * Used for updating statistic values since we only get changes from the server * diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestChunkRadiusTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestChunkRadiusTranslator.java new file mode 100644 index 000000000..0a27f7b64 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestChunkRadiusTranslator.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.translator.protocol.bedrock; + +import com.nukkitx.protocol.bedrock.packet.RequestChunkRadiusPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; + +/** + * Sent when the client updates its desired render distance. + */ +@Translator(packet = RequestChunkRadiusPacket.class) +public class BedrockRequestChunkRadiusTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, RequestChunkRadiusPacket packet) { + session.setClientRenderDistance(packet.getRadius()); + + if (session.isLoggedIn()) { + session.sendJavaClientSettings(); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 8521640bb..14dc37e5d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -25,31 +25,25 @@ package org.geysermc.geyser.translator.protocol.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.clientbound.ClientboundLoginPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientInformationPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCustomPayloadPacket; import com.nukkitx.protocol.bedrock.data.GameRuleData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; -import com.nukkitx.protocol.bedrock.packet.*; -import org.geysermc.geyser.session.auth.AuthType; +import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; +import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket; +import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.PluginMessageUtils; -import java.util.Arrays; -import java.util.List; - @Translator(packet = ClientboundLoginPacket.class) public class JavaLoginTranslator extends PacketTranslator { - private static final List SKIN_PART_VALUES = Arrays.asList(SkinPart.values()); @Override public void translate(GeyserSession session, ClientboundLoginPacket packet) { @@ -99,13 +93,10 @@ public class JavaLoginTranslator extends PacketTranslator Date: Sat, 15 Jan 2022 20:32:45 -0500 Subject: [PATCH 03/26] Add the Spigot change for the prior commit --- .../geyser/platform/spigot/world/GeyserPistonListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java index 999353d8a..981d00b97 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java @@ -97,7 +97,7 @@ public class GeyserPistonListener implements Listener { int dX = Math.abs(location.getBlockX() - player.getLocation().getBlockX()) >> 4; int dZ = Math.abs(location.getBlockZ() - player.getLocation().getBlockZ()) >> 4; - if ((dX * dX + dZ * dZ) > session.getRenderDistance() * session.getRenderDistance()) { + if ((dX * dX + dZ * dZ) > session.getServerRenderDistance() * session.getServerRenderDistance()) { // Ignore pistons outside the player's render distance continue; } From f702fb45b4c1f0f0095296d0f77511d2ce1dc237 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 16 Jan 2022 14:42:17 -0500 Subject: [PATCH 04/26] Handle correct internal slot for swapping from inventory --- .../geyser/inventory/BedrockContainerSlot.java | 6 +----- .../geysermc/geyser/inventory/Container.java | 6 ++++++ .../geysermc/geyser/inventory/Inventory.java | 5 ++++- .../geyser/inventory/PlayerInventory.java | 15 +++++++++++---- .../geyser/inventory/click/ClickPlan.java | 18 +++++++++--------- .../inventory/BeaconInventoryTranslator.java | 2 +- .../EnchantingInventoryTranslator.java | 2 +- .../inventory/InventoryTranslator.java | 6 +++--- .../inventory/LoomInventoryTranslator.java | 2 +- .../StonecutterInventoryTranslator.java | 2 +- 10 files changed, 38 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/BedrockContainerSlot.java b/core/src/main/java/org/geysermc/geyser/inventory/BedrockContainerSlot.java index e225c5f4d..87c0c92a3 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/BedrockContainerSlot.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/BedrockContainerSlot.java @@ -26,10 +26,6 @@ package org.geysermc.geyser.inventory; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; -import lombok.Value; -@Value -public class BedrockContainerSlot { - ContainerSlotType container; - int slot; +public record BedrockContainerSlot(ContainerSlotType container, int slot) { } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Container.java b/core/src/main/java/org/geysermc/geyser/inventory/Container.java index cf47b8bd6..073887a64 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Container.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Container.java @@ -30,6 +30,7 @@ import lombok.Getter; import lombok.NonNull; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; +import org.jetbrains.annotations.Range; /** * Combination of {@link Inventory} and {@link PlayerInventory} @@ -59,6 +60,11 @@ public class Container extends Inventory { } } + @Override + public int getOffsetForHotbar(@Range(from = 0, to = 8) int slot) { + return playerInventory.getOffsetForHotbar(slot) - InventoryTranslator.PLAYER_INVENTORY_OFFSET + this.size; + } + @Override public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { if (slot < this.size) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index a78c9cf51..3b307ba8d 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -36,11 +36,12 @@ import lombok.Setter; import lombok.ToString; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.Range; import java.util.Arrays; @ToString -public class Inventory { +public abstract class Inventory { @Getter protected final int id; @@ -110,6 +111,8 @@ public class Inventory { return items[slot]; } + public abstract int getOffsetForHotbar(@Range(from = 0, to = 8) int slot); + public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { if (slot > this.size) { session.getGeyser().getLogger().debug("Tried to set an item out of bounds! " + this); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java b/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java index c4a6a8363..14c796a5f 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/PlayerInventory.java @@ -26,10 +26,12 @@ package org.geysermc.geyser.inventory; import lombok.Getter; -import lombok.NonNull; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.Range; + +import javax.annotation.Nonnull; public class PlayerInventory extends Inventory { /** @@ -41,7 +43,7 @@ public class PlayerInventory extends Inventory { private int heldItemSlot; @Getter - @NonNull + @Nonnull private GeyserItemStack cursor = GeyserItemStack.EMPTY; public PlayerInventory() { @@ -49,7 +51,12 @@ public class PlayerInventory extends Inventory { heldItemSlot = 0; } - public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) { + @Override + public int getOffsetForHotbar(@Range(from = 0, to = 8) int slot) { + return slot + 36; + } + + public void setCursor(@Nonnull GeyserItemStack newCursor, GeyserSession session) { updateItemNetId(cursor, newCursor, session); cursor = newCursor; } @@ -62,7 +69,7 @@ public class PlayerInventory extends Inventory { return items[36 + heldItemSlot]; } - public void setItemInHand(@NonNull GeyserItemStack item) { + public void setItemInHand(@Nonnull GeyserItemStack item) { if (36 + heldItemSlot > this.size) { GeyserImpl.getInstance().getLogger().debug("Held item slot was larger than expected!"); return; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 0a1d0a36e..e973beadc 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -234,31 +234,31 @@ public class ClickPlan { } break; case SWAP_TO_HOTBAR_1: - swap(action.slot, 36, clicked); + swap(action.slot, inventory.getOffsetForHotbar(0), clicked); break; case SWAP_TO_HOTBAR_2: - swap(action.slot, 37, clicked); + swap(action.slot, inventory.getOffsetForHotbar(1), clicked); break; case SWAP_TO_HOTBAR_3: - swap(action.slot, 38, clicked); + swap(action.slot, inventory.getOffsetForHotbar(2), clicked); break; case SWAP_TO_HOTBAR_4: - swap(action.slot, 39, clicked); + swap(action.slot, inventory.getOffsetForHotbar(3), clicked); break; case SWAP_TO_HOTBAR_5: - swap(action.slot, 40, clicked); + swap(action.slot, inventory.getOffsetForHotbar(4), clicked); break; case SWAP_TO_HOTBAR_6: - swap(action.slot, 41, clicked); + swap(action.slot, inventory.getOffsetForHotbar(5), clicked); break; case SWAP_TO_HOTBAR_7: - swap(action.slot, 42, clicked); + swap(action.slot, inventory.getOffsetForHotbar(6), clicked); break; case SWAP_TO_HOTBAR_8: - swap(action.slot, 43, clicked); + swap(action.slot, inventory.getOffsetForHotbar(7), clicked); break; case SWAP_TO_HOTBAR_9: - swap(action.slot, 44, clicked); + swap(action.slot, inventory.getOffsetForHotbar(8), clicked); break; case LEFT_SHIFT: //TODO diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java index f6abdfcd2..19d9d6de5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java @@ -104,7 +104,7 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return action.getType() == StackRequestActionType.BEACON_PAYMENT; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java index 97b78aec5..97ece79d8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java @@ -104,7 +104,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return action.getType() == StackRequestActionType.CRAFT_RECIPE; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 8318e18f6..04d5fa3ad 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -136,7 +136,7 @@ public abstract class InventoryTranslator { * Should be overrided if this request matches a certain criteria and shouldn't be treated normally. * E.G. anvil renaming or enchanting */ - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return false; } @@ -864,8 +864,8 @@ public abstract class InventoryTranslator { Map> containerMap = new HashMap<>(); for (int slot : affectedSlots) { BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot); - List list = containerMap.computeIfAbsent(bedrockSlot.getContainer(), k -> new ArrayList<>()); - list.add(makeItemEntry(session, bedrockSlot.getSlot(), inventory.getItem(slot))); + List list = containerMap.computeIfAbsent(bedrockSlot.container(), k -> new ArrayList<>()); + list.add(makeItemEntry(session, bedrockSlot.slot(), inventory.getItem(slot))); } List containerEntries = new ArrayList<>(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java index acdaaf4c1..a862a7e0d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java @@ -117,7 +117,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { // If the LOOM_MATERIAL slot is not empty, we are crafting a pattern that does not come from an item // Remove the CRAFT_NON_IMPLEMENTED_DEPRECATED when 1.17.30 is dropped return (action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED || action.getType() == StackRequestActionType.CRAFT_LOOM) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/StonecutterInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/StonecutterInventoryTranslator.java index 3bc881696..ae25a9ffd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/StonecutterInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/StonecutterInventoryTranslator.java @@ -52,7 +52,7 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + protected boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { // First is pre-1.18. TODO remove after 1.17.40 support is dropped and refactor stonecutter support to use CraftRecipeStackRequestActionData's recipe ID return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED || action.getType() == StackRequestActionType.CRAFT_RECIPE; } From c1e4040cb6a5e97c941c311da0b54df5dc1afbc8 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 17 Jan 2022 16:10:56 -0500 Subject: [PATCH 05/26] Don't let Bedrock players send format character See https://github.com/PaperMC/Paper/issues/7362 --- .../protocol/bedrock/BedrockTextTranslator.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java index 035a2afe2..1a6771cc5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket; import com.nukkitx.protocol.bedrock.packet.TextPacket; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -44,6 +45,18 @@ public class BedrockTextTranslator extends PacketTranslator { return; } + if (message.indexOf(ChatColor.ESCAPE) != -1) { + // Filter out all escape characters - Java doesn't let you type these + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (c != ChatColor.ESCAPE) { + builder.append(c); + } + } + message = builder.toString(); + } + if (MessageTranslator.isTooLong(message, session)) { return; } From 001a1a7a155716c9745d0d65eb8daf2e45a3eea4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 19 Jan 2022 19:30:45 -0500 Subject: [PATCH 06/26] Support proper dimensions for player dying pose --- .../geyser/entity/type/player/PlayerEntity.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 71bab079d..70b5ede99 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -382,15 +382,26 @@ public class PlayerEntity extends LivingEntity { @Override protected void setDimensions(Pose pose) { float height; + float width; switch (pose) { - case SNEAKING -> height = SNEAKING_POSE_HEIGHT; - case FALL_FLYING, SPIN_ATTACK, SWIMMING -> height = 0.6f; + case SNEAKING -> { + height = SNEAKING_POSE_HEIGHT; + width = definition.width(); + } + case FALL_FLYING, SPIN_ATTACK, SWIMMING -> { + height = 0.6f; + width = definition.width(); + } + case DYING -> { + height = 0.2f; + width = 0.2f; + } default -> { super.setDimensions(pose); return; } } - setBoundingBoxWidth(definition.width()); + setBoundingBoxWidth(width); setBoundingBoxHeight(height); } From a6004af083394e6b1c5be9ef9e7c8986ae42d8f4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 19 Jan 2022 19:30:54 -0500 Subject: [PATCH 07/26] Minor cleanups --- .../BedrockBlockEntityDataTranslator.java | 12 ++++++---- .../entity/JavaEntityEventTranslator.java | 4 ++-- .../org/geysermc/geyser/util/FileUtils.java | 24 +++---------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java index 93ce71a3d..d00914fb1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java @@ -41,12 +41,14 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator SignUtils.JAVA_CHARACTER_WIDTH_MAX) { @@ -111,7 +113,7 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator Date: Wed, 19 Jan 2022 19:44:46 -0500 Subject: [PATCH 08/26] Better handling of invalid display tags --- .../translator/inventory/item/ItemTranslator.java | 11 ++++++----- .../inventory/item/nbt/BasicItemTranslator.java | 6 ++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index 6a2182279..5014969e1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -469,9 +469,8 @@ public abstract class ItemTranslator { public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemMapping mapping, char translationColor) { boolean hasCustomName = false; if (tag != null) { - CompoundTag display = tag.get("display"); - if (display != null && display.contains("Name")) { - String name = ((StringTag) display.get("Name")).getValue(); + if (tag.get("display") instanceof CompoundTag display && display.get("Name") instanceof StringTag tagName) { + String name = tagName.getValue(); // Get the translated name and prefix it with a reset char name = MessageTranslator.convertMessageLenient(name, session.getLocale()); @@ -491,8 +490,10 @@ public abstract class ItemTranslator { if (tag == null) { tag = new CompoundTag(""); } - CompoundTag display = tag.get("display"); - if (display == null) { + CompoundTag display; + if (tag.get("display") instanceof CompoundTag oldDisplay) { + display = oldDisplay; + } else { display = new CompoundTag("display"); // Add to the new root tag tag.put(display); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java index 42cfc0439..a507d02cc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java @@ -51,13 +51,11 @@ public class BasicItemTranslator extends NbtItemStackTranslator { } } - CompoundTag displayTag = itemTag.get("display"); - if (displayTag == null) { + if (!(itemTag.get("display") instanceof CompoundTag displayTag)) { return; } - Tag loreTag = displayTag.get("Lore"); - if (loreTag instanceof ListTag listTag) { + if (displayTag.get("Lore") instanceof ListTag listTag) { List lore = new ArrayList<>(); for (Tag tag : listTag.getValue()) { if (!(tag instanceof StringTag)) continue; From e92633d65771bac061cadc22c95e114122af87e4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 20 Jan 2022 18:09:35 -0500 Subject: [PATCH 09/26] Add an option to always quick-change armor With thanks to https://github.com/juancarloscp52/BedrockIfy/blob/f068217cb762a0dddd403a863d21d0686c01910d/src/main/java/me/juancarloscp52/bedrockify/client/features/quickArmorSwap/ArmorReplacer.java for making me realize this was possible. Currently disabled by default in the event that a server implementation also has this feature. May be enabled by default in the future. --- .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 3 + .../inventory/InventoryTranslator.java | 13 +--- ...BedrockInventoryTransactionTranslator.java | 59 ++++++++++++++----- .../geysermc/geyser/util/InventoryUtils.java | 18 ++++++ core/src/main/resources/config.yml | 4 ++ 6 files changed, 72 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index 06d6bdbc5..3b7cad44c 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -78,6 +78,8 @@ public interface GeyserConfiguration { boolean isDisableBedrockScaffolding(); + boolean isAlwaysQuickChangeArmor(); + EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); String getDefaultLocale(); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 825edf43e..97c5bfea8 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -108,6 +108,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("disable-bedrock-scaffolding") private boolean disableBedrockScaffolding = false; + @JsonProperty("always-quick-change-armor") + private boolean alwaysQuickChangeArmor = false; + @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) @JsonProperty("emote-offhand-workaround") private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 04d5fa3ad..e0b90db02 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -313,18 +313,7 @@ public abstract class InventoryTranslator { if (!isSourceCursor && destination.getContainer() == ContainerSlotType.HOTBAR || destination.getContainer() == ContainerSlotType.HOTBAR_AND_INVENTORY) { // Tell the server we're pressing one of the hotbar keys to save clicks - Click click = switch (destination.getSlot()) { - case 0 -> Click.SWAP_TO_HOTBAR_1; - case 1 -> Click.SWAP_TO_HOTBAR_2; - case 2 -> Click.SWAP_TO_HOTBAR_3; - case 3 -> Click.SWAP_TO_HOTBAR_4; - case 4 -> Click.SWAP_TO_HOTBAR_5; - case 5 -> Click.SWAP_TO_HOTBAR_6; - case 6 -> Click.SWAP_TO_HOTBAR_7; - case 7 -> Click.SWAP_TO_HOTBAR_8; - case 8 -> Click.SWAP_TO_HOTBAR_9; - default -> null; - }; + Click click = InventoryUtils.getClickForHotbarSwap(destination.getSlot()); if (click != null) { plan.add(click, sourceSlot); break; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 29308f9ec..be10452f4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; @@ -40,21 +41,26 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.inventory.*; import com.nukkitx.protocol.bedrock.packet.*; +import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity; import org.geysermc.geyser.entity.type.Entity; -import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.sound.EntitySoundInteractionTranslator; +import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.translator.sound.EntitySoundInteractionTranslator; import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.InventoryUtils; +import java.util.Collections; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -269,16 +275,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { - if (packet.getActions().size() == 1 && packet.getLegacySlots().size() > 0) { - InventoryActionData actionData = packet.getActions().get(0); - LegacySetItemSlotData slotData = packet.getLegacySlots().get(0); - if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) { - // The player is trying to swap out an armor piece that already has an item in it - // Java Edition does not allow this; let's revert it - session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); - } - } - // Handled when sneaking if (session.getPlayerInventory().getItemInHand().getJavaId() == mappings.getStoredItems().shield().getJavaId()) { break; @@ -298,6 +294,39 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator legacySlots = packet.getLegacySlots(); + if (packet.getActions().size() == 1 && legacySlots.size() > 0) { + InventoryActionData actionData = packet.getActions().get(0); + LegacySetItemSlotData slotData = legacySlots.get(0); + if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) { + // The player is trying to swap out an armor piece that already has an item in it + if (session.getGeyser().getConfig().isAlwaysQuickChangeArmor()) { + // Java doesn't know when a player is in its own inventory and not, so we + // can abuse this feature to send a swap inventory packet + int bedrockHotbarSlot = packet.getHotbarSlot(); + Click click = InventoryUtils.getClickForHotbarSwap(bedrockHotbarSlot); + if (click != null && slotData.getSlots().length != 0) { + Inventory playerInventory = session.getPlayerInventory(); + // Bedrock sends us the index of the slot in the armor container; armor in Java + // Edition is offset by 5 in the player inventory + int armorSlot = slotData.getSlots()[0] + 5; + GeyserItemStack armorSlotItem = playerInventory.getItem(armorSlot); + GeyserItemStack hotbarItem = playerInventory.getItem(playerInventory.getOffsetForHotbar(bedrockHotbarSlot)); + playerInventory.setItem(armorSlot, hotbarItem, session); + playerInventory.setItem(bedrockHotbarSlot, armorSlotItem, session); + + ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket( + playerInventory.getId(), playerInventory.getStateId(), armorSlot, + click.actionType, click.action, null, Collections.emptyMap()); + session.sendDownstreamPacket(clickPacket); + } + } else { + // Disallowed; let's revert + session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); + } + } + } } case 2 -> { int blockState = session.getGameMode() == GameMode.CREATIVE ? diff --git a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java index 76530f396..72f20797e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -41,6 +41,7 @@ import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; +import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; @@ -50,6 +51,7 @@ import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTransl import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; +import javax.annotation.Nullable; import java.util.Collections; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -330,4 +332,20 @@ public class InventoryUtils { session.sendUpstreamPacket(hotbarPacket); // No need to send a Java packet as Bedrock sends a confirmation packet back that we translate } + + @Nullable + public static Click getClickForHotbarSwap(int slot) { + return switch (slot) { + case 0 -> Click.SWAP_TO_HOTBAR_1; + case 1 -> Click.SWAP_TO_HOTBAR_2; + case 2 -> Click.SWAP_TO_HOTBAR_3; + case 3 -> Click.SWAP_TO_HOTBAR_4; + case 4 -> Click.SWAP_TO_HOTBAR_5; + case 5 -> Click.SWAP_TO_HOTBAR_6; + case 6 -> Click.SWAP_TO_HOTBAR_7; + case 7 -> Click.SWAP_TO_HOTBAR_8; + case 8 -> Click.SWAP_TO_HOTBAR_9; + default -> null; + }; + } } diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d762220a5..00e2521f3 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -128,6 +128,10 @@ show-coordinates: true # Whether Bedrock players are blocked from performing their scaffolding-style bridging. disable-bedrock-scaffolding: false +# Whether Bedrock players can right-click outside of their inventory to replace armor in their inventory, even if the +# armor slot is already occupied (which Java Edition doesn't allow) +always-quick-change-armor: false + # If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind # There are three options this can be set to: # disabled - the default/fallback, which doesn't apply this workaround From 1fba96c339ebbe1775bc886679afb5f35815c61f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 22 Jan 2022 16:22:27 -0500 Subject: [PATCH 10/26] Address armor stand invisibility edge case See https://github.com/GeyserMC/Geyser/issues/2780 --- .../java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java | 2 +- .../geysermc/geyser/entity/type/living/ArmorStandEntity.java | 2 +- .../protocol/java/entity/JavaSetEntityDataTranslator.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java b/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java index caa373549..f0095d26a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java +++ b/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java @@ -34,7 +34,7 @@ import java.util.Map; /** * A write-only wrapper for temporarily storing entity metadata that will be sent to Bedrock. */ -public class GeyserDirtyMetadata { +public final class GeyserDirtyMetadata { private final Map metadata = new Object2ObjectLinkedOpenHashMap<>(); public void put(EntityData entityData, Object value) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java index 9980cd2c1..10086be9c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java @@ -136,7 +136,7 @@ public class ArmorStandEntity extends LivingEntity { } isSmall = newIsSmall; - if (!isMarker) { + if (!isMarker && !isInvisible) { // Addition for isInvisible check caused by https://github.com/GeyserMC/Geyser/issues/2780 toggleSmallStatus(); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java index 235ecb1cd..ed9129c26 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java @@ -27,12 +27,12 @@ package org.geysermc.geyser.translator.protocol.java.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.InteractiveTagManager; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.entity.InteractiveTagManager; @Translator(packet = ClientboundSetEntityDataPacket.class) public class JavaSetEntityDataTranslator extends PacketTranslator { From f682cf1326775734f3c9289c8c488e4cd0c82a55 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 22 Jan 2022 16:36:41 -0500 Subject: [PATCH 11/26] Yes, Geyser supports Bedrock 1.18.0/1/2. --- .../java/org/geysermc/geyser/network/MinecraftProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java b/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java index d28b95203..f605f9089 100644 --- a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java @@ -60,7 +60,7 @@ public final class MinecraftProtocol { static { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v465.V465_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v471.V471_CODEC); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder().minecraftVersion("1.18.0/1.18.1/1.18.2").build()); } /** From 5ce2c113ae100a09e6a743297bf5eab5b16ae3dc Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 28 Jan 2022 13:08:10 +0100 Subject: [PATCH 12/26] Remove IPv6 scope if present --- .../java/org/geysermc/geyser/session/GeyserSession.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 742a2e4a9..99c8c5cc4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -796,6 +796,13 @@ public class GeyserSession implements GeyserConnection, CommandSender { FloodgateSkinUploader skinUploader = geyser.getSkinUploader(); FloodgateCipher cipher = geyser.getCipher(); + String bedrockAddress = upstream.getAddress().getAddress().getHostAddress(); + // both BungeeCord and Velocity remove the IPv6 scope (if there is one) for Spigot + int ipv6ScopeIndex = bedrockAddress.indexOf('%'); + if (ipv6ScopeIndex != -1) { + bedrockAddress = bedrockAddress.substring(0, ipv6ScopeIndex); + } + encryptedData = cipher.encryptFromString(BedrockData.of( clientData.getGameVersion(), authData.name(), @@ -804,7 +811,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { clientData.getLanguageCode(), clientData.getUiProfile().ordinal(), clientData.getCurrentInputMode().ordinal(), - upstream.getAddress().getAddress().getHostAddress(), + bedrockAddress, skinUploader.getId(), skinUploader.getVerifyCode() ).toString()); From 14882534c0f14be29c05c0cafb593889a2e0a550 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 30 Jan 2022 11:05:29 -0500 Subject: [PATCH 13/26] Don't fully translate item data to compare net IDs Just compare the item mappings of the two Java items. This should shave some NBT and display conversion processing time down. --- .../geysermc/geyser/inventory/Inventory.java | 6 +++- .../geyser/session/cache/LodestoneCache.java | 28 +++++++--------- .../inventory/item/BannerTranslator.java | 6 ++-- .../inventory/item/CompassTranslator.java | 33 +++++++++++++------ .../inventory/item/ItemTranslator.java | 31 ++++++++++++----- .../inventory/item/PotionTranslator.java | 2 +- .../inventory/item/TippedArrowTranslator.java | 2 +- ...tionTrackingDBClientRequestTranslator.java | 4 +-- 8 files changed, 68 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index 3b307ba8d..26dc261a0 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -35,7 +35,9 @@ import lombok.NonNull; import lombok.Setter; import lombok.ToString; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.jetbrains.annotations.Range; import java.util.Arrays; @@ -136,7 +138,9 @@ public abstract class Inventory { protected void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) { if (!newItem.isEmpty()) { - if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { + ItemMapping oldMapping = ItemTranslator.getBedrockItemMapping(session, oldItem); + ItemMapping newMapping = ItemTranslator.getBedrockItemMapping(session, newItem); + if (oldMapping.getBedrockId() == newMapping.getBedrockId()) { newItem.setNetId(oldItem.getNetId()); } else { newItem.setNetId(session.getNextItemNetId()); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java index f0cbbb189..05c2628df 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java @@ -30,9 +30,6 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; import org.geysermc.geyser.inventory.GeyserItemStack; import javax.annotation.Nullable; @@ -43,7 +40,7 @@ import java.util.WeakHashMap; * A temporary cache for lodestone information. * Bedrock requests the lodestone position information separately from the item. */ -public class LodestoneCache { +public final class LodestoneCache { /** * A list of any GeyserItemStacks that are lodestones. Used mainly to minimize Bedrock's "pop-in" effect * when a new item has been created; instead we can re-use already existing IDs @@ -121,8 +118,16 @@ public class LodestoneCache { } public @Nullable LodestonePos getPos(int id) { - // We should not need to check the activeLodestones map as Bedrock should already be aware of this ID - return this.lodestones.remove(id); + LodestonePos pos = this.lodestones.remove(id); + if (pos != null) { + return pos; + } + for (LodestonePos activePos : this.activeLodestones.values()) { + if (activePos.id == id) { + return activePos; + } + } + return null; } public void clear() { @@ -131,16 +136,7 @@ public class LodestoneCache { this.lodestones.clear(); } - @Getter - @AllArgsConstructor - @EqualsAndHashCode - public static class LodestonePos { - private final int id; - private final int x; - private final int y; - private final int z; - private final String dimension; - + public record LodestonePos(int id, int x, int y, int z, String dimension) { boolean equals(int x, int y, int z, String dimension) { return this.x == x && this.y == y && this.z == z && this.dimension.equals(dimension); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java index 3c566e76c..a5c3235a2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java @@ -155,7 +155,7 @@ public class BannerTranslator extends ItemTranslator { } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack.getNbt() == null) { return super.translateToBedrock(itemStack, mapping, mappings); } @@ -163,9 +163,7 @@ public class BannerTranslator extends ItemTranslator { ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings); CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); - if (blockEntityTag != null && blockEntityTag.contains("Patterns")) { - ListTag patterns = blockEntityTag.get("Patterns"); - + if (blockEntityTag != null && blockEntityTag.get("Patterns") instanceof ListTag patterns) { NbtMapBuilder nbtBuilder = builder.build().getTag().toBuilder(); //TODO fix ugly hack if (patterns.equals(OMINOUS_BANNER_PATTERN)) { // Remove the current patterns and set the ominous banner type diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java index 65f26542f..9637f1aa9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java @@ -26,7 +26,9 @@ package org.geysermc.geyser.translator.inventory.item; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.opennbt.tag.builtin.*; +import com.github.steveice10.opennbt.tag.builtin.ByteTag; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.registry.Registries; @@ -51,19 +53,30 @@ public class CompassTranslator extends ItemTranslator { } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { - if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); - - Tag lodestoneTag = itemStack.getNbt().get("LodestoneTracked"); - if (lodestoneTag instanceof ByteTag) { - // Get the fake lodestonecompass entry - mapping = mappings.getStoredItems().lodestoneCompass(); - // NBT will be translated in nbt/LodestoneCompassTranslator + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + if (isLodestoneCompass(itemStack.getNbt())) { + // NBT will be translated in nbt/LodestoneCompassTranslator if applicable + return super.translateToBedrock(itemStack, mappings.getStoredItems().lodestoneCompass(), mappings); } - return super.translateToBedrock(itemStack, mapping, mappings); } + @Override + protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { + if (isLodestoneCompass(nbt)) { + return mappings.getStoredItems().lodestoneCompass(); + } + return super.getItemMapping(javaId, nbt, mappings); + } + + private boolean isLodestoneCompass(CompoundTag nbt) { + if (nbt != null) { + Tag lodestoneTag = nbt.get("LodestoneTracked"); + return lodestoneTag instanceof ByteTag; + } + return false; + } + @Override public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index 5014969e1..b8a7b60e2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -37,6 +37,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -157,18 +158,13 @@ public abstract class ItemTranslator { nbt = translateDisplayProperties(session, nbt, bedrockItem); if (session.isAdvancedTooltips()) { - nbt = addAdvancedTooltips(nbt, session.getItemMappings().getMapping(stack), session.getLocale()); + nbt = addAdvancedTooltips(nbt, bedrockItem, session.getLocale()); } ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); - ItemData.Builder builder; - ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); - if (itemStackTranslator != null) { - builder = itemStackTranslator.translateToBedrock(itemStack, bedrockItem, session.getItemMappings()); - } else { - builder = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem, session.getItemMappings()); - } + ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.getOrDefault(bedrockItem.getJavaId(), DEFAULT_TRANSLATOR); + ItemData.Builder builder = itemStackTranslator.translateToBedrock(itemStack, bedrockItem, session.getItemMappings()); if (bedrockItem.isBlock()) { builder.blockRuntimeId(bedrockItem.getBedrockBlockId()); } @@ -263,6 +259,19 @@ public abstract class ItemTranslator { return canModifyBedrock; } + /** + * Given an item stack, determine the item mapping that should be applied to Bedrock players. + */ + @Nonnull + public static ItemMapping getBedrockItemMapping(GeyserSession session, @Nonnull GeyserItemStack itemStack) { + if (itemStack.isEmpty()) { + return ItemMapping.AIR; + } + int javaId = itemStack.getJavaId(); + return ITEM_STACK_TRANSLATORS.getOrDefault(javaId, DEFAULT_TRANSLATOR) + .getItemMapping(javaId, itemStack.getNbt(), session.getItemMappings()); + } + private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { @Override public List getAppliedItems() { @@ -270,7 +279,7 @@ public abstract class ItemTranslator { } }; - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack == null) { // Return, essentially, air return ItemData.builder(); @@ -295,6 +304,10 @@ public abstract class ItemTranslator { public abstract List getAppliedItems(); + protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { + return mappings.getMapping(javaId); + } + public NbtMap translateNbtToBedrock(CompoundTag tag) { NbtMapBuilder builder = NbtMap.builder(); if (tag.getValue() != null && !tag.getValue().isEmpty()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index 272092da6..54a6deadb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -54,7 +54,7 @@ public class PotionTranslator extends ItemTranslator { } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); Tag potionTag = itemStack.getNbt().get("Potion"); if (potionTag instanceof StringTag) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index 4925d3e69..35e8baa07 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -59,7 +59,7 @@ public class TippedArrowTranslator extends ItemTranslator { } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { // We're only concerned about minecraft:arrow when translating Bedrock -> Java return super.translateToBedrock(itemStack, mapping, mappings); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java index a6551afbd..f3d0ff344 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java @@ -58,14 +58,14 @@ public class BedrockPositionTrackingDBClientRequestTranslator extends PacketTran // Build the NBT data for the update NbtMapBuilder builder = NbtMap.builder(); - builder.putInt("dim", DimensionUtils.javaToBedrock(pos.getDimension())); + builder.putInt("dim", DimensionUtils.javaToBedrock(pos.dimension())); builder.putString("id", "0x" + String.format("%08X", packet.getTrackingId())); builder.putByte("version", (byte) 1); // Not sure what this is for builder.putByte("status", (byte) 0); // Not sure what this is for // Build the position for the update - builder.putList("pos", NbtType.INT, pos.getX(), pos.getY(), pos.getZ()); + builder.putList("pos", NbtType.INT, pos.x(), pos.y(), pos.z()); broadcastPacket.setTag(builder.build()); session.sendUpstreamPacket(broadcastPacket); From d0fa2d2b055e74fa28b0eaed63c8f61a4e6f32c6 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 30 Jan 2022 11:14:51 -0500 Subject: [PATCH 14/26] Don't send respawn code until Java is ready Fixes #2668 --- .../bedrock/BedrockRespawnTranslator.java | 25 ------------------- .../protocol/java/JavaRespawnTranslator.java | 2 ++ .../player/JavaPlayerPositionTranslator.java | 9 ++++--- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java index 77b7143b2..1631ea4c7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java @@ -27,10 +27,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.github.steveice10.mc.protocol.data.game.ClientCommand; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.RespawnPacket; -import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -41,28 +38,6 @@ public class BedrockRespawnTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, RespawnPacket packet) { if (packet.getState() == RespawnPacket.State.CLIENT_READY) { - // Previously we only sent the respawn packet before the server finished loading - // The message included was 'Otherwise when immediate respawn is on the client never loads' - // But I assume the new if statement below fixes that problem - RespawnPacket respawnPacket = new RespawnPacket(); - respawnPacket.setRuntimeEntityId(0); - respawnPacket.setPosition(Vector3f.ZERO); - respawnPacket.setState(RespawnPacket.State.SERVER_READY); - session.sendUpstreamPacket(respawnPacket); - - if (session.isSpawned()) { - // Client might be stuck; resend spawn information - PlayerEntity entity = session.getPlayerEntity(); - entity.updateBedrockMetadata(); // TODO test? - - MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); - movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); - movePlayerPacket.setPosition(entity.getPosition()); - movePlayerPacket.setRotation(entity.getBedrockRotation()); - movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN); - session.sendUpstreamPacket(movePlayerPacket); - } - ServerboundClientCommandPacket javaRespawnPacket = new ServerboundClientCommandPacket(ClientCommand.RESPAWN); session.sendDownstreamPacket(javaRespawnPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index 7b198b575..03d006a50 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -46,6 +46,8 @@ public class JavaRespawnTranslator extends PacketTranslator Date: Sun, 30 Jan 2022 11:15:07 -0500 Subject: [PATCH 15/26] Several inventory and parity improvements These changes fix up things that were missed with Java Edition inventory changes in 1.17 and 1.17.1. Working with the inventory in modern versions should be much nicer. --- .../org/geysermc/geyser/GeyserLogger.java | 11 ++ .../geysermc/geyser/inventory/Container.java | 5 +- .../geysermc/geyser/inventory/Inventory.java | 8 +- .../geyser/inventory/click/ClickPlan.java | 178 +++++++++--------- .../geyser/session/GeyserSession.java | 9 + .../inventory/BeaconInventoryTranslator.java | 9 +- .../CraftingInventoryTranslator.java | 5 + .../EnchantingInventoryTranslator.java | 19 +- .../inventory/InventoryTranslator.java | 72 +++---- .../inventory/PlayerInventoryTranslator.java | 18 +- ...BedrockInventoryTransactionTranslator.java | 10 +- .../protocol/java/JavaRecipeTranslator.java | 8 +- .../JavaContainerSetContentTranslator.java | 26 ++- .../JavaContainerSetSlotTranslator.java | 134 +++---------- .../geysermc/geyser/util/InventoryUtils.java | 117 ++++++++++++ 15 files changed, 364 insertions(+), 265 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java index a61c5db25..b47801cb5 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java @@ -25,6 +25,8 @@ package org.geysermc.geyser; +import javax.annotation.Nullable; + public interface GeyserLogger { /** @@ -78,6 +80,15 @@ public interface GeyserLogger { */ void debug(String message); + /** + * Logs an object to console if debug mode is enabled + * + * @param object the object to log + */ + default void debug(@Nullable Object object) { + debug(String.valueOf(object)); + } + /** * Sets if the logger should print debug messages * diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Container.java b/core/src/main/java/org/geysermc/geyser/inventory/Container.java index 073887a64..569802a5a 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Container.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Container.java @@ -27,11 +27,12 @@ package org.geysermc.geyser.inventory; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import lombok.Getter; -import lombok.NonNull; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.jetbrains.annotations.Range; +import javax.annotation.Nonnull; + /** * Combination of {@link Inventory} and {@link PlayerInventory} */ @@ -66,7 +67,7 @@ public class Container extends Inventory { } @Override - public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { + public void setItem(int slot, @Nonnull GeyserItemStack newItem, GeyserSession session) { if (slot < this.size) { super.setItem(slot, newItem, session); } else { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index 26dc261a0..ca7e90a25 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -31,7 +31,6 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector3i; import lombok.Getter; -import lombok.NonNull; import lombok.Setter; import lombok.ToString; import org.geysermc.geyser.GeyserImpl; @@ -40,11 +39,11 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.jetbrains.annotations.Range; +import javax.annotation.Nonnull; import java.util.Arrays; @ToString public abstract class Inventory { - @Getter protected final int id; @@ -72,8 +71,7 @@ public abstract class Inventory { protected final ContainerType containerType; @Getter - @Setter - protected String title; + protected final String title; protected final GeyserItemStack[] items; @@ -115,7 +113,7 @@ public abstract class Inventory { public abstract int getOffsetForHotbar(@Range(from = 0, to = 8) int slot); - public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { + public void setItem(int slot, @Nonnull GeyserItemStack newItem, GeyserSession session) { if (slot > this.size) { session.getGeyser().getLogger().debug("Tried to set an item out of bounds! " + this); return; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index e973beadc..e6eeea689 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -28,7 +28,6 @@ package org.geysermc.geyser.inventory.click; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerActionType; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; -import com.github.steveice10.mc.protocol.data.game.inventory.MoveToHotbarAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -40,20 +39,22 @@ import org.geysermc.geyser.inventory.SlotType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.CraftingInventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator; -import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator; import org.geysermc.geyser.util.InventoryUtils; import org.jetbrains.annotations.Contract; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.ListIterator; -public class ClickPlan { +public final class ClickPlan { private final List plan = new ArrayList<>(); private final Int2ObjectMap simulatedItems; + /** + * Used for 1.17.1+ proper packet translation - any non-cursor item that is changed in a single transaction gets sent here. + */ + private Int2ObjectMap changedItems; private GeyserItemStack simulatedCursor; - private boolean simulating; + private boolean finished; private final GeyserSession session; private final InventoryTranslator translator; @@ -66,21 +67,11 @@ public class ClickPlan { this.inventory = inventory; this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize()); + this.changedItems = null; this.simulatedCursor = session.getPlayerInventory().getCursor().copy(); - this.simulating = true; + this.finished = false; - if (translator instanceof PlayerInventoryTranslator) { - gridSize = 4; - } else if (translator instanceof CraftingInventoryTranslator) { - gridSize = 9; - } else { - gridSize = -1; - } - } - - private void resetSimulation() { - this.simulatedItems.clear(); - this.simulatedCursor = session.getPlayerInventory().getCursor().copy(); + gridSize = translator.getGridSize(); } public void add(Click click, int slot) { @@ -88,7 +79,7 @@ public class ClickPlan { } public void add(Click click, int slot, boolean force) { - if (!simulating) + if (finished) throw new UnsupportedOperationException("ClickPlan already executed"); if (click == Click.LEFT_OUTSIDE || click == Click.RIGHT_OUTSIDE) { @@ -97,12 +88,10 @@ public class ClickPlan { ClickAction action = new ClickAction(click, slot, force); plan.add(action); - simulateAction(action); } public void execute(boolean refresh) { //update geyser inventory after simulation to avoid net id desync - resetSimulation(); ListIterator planIter = plan.listIterator(); while (planIter.hasNext()) { ClickAction action = planIter.next(); @@ -112,33 +101,48 @@ public class ClickPlan { refresh = true; } - //int stateId = stateIdHack(action); + changedItems = new Int2ObjectOpenHashMap<>(); - //simulateAction(action); + boolean emulatePost1_16Logic = session.isEmulatePost1_16Logic(); + + int stateId; + if (emulatePost1_16Logic) { + stateId = stateIdHack(action); + simulateAction(action); + } else { + stateId = inventory.getStateId(); + } ItemStack clickedItemStack; if (!planIter.hasNext() && refresh) { clickedItemStack = InventoryUtils.REFRESH_ITEM; - } else if (action.click.actionType == ContainerActionType.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) { - clickedItemStack = null; } else { - //// The action must be simulated first as Java expects the new contents of the cursor (as of 1.18.1) - //clickedItemStack = simulatedCursor.getItemStack(); TODO fix - this is the proper behavior but it terribly breaks 1.16.5 - clickedItemStack = getItem(action.slot).getItemStack(); + if (emulatePost1_16Logic) { + // The action must be simulated first as Java expects the new contents of the cursor (as of 1.18.1) + clickedItemStack = simulatedCursor.getItemStack(); + } else { + if (action.click.actionType == ContainerActionType.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) { + clickedItemStack = null; + } else { + clickedItemStack = getItem(action.slot).getItemStack(); + } + } + } + + if (!emulatePost1_16Logic) { + simulateAction(action); } ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket( inventory.getId(), - inventory.getStateId(), + stateId, action.slot, action.click.actionType, action.click.action, clickedItemStack, - Collections.emptyMap() // Anything else we change, at this time, should have a packet sent to address + changedItems ); - simulateAction(action); - session.sendDownstreamPacket(clickPacket); } @@ -146,19 +150,11 @@ public class ClickPlan { for (Int2ObjectMap.Entry simulatedSlot : simulatedItems.int2ObjectEntrySet()) { inventory.setItem(simulatedSlot.getIntKey(), simulatedSlot.getValue(), session); } - simulating = false; + finished = true; } public GeyserItemStack getItem(int slot) { - return getItem(slot, true); - } - - public GeyserItemStack getItem(int slot, boolean generate) { - if (generate) { - return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy()); - } else { - return simulatedItems.getOrDefault(slot, inventory.getItem(slot)); - } + return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy()); } public GeyserItemStack getCursor() { @@ -166,23 +162,38 @@ public class ClickPlan { } private void setItem(int slot, GeyserItemStack item) { - if (simulating) { - simulatedItems.put(slot, item); - } else { - inventory.setItem(slot, item, session); - } + simulatedItems.put(slot, item); + onSlotItemChange(slot, item); } private void setCursor(GeyserItemStack item) { - if (simulating) { - simulatedCursor = item; - } else { - session.getPlayerInventory().setCursor(item, session); - } + simulatedCursor = item; + } + + private void add(int slot, GeyserItemStack itemStack, int amount) { + itemStack.add(amount); + onSlotItemChange(slot, itemStack); + } + + private void sub(int slot, GeyserItemStack itemStack, int amount) { + itemStack.sub(amount); + onSlotItemChange(slot, itemStack); + } + + private void setAmount(int slot, GeyserItemStack itemStack, int amount) { + itemStack.setAmount(amount); + onSlotItemChange(slot, itemStack); + } + + /** + * Does not need to be called for the cursor + */ + private void onSlotItemChange(int slot, GeyserItemStack itemStack) { + changedItems.put(slot, itemStack.getItemStack()); } private void simulateAction(ClickAction action) { - GeyserItemStack cursor = simulating ? getCursor() : session.getPlayerInventory().getCursor(); + GeyserItemStack cursor = getCursor(); switch (action.click) { case LEFT_OUTSIDE -> { setCursor(GeyserItemStack.EMPTY); @@ -196,7 +207,7 @@ public class ClickPlan { } } - GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot); + GeyserItemStack clicked = getItem(action.slot); if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { switch (action.click) { case LEFT, RIGHT -> { @@ -206,6 +217,7 @@ public class ClickPlan { cursor.add(clicked.getAmount()); } reduceCraftingGrid(false); + setItem(action.slot, GeyserItemStack.EMPTY); // Matches Java Edition 1.18.1 } case LEFT_SHIFT -> reduceCraftingGrid(true); } @@ -217,20 +229,20 @@ public class ClickPlan { setItem(action.slot, cursor); } else { setCursor(GeyserItemStack.EMPTY); - clicked.add(cursor.getAmount()); + add(action.slot, clicked, cursor.getAmount()); } break; case RIGHT: if (cursor.isEmpty() && !clicked.isEmpty()) { int half = clicked.getAmount() / 2; //smaller half setCursor(clicked.copy(clicked.getAmount() - half)); //larger half - clicked.setAmount(half); + setAmount(action.slot, clicked, half); } else if (!cursor.isEmpty() && clicked.isEmpty()) { cursor.sub(1); setItem(action.slot, cursor.copy(1)); } else if (InventoryUtils.canStack(cursor, clicked)) { cursor.sub(1); - clicked.add(1); + add(action.slot, clicked, 1); } break; case SWAP_TO_HOTBAR_1: @@ -265,7 +277,7 @@ public class ClickPlan { break; case DROP_ONE: if (!clicked.isEmpty()) { - clicked.sub(1); + sub(action.slot, clicked, 1); } break; case DROP_ALL: @@ -279,7 +291,7 @@ public class ClickPlan { * Swap between two inventory slots without a cursor. This should only be used with {@link ContainerActionType#MOVE_TO_HOTBAR_SLOT} */ private void swap(int sourceSlot, int destSlot, GeyserItemStack sourceItem) { - GeyserItemStack destinationItem = simulating ? getItem(destSlot) : inventory.getItem(destSlot); + GeyserItemStack destinationItem = getItem(destSlot); setItem(sourceSlot, destinationItem); setItem(destSlot, sourceItem); } @@ -292,63 +304,44 @@ public class ClickPlan { stateId = inventory.getStateId(); } - // This is a hack. - // Java will never ever send more than one container click packet per set of actions. + // Java will never ever send more than one container click packet per set of actions*. + // *(exception being Java's "quick craft"/painting feature) // Bedrock might, and this would generally fall into one of two categories: // - Bedrock is sending an item directly from one slot to another, without picking it up, that cannot // be expressed with a shift click // - Bedrock wants to pick up or place an arbitrary amount of items that cannot be expressed from // one left/right click action. - // When Bedrock does one of these actions and sends multiple packets, a 1.17.1+ server will - // increment the state ID on each confirmation packet it sends back (I.E. set slot). Then when it - // reads our next packet, because we kept the same state ID but the server incremented it, it'll be - // desynced and send the entire inventory contents back at us. - // This hack therefore increments the state ID to what the server will presumably send back to us. - // (This won't be perfect, but should get us through most vanilla situations, and if this is wrong the - // server will just send a set content packet back at us) + // Java typically doesn't increment the state ID if you send a vanilla-accurate container click packet, + // but it will increment the state ID with a vanilla client in at least the crafting table if (inventory.getContainerType() == ContainerType.CRAFTING && CraftingInventoryTranslator.isCraftingGrid(action.slot)) { // 1.18.1 sends a second set slot update for any action in the crafting grid // And an additional packet if something is removed (Mojmap: CraftingContainer#removeItem) - //TODO this code kind of really sucks; it's potentially possible to see what Bedrock sends us and send a PlaceRecipePacket int stateIdIncrements; GeyserItemStack clicked = getItem(action.slot); if (action.click == Click.LEFT) { if (!clicked.isEmpty() && !InventoryUtils.canStack(simulatedCursor, clicked)) { // An item is removed from the crafting table; yes deletion - stateIdIncrements = 3; + stateIdIncrements = 2; } else { // We can stack and we add all the items to the crafting slot; no deletion - stateIdIncrements = 2; + stateIdIncrements = 1; } } else if (action.click == Click.RIGHT) { - if (simulatedCursor.isEmpty() && !clicked.isEmpty()) { - // Items are taken; yes deletion - stateIdIncrements = 3; - } else if ((!simulatedCursor.isEmpty() && clicked.isEmpty()) || InventoryUtils.canStack(simulatedCursor, clicked)) { - // Adding our cursor item to the slot; no deletion - stateIdIncrements = 2; - } else { - // ?? nothing I guess - stateIdIncrements = 2; - } + stateIdIncrements = 1; + } else if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) { + stateIdIncrements = 1; } else { if (session.getGeyser().getConfig().isDebugMode()) { session.getGeyser().getLogger().debug("Not sure how to handle state ID hack in crafting table: " + plan); } - stateIdIncrements = 2; + stateIdIncrements = 1; } inventory.incrementStateId(stateIdIncrements); - } else if (action.click.action instanceof MoveToHotbarAction) { - // Two slot changes sent - inventory.incrementStateId(2); - } else { - inventory.incrementStateId(1); } return stateId; } - //TODO private void reduceCraftingGrid(boolean makeAll) { if (gridSize == -1) return; @@ -370,9 +363,12 @@ public class ClickPlan { } for (int i = 0; i < gridSize; i++) { - GeyserItemStack item = getItem(i + 1); - if (!item.isEmpty()) - item.sub(crafted); + final int slot = i + 1; + GeyserItemStack item = getItem(slot); + if (!item.isEmpty()) { + // These changes should be broadcasted to the server + sub(slot, item, crafted); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 99c8c5cc4..3a097f732 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -361,6 +361,15 @@ public class GeyserSession implements GeyserConnection, CommandSender { @Setter private Int2ObjectMap stonecutterRecipes; + /** + * Starting in 1.17, Java servers expect the carriedItem parameter of the serverbound click container + * packet to be the current contents of the mouse after the transaction has been done. 1.16 expects the clicked slot + * contents before any transaction is done. With the current ViaVersion structure, if we do not send what 1.16 expects + * and send multiple click container packets, then successive transactions will be rejected. + */ + @Setter + private boolean emulatePost1_16Logic = true; + /** * The current attack speed of the player. Used for sending proper cooldown timings. * Setting a default fixes cooldowns not showing up on a fresh world. diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java index 19d9d6de5..f194d0d3f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/BeaconInventoryTranslator.java @@ -38,17 +38,16 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import it.unimi.dsi.fastutil.ints.IntSets; import org.geysermc.geyser.inventory.BeaconContainer; +import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.holder.BlockInventoryHolder; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InventoryUtils; -import java.util.Collections; - public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator { public BeaconInventoryTranslator() { super(1, new BlockInventoryHolder("minecraft:beacon", com.nukkitx.protocol.bedrock.data.inventory.ContainerType.BEACON) { @@ -114,7 +113,7 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator BeaconPaymentStackRequestActionData beaconPayment = (BeaconPaymentStackRequestActionData) request.getActions()[0]; ServerboundSetBeaconPacket packet = new ServerboundSetBeaconPacket(beaconPayment.getPrimaryEffect(), beaconPayment.getSecondaryEffect()); session.sendDownstreamPacket(packet); - return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + return acceptRequest(request, makeContainerEntries(session, inventory, IntSets.emptySet())); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/CraftingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/CraftingInventoryTranslator.java index ec3335f3c..61e2258b6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/CraftingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/CraftingInventoryTranslator.java @@ -37,6 +37,11 @@ public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslato super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE); } + @Override + public int getGridSize() { + return 9; + } + @Override public SlotType getSlotType(int javaSlot) { if (javaSlot == 0) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java index 97ece79d8..800b35901 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java @@ -27,23 +27,22 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket; -import com.nukkitx.protocol.bedrock.data.inventory.*; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; +import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeStackRequestActionData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import com.nukkitx.protocol.bedrock.packet.PlayerEnchantOptionsPacket; -import org.geysermc.geyser.inventory.EnchantingContainer; -import org.geysermc.geyser.inventory.GeyserEnchantOption; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; -import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; +import it.unimi.dsi.fastutil.ints.IntSets; +import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.item.Enchantment; +import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; import java.util.Arrays; -import java.util.Collections; public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator { public EnchantingInventoryTranslator() { @@ -130,7 +129,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla } ServerboundContainerButtonClickPacket packet = new ServerboundContainerButtonClickPacket(inventory.getId(), javaSlot); session.sendDownstreamPacket(packet); - return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + return acceptRequest(request, makeContainerEntries(session, inventory, IntSets.emptySet())); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index e0b90db02..e6a9faf74 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -26,12 +26,11 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; -import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; @@ -43,15 +42,10 @@ import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.inventory.CartographyContainer; -import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; -import org.geysermc.geyser.inventory.SlotType; +import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.click.ClickPlan; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator; import org.geysermc.geyser.translator.inventory.chest.SingleChestInventoryTranslator; import org.geysermc.geyser.translator.inventory.furnace.BlastFurnaceInventoryTranslator; @@ -119,6 +113,13 @@ public abstract class InventoryTranslator { public abstract SlotType getSlotType(int javaSlot); public abstract Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory); + /** + * Used for crafting-related transactions. Will override in PlayerInventoryTranslator and CraftingInventoryTranslator. + */ + public int getGridSize() { + return -1; + } + /** * Should be overwritten in cases where specific inventories should reject an item being in a specific spot. * For examples, looms use this to reject items that are dyes in Bedrock but not in Java. @@ -147,7 +148,7 @@ public abstract class InventoryTranslator { return rejectRequest(request); } - public void translateRequests(GeyserSession session, Inventory inventory, List requests) { + public final void translateRequests(GeyserSession session, Inventory inventory, List requests) { boolean refresh = false; ItemStackResponsePacket responsePacket = new ItemStackResponsePacket(); for (ItemStackRequest request : requests) { @@ -199,10 +200,6 @@ public abstract class InventoryTranslator { case PLACE: { TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { - if (session.getGameMode().equals(GameMode.CREATIVE) && transferAction.getSource().getContainer() == ContainerSlotType.CRAFTING_INPUT && - transferAction.getSource().getSlot() >= 28 && transferAction.getSource().getSlot() <= 31) { - return rejectRequest(request, false); - } if (session.getGeyser().getConfig().isDebugMode()) { session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.name()); dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); @@ -212,17 +209,19 @@ public abstract class InventoryTranslator { int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int destSlot = bedrockSlotToJava(transferAction.getDestination()); + boolean isSourceCursor = isCursor(transferAction.getSource()); + boolean isDestCursor = isCursor(transferAction.getDestination()); if (shouldRejectItemPlace(session, inventory, transferAction.getSource().getContainer(), - isCursor(transferAction.getSource()) ? -1 : sourceSlot, - transferAction.getDestination().getContainer(), isCursor(transferAction.getDestination()) ? -1 : destSlot)) { + isSourceCursor ? -1 : sourceSlot, + transferAction.getDestination().getContainer(), isDestCursor ? -1 : destSlot)) { // This item would not be here in Java return rejectRequest(request, false); } - if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? + if (isSourceCursor && isDestCursor) { //??? return rejectRequest(request); - } else if (isCursor(transferAction.getSource())) { //releasing cursor + } else if (isSourceCursor) { //releasing cursor int sourceAmount = cursor.getAmount(); if (transferAction.getCount() == sourceAmount) { //release all plan.add(Click.LEFT, destSlot); @@ -231,7 +230,7 @@ public abstract class InventoryTranslator { plan.add(Click.RIGHT, destSlot); } } - } else if (isCursor(transferAction.getDestination())) { //picking up into cursor + } else if (isDestCursor) { //picking up into cursor GeyserItemStack sourceItem = plan.getItem(sourceSlot); int sourceAmount = sourceItem.getAmount(); if (cursor.isEmpty()) { //picking up into empty cursor @@ -431,6 +430,8 @@ public abstract class InventoryTranslator { int leftover = 0; ClickPlan plan = new ClickPlan(session, this, inventory); + // Track all the crafting table slots to report back the contents of the slots after crafting + IntSet affectedSlots = new IntOpenHashSet(); for (StackRequestActionData action : request.getActions()) { switch (action.getType()) { case CRAFT_RECIPE: { @@ -462,6 +463,7 @@ public abstract class InventoryTranslator { return rejectRequest(request); } craftState = CraftState.INGREDIENTS; + affectedSlots.add(bedrockSlotToJava(((ConsumeStackRequestActionData) action).getSource())); break; } case TAKE: @@ -522,21 +524,16 @@ public abstract class InventoryTranslator { } } plan.execute(false); - return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots())); + affectedSlots.addAll(plan.getAffectedSlots()); + return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); } public ItemStackResponsePacket.Response translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { - int gridSize; - int gridDimensions; - if (this instanceof PlayerInventoryTranslator) { - gridSize = 4; - gridDimensions = 2; - } else if (this instanceof CraftingInventoryTranslator) { - gridSize = 9; - gridDimensions = 3; - } else { + final int gridSize = getGridSize(); + if (gridSize == -1) { return rejectRequest(request); } + int gridDimensions = gridSize == 4 ? 2 : 3; Recipe recipe; Ingredient[] ingredients = new Ingredient[0]; @@ -722,7 +719,7 @@ public abstract class InventoryTranslator { /** * Handled in {@link PlayerInventoryTranslator} */ - public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { + protected ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { return rejectRequest(request); } @@ -757,14 +754,14 @@ public abstract class InventoryTranslator { } } - public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequest request, List containerEntries) { + protected static ItemStackResponsePacket.Response acceptRequest(ItemStackRequest request, List containerEntries) { return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.OK, request.getRequestId(), containerEntries); } /** * Reject an incorrect ItemStackRequest. */ - public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request) { + protected static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request) { return rejectRequest(request, true); } @@ -774,7 +771,7 @@ public abstract class InventoryTranslator { * @param throwError whether this request was truly erroneous (true), or known as an outcome and should not be treated * as bad (false). */ - public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request, boolean throwError) { + protected static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request, boolean throwError) { if (throwError && GeyserImpl.getInstance().getConfig().isDebugMode()) { new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace(); } @@ -849,9 +846,12 @@ public abstract class InventoryTranslator { return -1; } - public List makeContainerEntries(GeyserSession session, Inventory inventory, Set affectedSlots) { + protected final List makeContainerEntries(GeyserSession session, Inventory inventory, IntSet affectedSlots) { Map> containerMap = new HashMap<>(); - for (int slot : affectedSlots) { + // Manually call iterator to prevent Integer boxing + IntIterator it = affectedSlots.iterator(); + while (it.hasNext()) { + int slot = it.nextInt(); BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot); List list = containerMap.computeIfAbsent(bedrockSlot.container(), k -> new ArrayList<>()); list.add(makeItemEntry(session, bedrockSlot.slot(), inventory.getItem(slot))); @@ -868,7 +868,7 @@ public abstract class InventoryTranslator { return containerEntries; } - public static ItemStackResponsePacket.ItemEntry makeItemEntry(GeyserSession session, int bedrockSlot, GeyserItemStack itemStack) { + private static ItemStackResponsePacket.ItemEntry makeItemEntry(GeyserSession session, int bedrockSlot, GeyserItemStack itemStack) { ItemStackResponsePacket.ItemEntry itemEntry; if (!itemStack.isEmpty()) { // As of 1.16.210: Bedrock needs confirmation on what the current item durability is. diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java index 04de68a1e..e2349e5a5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java @@ -35,6 +35,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.geysermc.geyser.inventory.*; @@ -55,6 +56,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator { super(46); } + @Override + public int getGridSize() { + return 4; + } + @Override public void updateInventory(GeyserSession session, Inventory inventory) { updateCraftingGrid(session, inventory); @@ -370,14 +376,17 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } } } - for (int slot : affectedSlots) { + // Manually call iterator to prevent Integer boxing + IntIterator it = affectedSlots.iterator(); + while (it.hasNext()) { + int slot = it.nextInt(); sendCreativeAction(session, inventory, slot); } return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); } @Override - public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { + protected ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { ItemStack javaCreativeItem = null; IntSet affectedSlots = new IntOpenHashSet(); CraftState craftState = CraftState.START; @@ -478,7 +487,10 @@ public class PlayerInventoryTranslator extends InventoryTranslator { return rejectRequest(request); } } - for (int slot : affectedSlots) { + // Manually call iterator to prevent Integer boxing + IntIterator it = affectedSlots.iterator(); + while (it.hasNext()) { + int slot = it.nextInt(); sendCreativeAction(session, inventory, slot); } return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index be10452f4..869062da2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; @@ -41,6 +42,8 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.inventory.*; import com.nukkitx.protocol.bedrock.packet.*; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity; import org.geysermc.geyser.entity.type.Entity; @@ -59,7 +62,6 @@ import org.geysermc.geyser.translator.sound.EntitySoundInteractionTranslator; import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.util.InventoryUtils; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -316,9 +318,13 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator changedSlots = new Int2ObjectOpenHashMap<>(2); + changedSlots.put(armorSlot, hotbarItem.getItemStack()); + changedSlots.put(bedrockHotbarSlot, armorSlotItem.getItemStack()); + ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket( playerInventory.getId(), playerInventory.getStateId(), armorSlot, - click.actionType, click.action, null, Collections.emptyMap()); + click.actionType, click.action, null, changedSlots); session.sendDownstreamPacket(clickPacket); } } else { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeTranslator.java index b3a04e163..da35da60e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeTranslator.java @@ -31,7 +31,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import java.util.Arrays; +import java.util.Collections; /** * Used to list recipes that we can definitely use the recipe book for (and therefore save on packet usage) @@ -42,9 +42,11 @@ public class JavaRecipeTranslator extends PacketTranslator inventorySize) { + GeyserImpl geyser = session.getGeyser(); + geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.name() + + " that exceeds inventory size!"); + if (geyser.getConfig().isDebugMode()) { + geyser.getLogger().debug(packet); + geyser.getLogger().debug(inventory); + } + InventoryTranslator translator = session.getInventoryTranslator(); + if (translator != null) { + translator.updateInventory(session, inventory); + } + // 1.18.1 behavior: the previous items will be correctly set, but the state ID and carried item will not + // as this produces a stack trace on the client. + // If Java processes this correctly in the future, we can revert this behavior + return; + } + GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]); inventory.setItem(i, newItem, session); } @@ -55,6 +73,10 @@ public class JavaContainerSetContentTranslator extends PacketTranslator 0 || stateId != inventory.getStateId()); + inventory.setStateId(stateId); + session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session); InventoryUtils.updateCursor(session); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java index 283d95fc4..4bb2a8e60 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java @@ -30,7 +30,6 @@ import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; @@ -40,17 +39,15 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.inventory.InventoryTranslator; -import org.geysermc.geyser.translator.inventory.CraftingInventoryTranslator; import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.InventoryUtils; import java.util.Arrays; import java.util.Collections; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -72,14 +69,16 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator 0 || stateId != inventory.getStateId()); + inventory.setStateId(stateId); InventoryTranslator translator = session.getInventoryTranslator(); if (translator != null) { if (session.getCraftingGridFuture() != null) { session.getCraftingGridFuture().cancel(false); } - session.setCraftingGridFuture(session.scheduleInEventLoop(() -> updateCraftingGrid(session, packet, inventory, translator), 150, TimeUnit.MILLISECONDS)); + updateCraftingGrid(session, packet.getSlot(), packet.getItem(), inventory, translator); GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); if (packet.getContainerId() == 0 && !(translator instanceof PlayerInventoryTranslator)) { @@ -93,21 +92,23 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator { int offset = gridSize == 4 ? 28 : 32; int gridDimensions = gridSize == 4 ? 2 : 3; int firstRow = -1, height = -1; @@ -135,62 +136,10 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator null; }; } + + /** + * Test all known recipes to find a valid match + * + * @param output if not null, the recipe has to output this item + */ + @Nullable + public static Recipe getValidRecipe(final GeyserSession session, final @Nullable ItemStack output, final IntFunction inventoryGetter, + final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) { + int nonAirCount = 0; // Used for shapeless recipes for amount of items needed in recipe + for (int row = firstRow; row < height + firstRow; row++) { + for (int col = firstCol; col < width + firstCol; col++) { + if (!inventoryGetter.apply(col + (row * gridDimensions) + 1).isEmpty()) { + nonAirCount++; + } + } + } + + recipes: + for (Recipe recipe : session.getCraftingRecipes().values()) { + if (recipe.getType() == RecipeType.CRAFTING_SHAPED) { + ShapedRecipeData data = (ShapedRecipeData) recipe.getData(); + if (output != null && !data.getResult().equals(output)) { + continue; + } + Ingredient[] ingredients = data.getIngredients(); + if (data.getWidth() != width || data.getHeight() != height || width * height != ingredients.length) { + continue; + } + + if (!testShapedRecipe(ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { + Ingredient[] mirroredIngredients = new Ingredient[ingredients.length]; + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + mirroredIngredients[col + (row * width)] = ingredients[(width - 1 - col) + (row * width)]; + } + } + + if (Arrays.equals(ingredients, mirroredIngredients) || + !testShapedRecipe(mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { + continue; + } + } + return recipe; + } else if (recipe.getType() == RecipeType.CRAFTING_SHAPELESS) { + ShapelessRecipeData data = (ShapelessRecipeData) recipe.getData(); + if (output != null && !data.getResult().equals(output)) { + continue; + } + if (nonAirCount != data.getIngredients().length) { + // There is an amount of items on the crafting table that is not the same as the ingredient count so this is invalid + continue; + } + for (int i = 0; i < data.getIngredients().length; i++) { + Ingredient ingredient = data.getIngredients()[i]; + for (ItemStack itemStack : ingredient.getOptions()) { + boolean inventoryHasItem = false; + // Iterate only over the crafting table to find this item + crafting: + for (int row = firstRow; row < height + firstRow; row++) { + for (int col = firstCol; col < width + firstCol; col++) { + GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); + if (geyserItemStack.isEmpty()) { + inventoryHasItem = itemStack == null || itemStack.getId() == 0; + if (inventoryHasItem) { + break crafting; + } + } else if (itemStack.equals(geyserItemStack.getItemStack(1))) { + inventoryHasItem = true; + break crafting; + } + } + } + if (!inventoryHasItem) { + continue recipes; + } + } + } + return recipe; + } + } + return null; + } + + private static boolean testShapedRecipe(final Ingredient[] ingredients, final IntFunction inventoryGetter, + final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) { + int ingredientIndex = 0; + for (int row = firstRow; row < height + firstRow; row++) { + for (int col = firstCol; col < width + firstCol; col++) { + GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); + Ingredient ingredient = ingredients[ingredientIndex++]; + if (ingredient.getOptions().length == 0) { + if (!geyserItemStack.isEmpty()) { + return false; + } + } else { + boolean inventoryHasItem = false; + for (ItemStack item : ingredient.getOptions()) { + if (Objects.equals(geyserItemStack.getItemStack(1), item)) { + inventoryHasItem = true; + break; + } + } + if (!inventoryHasItem) { + return false; + } + } + } + } + return true; + } } From da33811e3b409db890abfdd2ea909e391ec27da2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 30 Jan 2022 17:06:45 -0500 Subject: [PATCH 16/26] Init SkinProvider on Geyser startup Prevents it from loading in the middle of the first Geyser player joining --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 2 ++ core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index f63e222cc..fd90696bd 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -62,6 +62,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.SessionManager; import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.skin.FloodgateSkinUploader; +import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; @@ -153,6 +154,7 @@ public class GeyserImpl implements GeyserApi { ItemTranslator.init(); MessageTranslator.init(); MinecraftLocale.init(); + SkinProvider.init(); start(); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 67364f5c6..453568341 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -819,4 +819,8 @@ public class SkinProvider { }; } } + + public static void init() { + // no-op + } } From 36afd3f2ec42e62203d6cf10c0bac0b4e5afc0b0 Mon Sep 17 00:00:00 2001 From: David Choo Date: Sun, 30 Jan 2022 19:26:31 -0500 Subject: [PATCH 17/26] Fix villager prices with demand price adjustments (#2767) * Fix villager prices with demand price adjustments * Don't cap second input/output item count * Handle negative item counts properly * Don't get item mapping twice * Add null check to getItemTag --- .../JavaMerchantOffersTranslator.java | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java index 50ee68381..8af5c8af1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMerchantOffersTranslator.java @@ -43,6 +43,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.util.MathUtils; import java.util.ArrayList; import java.util.List; @@ -99,16 +100,17 @@ public class JavaMerchantOffersTranslator extends PacketTranslator 0 ? packet.getVillagerLevel() - 1 : 0); // -1 crashes client - recipe.put("buyA", getItemTag(session, trade.getFirstInput(), trade.getSpecialPrice())); - if (trade.getSecondInput() != null) { - recipe.put("buyB", getItemTag(session, trade.getSecondInput(), 0)); - } + recipe.put("buyA", getItemTag(session, trade.getFirstInput(), trade.getSpecialPrice(), trade.getDemand(), trade.getPriceMultiplier())); + recipe.put("buyB", getItemTag(session, trade.getSecondInput())); recipe.putInt("uses", trade.getNumUses()); recipe.putByte("rewardExp", (byte) 1); tags.add(recipe.build()); @@ -144,12 +146,31 @@ public class JavaMerchantOffersTranslator extends PacketTranslator Date: Mon, 31 Jan 2022 09:57:43 -0500 Subject: [PATCH 18/26] Fix cache image task from last commit and make it work with reloading --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 3 ++- .../java/org/geysermc/geyser/skin/SkinProvider.java | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index fd90696bd..6226eec09 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -154,7 +154,6 @@ public class GeyserImpl implements GeyserApi { ItemTranslator.init(); MessageTranslator.init(); MinecraftLocale.init(); - SkinProvider.init(); start(); @@ -197,6 +196,8 @@ public class GeyserImpl implements GeyserApi { ScoreboardUpdater.init(); + SkinProvider.registerCacheImageTask(this); + ResourcePack.loadPacks(); if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 453568341..4383dc4e9 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -115,10 +115,12 @@ public class SkinProvider { WEARING_CUSTOM_SKULL = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkull\"}}", wearingCustomSkull, false); String wearingCustomSkullSlim = new String(FileUtils.readAllBytes("bedrock/skin/geometry.humanoid.wearingCustomSkullSlim.json"), StandardCharsets.UTF_8); WEARING_CUSTOM_SKULL_SLIM = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkullSlim\"}}", wearingCustomSkullSlim, false); + } + public static void registerCacheImageTask(GeyserImpl geyser) { // Schedule Daily Image Expiry if we are caching them - if (GeyserImpl.getInstance().getConfig().getCacheImages() > 0) { - GeyserImpl.getInstance().getScheduledThread().scheduleAtFixedRate(() -> { + if (geyser.getConfig().getCacheImages() > 0) { + geyser.getScheduledThread().scheduleAtFixedRate(() -> { File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile(); if (!cacheFolder.exists()) { return; @@ -819,8 +821,4 @@ public class SkinProvider { }; } } - - public static void init() { - // no-op - } } From 9d908c5598215b2c07bf3f7c5c848680a3457b83 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 1 Feb 2022 20:15:31 -0500 Subject: [PATCH 19/26] Create IntMappedRegistry to prevent boxing --- .../geyser/registry/IntMappedRegistry.java | 122 ++++++++++++++++++ .../geysermc/geyser/registry/Registries.java | 18 ++- .../loader/CollisionRegistryLoader.java | 4 +- 3 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/registry/IntMappedRegistry.java diff --git a/core/src/main/java/org/geysermc/geyser/registry/IntMappedRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/IntMappedRegistry.java new file mode 100644 index 000000000..892f4a6df --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/IntMappedRegistry.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.geysermc.geyser.registry.loader.RegistryLoader; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +/** + * A mapped registry with an integer as the key. This class is designed to minimize the need for boxing/unboxing keys. + * + * @param the value + */ +public class IntMappedRegistry extends AbstractMappedRegistry> { + protected IntMappedRegistry(I input, RegistryLoader> registryLoader) { + super(input, registryLoader); + } + + /** + * Returns the value registered by the given integer. + * + * @param i the integer + * @return the value registered by the given integer. + */ + public V get(int i) { + return this.mappings.get(i); + } + + @Nullable + @Override + @Deprecated + public V get(Integer key) { + return super.get(key); + } + + /** + * Returns the value registered by the given key or the default value + * specified if null. + * + * @param i the key + * @param defaultValue the default value + * @return the value registered by the given key or the default value + * specified if null. + */ + public V getOrDefault(int i, V defaultValue) { + return this.mappings.getOrDefault(i, defaultValue); + } + + @Override + @Deprecated + public V getOrDefault(Integer key, V defaultValue) { + return super.getOrDefault(key, defaultValue); + } + + /** + * Registers a new value into this registry with the given key. + * + * @param i the key + * @param value the value + * @return a new value into this registry with the given key. + */ + public V register(int i, V value) { + return this.mappings.put(i, value); + } + + @Override + @Deprecated + public V register(Integer key, V value) { + return super.register(key, value); + } + + /** + * Creates a new integer mapped registry with the given {@link RegistryLoader}. The + * input type is not specified here, meaning the loader return type is either + * predefined, or the registry is populated at a later point. + * + * @param registryLoader the registry loader + * @param the input + * @param the map value + * @return a new registry with the given RegistryLoader + */ + public static IntMappedRegistry create(RegistryLoader> registryLoader) { + return new IntMappedRegistry<>(null, registryLoader); + } + + /** + * Creates a new integer mapped registry with the given {@link RegistryLoader} and input. + * + * @param registryLoader the registry loader + * @param the input + * @param the map value + * @return a new registry with the given RegistryLoader supplier + */ + public static IntMappedRegistry create(I input, Supplier>> registryLoader) { + return new IntMappedRegistry<>(input, registryLoader.get()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 3b0c1f138..5a60351ce 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -31,7 +31,6 @@ import com.github.steveice10.mc.protocol.data.game.level.event.SoundEvent; import com.github.steveice10.mc.protocol.data.game.level.particle.ParticleType; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; import com.github.steveice10.packetlib.packet.Packet; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; @@ -43,26 +42,25 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; -import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; -import org.geysermc.geyser.translator.sound.SoundTranslator; -import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; -import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; -import org.geysermc.geyser.translator.level.event.LevelEventTranslator; import org.geysermc.geyser.registry.loader.*; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; +import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; import org.geysermc.geyser.registry.populator.RecipeRegistryPopulator; import org.geysermc.geyser.registry.type.EnchantmentData; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; import org.geysermc.geyser.registry.type.SoundMapping; +import org.geysermc.geyser.translator.collision.BlockCollision; +import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; +import org.geysermc.geyser.translator.level.event.LevelEventTranslator; +import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; +import org.geysermc.geyser.translator.sound.SoundTranslator; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.IntFunction; /** * Holds all the common registries in Geyser. @@ -96,7 +94,7 @@ public final class Registries { /** * A mapped registry containing which holds block IDs to its {@link BlockCollision}. */ - public static final SimpleMappedRegistry COLLISIONS = SimpleMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new); + public static final IntMappedRegistry COLLISIONS = IntMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new); /** * A versioned registry which holds a {@link RecipeType} to a corresponding list of {@link CraftingData}. @@ -149,7 +147,7 @@ public final class Registries { * A mapped registry holding the available records, with the ID of the record being the key, and the {@link com.nukkitx.protocol.bedrock.data.SoundEvent} * as the value. */ - public static final SimpleMappedRegistry RECORDS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + public static final IntMappedRegistry RECORDS = IntMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); /** * A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}. diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java index c6cd092d4..b74573a4e 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java @@ -50,10 +50,10 @@ import java.util.regex.Pattern; /** * Loads collision data from the given resource path. */ -public class CollisionRegistryLoader extends MultiResourceRegistryLoader> { +public class CollisionRegistryLoader extends MultiResourceRegistryLoader> { @Override - public Map load(Pair input) { + public Int2ObjectMap load(Pair input) { Int2ObjectMap collisions = new Int2ObjectOpenHashMap<>(); Map, CollisionInfo> annotationMap = new IdentityHashMap<>(); From c295e47940aeed0b913d3836973e410ba422aab1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 5 Feb 2022 17:12:00 -0500 Subject: [PATCH 20/26] Ensure inventory affected slot is added in MOVE_TO_HOTBAR actions --- .../java/org/geysermc/geyser/inventory/click/ClickPlan.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index e6eeea689..45d535167 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.inventory.click; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerActionType; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; +import com.github.steveice10.mc.protocol.data.game.inventory.MoveToHotbarAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -381,6 +382,10 @@ public final class ClickPlan { for (ClickAction action : plan) { if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) { affectedSlots.add(action.slot); + if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) { + //TODO won't work if offhand is added + affectedSlots.add(inventory.getOffsetForHotbar(((MoveToHotbarAction) action.click.action).ordinal())); + } } } return affectedSlots; From 08a78731dfa950f8a4cb8f3009de366f86ef79a7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 6 Feb 2022 17:15:12 -0500 Subject: [PATCH 21/26] Drop 1.17.30 support; add support for 1.18.10.28 beta --- core/pom.xml | 4 +- .../geyser/network/MinecraftProtocol.java | 8 +- .../populator/BlockRegistryPopulator.java | 37 +- .../populator/ItemRegistryPopulator.java | 4 +- .../bedrock/block_palette.1_17_30.nbt | Bin 41709 -> 0 bytes .../bedrock/block_palette.1_18_10.nbt | Bin 0 -> 41783 bytes ...17_30.json => creative_items.1_18_10.json} | 1786 +++++++++-------- ....json => runtime_item_states.1_18_10.json} | 168 +- 8 files changed, 1047 insertions(+), 960 deletions(-) delete mode 100644 core/src/main/resources/bedrock/block_palette.1_17_30.nbt create mode 100644 core/src/main/resources/bedrock/block_palette.1_18_10.nbt rename core/src/main/resources/bedrock/{creative_items.1_17_30.json => creative_items.1_18_10.json} (91%) rename core/src/main/resources/bedrock/{runtime_item_states.1_17_30.json => runtime_item_states.1_18_10.json} (98%) diff --git a/core/pom.xml b/core/pom.xml index 4da6bdbe0..c2e8b5f5b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -120,8 +120,8 @@ com.github.CloudburstMC.Protocol - bedrock-v475 - c22aa595 + bedrock-v486 + v1.18.10-c2c5a7069f-1 compile diff --git a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java b/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java index f605f9089..c4bd05b13 100644 --- a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java @@ -28,9 +28,9 @@ package org.geysermc.geyser.network; import com.github.steveice10.mc.protocol.codec.MinecraftCodec; import com.github.steveice10.mc.protocol.codec.PacketCodec; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; +import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; import java.util.ArrayList; import java.util.Arrays; @@ -45,7 +45,7 @@ public final class MinecraftProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v475.V475_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v486.V486_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -58,9 +58,9 @@ public final class MinecraftProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v465.V465_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v471.V471_CODEC); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder().minecraftVersion("1.18.0/1.18.1/1.18.2").build()); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v475.V475_CODEC.toBuilder().minecraftVersion("1.18.0/1.18.1/1.18.2").build()); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } /** diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index b1066c79c..8238bcea1 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; -import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; +import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -46,7 +46,10 @@ import org.geysermc.geyser.util.BlockUtils; import java.io.DataInputStream; import java.io.InputStream; -import java.util.*; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Map; import java.util.function.BiFunction; import java.util.zip.GZIPInputStream; @@ -59,8 +62,34 @@ public class BlockRegistryPopulator { static { ImmutableMap.Builder, BiFunction> stateMapperBuilder = ImmutableMap., BiFunction>builder() - .put(ObjectIntPair.of("1_17_30", Bedrock_v465.V465_CODEC.getProtocolVersion()), EMPTY_MAPPER) - .put(ObjectIntPair.of("1_17_40", Bedrock_v471.V471_CODEC.getProtocolVersion()), EMPTY_MAPPER); + .put(ObjectIntPair.of("1_17_40", Bedrock_v471.V471_CODEC.getProtocolVersion()), EMPTY_MAPPER) + .put(ObjectIntPair.of("1_18_10", Bedrock_v486.V486_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { + statesBuilder.remove("no_drop_bit"); // Used in skulls + if (bedrockIdentifier.equals("minecraft:glow_lichen")) { + // Moved around north, south, west + int bits = (int) statesBuilder.get("multi_face_direction_bits"); + boolean north = (bits & (1 << 2)) != 0; + boolean south = (bits & (1 << 3)) != 0; + boolean west = (bits & (1 << 4)) != 0; + if (north) { + bits |= 1 << 4; + } else { + bits &= ~(1 << 4); + } + if (south) { + bits |= 1 << 2; + } else { + bits &= ~(1 << 2); + } + if (west) { + bits |= 1 << 3; + } else { + bits &= ~(1 << 3); + } + statesBuilder.put("multi_face_direction_bits", bits); + } + return null; + }); BLOCK_MAPPERS = stateMapperBuilder.build(); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index d448bfa6a..1b56a83de 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -35,9 +35,9 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; +import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -63,9 +63,9 @@ public class ItemRegistryPopulator { static { PALETTE_VERSIONS = new Object2ObjectOpenHashMap<>(); - PALETTE_VERSIONS.put("1_17_30", new PaletteVersion(Bedrock_v465.V465_CODEC.getProtocolVersion(), Collections.emptyMap())); PALETTE_VERSIONS.put("1_17_40", new PaletteVersion(Bedrock_v471.V471_CODEC.getProtocolVersion(), Collections.emptyMap())); PALETTE_VERSIONS.put("1_18_0", new PaletteVersion(Bedrock_v475.V475_CODEC.getProtocolVersion(), Collections.emptyMap())); + PALETTE_VERSIONS.put("1_18_10", new PaletteVersion(Bedrock_v486.V486_CODEC.getProtocolVersion(), Collections.emptyMap())); } private record PaletteVersion(int protocolVersion, Map additionalTranslatedItems) { diff --git a/core/src/main/resources/bedrock/block_palette.1_17_30.nbt b/core/src/main/resources/bedrock/block_palette.1_17_30.nbt deleted file mode 100644 index fde145ca5239cc9a74e13b2a4a2ac77fca4971f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41709 zcmeGES6oy<^EQgABt;O(QPe?DvO~_O1eGWtIp-ibXGD-R0|E*R89@-qIR{AtNX`sH zlALppynFQhe&>JoIs0PYyx+~o#hO~xwT9{Ls_v(r>Y4kno7evS;V;y~V$o_PS+AM6 zU5ZU)W7%hNf2n>y)$#Pqsay)? z#?+(EkF8^S!mK?TrmTN0;$^&Cm#^p?n;*!%NR?ApZO~iZU-c-amY3$78TRw?fF@I_ zilNCC9m~!dJ6CoUJ<1ENT+a8d65(&t;dr5rT(7=#K2TBsQcyS-y-B!W`ui~i1HSwJ z6a>b`*8`sHm*h>iA>b*~+LH4FFKe&F>hrJ`D&KjEcq$j{Elyqe7VGBkS$S$dwY$1W z>PE-B_4-@Y=T{Esec^~N`t?=@3#&=JA6LLf8rRc$hnryuIa1B_MIPP9BT_1>O%*E7eRIP2=Ll}e%GPA^Pi#Pavwo%xpZZm*to3N<3OAFrP?HKcAZ+(Wes zl_)x!q^@=NrZJq!y*#Rp*a#LXxxd=c5a~8z6_c*TUt?A7ra`%1$M=B$%xm4`V2_D2 z$Afy}4I$z00eQ?t26K72NdO(~`^bRJMHz9oLr36+fYa`o@x$1C-TNMHO1uRXW0b1Z zy1jN+8nM{#EA}_A%96j|OUw6YNG5u6&1P8z{~7uu`$oU3SmCv@WFmH;`g|Lx{$Nxs z7!?;#4G{pIO=icdgtRob-XCsMkXKonSC0a zJ8^ISd{H1J=2$(r^BaW3U5-^}e0?59{x(HaR$i8>8XHZ)Varsb;xdY<_Uv*tbf($MpRM{;b2KJcc};?eFU^Cw5h?_xMhK+wUlhpcB-#9!soyF?svqk<{@E@z(?O z{ff%YoClxDrZB2U4i0`;rONq_&A~0FpxUytq!o@$vE?%lpCn|p?a-!E3flQBX`XLNZ25S= zLsf54b~awLV0<}=UVX!f?-Q#~-+Wil9489WCzNU==I7p8c6lYf*hBmCW$u*2p@iCn zb=g(HQNU)7?_QsFL+!_ok3Fl26t0@HLxE` z%&!Ub74&tLeElO(>!rQZxw3dEzSZ0qUjWlzqdh38JA0}qojP?fcKWkz#Mw!+yCC+& zQ&cGbYMD!Bp%i^}i|E4Jr~WJ*dZ+Sf6~!qeckPPqmM%q|_^H5zGEqbxw&<|(HmS~S zpR~;B$L_+XltdYdV<;;nHqHor`@ZeVb?1H8m`_ZmxWx4DZr%5ud%3P!p1jC%pC;R9 zr@F5fX65>0uh?NQ*!*m!vJ3Wc5QVh7@4Mww_-5H@zKlrh__K$au6#MiFNH&|n7o|5 z5+YL3fxF2TAt_cNDX%5P8#uKV zrjk3oQjL?@`NHk&Q}V;4F5J0$d^tulcF~t4;>U4+q##k|rARf`5A<6eEeZEgF2A|@ zmWEWL`h2Itmpud%ZPvvRm3r`!YsQ<`9#33nnz&x$G+X!Rjo=D(nlPRymh*C&`TG_+ zczGd~J$QYOMYnW~4z%p7|L5fy-R!*A^_-<5d3G(f8M3Z9Z|!pN_a`9&;+?r!uf%kh z*2A4%%SrcfbeUed)^zy&($+^Gzqyy2osylc@b)cHA1g#Vc$xgGr@J0;PVlm-dA*Pq ztiG4^#od`ke6Z9=hLMaZ{Fc|d9s*&c`5dm+deLDdfzFnUHh7jnI) zeRWIpL5~UD?Q1=)#n<|IFhbMk^-Uv{Jc=9-dz_|G=9hP^lO3^}mL#3G?ZucLY~Pc4 zt`KyS>p3p*l(4hWGj2$Y+e+*ErHr%buXnE{gl>5Szqn>`$K?1<=7Vt|#u~RLIK)c} zrP7geMnZ+I%BV!?muK@!sfupl$EUMrepSpAhs_$J5$dON3>+JuH%r78&h4vLf_)o) z<;X{|+g{oH*-@OaraUcfcZl{8*?7m5g5RcX~}E zjJpaqt~cL_w!UHoik3&(zNW&B%Oi~?csHkFL5uKb!fcUM@r;zyu7=y2F9)6e+tWMrw4|28&iwK&adO6 z6f?X&ik{=FKX0bLe1G`BkjSsmGqPy2`tSthWneqfT(PXiZtVpGPyNbXk^-+OBaDP7 znjT|k$~$%umi*Twv7pAjVQ5R_7i1ZkAo6OwW%3Tm(bLL+K%2hJ_s8hx;C)A3>uR}7L?okUHTNKuSkrcRx7K~8WPE>^+&_|UNLn0%S|+GFLBle}KeS+5^LjSkzHYv*D0 zaXI_Khk7}^V|RgaMt&ocMU2rcXe7{b&vM=GACNU@>o6Qyr8}@XMu9_ zLN&S3)UTgRJHv114#o3nJi_n?b{~SIBi)_}GyJ*ixW{(mDN%#C9Ug49yJb5pEF;YPBCc9s_C%p--(Cn!ej1^STV{` z8nR;_YK1ehvc$O9$syQ~LO0jS_ZVTt_;+{$>h#;Q=w}X#x`mCKoh=5k&*DV;K*)-HC3tq}{hdrIc&fZ_+pa0;h5xEkT#}82V1_S_O_XJy9;Hz-XM}R= z2wXMF3k;BFMazQmp|e(8G2GUM{^<5HZZLt4HZ>guR>ZO)Sph$$7nP;WmlbqnH;4tBb_w~O0VYTyWejD zI*#aPPFQ{$sB|nvw`t%7)4bq|%e$$%v+woxP~W#SKNiL2*Um#-@BO(aY_ZK*@9mjE z#?+PJVSnm2b{_xr_2dtSy*i$SiJoY0p|03{DV0r3>}L2-P_V1N|1?UK?Q&x{;ad4t zZ=BO6ZW3q6=&d*J2EQJTDCM8tSKr1FR;bo#K4EU~dEijo;oG@9B(JB6%A2$<>31Ae z*VrT#zOAEgXr8&wy20k*^D()6dj#uux{|bRyZ3~EnVX-u{+H5RB;Fr`l)|i=&jJ`Ey>r%v*(w2&c4rU@RljRdD;IJjwv(uZx4zJ zG(MOL*k2#M`1V6vFQ0S2EwML!Pjf%bDn*@0EF!$}yw3MHn6lGDc2xQXp;MyFXNXFf zh~ZL|eCBJS*NIeCC66Uztx=3|VFaq-3FbJ!+L;RO5MTz<*{@Dv=Cgn1=ftdO# zx!nS$l5|RP=4oz0vj^x=iE8@KmY-zfp2$Dx;)vr=3X{w7Dae$ zJK-Qwu2xsYGOWu4y*|hoRj<5(Kj_{Yl%%ij)+G=9bP z#j978GA-G3Ly@$pp9RsJ^2R=C_{BdwTv-0nS?bA9K^&)Q&f8cYPW>F7uX4ir#y`U3 zP0?p0Ot2!{JN%dL*fqUX?Tc<1y!*87%xhmoY+(EYlJ`D9{yMEJx#0z^#oh6V`5G`l zL~?%z=nmrV09_RR8zA3*1C)5PB9^7*lz;e55%{ka;PbsLDwfy0tB^gk>!Z?tL9p3l@JxHjEUgWoz#P}?QI zi#p5DyfIl-auxL)y6G!x(*_w0NRl?Slc0nH-XqL(;nMDx$1v68@DG0}%;%BH_DJQA z%sJ1}(35)?yLf(Y$o-(L{_3eN=dT`~T2aOJE3`+KUt)R6jM#lM>(!RBWa@Uja3?d| ztEXSzNt<2|E{Ja`%&Ooa^fmh+*1f(I%xtDk#Pa>y+%&z~)gd4@?&?{quN$Q(E-&`% z9q}kgbK32tC&UeTC~fA>TO9jM5KHnm>k%sqGsxK!BiB6aC9f?t5no5(y~Ng}ccm#k zJ=I>=h71R9UYUqaq=tRmamZ4!hLZ_D50j5Ha$Ac~SUTS26c#zXJ|uT7$Zlxv#Tii_uTD9|7at_?&0{>5 zl>PG~b_Y+ZxvwlR_1!JMY{v7b*qz&`V;Pdt}=uHV5 z9cCSIhWDKwq%hq3l(H;tw?^Alp`pLzw5+_>PseJJ^29=cjORX>ZkLgHZwN*TTx!{8 z_Ih#i3i;;A(4p?vH(Bf2FTTo+9S8U>XKgcYohtNiPaedqp3?6eN>CTtU6Xp*bvH-# z^77=Yb0yEWj5But)nlsL=XFzrCD+rjjGbL$D~Wd^pYlTF<~R;{*ZDasmD+Kj>H9vm3%`rrabFIo1{W2~g#mNf zui9}(wN#zCf5*Byo!(6>DWJ;jsvMGcZCLTmyPBJB5?|b3weqU3VTldz+j%=IvZvVWJn z`V@(gUPj z6t;M7)tjOfnNZqA@oj0N76+&tfNEL?sI)18Y6j;=ePE6cfvOjd>KS&<5%b$k-nX0F z*%Qle$~n`l^lOGMPY@FabxDoM(`1QVs~&o)QlDzYU(K9|TpM%~dk6@>K7t26%PQDF zrE?vqEHNs2pc=XfRNgm$su-hsim@UDsw6C+I>D%3V62FNsty~dp8EsUTZ|P2P)*_h zRVYSfhOwe~HM4)R33!}@KD+7Dl>bea-sMV4!i%mv3%-3oJhee`vrv6)o;e}8g&_!dWYWprhQ#F`{brn0uNNyRT24B_b3xg)h)+%XUM@C2uF$T0-Ha?ZbE8zBYlOGrH(M9#z_ZyjUDTg!XVvI6R(=6EEPGx^SDCDrV$ z5HIw}abkw*V(+~WzEq#X+3b=vJ>wqA;a^isl45bc8q4TJlH<_2J^5FQb%vfPcE3iZ zrt8=_jgHA(IhO3ufdOJxMd|z8mp@dA>r;zl$_!CSE-x7qN+l0rOSM-;GCY}eCtI?4 zBt7w!qpa@7x!B<^WiDnC&{r`PGqU3sgu9B*zFnhK>-lP*e*e)|`D944|Kw}m%s@UD zPy2~@i3_>pp5eC4(n#UCXlm-d7f0?eGaaGBvwKVYTh>_|EJ^;|#BtqK&bW^4+D|i@ zHFxl)7#GJf-*LDotr9FKD652g{vK-n!0SWFizE>#A<{ekmDbY8(n!&zX6nSg7ndQj z<|ZPy_53THk(u0)!az&i_v|R73;dJYw$FD)W-hT2vbu5tSn;Gj`!5rPq=c}lKYJen zNmPK8-oL^C`G5axx9|8@SV({5juZr+IIj2Emiuk9w*t2>uR$YA^(QF^KhT378ZtR{ zGbxgSfAv5@(CqW~SLV$RcVc@w+$l=hbp(v}H9PJ*tis{uO{_byy?=YBBfv0E-9hhw zg3FpWu|Lal+?c?T62U@Yg)buHQ(02|&kdQJy0gH02I@cHNGU#HTliGtEsa+e`F!aH zO=8~%rw1HqQM&{tK#N@Bjuc;-16}>#_KaY2y!?(Q7{K883235-Jb2Flf-Qu63MBbf z`%i5?hI!2ndZ*v;9`lEOt(n|hDL9QkwT0#fsXOigEgXz(t0^}}GEU_+RDxr>-2hv5 z6LkljnRUC>8X1^^JEgpt1H-FL@jExmaDd&%Nibi3`>D+{Kd5PcvrLNn|IaKUrk>=} zV2gs$#_CzrkWXPs@!#-w&EQG+e?5uUOL#L?OVe=8A2=bR=yHFkd;{~`J^DpjyGul!WZ%-;tkm)ro|`h#C@=mQ+ZaKRq?0vOzw-dtYYQ3#0zr-=V;CaW1p_%vdFG|sFI`&5U5x3>#W~=%;2qJFx zSWh{8^$?_s8|+!*?^Kwpnr(ysbod(2Oh-wR(B|;9I+Tv`oC(+YUK;;b!9*R&ohN;5 zfdpK-k{wUSq*~#nx|01=Gg8=a9zDr%_hl(;qH;aSNr7!CY(gG=$zhFSDeOCy`jQ)u zF1viYU^i2K`f1vD5~frk-Aw%D7jNrB?4a_8v!#aT=F7Se|b0ZU)mG%Mb_BV4jYjp%OG^{jCoPIi2SsgR~66<({y^s{q0z zfp3P=3Uq_d0v_M7tl9;(D|8WEzAdoesfPKIiLJSZO!hjiiMk@)qxa$#)hLO0KhJYb zt;{u<+3N(Mbwv8vBD%ak!|-%Bs)Ej3%md^L%4VffSfM7Nt1`DWmud!$C?*QZ(r_AB z*$s+!Wp1N~JTh@OEA=cLZr7`8^(*hkP?QIs$=sgo^UZ8im}`Vo)(w7myrZv_pl1ns zUHHnTuq>_Ttk5F7u&n6Exq98Gy4EDwC9B##D?8{mdF4e6h48Q+@g{q^!OxHt!#nU$ zYSX3=gabYNCXGdNNcM9McnFnMOGp*DHx;qyXb7=CRx%Ya?_e0QVL&|h8d~l`{L42o^cv**_lO2(%3ElTOMb= zs302A#8+4aPZM9YfG*!mFcjIrzU;DQh2zCH`BB>j?mkOA*3Uv4h>X9z>~bZA2PZcB zY1;blz8U(%c}Dn@-u1-xz~+^;aP*%o=~R+hYyQ)3AyTyi-zC{5!^Id)R&5WMp|Y+S zilOQav_3M!4WOxYs~CvmgHtOiMikrNsj@y`<=};R1EJvgu!}Q&=3?ON4!&2cj7B%w z$=8n?m5nO4s^%^()n$g|B3S3P{W1f4W~qVH6<|*(vA2!LpUWd-?RsDlSDoxF@LWY@pG&mN@CtoA7cNOO-f?n(5sS} zJqTvX)e`k2I|wejY|*fr6LnW5lYb!1S$q0f+WL}U#=GqIVIc`fzj!-8;$jKltgaMF z2;jW`bw}T-n$XiGZA2ZtJLaqyI4IY7-P9$buGIk?4!8GI1tZI+&lAG>E8jmqnz zKk#ig&ha)xW4ceQt^XRtd7(v5;kRtdbM=KIb>)p$SBxIzJz?9ev`?8lzhGq;;c7l* zGDF}Qe?Z1RWwL_bHik$~JY}-2BQj18rhziqCy^Sb<7z>ftn(<1A;F1ICU**cv&MT! z$-Y@+9$za0!90nWilrhg_u)6IW=6L5EF$G9+YmnmClSAqRHS^LXt`%-j6VeTGPyPq zo3{nBbTjS!Y%wDZjZ`WP73ru#Nz$9mT0@9nnNO~&*P?~sff!wu1u>5r32*`8|C<$LaLb0 z<9<_sU=6?Gnz_bcM0r+tltn+4@+bwD4eA+%B^$13)I?$&>rvF2_K}GVYUO`h7Or94 zLNb4JU>G{k6|P}4`{HpdRrzojW6p2*Y}u4$ z!P959Y+5R8P+JauibD2SDuG~H*a(#@L1KG&n@u<)E|H6|oc5O|<4`7V#Qls9($ri` z7D)`4VdY}7j(lwVA}E22$sR`?8ThH(RBqv!J~A+z$4qY0z#JJEQE4W3z%va`Q$+?U z{_g4K>q4Xn*2VvJr6%R`=jWb!YJB7IzFCE~ncTvo>2Aj%cxYlrBFZiiI$->r%HxP@ z0AaN7wOt-c0&U5n@<8~jz(x8yn5F5hhRGO+psrz2!P>^)LNC(RffbPiY=ab_dj=Wc z*$#5>z^vvZ)$+9yv=7~s7e*IMV<=+dfe>{gNGz`g`;dEiT$0bpk13#h&4TM z4MAya&YA6o@K9T0(wU2`&mZD8-BKw^EE>gH z<@pmkTEHs5;E&tGw0BJcxOEY^8L7uCSn5S4e*#+zu*wthx&vD}abOD;_znJ+kl9=3 zaU*I}#9RSaCFq+Bmq#(=$Uv($2gLo$ve66Ncn*jOVjW_HL~%eY;M$-i&hhQyEh;7m_2hPRQRdUzGYA=6v=4!n}g=UzqPo{SEWVhJRtc z1oi|>m|sGSf)(go8z;;9Ct+gd=sHErB>?~)2|)b?=Do2^H5Ip)HG z6FME@ZJl=wxCJ;`Y&hsGDB4XMI@nIGAlbSSaiEEwHh(>aDY~+u~o5=bpq+fWC*JFJ)l&)*3^OD3qQWNFF@J zL$<<83?#b+h@=5HG?W~EKq)OrP;MwWi7y5Zp?}vJ^3z`d9ztW;7E;ut2M?jMLx%i~ zbm9L*ddR;>XNUfabd~=^I_zJhhyN$id8M(zGW`t|C~3?Wg-Z|oJAfcj{@Zh~Y=aRs zC$&riOFYldaR6Yf!@~lmi1#-I_25R#l=H5##HZ7a&?T zg$o24uVP5RY7lk^unkn(O=lN_ZLFBu$#(&^X$Amx0HXjCPksUvFJ209q=!rI#KLaU zFIFQWpJ%~tQZLm2C|Lo!Ni$y?@;(X)yGgrU7c%>G6c$3eTpyC=zXS`RS#JpW`TGbK zlHBTtvI9Ve^bcpQJSW5i_sX<#QwrN=8elhXVndY^&Y^q|CHPmSRVPxCU~TKJYz!Iy zN*ZisSAnwg+x_pmx|@zoVSeX~wRS~ob>{RjUOsPi^oKZ)g0e`dELLWduj?{lnpU;Z zA7cL$e0#r$WM#J2UydQG3qF(y(_RBGg(4AP5}!$R<=K=YR%Q_GxEHn4Zzl7(r_H4j2!(M4rd zK0X>+2lUcx?DjuMf!?}pu=!}$$TG?+p|}iea2gw~pq;>$O+kW<-3*_fB9}3i0wgXJ zh0L)OAX$fvQ^?*7l*l?^;)Vwz}IbT0Q$-y|8np|iyL1|#fo%<}Gi^Ut? z&a7Nh8mH%X&qhU8cG0c6_{AV6PcnMcjH?JG ziJJdT6$<)|xR9nM1sp}`1d}m_qi{_$k;8BlixkwPpxfM24~#J!MG)Fp4#QD+JHc=1 zZ6GIveZ934-6x8NccgsT1T8`z$`WcVbtq?0Xq5~X;iRzfniMn35>5`qWCn3on*NBo z?W1Wmu527jQyI)HOE}e+kSVP=-vg=qrQGs;$4n`~^v822_tICJ;OSd+nq@(nRm!0i!3CE|k=+CZt zekVMP+G-%XgIt7$TzvGK(pM~98gl-@EG7Q{9~yGu>1?In`vSZl_&{s03OD*yMmm#& zRgkvj+ere29%t_Gu%wPNy z`uANkY^AV!$IVh;wU7ctoW41v(N%-yUvKZ-PRwirnTyc{P=I;YsGH%Jg@L_I>fEQa3}osW zWk7Koq^nYwKrtI-n^$jV(H%6mNkuO9>yyLt00L@qp6)0mrFk~s+Q`pJ4;kcv-%iIU z{g$wyCKv3DQ!>N~rzYngjaT~Gl21)88qQxj!;;Yqrd=)3^ld-gbhm9S96#}AqNXiE z$^Wwl_YgZb3z&7YDog1$`GubD%AV|%ufVC#lAg#xNd`c&8jnx{_uW;%$t`AqfAd;| zMTJlawY_T1#{|TuWTQtn@D!k6YJig$H38d&Kfv}cJ)!iEo~Y>jM^7+O|Dz|Q{?QZs z9slTwqyNwobjBazYKto<7hAUnr;-y^n1;vTj5wV8MwSm@c^b+cfC(Tg9S&i{Vc#_b zIjfC^@^}LWNJoiCW63Iq1AxB5;I-13K+(6KqJ$vle00g`%Ka~0{Z1ZB1`@Zhk(;vk zfSC##!2gWvlXVc4nMq@I!C|ZNp#S9m53z-6}4WxEI5i3e};9=AzO$dY` zJ^U7pc{3s#$^j3hvTQ+A?TW!e>8)B3KOGd{p)?k4h@xUWcqpAM67e_GT|q933H7`G zLS5VFU#MqM{R?%9f1ytMpHP1XzeO*UIDVm!L{6H6Azp|Q0G8_)I zQA#&xN9-8-!trTLI}m9O5tw1^L=-(wf#0IG`i1yOu0u^KHW~|!!tw#M%OC>k5Rgnw zDm0w{#VqX~)IjS;er`0I@rR`yvmmso+&K1hw{r{pR#JB&$~GPv_1T@fOP-s_7Gh`C zXoJM~?gFYy@-Oki4;-2x(P+UC7Z5McfG>Mw1HTt(9S04U!B*KGf}tiZ zmZ*s9yc^H>m}LNy(Wux2xvLYLy|N+)|vN z`!8SB(q&fS7Ayz}FZfbKO{ZSQ_m6mY{LISe{B5439&OfQF~|O2r`~Z2D^T{y_<(2G zC<>LJHyQ?<4Agcm5|>&4&@luF0^fdr1a!ilM0MaAFK%gX&s7?9x%!>VAQap(y+5mWjj0r)O#CB~(~8yEqCcmaoS$ zx3#Pq2Gfu1^)3C+P=#e+jZR{=ABl-2Y^aDUKM*mb zx6?o&_GzRH>C#$&W+|d%fV3w{{~d;Ou)LYk8c71?x!YbA9x~AYz#AFE+>)bl z1=>gwtZv+c7ITpxrGa!HNz>6womi!+?%?3Knx* z?Z5nu2q-Fn75$rk5a4g#WPoRX0)EmGbeZxwHyjUC8scp}?mhqo#g_1=^q7L8Gc6ns z>}Dw254-lDpxDL!lpa%1Y`l(a;g|_;APlv6N%EI>iQAM;B`dZ7WJ}0LK*Y1#=S9a+N(v9G#-Uv0W0)@6%T75}Oq0M<676N9R+SZpCa1)&Sn}EPvY5h8pXmfsA^(a0-cpsl8jib;?^S3It`-*Uo$o+ zh<1wVd$Cji$H4~g%EIHD!=L!bot0k%2_}#&7$dNB|j@5u$cv4HB|(qlZKH@CoT{N{LN18? z9Z+Nxl@0{C`38S`$n4_TR3&u4#(J(6)QxsZF1ChVP=IO^S&2PBD2Pl9j$y~tkqk}% zcriS~K%BaEAUeom(2rIfcxqS-W3^QcL=1300@WiNP+Xil2h}4`Q)V5-C@@6i89+6d za|Epb=7fQ0m%=Fi)>SbI;O~ed)~5e{RdubN#JmwO@&9$)zSTG}?MB*2;^t;%RI8wW z7DH0o?ZYU=#--0p3{GgsbSSUKkd(H1WVW*M>oF^xofd!<-dbH|TzL%W$DK2=H|MLB z=Z?yBn7=v+6pR!8hkM^F_!4r9PCd`;Ka0ECze5B@8LW)fxb5WmJuxIau=%jb1({p- zR;wOuS5QL}+`FW(Kkezrbvzi73M6pS1*H$P6n6NouH?^G^HPD~ZeaO-vo6&dzON_Q z|7>4MGTcR9a@PJ#sufdYs|3V6rpT5BP9R{4Y)6p4MK=D1e?_)YaQXmKWIKoaEwZ7( z8JqslmMwKOyh`CCKqju^tq+NyR-m#(#Sad~5MU0*;L6ht?z{j1CYv0bq!}*;C}#Zc zNty+KVtCwvEv-1P1rTn7zs?=h95`|}Iq1RZ3DZhcTMIZSq-pE2`-WSPVn&81NW>xl^KM$e%5L^`6F`>P+QAzXwBXo_ zXfl8*j_WZLmHl3sIPARomP%e#g=If4Hq^B+-tO4N%8se3X0FCiVA9m#BbnQnGar z2!x4r=)XwEV0BO=wJK~SEt2{<=E$o;pT#0yTvz(FmlX%qu?@cNhr z?HMoysILI*46guNd$1kQfI5RzKf$~|I5J|UZ76~{GGfmF#{)g)n!=rL0%s;F5)DK! zM@AfP!-K(*5tQBg-J&kH3_A%UbX4nKkYZXp@K}m%0w5Ek-vGZnl*Ql|%@P1w>h=Iq zNk76s8`n=jY>vegqDtKXv`O>9oT))O0kL0@PJIIdSimW%uzgTpA|@3m(7+U&N?qE? zw?R4SZ&?)sb};U~2^1g99CV^E3IN?96BgKZ)giM=DX?4ACN&|Eh6S(?YV+EVG-w0t zCY5DfNYQRDs6$!Rhg3OCgYuI_Lr8Y<7VIXSEhrgbGW1x0`Y{>$0pxFnjs_8d$4uE?51(!`UHejTY&Bzc%h|oA7jU>2RZfR*a@zlw+A$^9YtdY3 zE9X~Mo>|<`{)q6%?#k+>U+u-|Lgz~9g}(dWZ;BoGd}&87j+E4C-nqMWP%%Mpce%oI ztz8g3HhcZXCTq2CT4$Ed*JgOIwiEl;w08yUcN}}lTbl~1E1vc9j*&dE=uxE%7!kaB zWB88afkJ=6{wLC^r5iUs<_lgu>)`#aw0th{e2lKH_oTh4K+rpRpJ=#ZNz5*&x}n!q zKVU=0m;ni!sOfD;VoN43Soy4{gY)F!)+v#XP)^`0Kdr(Z1EdIv-9-pRTP zUGk%{3jtCN4%I)_n=2moqrHot*m+teafGe=rp`I5vK5{(>%2vwea($@+13Kie8N!Z zZp*tSsYd1bYOoaZ(&HQf;XDRm=S7DZ~rUX0gYRm}# zy9b7=s-yhxN;tz^@$E`X-}N$OC{ABUp~k-HJ#;-8B6Nt8oqAAP$6~zUl$R7S3OA-u zeTRJbu!if-t-%Bb0_m5?ha;uX=RwpWq1&)akz-&OpRE5R2pcmwQEe@_k9uld`g$H3q6|k-q+v6D=e+Uq^8+-gC#t|2$HgeVzAn zQ;@#>SFzFT5gjY8ZC|mG!S?#N=45%H@mnBKk&mAVN#h(fL1 zw4cF(^iOsSBk=GiQ|6yr-(r@=H_e}daJyxujPyeLo*hNGx0zh9^ivQ(Khw0Ji24N(AtnU2=@>niFl z@2&n=w`M*Zb~%dlToX!^7IjJ1O;4%mCENzL{0-|r)+etOe&14IoFe+-oXWf^rapM_ z=JhcH;}j)*Ljg;dyxVWFxz7RKIo~7teCjElDkZTZ(63)taE2nE%7pA>JR8W+eaM3pNJ-S$kulVwxKfF&2QFxCo{+$OdQM zwX#3|ejy)pdnAf=pwH9D@go6{OtK?%9!lK1LrLhU%N#63%`t3J_2ypnAUN(N;j$pJ zUbA=W@X#w%WpskwgsPoeaLn>y7&8s} zQOzA2+tK$vmQsuNW6T$?Q5SE_|JN6ihIu{T?bPLd^^&t?kL-@B zZ%rm;ch<&Aji%^XxVA*y57#}?YD70KFt%>LM+(h5yLPrA>veqe6wC|tyG5@aaz+G8 z;d?mIr99pVyXoNuUhTg{R<=L%AgRY9?EGx>Et0b1p@%Y@Y!6>hM6qTJqp9q;a(_g! zP``*7opBUog>_@&)&aRI-}O^`;l{&l)(vmttiqywn|Sc`QhrmQoQYlqN(qc|6M<10 zW0YYSCBt7!gt3q4A8ZI$h`KqYm`@17+{n;JMS4hpfzzC@O9%lZ9?yqTa(yZ%QsXP9 z`o~s$pIVkjYAfri_$SeFW9fT1;pC@5F)td@ieJnWuO)JBYJ9cRJnV^c&FeqTBiqY# z^|5Tn_ZeA3;}xr^7B}?f7#&B3_)e;X@IHz&0cA*8uK106UB^mq9>&Tvc;6==H+t>L zhH9(v!fo3oA!Xx0F*0fBl*q~zfq0AC`2W_J#32kEIys6SdYn`2*08ok zKc6{gH8YxO9!Gy+oRU?Jb>e)j`K$gBIoDPlR9V2jLd-;sBhG7Nb!n3&bqVFE zB0It#@;IX~;xgP+(}n|m^{ZyG;fl3=esZA^N&QKz?t(Vg@t#iDtNs@`d}N!}KAKnD zJ!Q-#Qhn&vtV=t~|BJP^fU4?w_lHqXLq(NG`I}csbjdX+3A>AO- z-CZJa=&nP)bNv49{lDvX?|s+$-}SD=p83pYo{7EZnY}l2_B>Cn42BGc`Cfl`qj;BL zXa>g|3qC%B=f)>gL(O;V*BbNC4Ti7squIvcHPRqIonwj|ABVmLd6R*%l|fzKG_((w z*E&X0DCNCO-DVONHr8ARO$ENqgW~TjESfh1N4Whn4oCK48VnTeofnA3>nsc!r%izE zu^D9*)1Y0g=yXQ%Wjr~*>>9(xqIaG8u)H3f3oIhJE>Y@i0E98;#&-4(6!bte zAT)z>N(euJz6JFa{eRalfw1A-glb-_%x=)HL>PWf@VI|yI%m(5@X`PKjz!@U6vYd? zU~t_K5M~2=rX=xrofA0;atg-%S%0ILpc+{;T-H-CB#j>ZF^;S?>B#@bJsPp2&4Ll1?qgI|4tMuugB;o&X4%M9-AZ9ZjHD4 z+8lj*7`{ z7i*RrOFG<;MiBEeR_xeuV&L_ZozQCZAVnPOeD#Q6DCA0r$?NTq4oh@cT$j!*dy3O)v)vdceV zCt*;HkRzzK^B?Ppqxj0Iy5H|`$f(&UHreDE_a*&prUJ&jC7tmY_NbmQ8Y{LLv#F~= zAt&MfZFXBME*{YOPpAe4U=%6nqishW4$Os{)!A-5=&96pQ_FFC>P*TjMb$}iVnn>Q zyQMvo-FK&jL(|wNX7W0#Oe;Ev2u-%f_2#fg|L|YL;`a6cl@ZmT(ub>zc~;l}?&59&dMcU7w|BAp#t-W^+I6SPPRE&I|-iB#z>zn2y8i>^ThsH^1!83%EwYPYmeZ47e3_ ztG}VB6#1)>4B$Gz_0*#Lq)VN0HCyBYA!-q?*^>qgq|*>nB`Z~IZVB1=u79y#Dbm+5HhGsr;w}PKt0FE!MV6Dne-c2fZK>Z{a5(8AdCs&NqOmbsjHi4nyjGJ~kiF86N1kiDeh&+S$b z|LOj6-ibx*Vy-^RmB~G_NOd>S-kmJ?-#(k^n7ZK@OADr`1!GOT`Q*G#6xUsnnsZzi zXRkz3YZuYCsxl-G8SSHiPuh%h#=co)<8oIIm(ObJX@MT2JjKN!4T)38*pm%yCx7Rg zI~SvMyz}(0{*4%e+aa4)eIZ3YZuK%!mkbgO0HE(C{crd%E zw`kt2hv9P1{?m>zM#!nlZA~Folxa6-V}8Y%I(OezN|SmaazUSCX?lWkd_unvFQX+L zT4U}a9U5VG_1F)2B04mpwM$7vXlMOcs%;BjQ03Cs{u@spcNSM*Wa%O+y{v4BY3<%5NTjB~o_j3aGxT}63hBBOLhb(C z@@jFjJ#G20#r*WirAc+k_C1Hdw}ii2c+U_&@s;%Lb*>{Y>#*q!RZ41N2qtQ?*NF{8 zmi3=q?XmqnUqC*cp5y;*=273sBV=Wxp72c+?Y*aj*@x{0N_IUb&6v4FVVvV3DTPN> zt1(K!v$hhCJ8xR(H!gDUV0L_HE)EATj>j7nQCs<)J8oV6u1gSCqbHYz4n7ymc=mGV zNDFG_EyuW=*1w|12eu}!kGQmV?$%+lGkEsK<-e#sk(_w4-GIri$65LzTuw_h`{~kA zkq{on%ag#GME;wrpATB-oRDr0_tj6Y&5Fc+vd(zahFVNJ_$_TV9@nBi@oLy?G+|RQ z+sPh%;^HuxA5~3H5Et5FKI($B=mAY)%q!vDq0Y%`Y^}MsvQRKm4LOdSDl+4?A3}UU zo|=X?xvPM+^4IVw)vp}5DQIctU$JQt8QF@wQ(6-GXK`#(+J7Sz9SQx`xKmRK(KxNI z{idF_qnwKu$|w2RrC(HGdaqOQ?~a$siyE&pH%B5<0+-OCKSiH5e_u!ZvNU#VD`ag7 zW6G+na{oG_q0T2hNpkGxCdh@W2=zP{RZ09h?J!u5rtCHlJMrisZEOV!~;8 zj_M6tEb84YY;xez+4C zf~ecqpqK|J1_U0Z{UY_noxlKWAYg?7OSWr)M4$g}>{Bj$FEn+z|9Wr-f}&;r=b(ME zV+~UO?V)Q7TD+zI=Xm7tvlW>>|9?CJ*Xe*DFX-ZzD>2Zm|Ig!7ydOAaydeI^py%K$ zBS9Cs!TIQeg3O?rmsOdb&(!7qW;afY>T~B`CDLjjSsFXfQ7)2F4Tm*))#jSwQs*sQ={TLI zZq{1H_I{)oR}U6#?Xu`t3&oOr+mLpTN@j%tlQfT>f7{SR0^rNY%sZBQh;r!(Z1G$# zt!O2MLab#zwfJO{zlL~`FD5L(p|sG;Ea3zH(2hT0;t)iB%imtxk+&Vjcf8|2-j`N} zGJ*mfSZpN;K-|lp(U?t&Ry~MYHkdpEx9@|YveJIN=2?|a#>NEEvi?20@tS0zj?9dK zdQ72?R0#SD`=yt{9jOHH00Y_AXxq3i9p5w)jLGTB!<+mi3KvCvaawZWH4}Es9j{6k;sfzbFiaY%s3BwY^gq zN`?a93qau}0I>ixYyt2MfWB=2ssWhW0iX+j?Ogz-Ge{*DCoq-8+n$cZ+=yl>Ngexn zE713r-Ahzc@}E&p#@v)hf zvu=`CWg#-2%^7ov+QvnVjGy@|Z??887QU9qCZ)@F%V(#52<8gLJY$obsC*+IS+-*P zmA#nw?2=$A!qpwkT#UCRpgM;{r+rTTcKgQ(jeIqFPWIKLu98wKS6IllYM?yenP>}j zh^Msvk=X|&0q;2d!+BzQFn`@Ob~vwdx96fZ27gS&XDLH~g=UWSRTBDNGmFJ1k^wXl zx%q~YF*IgLnEO0`@z4P?K`W(`{M(I2;yc{gUCDPci*;)ZxU&t{h6%EyL`+(SzDySp zwAScL2y$%cWLbjytdwj$z5)J^VIxr^uUH8%3D(4I=@#b2f2&-+$`X8xQ~L`U1+Aj6 z4u?GVJ&tEV=QJXXKO9Z;6$mwRZ<~K$O zu!Qef?6+X{DX=Z4thj`w9@j7;>s6GvQx8I zz@WZGK#IBBmLF;(*t+v=XGVyHdcF&!R*!cmF z0~^O$k546 z-0Euc)gWbc`XaJ7VVst3$vTjW#G~%|VvA?qptd=!cN1<8KOpA$pKR*_;ZBid<-m~8 zmU46)JP&JPT8680b?{=UNe?#%whqjJRTBYoVDZ2l*rc73W<~RwNF05rOvEz#i4dBvo59zLp?R0* zJ{_Ip^OnErdW&dN;!r?#Hf zrplSCLUKWT9~`=W#dY*;YiN2oIey>-L!PQ9a}AmbGfl z)S*`zs+1y96eIeR$o1!Wg2la~vxsz2#e$1RuT7HjNx|;z1Y;!eYd49r6y(7D7Gt#y z3`BnXw81_RweTS;hR|?(VHeqQF+=4HqOWtSJGV;<;njRA+X)phv~*yhZSp0WfoCchBN`K#f>hQF&6> zB&eId$T#lWL8Z(WD7e3Lj>i*A8nVb3`s){NHh6~;qHL7Z49d+MdNf*#*Z915Dg=*m zM&;ZWY2Hj{M9qTHTQH-% zgxbb<={d%8xaYT$*t_WmX(=R)+VM&{uxO7RtyC`mWH8BgUH>VvYpAGQ%Bh&bEt`US zG@AE)E&oocM%Zp!eP<8JKj2mbx}ax(F+?wV{(FJT@cuhliZOq4+R|EMU`WHa3A>%M zn!dau5vK!WZS9H4{V6-y>CR3TjVg~`?`UVwL3l6<4<_M3Yd8pcLC}%}53=Av0X!&& z2leov4Icaz8HJUza(cB8Azx9nx; zddiJc`wS$#Z0h1^)=bftUd}y<#MG1En={^eyM}wt$|3vH+0piv9iIK74O@Pkoj>l; z_rvbm5|VE1vDlOQj+w>B+p&8Xn2RDL0m%u5_Vj!=T0@ae_a5L@-f&}OUVYJe3*q)| zy6!xl?VLUn+3Ja0ONe;^^Xc5`!Fc;o%=5D?dPdq=8R_7AYQ)Z@XV`fZ{It`NMyU}u`I`KE zwgftN_inc3Lskt}>}OfZYWH8VZwIk-{*Q@XC5a1ZiHkN@?~=;@G~4?hoK1w*nI;C) zBG&6gV9&Q8W#=Y$p(-Cew}2^vRdU3zV~p8%ShnA{I8Au;ofexJ3GTGPY10x>qM4;ha5bPi7^h~8K)Di=G6)J+O2b9UEL9o zR4w7d0GVsq?ziX)8G%jraM4|ml5{d{4!x$*89Lqq6KSwB@c%DAImi`h68vi4Wp{4o zS~7}H+^)b=nsEc}6ek!w!xz%OWfa?M!~k+n{Hm9$mTU`yP?Hq@~0ImzP0$a9hsc;GQH^t`22B>>fD;LCw^+{(XP;aGW1|31$)13Tdv3+){vi!=QYp0cvjg8Ozf%0i(!6+%_5gW9?pZXL!Ji|Us|G&;hu0dr69M* z-w0_=JDWTqW8v6Wb|gHqlGCwP-$qnVTeyN8uGzQzuG6P~+tq#hmFJKgWFwL1{3O9a zJpcg3%gyFB<(5zE2I;$Z9?4p3ma}oh`vc#0ZH%W30=Zc!xfhbqW>>c-Q1fY9^V6~=4gJKNTFoipy6SkFMO zxr4*$TlD+l<&}&J?we4+Y;iRmN&Y5J8E&jK6Gf5Geus-9v7~?lPzp8*=Ydp<&=B`T zw@lVLRavq%`|TBC*^p9jM7(q|eT8!Q`SP^0SE7ObH|u0%Wjt8bUHjnr{26#$r3IEw z@MRBGj)_gw`(6iesoImuL;LnAXd&nt2K?+^->(0@@t4ngxK-C<^F4AF7f<)+rlgc@ zC&rAS{)3Lvt7sdD^ok-TZxvuHyFmXnntEZO<8KROWDxcJs3;z0c#&jyks(kd4+_LA zetp#A>aPifOuDRI^lrj!t1gB{3|Vd8%gKn6Hqn^1w!l55K!4egQQjV=|0}3O;T0Cw zKHSF_s_+vSem>;-67!G41&8wV?&y<|*KTYN9Xd(E5ww1YQ3y=$0BYS8P}O`IaU9jt zNu>$v;oI4ks7P9kmOhzMb@b02Ue`YIW!MB?pgAt$_S83}Jmwwt>`UP{ztgQxoPIi+ z0{=yhRHkp&jOWUvT2amVczvT(_^R#h!bLsBibR*BM0L_B9^?7eZd1Ndg7B+7Zc4-t zm}}yv?AVlILE_m&f>9qhg+6d4=Nx^We!&+*A@)=}f@p;T#qJw~c!eSu9-&Zp+%Szf z56bo5*_(iib%r4LkYo6W604ublo3ehB(wfHA621t=5hDmYikhK|IaFK~E zZ53y{IoYMB>Z+9+20{pS${%v|Hy{leYUi*y3ZVa58HGy zdOJLovL(~9B(80A9?ic<(=Ni!w>8_aV8yUf*kAFh$g@gI#`;ShE$J16zO05^dx_zF zr|g0DXcX$kN=R3bnfsU@PPn{BT{g2}PklDoaVZYk3u}$hY*^r*KXxad?{rzNTw2YC z?)aTNNAanU?;M60>D$**bz}m1Ia|G-#m4Tg?~p-nSeT`UX3x-L?VWyt+{HsCPGe&p zK)uEQ)Bv110TeYapp@^gAu+a_Asljr&*2OhRn^@5f%`^yLQy}Pl_uIX;Wzus>vZ}R`GPntpur{ z*EOPfS}ZDg*VKen%d$*W^^2R+QwoYfy}&Rw4`P};EAx8(L7Z6FxIS{K+ATl1svLss(Yf6;o!|ZF z249@a>O;@)(oW`7&o3c+x4MxTs(HI&7X~S@o5R-Bqsv?S#!H>kK1vsh-rSnAN5N@y z#TV53CA*g$&Gyin4wm)fxocY)mnIXjTLNS~ue zER<&Y%rHGqbNT|J3mvM*{xw7_S-KP2{KFpl^*T*zXlI9)5eI4v%M-?#NpMH7930*& zrURcK41#Ca?PGy?Q-1A*@fOcA3bk}t(~P$8dDG5!+J;xn%4z-i35E6#&J-LO(zFX> z%D$AIh}2_Sm0xbPw@;_laV>`^k&k zT}_4NxHLZ|5U?_l_32li@u+AF|F;pf*$(W3C2vN42^6dP%ssm%U3OyXBzVr1PhMjf zFg*WLlm`kbfr5o4P(@I%5EPu8Iw=4Jl{JQqL8dgwEGU7pg3R2S^kGnI63E0Vfo@Eb zufrUg`>f)_+Ym-|`>)28#&5D`DQ=37y2o!*ds>ho*R3lR!$#{c7`ZCu^8LX{VWL)B zCw~jW^iHkf;PZ?LOKzeiUoCl1$7&h>l$FMw4p(2m2-dD-rt}g=U2%qS3F%8$r2WImFl?O9Yb! z-aBY7WYRf4fYfu4HFfPO{6Vo#;{Z;x^m=IBDe(*6$&Qv>DzE;it#p^SDC3TV?cOr? zyW6Yr3oWoXl3WZPt>%IUNAdOJJMQk$Lv>$t%B|NLuhN&-RI5&ap)eO=GLP3By`N!w|0G>&P$Yl!Xt1 z1hNUdpHLQ#Yx`VHn0|QW9`QFaj^HzWJh!$q?V3cxL+Eq*dfDb;G{Jp%QB3F9vQ~Wg z!u8=+`%o+J$1&!dejkuVGdvABQ@rR8{-t$u8~43c_jlRbTY5wb9Y+lE5SfX_M z*gH%pUb2Iek$JMAblsJ;apoHt8MNj$!Tn-phxhh74uADp(UO2^@5PbN*wBLz;Qt6E zq-z>yeb|w6v0$igQj#h9uu(tIies$(e!}(Ap}A*>u>S^@t2ybVVx^uz@lO?H22no@;0!c&vp@H6zvya2`8VC#Ktov+Yd?6I zE53TlS-A;_IS!03cL&>7_cVB+dKVnRtTSRtfBKq5(vtQlwg>D;<&C?pgF;GYMQWPN z?gB31KMA%jmlx%AEI!uaRX)GAJ-dB#y;plxxXaVrF*hWfWgs{QU-QdS|8E~oItl?ik4ip4VLpl<8JJ=e-+74(zM zhQwiKen=U?`YDfwdH4Iqp(511R~J}}v$6P5E^$oA!K%T>ua0P$NvO1sJ$4FV}L_`Pgh@PRO1=JTSYH_0twU!}K2#;mNSfz!=oEFe*B8Z6#2h z`<{*1?d#%at}kdEJ9k&XS4Gb;XS^(VZ>;{D?-w;>mfgucNJYKWdHukb&(+!xYJ4|L zZg`3&$ZkfYVw@!(zWw%nVtxz9v&sBE$XT;bzNq7dvJ#3_MQ65rYwN=8wFCU~t6L3H zPT;#K7q=wRmE~%CbNN&JFj zf3~j@qa5+41)z?vQoX7GP57~`OKhTAXi7@eOJ{b7c2#|qxg!UCjiJb5tdJ)|Gi33> z1W7pt7^;PmxRfUn^Gub_X*^mm$@eTxJ*t!eYDKJZxp!pkG8?f4;--DkhY7PiiSZRl zLRsO1rZgESU*|xw3B{`*T*~xK0u44u22>;oB!Oganm}&K?mU(V=qyq=N0MwFZC;kU z9#!f<9;oyyzeMKD-_xV)vviP?!k5YV4U%h0m~ZN$nb8bcgY5`Y#^v}F-_(F>3c=|( zjP)qW76XkWZc(4~PIRz+mFYqF3NxTpNVa=(WB86G_q#=|oQb}@dY|#b?;!cw>z9t% zsscfu;nOc5nPovGXSAxIZbuzOujUMGtKgLP16OR|GWJY~7D&A|l2TdyUtFJEZO&65@;{_41>>nm#}fR<238$~aK!%2B=MPbw{RZ(h1xaAJng<-#Tq){WTV!XglshuCrX+56@2 ztyKs^z60FoDbiWJh^J3~Qy?rd`ncT27!$Y*O>$C_ zY+Amp+)`X)jpiSq`(!kp7nqmPymF2n6x&kg{_a>zi;n_vuIH8ggYbTipSRZe;Sb0c z@Adv9^^iEi!upNLy#_j2Y17dTydlDUrI3+TP|t*vyb)GDiV3+Nuu(ohB^Ny$357`Jo35_RYCl4p6r zhXrdNxP19BlPWX#nNXMeGwT2QUJbxL)nJBrQ?WcDIAu;1QWnDE>=?Lu`7w(s^97@z zmbgCF9Qd@FCOpD~B6OjX?Ohg{s-TuM1!8i~E5OQ_kOwY&VPnZe)A4zUSYbhTdMUkz z!HR6~-X|GeC;lUrQ~?n_3M=1wf6>QP(SrNr1Sun;=mq^I1_FfIzV%^U>)_e~7JuZ! z;{quYO+Lbp4=c)Zfy3%tA7gU2M8Q{${~E`x-IeLk<|u%O@KwV8lM$)rEpiV&Q*d$g+i5yhrKjN_GnO zCrA(8)eUgp&6SM?B20wl$ub!v=`ZdDG4 z>b6YOmI8&DHRt;*`P4_;P1oe)o!Ggx}4mvH=h`P6Y(6LO0rh3ViE=W$sEbBWChGA46NG!-lA9nVo5UbJDH z`yipM$%8ek&yX4oSLHf~uFmn+1GKXUnbd1-X)e@ykYjuF)*!OZLi9W93N8~xxY3?3 zN&1)FtPT8%W3BI|8LH-O^&8NFNZ|H2zfJ#|`;w2uSa{+%ZWKfHng07t2%lWcW~Fe$odsG8yFQ zXsuwxC{Dnn0wab7W;GZwmJAdq7_rqqz{Lb$2Y`zSa35euM>#;iUuL!-!3JI|FxXmw z8WnouvIOC;l&1*D@Dc9&)cbeAXF5>hw2=>R<+c#O;seSpx=|SgS8l=)A-HlI@L_#? z$h@dOBfhx;nlNhWwz^`2*U7S?&*2f`^6m!F_6Rgf?lrN_`VP!#7Q7K~-4+mSAB2a3 z5dthr#+MsraI0{kYC<%CN|j}ZNcav3?K7S50=FH1M=_1@e{rdPS~!_bG5qb7iFdcf zKfS7&{XTh0^Jn%DwWAX>9DF06Fgychb&sY1xNtV;9YDvy-~hJA9rw5!On^U0hv-ph zjQ<+H_JO~CJK*Ez)KZnq!^=e&0sNhGb+Sgo{hcI%`2+l&05$Uj{!T>Uj!OWG2b`(^ ze7Huy}b@Q00i>DMJ+b zv8&$F6?oeh0B^`!2~XcE(&D_|qqCV({VlKK)6L*BE#s2{(UzG$`{R+f!nK>?7PzkL zu|C+hqpUJXnq}thn|C2O;c_Z}TUzF%-;K%4IA?I zw9mLwrrlrspX5iKfW8MTa9Kp6K_AwidEp3uccxq|6D~h&^1RO*KM~<1}*&G z_3OPnNjBr97Cr(r&n(^c_pD#gR;6G3_0)=o^*2huco)XXLCW#V0xVB;j={nvbbdiT z0{>|zuM=$2Lx<5Rw@=b^I2^QFdW|}d^{#%;%8x`Km~ao12+Tv&AIv2~=Vz}#x{(|# z*|Z~N?(Kf}H;u%ZzW8M8@!v)nZ%PlaI?p_^>sn;7r!Rv4T=B_kE>nl>Tz|0jfHVJ7 zltEj1pa=>Uf`Xv_0#Hy{b2$$bRI>HRpea3I1(~^ZEyJLWB#^17xoiwFrK^CCU^CSs z5xz9Tei*rR5dYGBuWOZ#b!fcp3;t?mr9Gs=cRq!SX&lf=0*565GiteNiNHAy^lG?M z&ez-@TOxz?=8sk34FXxlvPQ7Sl5LpXe$)4ZG3|Bu)rQdFCnNskoYGLRf{2t%5 zv&*gHB9P{x1T5Z>e&2t8{RT2U>)b-;%XOWP&^^p2xoCTycAP=l)86=}?)eyXAPHIO z#apm@yP58lzcn5a-oqr+<$VqY{|@52?srA+S~nM?Utcxv_rxHqI2#1JMsnL_R5I2> zPvP~gIIocylfGVtMl9^?<~;OYj&@7+=p}}!!sZL=oUcj|`FY>}C_Wy_L2ozZLpXg# zR=@Qh0R$l`VDkuJgcXSekCz0&HNa-$PtFBdVX@17#Ff=Z^PThT?D-4ZbXn3DQ2dh@ zKB-vH={J1j_g=Qp^Tl^D3%Y_|4Q4Comw8u@=Im!@%_|y{k}URndwb1Ej^o9Mg8aq9 z1iafiziyOa2q>_KXE*FTZQhMAy|@t0X+RZlcEqXsEac-|w21wru2{w=)l7gyD9Tx@ zhlL0lD$;aa{iOO0c z>(fW!W8cHUvd(?xFSlr_Ety1}fXTP${Oo>AkD*=NmZftw$x)~ckI3-c8?o%#@b~~g z#m#Fgq&w(B|7;D54t-uvVC%NX4@ zGRk&e3h{tP{Oksw4p)T%5CZ^FB*4SKkGHaoucnQ!yp6BAEnS~ana~24Bl1eGd1&iw za`~sFjAB4xu)P2W1vM^ew(mXWChv9#N@aoJXz5(<7>+^u`2NunoTYFARy0}|<{TBJ(mzBEK|Ln^Px7OrQ`*7f6m zM5=sw@%Cp?ANu3ptB!(CP>&lT{2xG+)=~T9G^s?KSFUgN8T$;9MQH5o6TI6k)-Y`n zWi%e5!#sUEeRfcIi0UGKw)^ZL^AK&kMSp-?ig#%9o9h7vaw!G?4F~`r!x;cP?@w%g zJKsa_rrCBri;l86!QOc(jzq#zeEIs9u}@|1ax)o^IUe0cw9;(q-^d7h{jq*}g$_hy zFM&wgGIlE1LhxJjg5wdix|QaA;H^v>tfBh%h|z_Gj%63jeaZ9Ms@0Oi`gD~ig>sKu z_Un=-ME>34+YXzZcanN_j)bq77t~W?UTzk=?GRX|!bA-q3inPNG(qaH5T$r!#7gjh zIc4-okWbE$p=xnhsO-^NS=D!MyBGA8OnqIIC2QAZwf9s+!`Z)Xtthj7{S-bt63Yk* z(Ll>qV9qMslT?A~->;aRIXCa8tamm+_TrdCBgAtP<`Iqvc;W)pyz*GI%ny{}jTdjV zFg|(>YAHWb9O=Gg7WHp~t3MFEM%)>T!#rFMT~2CK zP}0OcYm!I0C@_S$J`;U&oY{>h!Teb3(TR{(X{BkQdlvO*9R%lrpCM#qyk{ z)irN+z}`jX)0*X0xkvK{!Z;SK$Y*mcijw6-VhITluB~jb!5v8)ElN{`#bONn)aBcH zNNh;AY9>Z*G$QM97Cyv4l#hjiG$mSgpa)~1hj$g%Rh4Bdi>v zX_~FiOND~(BMRR%)6hw#wl^5!8yi9uxlH=m%65e6X}QLj5)FgUj8rI(louYcM-I@! zu=R1p-JcCam$I)aHYtnz>4%(*rd3_vA3-@R4_-WYF}b1MhVlFbj^eYhkB<_pJ}B7? z%6@pnu>^qZ#}AKccL1P?S{2oCY?_T{$%oXQsi2hvpj*skpGoA*7gO}#B!pX z7f1tHkl;_0yEhTKCB)iZPL{hj5W1~IIb)=nXl2d1`z&-j>VFlBa&{n-@5P>V=ZA6z zDul}zV{NyBa+Cg7)>IQ-99ehB0R&lzKF|ccld~rUL#pVYAf%?HkTuzt@CXU zU*vF;48tYbPqoei@1DCanfn|vrT=a~rCxZ7MoZm3D@>*Zk(XB)d^ZPZf zv-GLewr>`ASm8ADogei3oR~)wE3~B9a^V!Gg_`!H={6yHTbo*ZU@W=L`{kaXnV*l{ zO#FyvKSx^p=zceo7VZ7?Dk~C+em4uv3EX-s+DKS`>}f<_DA&a5m8FWA61?MAsZ6PW zv=vb=AcrA^^m0oE=v73-WF@BN7`&lceS`m$Pl#_aisI+&FGQ+&MXf`SIR0k=86N6NF>^A`*fwsNx^_k^0Vu4)G zsz&Ei_7vsk7<{G{)D|IrJ@Rq+fCfL0t{M}m#OU=Cf?>&#p@0b*rZK zXP*!Z38FGfR}ixQ6A-4LjHMO@&(26kZD`6oQ{059SkSFLnJ7(}e%+t2{8tHT8@@%48-ZIUBkU9F5 zkf!~}(MqyDCJR@x9|PJn(Wy?|#_xJFP5VOx_EJt41%hcJ9?Woa^mLCdc4Q3B)nvcC zB-_C@{eEkwRjCbXLTWgKxCRur^Eq#E4#LU~f0D(%xtyk;o_Qm{Y2p(3dWV>WC^rs^z2kW(Yy zZnk2#P#pY%=Ln3TIOi^eo-wXu2lXHCw}({uMw_U>XooWq zL8bjAIagCz1QI5X6v2-x#beuTp)W zBy@v+%(5h%Wj1rx@Ka9Fql7)fPc~`V0gv!D0C)|6pMt?BrcWXOd;*{j4zvMyDGGoA z04{J~2EdGwmukrb-_smw!5Nu#$>%i+^_c!1N@YlAL80mXx%BznYNwQPf#wI46*Z?5 zW(tl*^E;-|)0PhwH;k`r)u{*_z2kdDiIWT~=npt9eY1ax8WQSOw5j4edUx59{qwPo zI`+)(l+cHS?jG`={SKI(S{#aq`k=K8zWLFnN&tkkPu58qX|ul=m$({>xL@m*=2f+x zTYYzUpEzd1^g1%_Cv{Fk8g?9ylY-LmjD)-?|HIEgFoUB1%n#d$Es5#=D0&i{eQG6Z zS}omhvOw{O0em^8isJd=GlfhdnmQ#+(HS_Q9kyLO9XW<7s;Ab zWdt=DW(n36^ka1dGG}ZF7E`Ii4pJE13fIbhonk3gX?L7Mnho!M)sFwpC_eB0#12vf zD~v0BRkHQtRvg`fYd3E!hU|~NxZfzqfAK5@iJ#O{lB2IAexn-YD8+ z$tZ+mSQPmxl`t*n&^pYsQHzLp^VIHHYX2y+4TDa1otu?&cO73sx4ve^{1$Vmtn+ET z7O5Zf)YBoo|AC$B&asb<)zOsHCZ2q}rD0^QwNl@Yd}UlNTurqo-L}a`__&(704P7k z$6W~ds@tX|4uCfR&0+vJ0YD4~765pN0-*O*S6*JxOqUGv4K0Bf{qqkGMMU@uzIcjf z_QYv^|BQix9rsXyzDfAuBab4ar>-g}*r!F9Z#I=*%Z=noW@d`T3E1AT;p;5=hko1L zaC)(JNOowWjXm7XDaV5WB`*)|~16uC0Ekj3GveTci?lQ-YJuN+W`C?jTV1`L-z+N%LkW ze?HLlv!EKXva5cHew4qWB2raY?h_MSnP@?ahkQxwB0r7Ur2LaT6%{`?JIKdcn}uvf zFqm!2$h?@4NRA8YYqNfVEs|D7t|(7k)T1Km|*Fwb{Gj0|HlHK^T17>^tI99kp?K75>dmx$A$2wsfDCDcTmauoNJ9mL=(Lh+lBEk_NPQ0c^f!d z)>f4ieBz$t#2`8=_|6r4oflu|Z*5i(0?Cb6>mFz>aW>m>8f2|@=;Tohr8>`%+U@o` zsY6rBY`wLs(mtE4uf=w6=^0nH96WToA5_?LcoIEtsUQ_@>|AS!P(DAnsmsOYooFmt zFr+;Yrhs!g^hTN8M6@6TfmKquog>GAdxF0woa-Ced92iK-n?=1lwx1bxdCgQNfA5I zcYPS*$aI3O77eeN$2+sQQ*!$7AQE%sx)_YOBifrviv)Z5kQT|b$ZO3X{w{p|3 z4V}oc-&olWf3{rWzuFcC^Vo`+@4I$(tthAYDa%!sxu%qukK5$@d?c;Zi%5=@`t~8! zdtM}IrY7N^NPgU&lZLpxdZV5q;e-O%xv(nM=fk)X$)X}JwW!wx5k7KJ9jGT=pwe#I zw>OH<{W&b&KeqX74SD~ga{2Ukmk0~v$=lS$w?^{P2j$)S?oHc0aVVrD+aQvG6pa9Uav!-<0u%o+?M^v!guxU8Evy&#F|PS)X1#>y$zW8$ zocOOjj{0#wRc^29;1|gsY;b8&YQy+^jQc1T*dl)(kUqFDNX5fXEs_xMj55f`W@E13 zymXDm`^|BP=dmUWVcGK5vAQ4JpsKwu_(&{*;^Ng<>WSa2?D7JyQ^rL;p>n9Zr?;vn zVQ`w3MTF(Czhqe0gRC(!eF5+m`0_dJ8S`T#&jWeE&o_KHNCCU8H_r;Q+0t<5X_SWN zN!usO>I^Aq5AtsX(VXz{J~yzi6c7CV;5ppzLytgp>2>;0^5-~%N$m#p{KeRhjk}f7 zHixuktY`s_?}9@OtJFh2O)|bWch^jo)q|pk20d*+AYf;zpF)6?r0LHoFS)Ne2mDHz z#8_Dp8uH_6>?1-sf7z2`sOf7T+#X7yyktAY!@w-IG5V9GOxY)gR!5)?k2qDnfY2AI zwhK+rS7RaIRa=Q!jxnl@5X(P3Ny%!L?sX^oc>HlY(tmb#Q}^in`#4+2n$ zz=@NFUiB*;bCQt-`u9|_*H=uOwQs=pm-J7;_m`o+5cykc0^}+#mvd#Hj>oo2%A+nd z5=f&rkeILX%&pfav*bEmor|OyUma(|%6m7E-3EV?uw7gQr=&OBPK-R;Z%ZeU!E#f$ z=KR^rUJl)V`E*~`^YH}}zsD`n?0%22}`{GsEUr$pk{ zj6XEes>f!V3skrQ5rM~W2VbAv$|8Nk3@u{1>)q{7v)!y-0~=eVelJb#65;tg@K>Hb zzI2#=u3z==c*I8!$krk^Wb*bxB(6)BXHPVMXZ8b8U=b-gi4mmk>U;Rj?ZPV}sG|xo zj|qiO3O_o93jIl22Z>50IZ3H?UIIkxP2pl!IL2qTysxFbnhM*G?SoBIb}UG=0^5@Y z=<>|pWu!K2x%+CLIQ~*BG*nR8Gn>oX$Q#xe&+)^X_rek#pQ+yO74t34h(uE5hl$%f z_!@~cEdT&M0ITws;!G@GB9T}nj-x*lNbq3}kO5&7N|-p48zm`9Zp0czTT`?$R|1=$$jb$m5V#kYIh9vY>Z+bf`;Y?(Hip%k8fn-IpWkLR4v^>>S5oD84EXx zhSJ`Ap&?pGI9%~SiQJcZX{j%*`v{BB95u(Xm_Dqgyymba-0wQM8xiGT-sRPb2O$O} zU~hRy#DVBFL>k9)nOd_W^nIb;%Jg}&f+F|Ojs{Y470_hwx@EnuEEm&J)Gr4gZ zw*j-cW+ZO2x)b5#q2iRR391%6NAmRlsqDsBg|yUi?U?PavZ6#OhOGJ z%oxYWOvBj6J|x>2owB9Ku4LkH7)wT$ib!t^Z5rzsYeeEO3XO2|-qX3>tM}jcpXd7A zzu)h9{=J{i_j>N@;aFlzN8knhIhH?GG%Jld+J)zfNPKhR?hcG?PO;5zIg+iF-f}vMsa{kcFD|ffov`Tm&!-1Kgk08r=SX;YaHrvXCWRrPPF_If>d@S7y`ED-ce0r(kU_f!gd{kF+y%qbW{B z{aT%41&35%F#2J4!eOtfPCpW#*D3XYL|8i2m6~=}l0YkmDckh6k)HNhwk)s66|;v4 zzI222Ij#S>JeB;wMtQQPiTnPr+qe&tQpfC%yb{}Q)}}GRGW+;=Sz$aI_{jt?5z5$z zL%wNZ828)O8Wsg87s$qU5MkUe2L2&I#3O*3toRzqcLN0h%5pK1w~V-6H`adIJTaL? z8T+r01kRTvv()AhasY=&P!8b*Obx|3nS zJ&IgzUko|uaNFiaR&1X4yUSN&KTt3(8nS0Ugen)akS8U!2``NOe+Uek67`of)F|=Q z55buOJ`=`fhVRlMzhLY1mV(^3v+r_`zt1D6WIsKu>Mi9K?_H8J@dwJxcRbHpo`l`q z{u8!3EkK2&|6PX z&V}x?!2n1B(2WDI%C8UsxaEP0AAqOByCS%(54ls0+{B%Rc5{E(XS1z?b&qQ`43K)F zEMX5Z%StLX$vp<=XgL5M3i zL7M0O7REmSs?(YWROg*D;Y!E_pgKc#Ky?x|>pS!Hf$FGN03Tw4Aw(e zheLdD0MMoi;4ARA6544^wg z>1y;(bAGEUe;-I5j$$`A@CI?A0b?(cB5^K`X@*_XM0$bu^J^Pqers z)NWd^QxBdVR}pZz^(LYrT^QM)(2;EIxitWd5?9n+iwoy*c@oaXO4}riYC2~B6`wmL|1u3y z_N1fZd z#(+T$nv?c)yXrq@i(iw)QD3tk8}o3%kaETxoF#|5dt>3c?#+}p?C|u04BhySIahso z96aIWz=7Kg(oD8|As*m}tx4i6z^Z9ciJ+u=u`TTnbcGv;!%()QG_cJS z@RyJ#`({_Uc60@Pq~d=sgYDx|&d%GMkDQzrIsEtwbUSx2{Y{#={`z1$A={GVUwx0e z0$sGSz3>@QHx7ZM>-z%iC7^A)jCj8mEqmd%bD{7*K^JHa5OyVIN|NJtzmI}d>1W#- z%1||m#jwGq)77n+rF6iR0nU*+^dcF>|EIJFt6{vEjaeu*5gU2%P5o4B^n}&H<;g(l zdlQHtsm5r0hA@*mX#0>pd!<2J`?25aPp3H4Tv}@EmcMT-@1N+sE$xWUaf{yc%9wKR zmPGxV6=AL_Eq}%e+yq&D;6U0)lEe6O|02E7Wl(s7wQyIJn@Tt{a9FTCb!dyPo*f`@d*Jc4ai z_SM0d<=0+zu4en%XeV>(xLOf6QhKSK+Q<{gh-^<4R9RnY_^RY?ICJiFQ<+n^5&Oak ed5@XQvQCl&fW^!*ULHbXSE{BK6G5 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 DvTLIl literal 0 HcmV?d00001 diff --git a/core/src/main/resources/bedrock/creative_items.1_17_30.json b/core/src/main/resources/bedrock/creative_items.1_18_10.json similarity index 91% rename from core/src/main/resources/bedrock/creative_items.1_17_30.json rename to core/src/main/resources/bedrock/creative_items.1_18_10.json index bb73854dc..dadabf91f 100644 --- a/core/src/main/resources/bedrock/creative_items.1_17_30.json +++ b/core/src/main/resources/bedrock/creative_items.1_18_10.json @@ -2,39 +2,35 @@ "items" : [ { "id" : "minecraft:planks", - "blockRuntimeId" : 5794 + "blockRuntimeId" : 5800 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 5795 + "blockRuntimeId" : 5801 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 5796 + "blockRuntimeId" : 5802 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 5797 + "blockRuntimeId" : 5803 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 5798 + "blockRuntimeId" : 5804 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 5799 + "blockRuntimeId" : 5805 }, { "id" : "minecraft:crimson_planks", - "blockRuntimeId" : 3839 + "blockRuntimeId" : 3840 }, { "id" : "minecraft:warped_planks", - "blockRuntimeId" : 7594 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1318 + "blockRuntimeId" : 7596 }, { "id" : "minecraft:cobblestone_wall", @@ -56,69 +52,69 @@ "id" : "minecraft:cobblestone_wall", "blockRuntimeId" : 1323 }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1330 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1325 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1326 - }, { "id" : "minecraft:cobblestone_wall", "blockRuntimeId" : 1324 }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1327 - }, { "id" : "minecraft:cobblestone_wall", "blockRuntimeId" : 1331 }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1326 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1327 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1325 + }, { "id" : "minecraft:cobblestone_wall", "blockRuntimeId" : 1328 }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1332 + }, { "id" : "minecraft:cobblestone_wall", "blockRuntimeId" : 1329 }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1330 + }, { "id" : "minecraft:blackstone_wall", "blockRuntimeId" : 507 }, { "id" : "minecraft:polished_blackstone_wall", - "blockRuntimeId" : 6038 + "blockRuntimeId" : 6044 }, { "id" : "minecraft:polished_blackstone_brick_wall", - "blockRuntimeId" : 5835 + "blockRuntimeId" : 5841 }, { "id" : "minecraft:cobbled_deepslate_wall", - "blockRuntimeId" : 1155 + "blockRuntimeId" : 1156 }, { "id" : "minecraft:deepslate_tile_wall", - "blockRuntimeId" : 4297 + "blockRuntimeId" : 4298 }, { "id" : "minecraft:polished_deepslate_wall", - "blockRuntimeId" : 6213 + "blockRuntimeId" : 6219 }, { "id" : "minecraft:deepslate_brick_wall", - "blockRuntimeId" : 4114 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 4773 + "blockRuntimeId" : 4115 }, { "id" : "minecraft:fence", @@ -141,24 +137,28 @@ "blockRuntimeId" : 4778 }, { - "id" : "minecraft:nether_brick_fence", - "blockRuntimeId" : 5686 - }, - { - "id" : "minecraft:crimson_fence", - "blockRuntimeId" : 3817 - }, - { - "id" : "minecraft:warped_fence", - "blockRuntimeId" : 7572 - }, - { - "id" : "minecraft:fence_gate", + "id" : "minecraft:fence", "blockRuntimeId" : 4779 }, + { + "id" : "minecraft:nether_brick_fence", + "blockRuntimeId" : 5690 + }, + { + "id" : "minecraft:crimson_fence", + "blockRuntimeId" : 3818 + }, + { + "id" : "minecraft:warped_fence", + "blockRuntimeId" : 7574 + }, + { + "id" : "minecraft:fence_gate", + "blockRuntimeId" : 4780 + }, { "id" : "minecraft:spruce_fence_gate", - "blockRuntimeId" : 7006 + "blockRuntimeId" : 7007 }, { "id" : "minecraft:birch_fence_gate", @@ -166,7 +166,7 @@ }, { "id" : "minecraft:jungle_fence_gate", - "blockRuntimeId" : 5252 + "blockRuntimeId" : 5254 }, { "id" : "minecraft:acacia_fence_gate", @@ -174,35 +174,35 @@ }, { "id" : "minecraft:dark_oak_fence_gate", - "blockRuntimeId" : 3980 + "blockRuntimeId" : 3981 }, { "id" : "minecraft:crimson_fence_gate", - "blockRuntimeId" : 3818 + "blockRuntimeId" : 3819 }, { "id" : "minecraft:warped_fence_gate", - "blockRuntimeId" : 7573 + "blockRuntimeId" : 7575 }, { "id" : "minecraft:normal_stone_stairs", - "blockRuntimeId" : 5705 + "blockRuntimeId" : 5709 }, { "id" : "minecraft:stone_stairs", - "blockRuntimeId" : 7277 + "blockRuntimeId" : 7278 }, { "id" : "minecraft:mossy_cobblestone_stairs", - "blockRuntimeId" : 5667 + "blockRuntimeId" : 5669 }, { "id" : "minecraft:oak_stairs", - "blockRuntimeId" : 5714 + "blockRuntimeId" : 5718 }, { "id" : "minecraft:spruce_stairs", - "blockRuntimeId" : 7038 + "blockRuntimeId" : 7039 }, { "id" : "minecraft:birch_stairs", @@ -210,7 +210,7 @@ }, { "id" : "minecraft:jungle_stairs", - "blockRuntimeId" : 5284 + "blockRuntimeId" : 5286 }, { "id" : "minecraft:acacia_stairs", @@ -218,47 +218,47 @@ }, { "id" : "minecraft:dark_oak_stairs", - "blockRuntimeId" : 4012 + "blockRuntimeId" : 4013 }, { "id" : "minecraft:stone_brick_stairs", - "blockRuntimeId" : 7183 + "blockRuntimeId" : 7184 }, { "id" : "minecraft:mossy_stone_brick_stairs", - "blockRuntimeId" : 5675 + "blockRuntimeId" : 5677 }, { "id" : "minecraft:sandstone_stairs", - "blockRuntimeId" : 6707 + "blockRuntimeId" : 6713 }, { "id" : "minecraft:smooth_sandstone_stairs", - "blockRuntimeId" : 6899 + "blockRuntimeId" : 6900 }, { "id" : "minecraft:red_sandstone_stairs", - "blockRuntimeId" : 6634 + "blockRuntimeId" : 6640 }, { "id" : "minecraft:smooth_red_sandstone_stairs", - "blockRuntimeId" : 6891 + "blockRuntimeId" : 6892 }, { "id" : "minecraft:granite_stairs", - "blockRuntimeId" : 4988 + "blockRuntimeId" : 4990 }, { "id" : "minecraft:polished_granite_stairs", - "blockRuntimeId" : 6383 + "blockRuntimeId" : 6389 }, { "id" : "minecraft:diorite_stairs", - "blockRuntimeId" : 4475 + "blockRuntimeId" : 4476 }, { "id" : "minecraft:polished_diorite_stairs", - "blockRuntimeId" : 6375 + "blockRuntimeId" : 6381 }, { "id" : "minecraft:andesite_stairs", @@ -266,7 +266,7 @@ }, { "id" : "minecraft:polished_andesite_stairs", - "blockRuntimeId" : 5811 + "blockRuntimeId" : 5817 }, { "id" : "minecraft:brick_stairs", @@ -274,47 +274,47 @@ }, { "id" : "minecraft:nether_brick_stairs", - "blockRuntimeId" : 5687 + "blockRuntimeId" : 5691 }, { "id" : "minecraft:red_nether_brick_stairs", - "blockRuntimeId" : 6622 + "blockRuntimeId" : 6628 }, { "id" : "minecraft:end_brick_stairs", - "blockRuntimeId" : 4719 + "blockRuntimeId" : 4720 }, { "id" : "minecraft:quartz_stairs", - "blockRuntimeId" : 6556 + "blockRuntimeId" : 6562 }, { "id" : "minecraft:smooth_quartz_stairs", - "blockRuntimeId" : 6883 + "blockRuntimeId" : 6884 }, { "id" : "minecraft:purpur_stairs", - "blockRuntimeId" : 6534 + "blockRuntimeId" : 6540 }, { "id" : "minecraft:prismarine_stairs", - "blockRuntimeId" : 6446 + "blockRuntimeId" : 6452 }, { "id" : "minecraft:dark_prismarine_stairs", - "blockRuntimeId" : 4036 + "blockRuntimeId" : 4037 }, { "id" : "minecraft:prismarine_bricks_stairs", - "blockRuntimeId" : 6438 + "blockRuntimeId" : 6444 }, { "id" : "minecraft:crimson_stairs", - "blockRuntimeId" : 3859 + "blockRuntimeId" : 3860 }, { "id" : "minecraft:warped_stairs", - "blockRuntimeId" : 7614 + "blockRuntimeId" : 7616 }, { "id" : "minecraft:blackstone_stairs", @@ -322,59 +322,59 @@ }, { "id" : "minecraft:polished_blackstone_stairs", - "blockRuntimeId" : 6030 + "blockRuntimeId" : 6036 }, { "id" : "minecraft:polished_blackstone_brick_stairs", - "blockRuntimeId" : 5827 + "blockRuntimeId" : 5833 }, { "id" : "minecraft:cut_copper_stairs", - "blockRuntimeId" : 3912 + "blockRuntimeId" : 3913 }, { "id" : "minecraft:exposed_cut_copper_stairs", - "blockRuntimeId" : 4755 + "blockRuntimeId" : 4756 }, { "id" : "minecraft:weathered_cut_copper_stairs", - "blockRuntimeId" : 7741 + "blockRuntimeId" : 7743 }, { "id" : "minecraft:oxidized_cut_copper_stairs", - "blockRuntimeId" : 5755 + "blockRuntimeId" : 5760 }, { "id" : "minecraft:waxed_cut_copper_stairs", - "blockRuntimeId" : 7685 + "blockRuntimeId" : 7687 }, { "id" : "minecraft:waxed_exposed_cut_copper_stairs", - "blockRuntimeId" : 7699 + "blockRuntimeId" : 7701 }, { "id" : "minecraft:waxed_weathered_cut_copper_stairs", - "blockRuntimeId" : 7727 + "blockRuntimeId" : 7729 }, { "id" : "minecraft:waxed_oxidized_cut_copper_stairs", - "blockRuntimeId" : 7713 + "blockRuntimeId" : 7715 }, { "id" : "minecraft:cobbled_deepslate_stairs", - "blockRuntimeId" : 1147 + "blockRuntimeId" : 1148 }, { "id" : "minecraft:deepslate_tile_stairs", - "blockRuntimeId" : 4289 + "blockRuntimeId" : 4290 }, { "id" : "minecraft:polished_deepslate_stairs", - "blockRuntimeId" : 6205 + "blockRuntimeId" : 6211 }, { "id" : "minecraft:deepslate_brick_stairs", - "blockRuntimeId" : 4106 + "blockRuntimeId" : 4107 }, { "id" : "minecraft:wooden_door" @@ -405,11 +405,11 @@ }, { "id" : "minecraft:trapdoor", - "blockRuntimeId" : 7359 + "blockRuntimeId" : 7360 }, { "id" : "minecraft:spruce_trapdoor", - "blockRuntimeId" : 7062 + "blockRuntimeId" : 7063 }, { "id" : "minecraft:birch_trapdoor", @@ -417,7 +417,7 @@ }, { "id" : "minecraft:jungle_trapdoor", - "blockRuntimeId" : 5308 + "blockRuntimeId" : 5310 }, { "id" : "minecraft:acacia_trapdoor", @@ -425,51 +425,27 @@ }, { "id" : "minecraft:dark_oak_trapdoor", - "blockRuntimeId" : 4020 + "blockRuntimeId" : 4021 }, { "id" : "minecraft:iron_trapdoor", - "blockRuntimeId" : 5167 + "blockRuntimeId" : 5169 }, { "id" : "minecraft:crimson_trapdoor", - "blockRuntimeId" : 3886 + "blockRuntimeId" : 3887 }, { "id" : "minecraft:warped_trapdoor", - "blockRuntimeId" : 7641 + "blockRuntimeId" : 7643 }, { "id" : "minecraft:iron_bars", - "blockRuntimeId" : 5132 + "blockRuntimeId" : 5134 }, { "id" : "minecraft:glass", - "blockRuntimeId" : 4882 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7084 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7092 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7091 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7099 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7096 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7098 + "blockRuntimeId" : 4884 }, { "id" : "minecraft:stained_glass", @@ -477,11 +453,15 @@ }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7088 + "blockRuntimeId" : 7093 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7089 + "blockRuntimeId" : 7092 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7100 }, { "id" : "minecraft:stained_glass", @@ -489,59 +469,55 @@ }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7093 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7087 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7095 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 7094 + "blockRuntimeId" : 7099 }, { "id" : "minecraft:stained_glass", "blockRuntimeId" : 7086 }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7089 + }, { "id" : "minecraft:stained_glass", "blockRuntimeId" : 7090 }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7098 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7094 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7088 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7096 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7095 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7087 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 7091 + }, { "id" : "minecraft:tinted_glass", - "blockRuntimeId" : 7348 + "blockRuntimeId" : 7349 }, { "id" : "minecraft:glass_pane", - "blockRuntimeId" : 4883 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7100 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7108 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7107 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7115 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7112 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7114 + "blockRuntimeId" : 4885 }, { "id" : "minecraft:stained_glass_pane", @@ -549,11 +525,15 @@ }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7104 + "blockRuntimeId" : 7109 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7105 + "blockRuntimeId" : 7108 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7116 }, { "id" : "minecraft:stained_glass_pane", @@ -561,59 +541,71 @@ }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7109 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7103 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7111 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 7110 + "blockRuntimeId" : 7115 }, { "id" : "minecraft:stained_glass_pane", "blockRuntimeId" : 7102 }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7105 + }, { "id" : "minecraft:stained_glass_pane", "blockRuntimeId" : 7106 }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7114 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7110 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7104 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7112 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7111 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7103 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7107 + }, { "id" : "minecraft:ladder", - "blockRuntimeId" : 5356 + "blockRuntimeId" : 5358 }, { "id" : "minecraft:scaffolding", - "blockRuntimeId" : 6727 + "blockRuntimeId" : 6733 }, { "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7219 + "blockRuntimeId" : 7220 }, { "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 7269 + "blockRuntimeId" : 7270 }, { "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7222 + "blockRuntimeId" : 7223 }, { "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 7240 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 7899 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 7900 + "blockRuntimeId" : 7241 }, { "id" : "minecraft:wooden_slab", @@ -632,76 +624,12 @@ "blockRuntimeId" : 7904 }, { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7224 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7905 }, { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 7267 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7220 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 7270 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 7241 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 7235 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 7271 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7252 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7257 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7258 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7255 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7256 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7254 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7253 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7223 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 7226 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 7242 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 7251 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7906 }, { "id" : "minecraft:double_stone_slab", @@ -711,10 +639,78 @@ "id" : "minecraft:double_stone_slab4", "blockRuntimeId" : 7268 }, + { + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 7221 + }, + { + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7271 + }, + { + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 7242 + }, { "id" : "minecraft:double_stone_slab2", "blockRuntimeId" : 7236 }, + { + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7272 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7253 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7258 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7259 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7256 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7257 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7255 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7254 + }, + { + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 7224 + }, + { + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 7227 + }, + { + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 7243 + }, + { + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7252 + }, + { + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 7226 + }, + { + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7269 + }, { "id" : "minecraft:double_stone_slab2", "blockRuntimeId" : 7237 @@ -727,13 +723,17 @@ "id" : "minecraft:double_stone_slab2", "blockRuntimeId" : 7239 }, + { + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 7240 + }, { "id" : "minecraft:crimson_slab", - "blockRuntimeId" : 3857 + "blockRuntimeId" : 3858 }, { "id" : "minecraft:warped_slab", - "blockRuntimeId" : 7612 + "blockRuntimeId" : 7614 }, { "id" : "minecraft:blackstone_slab", @@ -741,59 +741,59 @@ }, { "id" : "minecraft:polished_blackstone_slab", - "blockRuntimeId" : 6028 + "blockRuntimeId" : 6034 }, { "id" : "minecraft:polished_blackstone_brick_slab", - "blockRuntimeId" : 5825 + "blockRuntimeId" : 5831 }, { "id" : "minecraft:cut_copper_slab", - "blockRuntimeId" : 3910 + "blockRuntimeId" : 3911 }, { "id" : "minecraft:exposed_cut_copper_slab", - "blockRuntimeId" : 4753 + "blockRuntimeId" : 4754 }, { "id" : "minecraft:weathered_cut_copper_slab", - "blockRuntimeId" : 7739 + "blockRuntimeId" : 7741 }, { "id" : "minecraft:oxidized_cut_copper_slab", - "blockRuntimeId" : 5753 + "blockRuntimeId" : 5758 }, { "id" : "minecraft:waxed_cut_copper_slab", - "blockRuntimeId" : 7683 + "blockRuntimeId" : 7685 }, { "id" : "minecraft:waxed_exposed_cut_copper_slab", - "blockRuntimeId" : 7697 + "blockRuntimeId" : 7699 }, { "id" : "minecraft:waxed_weathered_cut_copper_slab", - "blockRuntimeId" : 7725 + "blockRuntimeId" : 7727 }, { "id" : "minecraft:waxed_oxidized_cut_copper_slab", - "blockRuntimeId" : 7711 + "blockRuntimeId" : 7713 }, { "id" : "minecraft:cobbled_deepslate_slab", - "blockRuntimeId" : 1145 + "blockRuntimeId" : 1146 }, { "id" : "minecraft:polished_deepslate_slab", - "blockRuntimeId" : 6203 + "blockRuntimeId" : 6209 }, { "id" : "minecraft:deepslate_tile_slab", - "blockRuntimeId" : 4287 + "blockRuntimeId" : 4288 }, { "id" : "minecraft:deepslate_brick_slab", - "blockRuntimeId" : 4104 + "blockRuntimeId" : 4105 }, { "id" : "minecraft:brick_block", @@ -805,15 +805,11 @@ }, { "id" : "minecraft:cracked_nether_bricks", - "blockRuntimeId" : 3768 + "blockRuntimeId" : 3769 }, { "id" : "minecraft:quartz_bricks", - "blockRuntimeId" : 6554 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 7285 + "blockRuntimeId" : 6560 }, { "id" : "minecraft:stonebrick", @@ -827,25 +823,29 @@ "id" : "minecraft:stonebrick", "blockRuntimeId" : 7288 }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 7289 + }, { "id" : "minecraft:end_bricks", - "blockRuntimeId" : 4727 + "blockRuntimeId" : 4728 }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6437 + "blockRuntimeId" : 6443 }, { "id" : "minecraft:polished_blackstone_bricks", - "blockRuntimeId" : 5997 + "blockRuntimeId" : 6003 }, { "id" : "minecraft:cracked_polished_blackstone_bricks", - "blockRuntimeId" : 3769 + "blockRuntimeId" : 3770 }, { "id" : "minecraft:gilded_blackstone", - "blockRuntimeId" : 4881 + "blockRuntimeId" : 4883 }, { "id" : "minecraft:chiseled_polished_blackstone", @@ -853,19 +853,19 @@ }, { "id" : "minecraft:deepslate_tiles", - "blockRuntimeId" : 4459 + "blockRuntimeId" : 4460 }, { "id" : "minecraft:cracked_deepslate_tiles", - "blockRuntimeId" : 3767 + "blockRuntimeId" : 3768 }, { "id" : "minecraft:deepslate_bricks", - "blockRuntimeId" : 4276 + "blockRuntimeId" : 4277 }, { "id" : "minecraft:cracked_deepslate_bricks", - "blockRuntimeId" : 3766 + "blockRuntimeId" : 3767 }, { "id" : "minecraft:chiseled_deepslate", @@ -873,195 +873,195 @@ }, { "id" : "minecraft:cobblestone", - "blockRuntimeId" : 1317 + "blockRuntimeId" : 1318 }, { "id" : "minecraft:mossy_cobblestone", - "blockRuntimeId" : 5666 + "blockRuntimeId" : 5668 }, { "id" : "minecraft:cobbled_deepslate", - "blockRuntimeId" : 1142 + "blockRuntimeId" : 1143 }, { "id" : "minecraft:smooth_stone", - "blockRuntimeId" : 6907 + "blockRuntimeId" : 6908 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 6703 + "blockRuntimeId" : 6709 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 6704 + "blockRuntimeId" : 6710 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 6705 + "blockRuntimeId" : 6711 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 6706 + "blockRuntimeId" : 6712 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6630 + "blockRuntimeId" : 6636 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6631 + "blockRuntimeId" : 6637 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6632 + "blockRuntimeId" : 6638 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6633 + "blockRuntimeId" : 6639 }, { "id" : "minecraft:coal_block", - "blockRuntimeId" : 1140 + "blockRuntimeId" : 1141 }, { "id" : "minecraft:dried_kelp_block", - "blockRuntimeId" : 4583 + "blockRuntimeId" : 4584 }, { "id" : "minecraft:gold_block", - "blockRuntimeId" : 4974 + "blockRuntimeId" : 4976 }, { "id" : "minecraft:iron_block", - "blockRuntimeId" : 5133 + "blockRuntimeId" : 5135 }, { "id" : "minecraft:copper_block", - "blockRuntimeId" : 3676 + "blockRuntimeId" : 3677 }, { "id" : "minecraft:exposed_copper", - "blockRuntimeId" : 4751 - }, - { - "id" : "minecraft:weathered_copper", - "blockRuntimeId" : 7737 - }, - { - "id" : "minecraft:oxidized_copper", - "blockRuntimeId" : 5751 - }, - { - "id" : "minecraft:waxed_copper", - "blockRuntimeId" : 7681 - }, - { - "id" : "minecraft:waxed_exposed_copper", - "blockRuntimeId" : 7695 - }, - { - "id" : "minecraft:waxed_weathered_copper", - "blockRuntimeId" : 7723 - }, - { - "id" : "minecraft:waxed_oxidized_copper", - "blockRuntimeId" : 7709 - }, - { - "id" : "minecraft:cut_copper", - "blockRuntimeId" : 3909 - }, - { - "id" : "minecraft:exposed_cut_copper", "blockRuntimeId" : 4752 }, + { + "id" : "minecraft:weathered_copper", + "blockRuntimeId" : 7739 + }, + { + "id" : "minecraft:oxidized_copper", + "blockRuntimeId" : 5756 + }, + { + "id" : "minecraft:waxed_copper", + "blockRuntimeId" : 7683 + }, + { + "id" : "minecraft:waxed_exposed_copper", + "blockRuntimeId" : 7697 + }, + { + "id" : "minecraft:waxed_weathered_copper", + "blockRuntimeId" : 7725 + }, + { + "id" : "minecraft:waxed_oxidized_copper", + "blockRuntimeId" : 7711 + }, + { + "id" : "minecraft:cut_copper", + "blockRuntimeId" : 3910 + }, + { + "id" : "minecraft:exposed_cut_copper", + "blockRuntimeId" : 4753 + }, { "id" : "minecraft:weathered_cut_copper", - "blockRuntimeId" : 7738 + "blockRuntimeId" : 7740 }, { "id" : "minecraft:oxidized_cut_copper", - "blockRuntimeId" : 5752 + "blockRuntimeId" : 5757 }, { "id" : "minecraft:waxed_cut_copper", - "blockRuntimeId" : 7682 + "blockRuntimeId" : 7684 }, { "id" : "minecraft:waxed_exposed_cut_copper", - "blockRuntimeId" : 7696 + "blockRuntimeId" : 7698 }, { "id" : "minecraft:waxed_weathered_cut_copper", - "blockRuntimeId" : 7724 + "blockRuntimeId" : 7726 }, { "id" : "minecraft:waxed_oxidized_cut_copper", - "blockRuntimeId" : 7710 + "blockRuntimeId" : 7712 }, { "id" : "minecraft:emerald_block", - "blockRuntimeId" : 4716 + "blockRuntimeId" : 4717 }, { "id" : "minecraft:diamond_block", - "blockRuntimeId" : 4473 + "blockRuntimeId" : 4474 }, { "id" : "minecraft:lapis_block", - "blockRuntimeId" : 5364 + "blockRuntimeId" : 5366 }, { "id" : "minecraft:raw_iron_block", - "blockRuntimeId" : 6576 + "blockRuntimeId" : 6582 }, { "id" : "minecraft:raw_copper_block", - "blockRuntimeId" : 6574 + "blockRuntimeId" : 6580 }, { "id" : "minecraft:raw_gold_block", - "blockRuntimeId" : 6575 + "blockRuntimeId" : 6581 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 6542 + "blockRuntimeId" : 6548 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 6544 + "blockRuntimeId" : 6550 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 6543 + "blockRuntimeId" : 6549 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 6545 + "blockRuntimeId" : 6551 }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6435 + "blockRuntimeId" : 6441 }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6436 + "blockRuntimeId" : 6442 }, { "id" : "minecraft:slime", - "blockRuntimeId" : 6860 + "blockRuntimeId" : 6861 }, { "id" : "minecraft:honey_block", - "blockRuntimeId" : 5111 + "blockRuntimeId" : 5113 }, { "id" : "minecraft:honeycomb_block", - "blockRuntimeId" : 5112 + "blockRuntimeId" : 5114 }, { "id" : "minecraft:hay_block", - "blockRuntimeId" : 5083 + "blockRuntimeId" : 5085 }, { "id" : "minecraft:bone_block", @@ -1069,27 +1069,51 @@ }, { "id" : "minecraft:nether_brick", - "blockRuntimeId" : 5685 + "blockRuntimeId" : 5689 }, { "id" : "minecraft:red_nether_brick", - "blockRuntimeId" : 6621 + "blockRuntimeId" : 6627 }, { "id" : "minecraft:netherite_block", - "blockRuntimeId" : 5702 + "blockRuntimeId" : 5706 }, { "id" : "minecraft:lodestone", - "blockRuntimeId" : 5562 + "blockRuntimeId" : 5564 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7911 + "blockRuntimeId" : 7913 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7919 + "blockRuntimeId" : 7921 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7920 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7928 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7925 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7927 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7914 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7917 }, { "id" : "minecraft:wool", @@ -1101,19 +1125,7 @@ }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7923 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7925 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7912 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7915 + "blockRuntimeId" : 7922 }, { "id" : "minecraft:wool", @@ -1125,27 +1137,15 @@ }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7920 + "blockRuntimeId" : 7923 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7914 + "blockRuntimeId" : 7915 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 7922 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7921 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7913 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 7917 + "blockRuntimeId" : 7919 }, { "id" : "minecraft:carpet", @@ -1211,93 +1211,69 @@ "id" : "minecraft:carpet", "blockRuntimeId" : 969 }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3659 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3667 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3666 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3674 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3671 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3673 - }, { "id" : "minecraft:concrete_powder", "blockRuntimeId" : 3660 }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3663 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3664 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3672 - }, { "id" : "minecraft:concrete_powder", "blockRuntimeId" : 3668 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3662 + "blockRuntimeId" : 3667 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3670 + "blockRuntimeId" : 3675 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3669 + "blockRuntimeId" : 3672 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3674 }, { "id" : "minecraft:concrete_powder", "blockRuntimeId" : 3661 }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3664 + }, { "id" : "minecraft:concrete_powder", "blockRuntimeId" : 3665 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3643 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3673 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3651 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3669 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3650 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3663 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3658 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3671 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3655 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3670 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3657 + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3662 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3666 }, { "id" : "minecraft:concrete", @@ -1305,11 +1281,15 @@ }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 3647 + "blockRuntimeId" : 3652 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 3648 + "blockRuntimeId" : 3651 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3659 }, { "id" : "minecraft:concrete", @@ -1317,59 +1297,55 @@ }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 3652 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3646 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3654 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3653 + "blockRuntimeId" : 3658 }, { "id" : "minecraft:concrete", "blockRuntimeId" : 3645 }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3648 + }, { "id" : "minecraft:concrete", "blockRuntimeId" : 3649 }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3657 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3653 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3647 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3655 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3654 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3646 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3650 + }, { "id" : "minecraft:clay", "blockRuntimeId" : 1139 }, { "id" : "minecraft:hardened_clay", - "blockRuntimeId" : 5082 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7116 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7124 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7123 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7131 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7128 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7130 + "blockRuntimeId" : 5084 }, { "id" : "minecraft:stained_hardened_clay", @@ -1377,11 +1353,15 @@ }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7120 + "blockRuntimeId" : 7125 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7121 + "blockRuntimeId" : 7124 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7132 }, { "id" : "minecraft:stained_hardened_clay", @@ -1389,39 +1369,59 @@ }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7125 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7119 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7127 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 7126 + "blockRuntimeId" : 7131 }, { "id" : "minecraft:stained_hardened_clay", "blockRuntimeId" : 7118 }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7121 + }, { "id" : "minecraft:stained_hardened_clay", "blockRuntimeId" : 7122 }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7130 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7126 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7120 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7128 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7127 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7119 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 7123 + }, { "id" : "minecraft:white_glazed_terracotta", - "blockRuntimeId" : 7796 + "blockRuntimeId" : 7798 }, { "id" : "minecraft:silver_glazed_terracotta", - "blockRuntimeId" : 6842 + "blockRuntimeId" : 6849 }, { "id" : "minecraft:gray_glazed_terracotta", - "blockRuntimeId" : 5009 + "blockRuntimeId" : 5011 }, { "id" : "minecraft:black_glazed_terracotta", @@ -1433,31 +1433,31 @@ }, { "id" : "minecraft:red_glazed_terracotta", - "blockRuntimeId" : 6598 + "blockRuntimeId" : 6604 }, { "id" : "minecraft:orange_glazed_terracotta", - "blockRuntimeId" : 5745 + "blockRuntimeId" : 5750 }, { "id" : "minecraft:yellow_glazed_terracotta", - "blockRuntimeId" : 7938 + "blockRuntimeId" : 7940 }, { "id" : "minecraft:lime_glazed_terracotta", - "blockRuntimeId" : 5531 + "blockRuntimeId" : 5533 }, { "id" : "minecraft:green_glazed_terracotta", - "blockRuntimeId" : 5025 + "blockRuntimeId" : 5027 }, { "id" : "minecraft:cyan_glazed_terracotta", - "blockRuntimeId" : 3930 + "blockRuntimeId" : 3931 }, { "id" : "minecraft:light_blue_glazed_terracotta", - "blockRuntimeId" : 5483 + "blockRuntimeId" : 5485 }, { "id" : "minecraft:blue_glazed_terracotta", @@ -1465,43 +1465,43 @@ }, { "id" : "minecraft:purple_glazed_terracotta", - "blockRuntimeId" : 6516 - }, - { - "id" : "minecraft:magenta_glazed_terracotta", - "blockRuntimeId" : 5595 - }, - { - "id" : "minecraft:pink_glazed_terracotta", - "blockRuntimeId" : 5776 - }, - { - "id" : "minecraft:purpur_block", "blockRuntimeId" : 6522 }, + { + "id" : "minecraft:magenta_glazed_terracotta", + "blockRuntimeId" : 5597 + }, + { + "id" : "minecraft:pink_glazed_terracotta", + "blockRuntimeId" : 5782 + }, { "id" : "minecraft:purpur_block", - "blockRuntimeId" : 6524 + "blockRuntimeId" : 6528 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 6530 }, { "id" : "minecraft:nether_wart_block", - "blockRuntimeId" : 5701 + "blockRuntimeId" : 5705 }, { "id" : "minecraft:warped_wart_block", - "blockRuntimeId" : 7663 + "blockRuntimeId" : 7665 }, { "id" : "minecraft:shroomlight", - "blockRuntimeId" : 6825 + "blockRuntimeId" : 6832 }, { "id" : "minecraft:crimson_nylium", - "blockRuntimeId" : 3838 + "blockRuntimeId" : 3839 }, { "id" : "minecraft:warped_nylium", - "blockRuntimeId" : 7593 + "blockRuntimeId" : 7595 }, { "id" : "minecraft:basalt", @@ -1509,87 +1509,87 @@ }, { "id" : "minecraft:polished_basalt", - "blockRuntimeId" : 5819 + "blockRuntimeId" : 5825 }, { "id" : "minecraft:smooth_basalt", - "blockRuntimeId" : 6882 + "blockRuntimeId" : 6883 }, { "id" : "minecraft:soul_soil", - "blockRuntimeId" : 6952 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 4483 + "blockRuntimeId" : 6953 }, { "id" : "minecraft:dirt", "blockRuntimeId" : 4484 }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 4485 + }, { "id" : "minecraft:farmland", - "blockRuntimeId" : 4765 + "blockRuntimeId" : 4766 }, { "id" : "minecraft:grass", - "blockRuntimeId" : 4996 + "blockRuntimeId" : 4998 }, { "id" : "minecraft:grass_path", - "blockRuntimeId" : 4997 + "blockRuntimeId" : 4999 }, { "id" : "minecraft:podzol", - "blockRuntimeId" : 5800 + "blockRuntimeId" : 5806 }, { "id" : "minecraft:mycelium", - "blockRuntimeId" : 5684 + "blockRuntimeId" : 5686 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 7176 + "blockRuntimeId" : 7177 }, { "id" : "minecraft:iron_ore", - "blockRuntimeId" : 5166 + "blockRuntimeId" : 5168 }, { "id" : "minecraft:gold_ore", - "blockRuntimeId" : 4975 + "blockRuntimeId" : 4977 }, { "id" : "minecraft:diamond_ore", - "blockRuntimeId" : 4474 + "blockRuntimeId" : 4475 }, { "id" : "minecraft:lapis_ore", - "blockRuntimeId" : 5365 + "blockRuntimeId" : 5367 }, { "id" : "minecraft:redstone_ore", - "blockRuntimeId" : 6644 + "blockRuntimeId" : 6650 }, { "id" : "minecraft:coal_ore", - "blockRuntimeId" : 1141 + "blockRuntimeId" : 1142 }, { "id" : "minecraft:copper_ore", - "blockRuntimeId" : 3677 + "blockRuntimeId" : 3678 }, { "id" : "minecraft:emerald_ore", - "blockRuntimeId" : 4717 + "blockRuntimeId" : 4718 }, { "id" : "minecraft:quartz_ore", - "blockRuntimeId" : 6555 + "blockRuntimeId" : 6561 }, { "id" : "minecraft:nether_gold_ore", - "blockRuntimeId" : 5695 + "blockRuntimeId" : 5699 }, { "id" : "minecraft:ancient_debris", @@ -1597,59 +1597,39 @@ }, { "id" : "minecraft:deepslate_iron_ore", - "blockRuntimeId" : 4282 - }, - { - "id" : "minecraft:deepslate_gold_ore", - "blockRuntimeId" : 4281 - }, - { - "id" : "minecraft:deepslate_diamond_ore", - "blockRuntimeId" : 4279 - }, - { - "id" : "minecraft:deepslate_lapis_ore", "blockRuntimeId" : 4283 }, { - "id" : "minecraft:deepslate_redstone_ore", - "blockRuntimeId" : 4284 + "id" : "minecraft:deepslate_gold_ore", + "blockRuntimeId" : 4282 }, { - "id" : "minecraft:deepslate_emerald_ore", + "id" : "minecraft:deepslate_diamond_ore", "blockRuntimeId" : 4280 }, { - "id" : "minecraft:deepslate_coal_ore", - "blockRuntimeId" : 4277 + "id" : "minecraft:deepslate_lapis_ore", + "blockRuntimeId" : 4284 }, { - "id" : "minecraft:deepslate_copper_ore", + "id" : "minecraft:deepslate_redstone_ore", + "blockRuntimeId" : 4285 + }, + { + "id" : "minecraft:deepslate_emerald_ore", + "blockRuntimeId" : 4281 + }, + { + "id" : "minecraft:deepslate_coal_ore", "blockRuntimeId" : 4278 }, + { + "id" : "minecraft:deepslate_copper_ore", + "blockRuntimeId" : 4279 + }, { "id" : "minecraft:gravel", - "blockRuntimeId" : 4998 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 7177 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 7179 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 7181 - }, - { - "id" : "minecraft:blackstone", - "blockRuntimeId" : 494 - }, - { - "id" : "minecraft:deepslate", - "blockRuntimeId" : 4099 + "blockRuntimeId" : 5000 }, { "id" : "minecraft:stone", @@ -1663,105 +1643,109 @@ "id" : "minecraft:stone", "blockRuntimeId" : 7182 }, + { + "id" : "minecraft:blackstone", + "blockRuntimeId" : 494 + }, + { + "id" : "minecraft:deepslate", + "blockRuntimeId" : 4100 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 7179 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 7181 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 7183 + }, { "id" : "minecraft:polished_blackstone", - "blockRuntimeId" : 5822 + "blockRuntimeId" : 5828 }, { "id" : "minecraft:polished_deepslate", - "blockRuntimeId" : 6200 + "blockRuntimeId" : 6206 }, { "id" : "minecraft:sand", - "blockRuntimeId" : 6701 + "blockRuntimeId" : 6707 }, { "id" : "minecraft:sand", - "blockRuntimeId" : 6702 + "blockRuntimeId" : 6708 }, { "id" : "minecraft:cactus", "blockRuntimeId" : 920 }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 5563 - }, - { - "id" : "minecraft:stripped_oak_log", - "blockRuntimeId" : 7315 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 5564 - }, - { - "id" : "minecraft:stripped_spruce_log", - "blockRuntimeId" : 7318 - }, { "id" : "minecraft:log", "blockRuntimeId" : 5565 }, { - "id" : "minecraft:stripped_birch_log", - "blockRuntimeId" : 7300 + "id" : "minecraft:stripped_oak_log", + "blockRuntimeId" : 7316 }, { "id" : "minecraft:log", "blockRuntimeId" : 5566 }, + { + "id" : "minecraft:stripped_spruce_log", + "blockRuntimeId" : 7319 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5567 + }, + { + "id" : "minecraft:stripped_birch_log", + "blockRuntimeId" : 7301 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5568 + }, { "id" : "minecraft:stripped_jungle_log", - "blockRuntimeId" : 7312 + "blockRuntimeId" : 7313 }, { "id" : "minecraft:log2", - "blockRuntimeId" : 5575 + "blockRuntimeId" : 5577 }, { "id" : "minecraft:stripped_acacia_log", - "blockRuntimeId" : 7297 + "blockRuntimeId" : 7298 }, { "id" : "minecraft:log2", - "blockRuntimeId" : 5576 + "blockRuntimeId" : 5578 }, { "id" : "minecraft:stripped_dark_oak_log", - "blockRuntimeId" : 7309 + "blockRuntimeId" : 7310 }, { "id" : "minecraft:crimson_stem", - "blockRuntimeId" : 3883 + "blockRuntimeId" : 3884 }, { "id" : "minecraft:stripped_crimson_stem", - "blockRuntimeId" : 7306 + "blockRuntimeId" : 7307 }, { "id" : "minecraft:warped_stem", - "blockRuntimeId" : 7638 + "blockRuntimeId" : 7640 }, { "id" : "minecraft:stripped_warped_stem", - "blockRuntimeId" : 7324 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 7803 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 7809 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 7804 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 7810 + "blockRuntimeId" : 7325 }, { "id" : "minecraft:wood", @@ -1795,29 +1779,37 @@ "id" : "minecraft:wood", "blockRuntimeId" : 7814 }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7809 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7815 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7810 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7816 + }, { "id" : "minecraft:crimson_hyphae", - "blockRuntimeId" : 3835 + "blockRuntimeId" : 3836 }, { "id" : "minecraft:stripped_crimson_hyphae", - "blockRuntimeId" : 7303 + "blockRuntimeId" : 7304 }, { "id" : "minecraft:warped_hyphae", - "blockRuntimeId" : 7590 + "blockRuntimeId" : 7592 }, { "id" : "minecraft:stripped_warped_hyphae", - "blockRuntimeId" : 7321 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 5409 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 5410 + "blockRuntimeId" : 7322 }, { "id" : "minecraft:leaves", @@ -1828,12 +1820,20 @@ "blockRuntimeId" : 5412 }, { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 5425 + "id" : "minecraft:leaves", + "blockRuntimeId" : 5413 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 5414 }, { "id" : "minecraft:leaves2", - "blockRuntimeId" : 5426 + "blockRuntimeId" : 5427 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 5428 }, { "id" : "minecraft:azalea_leaves", @@ -1845,27 +1845,27 @@ }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6715 + "blockRuntimeId" : 6721 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6716 + "blockRuntimeId" : 6722 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6717 + "blockRuntimeId" : 6723 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6718 + "blockRuntimeId" : 6724 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6719 + "blockRuntimeId" : 6725 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 6720 + "blockRuntimeId" : 6726 }, { "id" : "minecraft:bee_nest", @@ -1912,7 +1912,7 @@ }, { "id" : "minecraft:melon_block", - "blockRuntimeId" : 5608 + "blockRuntimeId" : 5610 }, { "id" : "minecraft:melon_slice" @@ -1928,7 +1928,7 @@ }, { "id" : "minecraft:pumpkin", - "blockRuntimeId" : 6454 + "blockRuntimeId" : 6460 }, { "id" : "minecraft:carved_pumpkin", @@ -1936,11 +1936,19 @@ }, { "id" : "minecraft:lit_pumpkin", - "blockRuntimeId" : 5550 + "blockRuntimeId" : 5552 }, { "id" : "minecraft:honeycomb" }, + { + "id" : "minecraft:tallgrass", + "blockRuntimeId" : 7346 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 4504 + }, { "id" : "minecraft:tallgrass", "blockRuntimeId" : 7345 @@ -1949,17 +1957,17 @@ "id" : "minecraft:double_plant", "blockRuntimeId" : 4503 }, - { - "id" : "minecraft:tallgrass", - "blockRuntimeId" : 7344 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 4502 - }, { "id" : "minecraft:nether_sprouts" }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 3682 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 3680 + }, { "id" : "minecraft:coral", "blockRuntimeId" : 3681 @@ -1970,15 +1978,15 @@ }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3680 + "blockRuntimeId" : 3683 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3678 + "blockRuntimeId" : 3687 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3682 + "blockRuntimeId" : 3685 }, { "id" : "minecraft:coral", @@ -1990,15 +1998,15 @@ }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3685 + "blockRuntimeId" : 3688 }, { - "id" : "minecraft:coral", - "blockRuntimeId" : 3683 + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 3702 }, { - "id" : "minecraft:coral", - "blockRuntimeId" : 3687 + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 3700 }, { "id" : "minecraft:coral_fan", @@ -2010,15 +2018,15 @@ }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3700 + "blockRuntimeId" : 3703 }, { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3698 + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 3712 }, { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3702 + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 3710 }, { "id" : "minecraft:coral_fan_dead", @@ -2030,58 +2038,26 @@ }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3710 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3708 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3712 + "blockRuntimeId" : 3713 }, { "id" : "minecraft:kelp" }, { "id" : "minecraft:seagrass", - "blockRuntimeId" : 6821 + "blockRuntimeId" : 6828 }, { "id" : "minecraft:crimson_roots", - "blockRuntimeId" : 3856 + "blockRuntimeId" : 3857 }, { "id" : "minecraft:warped_roots", - "blockRuntimeId" : 7611 + "blockRuntimeId" : 7613 }, { "id" : "minecraft:yellow_flower", - "blockRuntimeId" : 7937 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6587 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6588 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6589 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6590 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6591 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 6592 + "blockRuntimeId" : 7939 }, { "id" : "minecraft:red_flower", @@ -2104,8 +2080,28 @@ "blockRuntimeId" : 6597 }, { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 4500 + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6598 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6599 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6600 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6601 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6602 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 6603 }, { "id" : "minecraft:double_plant", @@ -2113,15 +2109,19 @@ }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 4504 + "blockRuntimeId" : 4502 }, { "id" : "minecraft:double_plant", "blockRuntimeId" : 4505 }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 4506 + }, { "id" : "minecraft:wither_rose", - "blockRuntimeId" : 7802 + "blockRuntimeId" : 7804 }, { "id" : "minecraft:white_dye" @@ -2188,23 +2188,23 @@ }, { "id" : "minecraft:vine", - "blockRuntimeId" : 7498 + "blockRuntimeId" : 7500 }, { "id" : "minecraft:weeping_vines", - "blockRuntimeId" : 7752 + "blockRuntimeId" : 7754 }, { "id" : "minecraft:twisting_vines", - "blockRuntimeId" : 7426 + "blockRuntimeId" : 7427 }, { "id" : "minecraft:waterlily", - "blockRuntimeId" : 7680 + "blockRuntimeId" : 7682 }, { "id" : "minecraft:deadbush", - "blockRuntimeId" : 4098 + "blockRuntimeId" : 4099 }, { "id" : "minecraft:bamboo", @@ -2212,15 +2212,15 @@ }, { "id" : "minecraft:snow", - "blockRuntimeId" : 6908 + "blockRuntimeId" : 6909 }, { "id" : "minecraft:ice", - "blockRuntimeId" : 5125 + "blockRuntimeId" : 5127 }, { "id" : "minecraft:packed_ice", - "blockRuntimeId" : 5765 + "blockRuntimeId" : 5770 }, { "id" : "minecraft:blue_ice", @@ -2228,35 +2228,31 @@ }, { "id" : "minecraft:snow_layer", - "blockRuntimeId" : 6909 + "blockRuntimeId" : 6910 }, { "id" : "minecraft:pointed_dripstone", - "blockRuntimeId" : 5806 - }, - { - "id" : "minecraft:sculk_sensor", - "blockRuntimeId" : 6745 + "blockRuntimeId" : 5812 }, { "id" : "minecraft:dripstone_block", - "blockRuntimeId" : 4584 + "blockRuntimeId" : 4585 }, { "id" : "minecraft:moss_carpet", - "blockRuntimeId" : 5665 + "blockRuntimeId" : 5667 }, { "id" : "minecraft:moss_block", - "blockRuntimeId" : 5664 + "blockRuntimeId" : 5666 }, { "id" : "minecraft:dirt_with_roots", - "blockRuntimeId" : 4485 + "blockRuntimeId" : 4486 }, { "id" : "minecraft:hanging_roots", - "blockRuntimeId" : 5047 + "blockRuntimeId" : 5049 }, { "id" : "minecraft:big_dripleaf", @@ -2264,11 +2260,11 @@ }, { "id" : "minecraft:small_dripleaf_block", - "blockRuntimeId" : 6874 + "blockRuntimeId" : 6875 }, { "id" : "minecraft:spore_blossom", - "blockRuntimeId" : 6961 + "blockRuntimeId" : 6962 }, { "id" : "minecraft:azalea", @@ -2276,11 +2272,11 @@ }, { "id" : "minecraft:flowering_azalea", - "blockRuntimeId" : 4814 + "blockRuntimeId" : 4815 }, { "id" : "minecraft:glow_lichen", - "blockRuntimeId" : 4971 + "blockRuntimeId" : 4973 }, { "id" : "minecraft:amethyst_block", @@ -2292,23 +2288,23 @@ }, { "id" : "minecraft:amethyst_cluster", - "blockRuntimeId" : 137 + "blockRuntimeId" : 138 }, { "id" : "minecraft:large_amethyst_bud", - "blockRuntimeId" : 5366 + "blockRuntimeId" : 5369 }, { "id" : "minecraft:medium_amethyst_bud", - "blockRuntimeId" : 5602 + "blockRuntimeId" : 5605 }, { "id" : "minecraft:small_amethyst_bud", - "blockRuntimeId" : 6861 + "blockRuntimeId" : 6863 }, { "id" : "minecraft:tuff", - "blockRuntimeId" : 7413 + "blockRuntimeId" : 7414 }, { "id" : "minecraft:calcite", @@ -2347,15 +2343,15 @@ }, { "id" : "minecraft:red_mushroom", - "blockRuntimeId" : 6604 + "blockRuntimeId" : 6610 }, { "id" : "minecraft:crimson_fungus", - "blockRuntimeId" : 3834 + "blockRuntimeId" : 3835 }, { "id" : "minecraft:warped_fungus", - "blockRuntimeId" : 7589 + "blockRuntimeId" : 7591 }, { "id" : "minecraft:brown_mushroom_block", @@ -2363,7 +2359,7 @@ }, { "id" : "minecraft:red_mushroom_block", - "blockRuntimeId" : 6619 + "blockRuntimeId" : 6625 }, { "id" : "minecraft:brown_mushroom_block", @@ -2390,21 +2386,13 @@ }, { "id" : "minecraft:web", - "blockRuntimeId" : 7751 + "blockRuntimeId" : 7753 }, { "id" : "minecraft:spider_eye" }, { "id" : "minecraft:mob_spawner", - "blockRuntimeId" : 5657 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 5658 - }, - { - "id" : "minecraft:monster_egg", "blockRuntimeId" : 5659 }, { @@ -2423,17 +2411,25 @@ "id" : "minecraft:monster_egg", "blockRuntimeId" : 5663 }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5664 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5665 + }, { "id" : "minecraft:infested_deepslate", - "blockRuntimeId" : 5126 + "blockRuntimeId" : 5128 }, { "id" : "minecraft:dragon_egg", - "blockRuntimeId" : 4582 + "blockRuntimeId" : 4583 }, { "id" : "minecraft:turtle_egg", - "blockRuntimeId" : 7414 + "blockRuntimeId" : 7415 }, { "id" : "minecraft:chicken_spawn_egg" @@ -2635,11 +2631,11 @@ }, { "id" : "minecraft:obsidian", - "blockRuntimeId" : 5734 + "blockRuntimeId" : 5738 }, { "id" : "minecraft:crying_obsidian", - "blockRuntimeId" : 3908 + "blockRuntimeId" : 3909 }, { "id" : "minecraft:bedrock", @@ -2647,22 +2643,22 @@ }, { "id" : "minecraft:soul_sand", - "blockRuntimeId" : 6951 + "blockRuntimeId" : 6952 }, { "id" : "minecraft:netherrack", - "blockRuntimeId" : 5703 + "blockRuntimeId" : 5707 }, { "id" : "minecraft:magma", - "blockRuntimeId" : 5601 + "blockRuntimeId" : 5603 }, { "id" : "minecraft:nether_wart" }, { "id" : "minecraft:end_stone", - "blockRuntimeId" : 4744 + "blockRuntimeId" : 4745 }, { "id" : "minecraft:chorus_flower", @@ -2678,17 +2674,13 @@ { "id" : "minecraft:popped_chorus_fruit" }, - { - "id" : "minecraft:sponge", - "blockRuntimeId" : 6959 - }, { "id" : "minecraft:sponge", "blockRuntimeId" : 6960 }, { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 3688 + "id" : "minecraft:sponge", + "blockRuntimeId" : 6961 }, { "id" : "minecraft:coral_block", @@ -2726,6 +2718,10 @@ "id" : "minecraft:coral_block", "blockRuntimeId" : 3697 }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 3698 + }, { "id" : "minecraft:leather_helmet" }, @@ -3751,23 +3747,23 @@ }, { "id" : "minecraft:torch", - "blockRuntimeId" : 7353 + "blockRuntimeId" : 7354 }, { "id" : "minecraft:soul_torch", - "blockRuntimeId" : 6953 + "blockRuntimeId" : 6954 }, { "id" : "minecraft:sea_pickle", - "blockRuntimeId" : 6813 + "blockRuntimeId" : 6820 }, { "id" : "minecraft:lantern", - "blockRuntimeId" : 5362 + "blockRuntimeId" : 5364 }, { "id" : "minecraft:soul_lantern", - "blockRuntimeId" : 6949 + "blockRuntimeId" : 6950 }, { "id" : "minecraft:candle", @@ -3775,47 +3771,47 @@ }, { "id" : "minecraft:white_candle", - "blockRuntimeId" : 7786 + "blockRuntimeId" : 7788 }, { "id" : "minecraft:orange_candle", - "blockRuntimeId" : 5735 + "blockRuntimeId" : 5740 }, { "id" : "minecraft:magenta_candle", - "blockRuntimeId" : 5585 + "blockRuntimeId" : 5587 }, { "id" : "minecraft:light_blue_candle", - "blockRuntimeId" : 5473 + "blockRuntimeId" : 5475 }, { "id" : "minecraft:yellow_candle", - "blockRuntimeId" : 7927 + "blockRuntimeId" : 7929 }, { "id" : "minecraft:lime_candle", - "blockRuntimeId" : 5521 + "blockRuntimeId" : 5523 }, { "id" : "minecraft:pink_candle", - "blockRuntimeId" : 5766 + "blockRuntimeId" : 5772 }, { "id" : "minecraft:gray_candle", - "blockRuntimeId" : 4999 + "blockRuntimeId" : 5001 }, { "id" : "minecraft:light_gray_candle", - "blockRuntimeId" : 5489 + "blockRuntimeId" : 5491 }, { "id" : "minecraft:cyan_candle", - "blockRuntimeId" : 3920 + "blockRuntimeId" : 3921 }, { "id" : "minecraft:purple_candle", - "blockRuntimeId" : 6506 + "blockRuntimeId" : 6512 }, { "id" : "minecraft:blue_candle", @@ -3827,11 +3823,11 @@ }, { "id" : "minecraft:green_candle", - "blockRuntimeId" : 5015 + "blockRuntimeId" : 5017 }, { "id" : "minecraft:red_candle", - "blockRuntimeId" : 6577 + "blockRuntimeId" : 6583 }, { "id" : "minecraft:black_candle", @@ -3839,7 +3835,7 @@ }, { "id" : "minecraft:crafting_table", - "blockRuntimeId" : 3770 + "blockRuntimeId" : 3771 }, { "id" : "minecraft:cartography_table", @@ -3847,11 +3843,11 @@ }, { "id" : "minecraft:fletching_table", - "blockRuntimeId" : 4811 + "blockRuntimeId" : 4812 }, { "id" : "minecraft:smithing_table", - "blockRuntimeId" : 6875 + "blockRuntimeId" : 6876 }, { "id" : "minecraft:beehive", @@ -3865,7 +3861,7 @@ }, { "id" : "minecraft:furnace", - "blockRuntimeId" : 4875 + "blockRuntimeId" : 4877 }, { "id" : "minecraft:blast_furnace", @@ -3873,11 +3869,11 @@ }, { "id" : "minecraft:smoker", - "blockRuntimeId" : 6876 + "blockRuntimeId" : 6877 }, { "id" : "minecraft:respawn_anchor", - "blockRuntimeId" : 6696 + "blockRuntimeId" : 6702 }, { "id" : "minecraft:brewing_stand" @@ -3896,11 +3892,11 @@ }, { "id" : "minecraft:grindstone", - "blockRuntimeId" : 5031 + "blockRuntimeId" : 5033 }, { "id" : "minecraft:enchanting_table", - "blockRuntimeId" : 4718 + "blockRuntimeId" : 4719 }, { "id" : "minecraft:bookshelf", @@ -3908,14 +3904,14 @@ }, { "id" : "minecraft:lectern", - "blockRuntimeId" : 5433 + "blockRuntimeId" : 5435 }, { "id" : "minecraft:cauldron" }, { "id" : "minecraft:composter", - "blockRuntimeId" : 3634 + "blockRuntimeId" : 3635 }, { "id" : "minecraft:chest", @@ -3923,11 +3919,11 @@ }, { "id" : "minecraft:trapped_chest", - "blockRuntimeId" : 7375 + "blockRuntimeId" : 7376 }, { "id" : "minecraft:ender_chest", - "blockRuntimeId" : 4745 + "blockRuntimeId" : 4746 }, { "id" : "minecraft:barrel", @@ -3935,15 +3931,7 @@ }, { "id" : "minecraft:undyed_shulker_box", - "blockRuntimeId" : 7458 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6826 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6834 + "blockRuntimeId" : 7459 }, { "id" : "minecraft:shulker_box", @@ -3953,64 +3941,72 @@ "id" : "minecraft:shulker_box", "blockRuntimeId" : 6841 }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6838 - }, { "id" : "minecraft:shulker_box", "blockRuntimeId" : 6840 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6827 + "blockRuntimeId" : 6848 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6830 + "blockRuntimeId" : 6845 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6831 + "blockRuntimeId" : 6847 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6839 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6835 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6829 + "blockRuntimeId" : 6834 }, { "id" : "minecraft:shulker_box", "blockRuntimeId" : 6837 }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 6838 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 6846 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 6842 + }, { "id" : "minecraft:shulker_box", "blockRuntimeId" : 6836 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6828 + "blockRuntimeId" : 6844 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 6832 + "blockRuntimeId" : 6843 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 6835 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 6839 }, { "id" : "minecraft:armor_stand" }, { "id" : "minecraft:noteblock", - "blockRuntimeId" : 5713 + "blockRuntimeId" : 5717 }, { "id" : "minecraft:jukebox", - "blockRuntimeId" : 5207 + "blockRuntimeId" : 5209 }, { "id" : "minecraft:music_disc_13" @@ -4048,6 +4044,9 @@ { "id" : "minecraft:music_disc_wait" }, + { + "id" : "minecraft:music_disc_otherside" + }, { "id" : "minecraft:music_disc_pigstep" }, @@ -4056,15 +4055,15 @@ }, { "id" : "minecraft:glowstone", - "blockRuntimeId" : 4973 + "blockRuntimeId" : 4975 }, { "id" : "minecraft:redstone_lamp", - "blockRuntimeId" : 6643 + "blockRuntimeId" : 6649 }, { "id" : "minecraft:sealantern", - "blockRuntimeId" : 6824 + "blockRuntimeId" : 6831 }, { "id" : "minecraft:oak_sign" @@ -4171,15 +4170,15 @@ }, { "id" : "minecraft:conduit", - "blockRuntimeId" : 3675 + "blockRuntimeId" : 3676 }, { "id" : "minecraft:stonecutter_block", - "blockRuntimeId" : 7291 + "blockRuntimeId" : 7292 }, { "id" : "minecraft:end_portal_frame", - "blockRuntimeId" : 4730 + "blockRuntimeId" : 4731 }, { "id" : "minecraft:coal" @@ -4315,11 +4314,11 @@ }, { "id" : "minecraft:end_rod", - "blockRuntimeId" : 4738 + "blockRuntimeId" : 4739 }, { "id" : "minecraft:lightning_rod", - "blockRuntimeId" : 5515 + "blockRuntimeId" : 5517 }, { "id" : "minecraft:end_crystal" @@ -4781,15 +4780,15 @@ }, { "id" : "minecraft:rail", - "blockRuntimeId" : 6564 + "blockRuntimeId" : 6570 }, { "id" : "minecraft:golden_rail", - "blockRuntimeId" : 4976 + "blockRuntimeId" : 4978 }, { "id" : "minecraft:detector_rail", - "blockRuntimeId" : 4461 + "blockRuntimeId" : 4462 }, { "id" : "minecraft:activator_rail", @@ -4812,23 +4811,23 @@ }, { "id" : "minecraft:redstone_block", - "blockRuntimeId" : 6642 + "blockRuntimeId" : 6648 }, { "id" : "minecraft:redstone_torch", - "blockRuntimeId" : 6645 + "blockRuntimeId" : 6651 }, { "id" : "minecraft:lever", - "blockRuntimeId" : 5441 + "blockRuntimeId" : 5443 }, { "id" : "minecraft:wooden_button", - "blockRuntimeId" : 7839 + "blockRuntimeId" : 7841 }, { "id" : "minecraft:spruce_button", - "blockRuntimeId" : 6962 + "blockRuntimeId" : 6963 }, { "id" : "minecraft:birch_button", @@ -4836,42 +4835,42 @@ }, { "id" : "minecraft:jungle_button", - "blockRuntimeId" : 5208 + "blockRuntimeId" : 5210 }, { "id" : "minecraft:acacia_button" }, { "id" : "minecraft:dark_oak_button", - "blockRuntimeId" : 3936 + "blockRuntimeId" : 3937 }, { "id" : "minecraft:stone_button", - "blockRuntimeId" : 7191 + "blockRuntimeId" : 7192 }, { "id" : "minecraft:crimson_button", - "blockRuntimeId" : 3771 + "blockRuntimeId" : 3772 }, { "id" : "minecraft:warped_button", - "blockRuntimeId" : 7526 + "blockRuntimeId" : 7528 }, { "id" : "minecraft:polished_blackstone_button", - "blockRuntimeId" : 5998 + "blockRuntimeId" : 6004 }, { "id" : "minecraft:tripwire_hook", - "blockRuntimeId" : 7397 + "blockRuntimeId" : 7398 }, { "id" : "minecraft:wooden_pressure_plate", - "blockRuntimeId" : 7883 + "blockRuntimeId" : 7885 }, { "id" : "minecraft:spruce_pressure_plate", - "blockRuntimeId" : 7022 + "blockRuntimeId" : 7023 }, { "id" : "minecraft:birch_pressure_plate", @@ -4879,7 +4878,7 @@ }, { "id" : "minecraft:jungle_pressure_plate", - "blockRuntimeId" : 5268 + "blockRuntimeId" : 5270 }, { "id" : "minecraft:acacia_pressure_plate", @@ -4887,39 +4886,39 @@ }, { "id" : "minecraft:dark_oak_pressure_plate", - "blockRuntimeId" : 3996 + "blockRuntimeId" : 3997 }, { "id" : "minecraft:crimson_pressure_plate", - "blockRuntimeId" : 3840 + "blockRuntimeId" : 3841 }, { "id" : "minecraft:warped_pressure_plate", - "blockRuntimeId" : 7595 + "blockRuntimeId" : 7597 }, { "id" : "minecraft:stone_pressure_plate", - "blockRuntimeId" : 7203 + "blockRuntimeId" : 7204 }, { "id" : "minecraft:light_weighted_pressure_plate", - "blockRuntimeId" : 5499 + "blockRuntimeId" : 5501 }, { "id" : "minecraft:heavy_weighted_pressure_plate", - "blockRuntimeId" : 5095 + "blockRuntimeId" : 5097 }, { "id" : "minecraft:polished_blackstone_pressure_plate", - "blockRuntimeId" : 6012 + "blockRuntimeId" : 6018 }, { "id" : "minecraft:observer", - "blockRuntimeId" : 5722 + "blockRuntimeId" : 5726 }, { "id" : "minecraft:daylight_detector", - "blockRuntimeId" : 4066 + "blockRuntimeId" : 4067 }, { "id" : "minecraft:repeater" @@ -4932,30 +4931,30 @@ }, { "id" : "minecraft:dropper", - "blockRuntimeId" : 4588 + "blockRuntimeId" : 4589 }, { "id" : "minecraft:dispenser", - "blockRuntimeId" : 4489 + "blockRuntimeId" : 4490 }, { "id" : "minecraft:piston", - "blockRuntimeId" : 5783 + "blockRuntimeId" : 5789 }, { "id" : "minecraft:sticky_piston", - "blockRuntimeId" : 7165 + "blockRuntimeId" : 7166 }, { "id" : "minecraft:tnt", - "blockRuntimeId" : 7349 + "blockRuntimeId" : 7350 }, { "id" : "minecraft:name_tag" }, { "id" : "minecraft:loom", - "blockRuntimeId" : 5581 + "blockRuntimeId" : 5583 }, { "id" : "minecraft:banner" @@ -5046,6 +5045,9 @@ { "id" : "minecraft:piglin_banner_pattern" }, + { + "id" : "minecraft:globe_banner_pattern" + }, { "id" : "minecraft:firework_rocket", "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwAAAAAAAQYARmxpZ2h0AQAA" @@ -5198,7 +5200,7 @@ }, { "id" : "minecraft:target", - "blockRuntimeId" : 7347 + "blockRuntimeId" : 7348 }, { "id" : "minecraft:lodestone_compass" diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_17_30.json b/core/src/main/resources/bedrock/runtime_item_states.1_18_10.json similarity index 98% rename from core/src/main/resources/bedrock/runtime_item_states.1_17_30.json rename to core/src/main/resources/bedrock/runtime_item_states.1_18_10.json index 79690e3da..5bebcaf99 100644 --- a/core/src/main/resources/bedrock/runtime_item_states.1_17_30.json +++ b/core/src/main/resources/bedrock/runtime_item_states.1_18_10.json @@ -51,6 +51,10 @@ "name" : "minecraft:air", "id" : -158 }, + { + "name" : "minecraft:allay_spawn_egg", + "id" : 631 + }, { "name" : "minecraft:allow", "id" : 210 @@ -65,7 +69,7 @@ }, { "name" : "minecraft:amethyst_shard", - "id" : 623 + "id" : 625 }, { "name" : "minecraft:ancient_debris", @@ -117,7 +121,7 @@ }, { "name" : "minecraft:balloon", - "id" : 597 + "id" : 598 }, { "name" : "minecraft:bamboo", @@ -133,7 +137,7 @@ }, { "name" : "minecraft:banner_pattern", - "id" : 627 + "id" : 635 }, { "name" : "minecraft:barrel", @@ -293,7 +297,7 @@ }, { "name" : "minecraft:bleach", - "id" : 595 + "id" : 596 }, { "name" : "minecraft:blue_candle", @@ -317,7 +321,7 @@ }, { "name" : "minecraft:boat", - "id" : 625 + "id" : 633 }, { "name" : "minecraft:bone", @@ -429,11 +433,11 @@ }, { "name" : "minecraft:camera", - "id" : 592 + "id" : 593 }, { "name" : "minecraft:campfire", - "id" : 588 + "id" : 589 }, { "name" : "minecraft:candle", @@ -493,7 +497,7 @@ }, { "name" : "minecraft:chain", - "id" : 617 + "id" : 619 }, { "name" : "minecraft:chain_command_block", @@ -575,6 +579,10 @@ "name" : "minecraft:clay_ball", "id" : 384 }, + { + "name" : "minecraft:client_request_placeholder_block", + "id" : -465 + }, { "name" : "minecraft:clock", "id" : 393 @@ -669,7 +677,7 @@ }, { "name" : "minecraft:compound", - "id" : 593 + "id" : 594 }, { "name" : "minecraft:concrete", @@ -793,7 +801,7 @@ }, { "name" : "minecraft:crimson_door", - "id" : 614 + "id" : 616 }, { "name" : "minecraft:crimson_double_slab", @@ -833,7 +841,7 @@ }, { "name" : "minecraft:crimson_sign", - "id" : 612 + "id" : 614 }, { "name" : "minecraft:crimson_slab", @@ -1169,7 +1177,7 @@ }, { "name" : "minecraft:dye", - "id" : 626 + "id" : 634 }, { "name" : "minecraft:egg", @@ -1697,7 +1705,7 @@ }, { "name" : "minecraft:end_crystal", - "id" : 629 + "id" : 637 }, { "name" : "minecraft:end_gateway", @@ -1803,6 +1811,10 @@ "name" : "minecraft:fire_charge", "id" : 509 }, + { + "name" : "minecraft:firefly_spawn_egg", + "id" : 632 + }, { "name" : "minecraft:firework_rocket", "id" : 519 @@ -1855,6 +1867,14 @@ "name" : "minecraft:frame", "id" : 513 }, + { + "name" : "minecraft:frog_egg", + "id" : -468 + }, + { + "name" : "minecraft:frog_spawn_egg", + "id" : 628 + }, { "name" : "minecraft:frosted_ice", "id" : 207 @@ -1891,13 +1911,17 @@ "name" : "minecraft:glistering_melon_slice", "id" : 434 }, + { + "name" : "minecraft:globe_banner_pattern", + "id" : 588 + }, { "name" : "minecraft:glow_berries", - "id" : 630 + "id" : 638 }, { "name" : "minecraft:glow_frame", - "id" : 621 + "id" : 623 }, { "name" : "minecraft:glow_ink_sac", @@ -1913,7 +1937,7 @@ }, { "name" : "minecraft:glow_stick", - "id" : 166 + "id" : 601 }, { "name" : "minecraft:glowingobsidian", @@ -1929,7 +1953,7 @@ }, { "name" : "minecraft:goat_horn", - "id" : 622 + "id" : 624 }, { "name" : "minecraft:goat_spawn_egg", @@ -2109,11 +2133,11 @@ }, { "name" : "minecraft:honey_bottle", - "id" : 591 + "id" : 592 }, { "name" : "minecraft:honeycomb", - "id" : 590 + "id" : 591 }, { "name" : "minecraft:honeycomb_block", @@ -2141,7 +2165,7 @@ }, { "name" : "minecraft:ice_bomb", - "id" : 594 + "id" : 595 }, { "name" : "minecraft:infested_deepslate", @@ -2569,7 +2593,7 @@ }, { "name" : "minecraft:lodestone_compass", - "id" : 600 + "id" : 602 }, { "name" : "minecraft:log", @@ -2613,7 +2637,7 @@ }, { "name" : "minecraft:medicine", - "id" : 598 + "id" : 599 }, { "name" : "minecraft:medium_amethyst_bud", @@ -2723,9 +2747,13 @@ "name" : "minecraft:music_disc_mellohi", "id" : 540 }, + { + "name" : "minecraft:music_disc_otherside", + "id" : 627 + }, { "name" : "minecraft:music_disc_pigstep", - "id" : 618 + "id" : 620 }, { "name" : "minecraft:music_disc_stal", @@ -2751,6 +2779,14 @@ "name" : "minecraft:mycelium", "id" : 110 }, + { + "name" : "minecraft:mysterious_frame", + "id" : -466 + }, + { + "name" : "minecraft:mysterious_frame_slot", + "id" : -467 + }, { "name" : "minecraft:name_tag", "id" : 548 @@ -2777,7 +2813,7 @@ }, { "name" : "minecraft:nether_sprouts", - "id" : 619 + "id" : 621 }, { "name" : "minecraft:nether_star", @@ -2797,7 +2833,7 @@ }, { "name" : "minecraft:netherite_axe", - "id" : 605 + "id" : 607 }, { "name" : "minecraft:netherite_block", @@ -2805,43 +2841,43 @@ }, { "name" : "minecraft:netherite_boots", - "id" : 610 + "id" : 612 }, { "name" : "minecraft:netherite_chestplate", - "id" : 608 + "id" : 610 }, { "name" : "minecraft:netherite_helmet", - "id" : 607 - }, - { - "name" : "minecraft:netherite_hoe", - "id" : 606 - }, - { - "name" : "minecraft:netherite_ingot", - "id" : 601 - }, - { - "name" : "minecraft:netherite_leggings", "id" : 609 }, { - "name" : "minecraft:netherite_pickaxe", - "id" : 604 + "name" : "minecraft:netherite_hoe", + "id" : 608 }, { - "name" : "minecraft:netherite_scrap", - "id" : 611 - }, - { - "name" : "minecraft:netherite_shovel", + "name" : "minecraft:netherite_ingot", "id" : 603 }, + { + "name" : "minecraft:netherite_leggings", + "id" : 611 + }, + { + "name" : "minecraft:netherite_pickaxe", + "id" : 606 + }, + { + "name" : "minecraft:netherite_scrap", + "id" : 613 + }, + { + "name" : "minecraft:netherite_shovel", + "id" : 605 + }, { "name" : "minecraft:netherite_sword", - "id" : 602 + "id" : 604 }, { "name" : "minecraft:netherrack", @@ -2887,6 +2923,10 @@ "name" : "minecraft:ocelot_spawn_egg", "id" : 451 }, + { + "name" : "minecraft:ochre_froglight", + "id" : -471 + }, { "name" : "minecraft:orange_candle", "id" : -414 @@ -2943,6 +2983,10 @@ "name" : "minecraft:parrot_spawn_egg", "id" : 478 }, + { + "name" : "minecraft:pearlescent_froglight", + "id" : -469 + }, { "name" : "minecraft:phantom_membrane", "id" : 574 @@ -3257,7 +3301,7 @@ }, { "name" : "minecraft:rapid_fertilizer", - "id" : 596 + "id" : 597 }, { "name" : "minecraft:ravager_spawn_egg", @@ -3577,7 +3621,7 @@ }, { "name" : "minecraft:soul_campfire", - "id" : 620 + "id" : 622 }, { "name" : "minecraft:soul_fire", @@ -3601,11 +3645,11 @@ }, { "name" : "minecraft:sparkler", - "id" : 599 + "id" : 600 }, { "name" : "minecraft:spawn_egg", - "id" : 628 + "id" : 636 }, { "name" : "minecraft:spider_eye", @@ -3669,7 +3713,7 @@ }, { "name" : "minecraft:spyglass", - "id" : 624 + "id" : 626 }, { "name" : "minecraft:squid_spawn_egg", @@ -3829,7 +3873,7 @@ }, { "name" : "minecraft:suspicious_stew", - "id" : 589 + "id" : 590 }, { "name" : "minecraft:sweet_berries", @@ -3839,6 +3883,14 @@ "name" : "minecraft:sweet_berry_bush", "id" : -207 }, + { + "name" : "minecraft:tadpole_bucket", + "id" : 630 + }, + { + "name" : "minecraft:tadpole_spawn_egg", + "id" : 629 + }, { "name" : "minecraft:tallgrass", "id" : 31 @@ -3943,6 +3995,10 @@ "name" : "minecraft:unpowered_repeater", "id" : 93 }, + { + "name" : "minecraft:verdant_froglight", + "id" : -470 + }, { "name" : "minecraft:vex_spawn_egg", "id" : 476 @@ -3977,7 +4033,7 @@ }, { "name" : "minecraft:warped_door", - "id" : 615 + "id" : 617 }, { "name" : "minecraft:warped_double_slab", @@ -3997,7 +4053,7 @@ }, { "name" : "minecraft:warped_fungus_on_a_stick", - "id" : 616 + "id" : 618 }, { "name" : "minecraft:warped_hyphae", @@ -4021,7 +4077,7 @@ }, { "name" : "minecraft:warped_sign", - "id" : 613 + "id" : 615 }, { "name" : "minecraft:warped_slab", From e73a4efe606c4531769f4c045bb6cc20ef061550 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 7 Feb 2022 12:38:28 -0500 Subject: [PATCH 22/26] Fix build; bump to 2.0.1-SNAPSHOT --- ap/pom.xml | 4 ++-- api/base/pom.xml | 2 +- api/geyser/pom.xml | 4 ++-- api/pom.xml | 2 +- bootstrap/bungeecord/pom.xml | 4 ++-- bootstrap/pom.xml | 4 ++-- bootstrap/spigot/pom.xml | 4 ++-- bootstrap/sponge/pom.xml | 4 ++-- bootstrap/standalone/pom.xml | 4 ++-- bootstrap/velocity/pom.xml | 4 ++-- common/pom.xml | 2 +- core/pom.xml | 10 +++++----- pom.xml | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ap/pom.xml b/ap/pom.xml index cc282dd55..dce28a7d7 100644 --- a/ap/pom.xml +++ b/ap/pom.xml @@ -6,9 +6,9 @@ org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT ap - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT \ No newline at end of file diff --git a/api/base/pom.xml b/api/base/pom.xml index 0d7ed05da..17edb1a85 100644 --- a/api/base/pom.xml +++ b/api/base/pom.xml @@ -5,7 +5,7 @@ org.geysermc api-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT 4.0.0 diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml index 89349e8ac..26f4ddf30 100644 --- a/api/geyser/pom.xml +++ b/api/geyser/pom.xml @@ -5,7 +5,7 @@ org.geysermc api-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT 4.0.0 @@ -26,7 +26,7 @@ org.geysermc base-api - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/api/pom.xml b/api/pom.xml index b3d0262ea..bc70be6c3 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT api-parent diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 9dcd0943e..45a08c7db 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-bungeecord @@ -14,7 +14,7 @@ org.geysermc core - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 3b0bdda55..58c651455 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-parent pom @@ -34,7 +34,7 @@ org.geysermc ap - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT provided diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 5aa2c59cf..6eda527f3 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-spigot @@ -25,7 +25,7 @@ org.geysermc core - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index fa7989b43..ab3b7d970 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-sponge @@ -14,7 +14,7 @@ org.geysermc core - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml index 00c0410e4..881c87e6c 100644 --- a/bootstrap/standalone/pom.xml +++ b/bootstrap/standalone/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-standalone @@ -18,7 +18,7 @@ org.geysermc core - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index e1e3331ef..ff052471d 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT bootstrap-velocity @@ -14,7 +14,7 @@ org.geysermc core - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile diff --git a/common/pom.xml b/common/pom.xml index 8e7be26f4..fde2605bc 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT common diff --git a/core/pom.xml b/core/pom.xml index c2e8b5f5b..f2812fea0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT core @@ -21,19 +21,19 @@ org.geysermc ap - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT provided org.geysermc geyser-api - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile org.geysermc common - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT compile @@ -121,7 +121,7 @@ com.github.CloudburstMC.Protocol bedrock-v486 - v1.18.10-c2c5a7069f-1 + 0cd24c0 compile diff --git a/pom.xml b/pom.xml index 004d58666..a528dcf98 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.geysermc geyser-parent - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT pom Geyser Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers. From b0e91275dc517e41ee2d793e12fc40193da96481 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Tue, 8 Feb 2022 19:02:02 +0100 Subject: [PATCH 23/26] Updated the supported versions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4b375a5c..593514e52 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.17.30 - 1.17.41 + 1.18.0 - 1.18.2 and Minecraft Java 1.18/1.18.1. +### Currently supporting Minecraft Bedrock 1.17.41 + 1.18.0 - 1.18.10 and Minecraft Java 1.18/1.18.1. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set up Geyser. From 0eed6025a6cea3e5a197976ed8f2b68120c2918a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 8 Feb 2022 22:57:03 -0500 Subject: [PATCH 24/26] Remove protocol checks referencing 1.17.30 These are now unnecessary. --- .../geyser/entity/type/ItemFrameEntity.java | 9 +++------ .../java/level/JavaLevelEventTranslator.java | 19 +++++-------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java index 9139e0a99..69aac5a26 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java @@ -37,12 +37,11 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; -import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import lombok.Getter; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; import java.util.UUID; @@ -85,10 +84,8 @@ public class ItemFrameEntity extends Entity { .putInt("version", session.getBlockMappings().getBlockStateVersion()); NbtMapBuilder statesBuilder = NbtMap.builder() .putInt("facing_direction", direction.ordinal()) - .putByte("item_frame_map_bit", (byte) 0); - if (session.getUpstream().getProtocolVersion() >= Bedrock_v465.V465_CODEC.getProtocolVersion()) { - statesBuilder.putByte("item_frame_photo_bit", (byte) 0); - } + .putByte("item_frame_map_bit", (byte) 0) + .putByte("item_frame_photo_bit", (byte) 0); blockBuilder.put("states", statesBuilder.build()); bedrockRuntimeId = session.getBlockMappings().getItemFrame(blockBuilder.build()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java index b24c7a363..2271388c2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java @@ -25,14 +25,7 @@ package org.geysermc.geyser.translator.protocol.java.level; -import com.github.steveice10.mc.protocol.data.game.level.event.BonemealGrowEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.BreakBlockEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.BreakPotionEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.ComposterEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.DragonFireballEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.ParticleEvent; -import com.github.steveice10.mc.protocol.data.game.level.event.RecordEventData; -import com.github.steveice10.mc.protocol.data.game.level.event.SmokeEventData; +import com.github.steveice10.mc.protocol.data.game.level.event.*; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLevelEventPacket; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; @@ -40,14 +33,13 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; import com.nukkitx.protocol.bedrock.packet.TextPacket; -import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; +import org.geysermc.geyser.translator.level.event.LevelEventTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.level.event.LevelEventTranslator; -import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.text.MinecraftLocale; import java.util.Collections; import java.util.Locale; @@ -218,8 +210,7 @@ public class JavaLevelEventTranslator extends PacketTranslator effectPacket.setType(LevelEventType.PARTICLE_EYE_OF_ENDER_DEATH); case MOB_SPAWN -> effectPacket.setType(LevelEventType.PARTICLE_MOB_BLOCK_SPAWN); // TODO: Check, but I don't think I really verified this ever went into effect on Java case BONEMEAL_GROW_WITH_SOUND, BONEMEAL_GROW -> { - effectPacket.setType((particleEvent == ParticleEvent.BONEMEAL_GROW - && session.getUpstream().getProtocolVersion() >= Bedrock_v465.V465_CODEC.getProtocolVersion()) ? LevelEventType.PARTICLE_TURTLE_EGG : LevelEventType.PARTICLE_CROP_GROWTH); + effectPacket.setType(particleEvent == ParticleEvent.BONEMEAL_GROW ? LevelEventType.PARTICLE_TURTLE_EGG : LevelEventType.PARTICLE_CROP_GROWTH); BonemealGrowEventData growEventData = (BonemealGrowEventData) packet.getData(); effectPacket.setData(growEventData.getParticleCount()); From 0479af7cd3ef13edc632c3b88ab5ddee94326d6b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 8 Feb 2022 23:15:41 -0500 Subject: [PATCH 25/26] Restore ClickPlan double-simulation This is used in some autocrafting situations. Oops. --- .../geysermc/geyser/inventory/click/ClickPlan.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 45d535167..b0cca53d9 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -75,6 +75,11 @@ public final class ClickPlan { gridSize = translator.getGridSize(); } + private void resetSimulation() { + this.simulatedItems.clear(); + this.simulatedCursor = session.getPlayerInventory().getCursor().copy(); + } + public void add(Click click, int slot) { add(click, slot, false); } @@ -89,10 +94,14 @@ public final class ClickPlan { ClickAction action = new ClickAction(click, slot, force); plan.add(action); + // RUNNING THE SIMULATION HERE IS IMPORTANT. The contents of the simulation are used in complex, multi-stage tasks + // such as autocrafting. + simulateAction(action); } public void execute(boolean refresh) { //update geyser inventory after simulation to avoid net id desync + resetSimulation(); ListIterator planIter = plan.listIterator(); while (planIter.hasNext()) { ClickAction action = planIter.next(); @@ -190,7 +199,9 @@ public final class ClickPlan { * Does not need to be called for the cursor */ private void onSlotItemChange(int slot, GeyserItemStack itemStack) { - changedItems.put(slot, itemStack.getItemStack()); + if (changedItems != null) { + changedItems.put(slot, itemStack.getItemStack()); + } } private void simulateAction(ClickAction action) { From 559c5d655ad2570fe6f2bdd8a392928db2f94a25 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 9 Feb 2022 15:08:58 -0500 Subject: [PATCH 26/26] Allow smithing recipes to work again in 1.18.10 --- .../java/JavaUpdateRecipesTranslator.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index 7a346dce1..c3c8abfb4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -31,12 +31,14 @@ import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; +import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; +import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -76,6 +78,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator= Bedrock_v486.V486_CODEC.getProtocolVersion(); + Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion())); Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); @@ -128,6 +132,27 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator { + // Required to translate these as of 1.18.10, or else they cannot be crafted + if (!applySmithingRecipes) { + continue; + } + + SmithingRecipeData recipeData = (SmithingRecipeData) recipe.getData(); + ItemData output = ItemTranslator.translateToBedrock(session, recipeData.getResult()); + for (ItemStack base : recipeData.getBase().getOptions()) { + ItemData bedrockBase = ItemTranslator.translateToBedrock(session, base); + + for (ItemStack addition : recipeData.getAddition().getOptions()) { + ItemData bedrockAddition = ItemTranslator.translateToBedrock(session, addition); + + UUID uuid = UUID.randomUUID(); + craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(), + Arrays.asList(bedrockBase, bedrockAddition), + Collections.singletonList(output), uuid, "smithing_table", 2, netId++)); + } + } + } default -> { List craftingData = recipeTypes.get(recipe.getType()); if (craftingData != null) {